added iOS source code
authorPawel Dabrowski <p.dabrowski@lightcoding.com>
Fri, 21 Dec 2018 16:52:10 +0000 (17:52 +0100)
committerPawel Dabrowski <p.dabrowski@lightcoding.com>
Fri, 21 Dec 2018 16:52:10 +0000 (17:52 +0100)
1604 files changed:
iOS/Podfile [new file with mode: 0644]
iOS/Podfile.lock [new file with mode: 0644]
iOS/Pods/AEXML/LICENSE [new file with mode: 0644]
iOS/Pods/AEXML/README.md [new file with mode: 0644]
iOS/Pods/AEXML/Sources/Document.swift [new file with mode: 0644]
iOS/Pods/AEXML/Sources/Element.swift [new file with mode: 0644]
iOS/Pods/AEXML/Sources/Error.swift [new file with mode: 0644]
iOS/Pods/AEXML/Sources/Options.swift [new file with mode: 0644]
iOS/Pods/AEXML/Sources/Parser.swift [new file with mode: 0644]
iOS/Pods/Alamofire/LICENSE [new file with mode: 0644]
iOS/Pods/Alamofire/README.md [new file with mode: 0644]
iOS/Pods/Alamofire/Source/AFError.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/Alamofire.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/MultipartFormData.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/NetworkReachabilityManager.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/Notifications.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/ParameterEncoding.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/Request.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/Response.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/ResponseSerialization.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/Result.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/ServerTrustPolicy.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/SessionDelegate.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/SessionManager.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/TaskDelegate.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/Timeline.swift [new file with mode: 0644]
iOS/Pods/Alamofire/Source/Validation.swift [new file with mode: 0644]
iOS/Pods/AlamofireActivityLogger/LICENSE [new file with mode: 0644]
iOS/Pods/AlamofireActivityLogger/README.md [new file with mode: 0644]
iOS/Pods/AlamofireActivityLogger/alamofire_activity_logger/ActivityLogger/LoggeableRequest.swift [new file with mode: 0644]
iOS/Pods/AlamofireActivityLogger/alamofire_activity_logger/ActivityLogger/Logger.swift [new file with mode: 0644]
iOS/Pods/AlamofireActivityLogger/alamofire_activity_logger/ActivityLogger/Tools.swift [new file with mode: 0644]
iOS/Pods/Crashlytics/Crashlytics.framework/README [new file with mode: 0644]
iOS/Pods/Crashlytics/Crashlytics.framework/submit [new file with mode: 0755]
iOS/Pods/Crashlytics/README.md [new file with mode: 0644]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics [new file with mode: 0755]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h [new file with mode: 0644]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h [new file with mode: 0644]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h [new file with mode: 0644]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h [new file with mode: 0644]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h [new file with mode: 0644]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h [new file with mode: 0644]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h [new file with mode: 0644]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist [new file with mode: 0644]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap [new file with mode: 0644]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/run [new file with mode: 0755]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/submit [new file with mode: 0755]
iOS/Pods/Crashlytics/iOS/Crashlytics.framework/uploadDSYM [new file with mode: 0755]
iOS/Pods/Crashlytics/submit [new file with mode: 0755]
iOS/Pods/Fabric/Fabric.framework/README [new file with mode: 0644]
iOS/Pods/Fabric/Fabric.framework/run [new file with mode: 0755]
iOS/Pods/Fabric/README.md [new file with mode: 0644]
iOS/Pods/Fabric/iOS/Fabric.framework/Fabric [new file with mode: 0755]
iOS/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h [new file with mode: 0644]
iOS/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h [new file with mode: 0644]
iOS/Pods/Fabric/iOS/Fabric.framework/Info.plist [new file with mode: 0644]
iOS/Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap [new file with mode: 0644]
iOS/Pods/Fabric/iOS/Fabric.framework/run [new file with mode: 0755]
iOS/Pods/Fabric/iOS/Fabric.framework/uploadDSYM [new file with mode: 0755]
iOS/Pods/Fabric/run [new file with mode: 0755]
iOS/Pods/Fabric/upload-symbols [new file with mode: 0755]
iOS/Pods/Fabric/uploadDSYM [new file with mode: 0755]
iOS/Pods/Firebase/CoreOnly/Sources/Firebase.h [new file with mode: 0755]
iOS/Pods/Firebase/CoreOnly/Sources/module.modulemap [new file with mode: 0755]
iOS/Pods/Firebase/README.md [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/Modules/module.modulemap [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Modules/module.modulemap [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics [new file with mode: 0755]
iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/Modules/module.modulemap [new file with mode: 0755]
iOS/Pods/FirebaseCore/Firebase/Core/FIRAnalyticsConfiguration.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRApp.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRAppAssociationRegistration.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRBundleUtil.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRComponent.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRComponentContainer.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRComponentType.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRConfiguration.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRDependency.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRErrors.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRLogger.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIROptions.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/FIRVersion.m [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRAppAssociationRegistration.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRAppInternal.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRBundleUtil.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponent.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentContainer.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentContainerInternal.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentRegistrant.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentType.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRCoreConfigurable.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRDependency.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRErrorCode.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRErrors.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRLogger.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIROptionsInternal.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRVersion.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRAnalyticsConfiguration.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRApp.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRConfiguration.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRLoggerLevel.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Public/FIROptions.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/Firebase/Core/Public/FirebaseCore.h [new file with mode: 0644]
iOS/Pods/FirebaseCore/LICENSE [new file with mode: 0644]
iOS/Pods/FirebaseCore/README.md [new file with mode: 0644]
iOS/Pods/FirebaseInstanceID/CHANGELOG.md [new file with mode: 0755]
iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/FirebaseInstanceID [new file with mode: 0755]
iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/Headers/FIRInstanceID.h [new file with mode: 0755]
iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/Headers/FirebaseInstanceID.h [new file with mode: 0755]
iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/Modules/module.modulemap [new file with mode: 0755]
iOS/Pods/FirebaseInstanceID/README.md [new file with mode: 0755]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMMessageCode.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessaging.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCheckinService.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCheckinService.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingClient.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingClient.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCodedInputStream.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCodedInputStream.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConnection.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConnection.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConstants.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConstants.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingContextManagerService.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingContextManagerService.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDataMessageManager.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDataMessageManager.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDefines.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDelayedMessageQueue.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDelayedMessageQueue.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingLogger.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingLogger.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPacketQueue.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPacketQueue.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPendingTopicsList.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPendingTopicsList.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPersistentSyncMessage.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPersistentSyncMessage.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSub.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSub.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSubRegistrar.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSubRegistrar.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingReceiver.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingReceiver.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRegistrar.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRegistrar.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmq2PersistentStore.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmqManager.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmqManager.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSecureSocket.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSecureSocket.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSyncMessageManager.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSyncMessageManager.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingTopicOperation.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingTopicOperation.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingTopicsCommon.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingUtilities.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingUtilities.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingVersionUtilities.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingVersionUtilities.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessaging_Private.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/FirebaseMessaging.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/InternalHeaders/FIRMessagingInternalUtilities.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSDictionary+FIRMessaging.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSDictionary+FIRMessaging.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSError+FIRMessaging.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSError+FIRMessaging.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkCore.pbobjc.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkCore.pbobjc.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkExtensions.pbobjc.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkExtensions.pbobjc.m [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/Public/FIRMessaging.h [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/Firebase/Messaging/Public/FirebaseMessaging.h [new file with mode: 0755]
iOS/Pods/FirebaseMessaging/LICENSE [new file with mode: 0644]
iOS/Pods/FirebaseMessaging/README.md [new file with mode: 0644]
iOS/Pods/FolioReaderKit/LICENSE [new file with mode: 0644]
iOS/Pods/FolioReaderKit/README.md [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/EPUBCore/FRBook.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/EPUBCore/FREpubParser.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/EPUBCore/FRMetadata.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/EPUBCore/FRResource.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/EPUBCore/FRResources.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/EPUBCore/FRSmilElement.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/EPUBCore/FRSmils.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/EPUBCore/FRSpine.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/EPUBCore/FRTocReference.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/EPUBCore/MediaType.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Extensions.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/FolioReaderAudioPlayer.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/FolioReaderCenter.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/FolioReaderChapterList.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/FolioReaderChapterListCell.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/FolioReaderConfig.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/FolioReaderContainer.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/FolioReaderFontsMenu.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/FolioReaderHighlightList.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/FolioReaderKit.h [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/FolioReaderKit.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/FolioReaderPage.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/FolioReaderPageIndicator.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/FolioReaderPlayerMenu.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/FolioReaderQuoteShare.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/FolioReaderSharingProvider.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/FolioReaderUserDefaults.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/FolioReaderWebView.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Models/Highlight+Helper.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Models/Highlight.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/PageViewController.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/QuoteImage.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Bridge.js [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Bold.otf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-BoldItalic.otf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Italic.otf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Regular.otf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Bold.ttf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-BoldItalic.ttf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Italic.ttf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Regular.ttf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Bold.ttf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-BoldItalic.ttf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Italic.ttf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Regular.ttf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Bold.ttf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-BoldItalic.ttf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Italic.ttf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Regular.ttf [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/blue-marker.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/blue-marker@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/border-dashed-pattern.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/border-dashed-pattern.imageset/border-dashed-pattern.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/green-marker.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/green-marker@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-camera.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-camera.imageset/icon-camera.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-logo.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-logo.imageset/icon-logo.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-horizontal.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-horizontal.imageset/icon-menu-horizontal.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-vertical.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-vertical.imageset/icon-menu-vertical.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-close.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-close.imageset/icon-navbar-close.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-font.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-font.imageset/icon-navbar-font.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-search.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-search.imageset/icon-navbar-search.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-share.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-share.imageset/icon-navbar-share.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-toc.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-toc.imageset/icon-navbar-toc.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-tts.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-tts.imageset/icon-navbar-tts.pdf [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/pink-marker.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/pink-marker@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker@3x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/underline-marker.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/underline-marker@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/Contents.json [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/yellow-marker.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/yellow-marker@2x.png [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Source/Resources/Style.css [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Source/ScrollScrubber.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Vendor/HAControls/HADiscreteSlider.swift [new file with mode: 0644]
iOS/Pods/FolioReaderKit/Vendor/SMSegmentView/SMSegment.swift [new file with mode: 0755]
iOS/Pods/FolioReaderKit/Vendor/SMSegmentView/SMSegmentView.swift [new file with mode: 0755]
iOS/Pods/FontBlaster/LICENSE [new file with mode: 0755]
iOS/Pods/FontBlaster/README.md [new file with mode: 0755]
iOS/Pods/FontBlaster/Sources/FontBlaster.swift [new file with mode: 0644]
iOS/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement [new file with mode: 0755]
iOS/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/Modules/module.modulemap [new file with mode: 0755]
iOS/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Common/GULLoggerCodes.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Logger/Private/GULLogger.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GULLoggerLevel.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/GULSwizzler.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/Private/GULOriginalIMPConvenienceMacros.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/Private/GULSwizzler.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/NSData+zlib/GULNSData+zlib.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/NSData+zlib/GULNSData+zlib.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULMutableDictionary.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULNetwork.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkConstants.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkURLSession.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULMutableDictionary.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetwork.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkConstants.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkLoggerProtocol.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkMessageCode.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkURLSession.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityChecker.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/Private/GULReachabilityChecker.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/Private/GULReachabilityMessageCode.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/UserDefaults/GULUserDefaults.m [new file with mode: 0644]
iOS/Pods/GoogleUtilities/GoogleUtilities/UserDefaults/Private/GULUserDefaults.h [new file with mode: 0644]
iOS/Pods/GoogleUtilities/LICENSE [new file with mode: 0644]
iOS/Pods/GoogleUtilities/README.md [new file with mode: 0644]
iOS/Pods/Headers/Private/Firebase/Firebase.h [new symlink]
iOS/Pods/Headers/Public/Firebase/Firebase.h [new symlink]
iOS/Pods/JSQWebViewController/LICENSE [new file with mode: 0644]
iOS/Pods/JSQWebViewController/README.md [new file with mode: 0644]
iOS/Pods/JSQWebViewController/Source/WebViewController.swift [new file with mode: 0644]
iOS/Pods/Kingfisher/LICENSE [new file with mode: 0644]
iOS/Pods/Kingfisher/README.md [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/AnimatedImageView.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/Box.swift [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/CacheSerializer.swift [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/Filter.swift [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/FormatIndicatedCacheSerializer.swift [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/Image.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/ImageCache.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/ImageDownloader.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/ImageModifier.swift [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/ImagePrefetcher.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/ImageProcessor.swift [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/ImageTransition.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/Indicator.swift [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/Kingfisher.h [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/Kingfisher.swift [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/KingfisherManager.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/Placeholder.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/RequestModifier.swift [new file with mode: 0644]
iOS/Pods/Kingfisher/Sources/Resource.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/String+MD5.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/ThreadHelper.swift [new file with mode: 0755]
iOS/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift [new file with mode: 0755]
iOS/Pods/MBProgressHUD/LICENSE [new file with mode: 0644]
iOS/Pods/MBProgressHUD/MBProgressHUD.h [new file with mode: 0644]
iOS/Pods/MBProgressHUD/MBProgressHUD.m [new file with mode: 0644]
iOS/Pods/MBProgressHUD/README.mdown [new file with mode: 0644]
iOS/Pods/MZDownloadManager/LICENSE [new file with mode: 0644]
iOS/Pods/MZDownloadManager/MZDownloadManager/Classes/MZDownloadManager.swift [new file with mode: 0644]
iOS/Pods/MZDownloadManager/MZDownloadManager/Classes/MZDownloadModel.swift [new file with mode: 0644]
iOS/Pods/MZDownloadManager/MZDownloadManager/Classes/MZUtility.swift [new file with mode: 0644]
iOS/Pods/MZDownloadManager/README.md [new file with mode: 0644]
iOS/Pods/Manifest.lock [new file with mode: 0644]
iOS/Pods/MatomoTracker/LICENSE.md [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/Application.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/CustomDimension.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/CustomVariable.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/Device.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/Dispatcher.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/Event.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/EventSerializer.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/Locale+HttpAcceptLanguage.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/Logger.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/MainThread.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/MatomoTracker.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/MatomoUserDefaults.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/MemoryQueue.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/Queue.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/Session.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/URLSessionDispatcher.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/MatomoTracker/Visitor.swift [new file with mode: 0644]
iOS/Pods/MatomoTracker/README.md [new file with mode: 0644]
iOS/Pods/MenuItemKit/LICENSE [new file with mode: 0644]
iOS/Pods/MenuItemKit/MenuItemKit/Internals.swift [new file with mode: 0644]
iOS/Pods/MenuItemKit/MenuItemKit/MenuItemKit.h [new file with mode: 0644]
iOS/Pods/MenuItemKit/MenuItemKit/Swizzlings.m [new file with mode: 0644]
iOS/Pods/MenuItemKit/MenuItemKit/Swizzlings.swift [new file with mode: 0644]
iOS/Pods/MenuItemKit/MenuItemKit/UIMenuItem.swift [new file with mode: 0644]
iOS/Pods/MenuItemKit/README.md [new file with mode: 0644]
iOS/Pods/OAuthSwift/LICENSE [new file with mode: 0644]
iOS/Pods/OAuthSwift/README.md [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/Collection+OAuthSwift.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/Data+OAuthSwift.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/Dictionary+OAuthSwift.swift [new file with mode: 0755]
iOS/Pods/OAuthSwift/Sources/HMAC.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/Int+OAuthSwift.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/NSError+OAuthSwift.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/NotificationCenter+OAuthSwift.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuth1Swift.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuth2Swift.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuthSwift.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuthSwiftClient.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuthSwiftCredential.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuthSwiftError.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuthSwiftHTTPRequest.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuthSwiftMultipartData.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuthSwiftResponse.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuthSwiftURLHandlerType.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/OAuthWebViewController.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/Objc.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/SHA1.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/String+OAuthSwift.swift [new file with mode: 0755]
iOS/Pods/OAuthSwift/Sources/UIApplication+OAuthSwift.swift [new file with mode: 0644]
iOS/Pods/OAuthSwift/Sources/URL+OAuthSwift.swift [new file with mode: 0755]
iOS/Pods/OAuthSwift/Sources/Utils.swift [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/project.pbxproj [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/AEXML.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Alamofire.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/AlamofireActivityLogger.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FirebaseCore.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FirebaseMessaging.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FolioReaderKit.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FontBlaster.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/GoogleUtilities.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/JSQWebViewController.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Kingfisher.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MBProgressHUD.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MZDownloadManager.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MatomoTracker.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MenuItemKit.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/OAuthSwift.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Pods-WolneLektury.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Protobuf.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Realm.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/RealmSwift.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/SSZipArchive.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/SideMenu.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/SwiftKeychainWrapper.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Toast-Swift.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/ZFDragableModalTransition.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/nanopb.xcscheme [new file with mode: 0644]
iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/xcschememanagement.plist [new file with mode: 0644]
iOS/Pods/Protobuf/LICENSE [new file with mode: 0644]
iOS/Pods/Protobuf/README.md [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBArray.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBArray.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBArray_PackagePrivate.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBBootstrap.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBCodedInputStream.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBCodedInputStream.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBCodedInputStream_PackagePrivate.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBCodedOutputStream.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBCodedOutputStream.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBCodedOutputStream_PackagePrivate.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBDescriptor.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBDescriptor.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBDescriptor_PackagePrivate.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBDictionary.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBDictionary.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBDictionary_PackagePrivate.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBExtensionInternals.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBExtensionInternals.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBExtensionRegistry.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBExtensionRegistry.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBMessage.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBMessage.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBMessage_PackagePrivate.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBProtocolBuffers.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBProtocolBuffers_RuntimeSupport.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBRootObject.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBRootObject.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBRootObject_PackagePrivate.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBRuntimeTypes.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBUnknownField.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBUnknownField.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBUnknownFieldSet.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBUnknownFieldSet.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBUnknownFieldSet_PackagePrivate.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBUnknownField_PackagePrivate.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBUtilities.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBUtilities.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBUtilities_PackagePrivate.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBWellKnownTypes.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBWellKnownTypes.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBWireFormat.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/GPBWireFormat.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Any.pbobjc.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Any.pbobjc.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Api.pbobjc.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Api.pbobjc.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Duration.pbobjc.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Duration.pbobjc.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Empty.pbobjc.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Empty.pbobjc.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/FieldMask.pbobjc.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/FieldMask.pbobjc.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/SourceContext.pbobjc.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/SourceContext.pbobjc.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Struct.pbobjc.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Struct.pbobjc.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Timestamp.pbobjc.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Timestamp.pbobjc.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Type.pbobjc.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Type.pbobjc.m [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Wrappers.pbobjc.h [new file with mode: 0644]
iOS/Pods/Protobuf/objectivec/google/protobuf/Wrappers.pbobjc.m [new file with mode: 0644]
iOS/Pods/Realm/LICENSE [new file with mode: 0644]
iOS/Pods/Realm/README.md [new file with mode: 0644]
iOS/Pods/Realm/Realm/NSError+RLMSync.m [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/binding_callback_thread_observer.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/object_notifier.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/primitive_list_notifier.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/index_set.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/list.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/object.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/object_store.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/placeholder.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/results.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/schema.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/network_reachability_observer.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/system_configuration.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_metadata.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/sync/partial_sync.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_config.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_manager.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_permission.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_session.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_user.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/thread_safe_reference.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/ObjectStore/src/util/uuid.cpp [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMAccessor.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMAnalytics.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMArray.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMClassInfo.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMCollection.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMConstants.m [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMJSONModels.m [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMListBase.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMManagedArray.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMMigration.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMNetworkClient.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMObject.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMObjectBase.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMObjectSchema.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMObjectStore.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMObservation.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMOptionalBase.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMPredicateUtil.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMProperty.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMQueryUtil.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMRealm+Sync.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMRealm.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMRealmConfiguration.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMRealmUtil.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMResults.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSchema.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSwiftSupport.m [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSyncConfiguration.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSyncCredentials.m [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSyncManager.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSyncPermission.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSyncPermissionResults.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSyncSession.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSyncSessionRefreshHandle.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSyncUser.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMSyncUtil.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMThreadSafeReference.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMUpdateChecker.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/RLMUtil.mm [new file with mode: 0644]
iOS/Pods/Realm/Realm/Realm.modulemap [new file with mode: 0644]
iOS/Pods/Realm/build.sh [new file with mode: 0755]
iOS/Pods/Realm/core/librealmcore-ios.a [new file with mode: 0644]
iOS/Pods/Realm/include/NSError+RLMSync.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMAccessor.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMAccessor.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMAnalytics.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMArray.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMArray_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMArray_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMClassInfo.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMCollection.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMCollection_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMCollection_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMConstants.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMJSONModels.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMListBase.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMMigration.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMMigration_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMNetworkClient.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObject.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObjectBase.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObjectBase_Dynamic.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObjectBase_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObjectSchema.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObjectSchema_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObjectSchema_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObjectStore.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObject_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObject_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMObservation.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMOptionalBase.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMPlatform.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMPredicateUtil.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMPrefix.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMProperty.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMProperty_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMProperty_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMQueryUtil.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMRealm+Sync.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMRealm.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMRealmConfiguration+Sync.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMRealmConfiguration.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMRealmConfiguration_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMRealmConfiguration_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMRealmUtil.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMRealm_Dynamic.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMRealm_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMRealm_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMResults.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMResults_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMResults_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSchema.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSchema_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSchema_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSwiftBridgingHeader.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSwiftSupport.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncConfiguration.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncConfiguration_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncConfiguration_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncCredentials.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncManager.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncManager_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncPermission.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncPermissionResults.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncPermission_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncSession.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncSession_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncUser.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncUser_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncUtil.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncUtil_Private.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMSyncUtil_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMThreadSafeReference.h [new file with mode: 0644]
iOS/Pods/Realm/include/RLMThreadSafeReference_Private.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMUpdateChecker.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/RLMUtil.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/Realm.h [new file with mode: 0644]
iOS/Pods/Realm/include/binding_callback_thread_observer.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/binding_context.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/collection_notifications.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/alloc.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/alloc_slab.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/array.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/array_basic.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/array_basic_tpl.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/array_binary.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/array_blob.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/array_blobs_big.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/array_direct.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/array_integer.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/array_string.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/array_string_long.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/binary_data.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/bptree.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/chunked_binary.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_backlink.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_binary.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_fwd.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_link.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_linkbase.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_linklist.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_mixed.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_mixed_tpl.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_string.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_string_enum.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_table.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_timestamp.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_tpl.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_type.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/column_type_traits.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/data_type.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/descriptor.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/descriptor_fwd.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/disable_sync_to_disk.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/exceptions.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/group.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/group_shared.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/group_shared_options.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/group_writer.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/handover_defs.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/history.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/impl/array_writer.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/impl/clamped_hex_dump.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/impl/cont_transact_hist.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/impl/destroy_guard.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/impl/input_stream.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/impl/output_stream.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/impl/sequential_getter.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/impl/simulated_failure.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/impl/transact_log.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/index_string.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/lang_bind_helper.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/link_view.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/link_view_fwd.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/metrics/metric_timer.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/metrics/metrics.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/metrics/query_info.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/metrics/transaction_info.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/mixed.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/null.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/olddatetime.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/owned_data.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/parser/parser.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/parser/query_builder.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/query.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/query_conditions.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/query_engine.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/query_expression.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/query_operators.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/realm_nmmintrin.h [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/replication.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/row.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/spec.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/string_data.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/changeset.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/changeset_cooker.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/changeset_encoder.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/changeset_parser.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/client.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/crypto.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/crypto_server.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/feature_token.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/history.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/instruction_applier.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/instruction_replication.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/instructions.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/metrics.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/object.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/object_id.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/protocol.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/server.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/server_configuration.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/transform.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/sync/version.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/table.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/table_ref.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/table_view.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/timestamp.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/unicode.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/aes_cryptor.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/any.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/assert.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/base64.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/basic_system_errors.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/bind_ptr.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/buffer.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/buffer_stream.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/call_with_tuple.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/cf_ptr.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/compression.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/config.h [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/encrypted_file_mapping.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/errno.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/features.h [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/file.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/file_mapper.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/hex_dump.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/http.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/inspect.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/interprocess_condvar.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/interprocess_mutex.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/json_parser.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/logger.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/memory_stream.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/misc_errors.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/miscellaneous.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/network.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/network_ssl.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/optional.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/overload.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/priority_queue.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/random.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/safe_int_ops.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/scope_exit.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/serializer.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/shared_ptr.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/string_buffer.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/terminate.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/thread.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/time.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/to_string.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/type_list.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/type_traits.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/uri.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/utf8.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/util/websocket.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/utilities.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/version.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/version_id.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/core/realm/views.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/execution_context_id.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/feature_checks.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/apple/external_commit_helper.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/apple/keychain_helper.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/collection_change_builder.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/collection_notifier.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/external_commit_helper.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/list_notifier.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/notification_wrapper.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/object_accessor_impl.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/object_notifier.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/primitive_list_notifier.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/realm_coordinator.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/results_notifier.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/transact_log_handler.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/impl/weak_realm_notifier.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/index_set.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/list.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/object.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/object_accessor.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/object_schema.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/object_store.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/property.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/results.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/schema.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/shared_realm.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/impl/apple/network_reachability_observer.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/impl/apple/system_configuration.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/impl/network_reachability.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/impl/sync_client.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/impl/sync_file.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/impl/sync_metadata.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/partial_sync.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/sync_config.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/sync_manager.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/sync_permission.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/sync_session.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/sync/sync_user.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/thread_safe_reference.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/util/aligned_union.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/util/apple/event_loop_signal.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/util/atomic_shared_ptr.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/util/event_loop_signal.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/util/tagged_bool.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/util/time.hpp [new file with mode: 0644]
iOS/Pods/Realm/include/util/uuid.hpp [new file with mode: 0644]
iOS/Pods/RealmSwift/LICENSE [new file with mode: 0644]
iOS/Pods/RealmSwift/README.md [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Aliases.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Error.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/LinkingObjects.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/List.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Migration.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Object.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/ObjectSchema.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/ObjectiveCSupport.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Optional.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Property.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Realm.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/RealmCollection.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/RealmConfiguration.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Results.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Schema.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/SortDescriptor.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/SwiftVersion.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Sync.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/ThreadSafeReference.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/RealmSwift/Util.swift [new file with mode: 0644]
iOS/Pods/RealmSwift/build.sh [new file with mode: 0755]
iOS/Pods/SSZipArchive/LICENSE.txt [new file with mode: 0644]
iOS/Pods/SSZipArchive/README.md [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/SSZipArchive.h [new file with mode: 0755]
iOS/Pods/SSZipArchive/SSZipArchive/SSZipArchive.m [new file with mode: 0755]
iOS/Pods/SSZipArchive/SSZipArchive/SSZipCommon.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/ZipArchive.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aes.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aes_ni.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aes_ni.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aescrypt.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aeskey.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aesopt.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aestab.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aestab.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/brg_endian.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/brg_types.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/fileenc.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/fileenc.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/hmac.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/hmac.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/prng.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/prng.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/pwd2key.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/pwd2key.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/sha1.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/sha1.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/crypt.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/crypt.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi.c [new file with mode: 0755]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi.h [new file with mode: 0755]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_buf.c [new file with mode: 0755]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_buf.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_mem.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_mem.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/minishared.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/minishared.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/unzip.c [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/unzip.h [new file with mode: 0644]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/zip.c [new file with mode: 0755]
iOS/Pods/SSZipArchive/SSZipArchive/minizip/zip.h [new file with mode: 0644]
iOS/Pods/SideMenu/LICENSE [new file with mode: 0644]
iOS/Pods/SideMenu/Pod/Classes/SideMenuManager.swift [new file with mode: 0644]
iOS/Pods/SideMenu/Pod/Classes/SideMenuTransition.swift [new file with mode: 0644]
iOS/Pods/SideMenu/Pod/Classes/UISideMenuNavigationController.swift [new file with mode: 0644]
iOS/Pods/SideMenu/Pod/Classes/UITableViewVibrantCell.swift [new file with mode: 0644]
iOS/Pods/SideMenu/README.md [new file with mode: 0644]
iOS/Pods/SwiftKeychainWrapper/LICENSE [new file with mode: 0644]
iOS/Pods/SwiftKeychainWrapper/README.md [new file with mode: 0644]
iOS/Pods/SwiftKeychainWrapper/SwiftKeychainWrapper/KeychainItemAccessibility.swift [new file with mode: 0644]
iOS/Pods/SwiftKeychainWrapper/SwiftKeychainWrapper/KeychainWrapper.swift [new file with mode: 0644]
iOS/Pods/SwiftKeychainWrapper/SwiftKeychainWrapper/SwiftKeychainWrapper.h [new file with mode: 0644]
iOS/Pods/Target Support Files/AEXML/AEXML-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/AEXML/AEXML-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/AEXML/AEXML-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/AEXML/AEXML.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/AEXML/AEXML.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/AEXML/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/Alamofire/Alamofire-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/Alamofire/Alamofire.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/Alamofire/Alamofire.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/Alamofire/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/AlamofireActivityLogger/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/FolioReaderKit/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/FontBlaster/FontBlaster-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/FontBlaster/FontBlaster-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/FontBlaster/FontBlaster-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/FontBlaster/FontBlaster.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/FontBlaster/FontBlaster.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/FontBlaster/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/GoogleUtilities/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/JSQWebViewController/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/Kingfisher/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/MBProgressHUD/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/MZDownloadManager/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/MatomoTracker/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/MenuItemKit/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/OAuthSwift/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/Pods-WolneLektury/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-acknowledgements.markdown [new file with mode: 0644]
iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-acknowledgements.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-frameworks.sh [new file with mode: 0755]
iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-resources.sh [new file with mode: 0755]
iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.debug.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.release.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/Protobuf/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/Protobuf/Protobuf-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/Protobuf/Protobuf-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/Protobuf/Protobuf-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/Protobuf/Protobuf.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/Protobuf/Protobuf.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/Realm/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/Realm/Realm-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/Realm/Realm-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/Realm/Realm.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/Realm/Realm.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/RealmSwift/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/RealmSwift/RealmSwift-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/RealmSwift/RealmSwift-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/RealmSwift/RealmSwift-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/RealmSwift/RealmSwift.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/RealmSwift/RealmSwift.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/SSZipArchive/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/SideMenu/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/SideMenu/SideMenu-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/SideMenu/SideMenu-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/SideMenu/SideMenu-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/SideMenu/SideMenu.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/SideMenu/SideMenu.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/SwiftKeychainWrapper/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/Toast-Swift/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/ZFDragableModalTransition/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition.xcconfig [new file with mode: 0644]
iOS/Pods/Target Support Files/nanopb/Info.plist [new file with mode: 0644]
iOS/Pods/Target Support Files/nanopb/nanopb-dummy.m [new file with mode: 0644]
iOS/Pods/Target Support Files/nanopb/nanopb-prefix.pch [new file with mode: 0644]
iOS/Pods/Target Support Files/nanopb/nanopb-umbrella.h [new file with mode: 0644]
iOS/Pods/Target Support Files/nanopb/nanopb.modulemap [new file with mode: 0644]
iOS/Pods/Target Support Files/nanopb/nanopb.xcconfig [new file with mode: 0644]
iOS/Pods/Toast-Swift/LICENSE [new file with mode: 0644]
iOS/Pods/Toast-Swift/README.md [new file with mode: 0644]
iOS/Pods/Toast-Swift/Toast/Toast.swift [new file with mode: 0644]
iOS/Pods/ZFDragableModalTransition/Classes/ZFModalTransitionAnimator.h [new file with mode: 0644]
iOS/Pods/ZFDragableModalTransition/Classes/ZFModalTransitionAnimator.m [new file with mode: 0644]
iOS/Pods/ZFDragableModalTransition/LICENSE [new file with mode: 0644]
iOS/Pods/ZFDragableModalTransition/README.md [new file with mode: 0644]
iOS/Pods/nanopb/LICENSE.txt [new file with mode: 0644]
iOS/Pods/nanopb/README.md [new file with mode: 0644]
iOS/Pods/nanopb/pb.h [new file with mode: 0644]
iOS/Pods/nanopb/pb_common.c [new file with mode: 0644]
iOS/Pods/nanopb/pb_common.h [new file with mode: 0644]
iOS/Pods/nanopb/pb_decode.c [new file with mode: 0644]
iOS/Pods/nanopb/pb_decode.h [new file with mode: 0644]
iOS/Pods/nanopb/pb_encode.c [new file with mode: 0644]
iOS/Pods/nanopb/pb_encode.h [new file with mode: 0644]
iOS/Settings.bundle/Pods-acknowledgements.plist [new file with mode: 0644]
iOS/Settings.bundle/Root.plist [new file with mode: 0644]
iOS/Settings.bundle/en.lproj/Root.strings [new file with mode: 0644]
iOS/WolneLektury.xcodeproj/project.pbxproj [new file with mode: 0644]
iOS/WolneLektury.xcodeproj/project.xcworkspace/contents.xcworkspacedata [new file with mode: 0644]
iOS/WolneLektury.xcodeproj/project.xcworkspace/xcuserdata/pawel.xcuserdatad/UserInterfaceState.xcuserstate [new file with mode: 0644]
iOS/WolneLektury.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/WolneLektury.xcscheme [new file with mode: 0644]
iOS/WolneLektury.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/WolneLektury_release.xcscheme [new file with mode: 0644]
iOS/WolneLektury.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/xcschememanagement.plist [new file with mode: 0644]
iOS/WolneLektury.xcworkspace/contents.xcworkspacedata [new file with mode: 0644]
iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/IDEFindNavigatorScopes.plist [new file with mode: 0644]
iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/UserInterfaceState.xcuserstate [new file with mode: 0644]
iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist [new file with mode: 0644]
iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/xcdebugger/Expressions.xcexplist [new file with mode: 0644]
iOS/WolneLektury/AppDelegate.swift [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-76.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40 copy.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-41.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-42.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-iPadPro@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_about.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_library.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_news.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/share.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/share.imageset/share.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/share.imageset/share@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/share.imageset/share@3x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/Contents.json [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo@2x.png [new file with mode: 0644]
iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo@3x.png [new file with mode: 0644]
iOS/WolneLektury/Base.lproj/LaunchScreen.storyboard [new file with mode: 0644]
iOS/WolneLektury/Base.lproj/Main.storyboard [new file with mode: 0644]
iOS/WolneLektury/Config.swift [new file with mode: 0644]
iOS/WolneLektury/Connection/DownloadManager.swift [new file with mode: 0644]
iOS/WolneLektury/Connection/NetworkService.swift [new file with mode: 0644]
iOS/WolneLektury/Connection/RestAction.swift [new file with mode: 0644]
iOS/WolneLektury/Connection/Routes.swift [new file with mode: 0644]
iOS/WolneLektury/Connection/SyncManager.swift [new file with mode: 0644]
iOS/WolneLektury/Constants.swift [new file with mode: 0644]
iOS/WolneLektury/Controls/ActivityIndicatorButton.swift [new file with mode: 0644]
iOS/WolneLektury/Controls/ProgressButton.swift [new file with mode: 0644]
iOS/WolneLektury/Controls/ProgressLabel.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/DateFormatter+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/Dictionary+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/MatomoTracker+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/NSAttributedString+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/NSObject+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/String+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/UIButton+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/UIColor+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/UIImage+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/UITableView+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/UIView+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Extensions/UIViewController+Ext.swift [new file with mode: 0644]
iOS/WolneLektury/Info.plist [new file with mode: 0644]
iOS/WolneLektury/MainNavigator.swift [new file with mode: 0644]
iOS/WolneLektury/Model/BookDetailsModel.swift [new file with mode: 0644]
iOS/WolneLektury/Model/BookModel.swift [new file with mode: 0644]
iOS/WolneLektury/Model/CategoryModel.swift [new file with mode: 0644]
iOS/WolneLektury/Model/Credentials.swift [new file with mode: 0644]
iOS/WolneLektury/Model/FragmentModel.swift [new file with mode: 0644]
iOS/WolneLektury/Model/LikeModel.swift [new file with mode: 0644]
iOS/WolneLektury/Model/MediaModel.swift [new file with mode: 0644]
iOS/WolneLektury/Model/NewsModel.swift [new file with mode: 0644]
iOS/WolneLektury/Model/OAuthTokenModel.swift [new file with mode: 0644]
iOS/WolneLektury/Model/Parameters/FilterBooksParameters.swift [new file with mode: 0644]
iOS/WolneLektury/Model/ReadingStateModel.swift [new file with mode: 0644]
iOS/WolneLektury/Model/Realm/RLMApplication.swift [new file with mode: 0644]
iOS/WolneLektury/Model/Realm/RLMUser.swift [new file with mode: 0644]
iOS/WolneLektury/Model/UsernameModel.swift [new file with mode: 0644]
iOS/WolneLektury/Reader/WLReaderConfig.swift [new file with mode: 0644]
iOS/WolneLektury/Resources/Font/Roboto/Roboto-Bold.ttf [new file with mode: 0644]
iOS/WolneLektury/Resources/Font/Roboto/Roboto-Light.ttf [new file with mode: 0644]
iOS/WolneLektury/Resources/Font/Roboto/Roboto-Medium.ttf [new file with mode: 0644]
iOS/WolneLektury/Resources/Font/Roboto/Roboto-Regular.ttf [new file with mode: 0644]
iOS/WolneLektury/Resources/Font/Roboto/Roboto-Thin.ttf [new file with mode: 0644]
iOS/WolneLektury/Screens/About/AboutViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Authorization/AuthorizationWebViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/BookDetailsViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsButtonTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsButtonTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsFragmentTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsFragmentTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsHeaderTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsHeaderTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsInfoTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsInfoTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsSeparatorTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsSeparatorTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/BookList/BookListViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/Cells/BookTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/Cells/BookTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/Cells/WLTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/Cells/WLTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/ListViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/MainNavigationViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/MainViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/Views/BookDescriptionView.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/Views/BookDescriptionView.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/Views/BookImageOverlayView.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/Views/BookImageOverlayView.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/Views/DesignableXibView.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/WLNavigationController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/WLSideMenuNavigationController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Common/WLViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/DownloadedList/DownloadedListViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Filter/Cells/FilterCollectionViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Filter/Cells/FilterCollectionViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Filter/Cells/FilterOnlyLecturesReusableView.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Filter/Cells/FilterOnlyLecturesReusableView.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Filter/Cells/FilterSectionHeaderCollectionReusableView.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Filter/Cells/FilterSectionHeaderCollectionReusableView.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Filter/FilterHeaderView.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Filter/FilterViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Library/Cells/BecomeFriendTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Library/Cells/BecomeFriendTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Library/Cells/BookCollectionViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Library/Cells/BookCollectionViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Library/Cells/LibraryCollectionTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Library/Cells/LibraryCollectionTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Library/Cells/LibraryEarlyAccessTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Library/Cells/LibraryEarlyAccessTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Library/LibraryViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Login/LoginViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Menu/Cells/MenuBottomTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Menu/Cells/MenuBottomTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Menu/Cells/MenuLineTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Menu/Cells/MenuLineTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Menu/Cells/MenuSupportUsTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Menu/Cells/MenuSupportUsTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Menu/Cells/MenuTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Menu/Cells/MenuTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Menu/MenuItem.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Menu/MenuViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/News/Cells/NewsTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/News/Cells/NewsTableViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/News/NewsViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/NewsDetails/Cells/SimpleGalleryCollectionViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/NewsDetails/Cells/SimpleGalleryCollectionViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/NewsDetails/Gallery/Cells/GalleryCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/NewsDetails/Gallery/GalleryViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/NewsDetails/NewsDetailsViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Player/PlayerController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Player/PlayerItemTableViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Player/PlayerViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Search/Cells/SearchFilterCollectionViewCell.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Search/Cells/SearchFilterCollectionViewCell.xib [new file with mode: 0644]
iOS/WolneLektury/Screens/Search/SearchFiltersManager.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Search/SearchViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/Settings/SettingsViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Screens/SupportUs/SupportUsViewController.swift [new file with mode: 0644]
iOS/WolneLektury/Utils/DatabaseManager.swift [new file with mode: 0644]
iOS/WolneLektury/Utils/SharedGlobals.swift [new file with mode: 0644]
iOS/WolneLektury/WolneLektury.entitlements [new file with mode: 0644]
iOS/WolneLektury/en.lproj/Localizable.strings [new file with mode: 0644]
iOS/WolneLekturyTests/Info.plist [new file with mode: 0644]
iOS/WolneLekturyTests/WolneLekturyTests.swift [new file with mode: 0644]
iOS/WolneLekturyUITests/Info.plist [new file with mode: 0644]
iOS/WolneLekturyUITests/WolneLekturyUITests.swift [new file with mode: 0644]

diff --git a/iOS/Podfile b/iOS/Podfile
new file mode 100644 (file)
index 0000000..27d81e7
--- /dev/null
@@ -0,0 +1,30 @@
+platform :ios, '9.0'
+use_frameworks!
+
+def shared_pods
+  pod 'Alamofire'
+#  pod 'AlamofireLogger'
+pod 'AlamofireActivityLogger'
+  # pod 'SwiftyJSON'
+  # pod 'ObjectMapper'
+  pod 'SwiftKeychainWrapper'
+  pod 'MBProgressHUD'
+  # pod 'IQKeyboardManagerSwift'
+  pod 'Kingfisher'
+  pod 'Fabric'
+  pod 'Crashlytics'
+  # pod 'RealmSwift'
+  # pod 'Toast-Swift'
+  pod 'FolioReaderKit'
+  pod 'SideMenu'
+  pod 'Toast-Swift'
+  pod 'MZDownloadManager'
+  pod 'OAuthSwift'
+  pod 'MatomoTracker'
+  pod 'Firebase/Core'
+  pod 'Firebase/Messaging'
+end
+
+target 'WolneLektury' do
+    shared_pods
+end
diff --git a/iOS/Podfile.lock b/iOS/Podfile.lock
new file mode 100644 (file)
index 0000000..4338c7c
--- /dev/null
@@ -0,0 +1,180 @@
+PODS:
+  - AEXML (4.2.2)
+  - Alamofire (4.6.0)
+  - AlamofireActivityLogger (2.4.0):
+    - Alamofire (~> 4.5)
+  - Crashlytics (3.10.0):
+    - Fabric (~> 1.7.3)
+  - Fabric (1.7.3)
+  - Firebase/Core (5.9.0):
+    - Firebase/CoreOnly
+    - FirebaseAnalytics (= 5.2.0)
+  - Firebase/CoreOnly (5.9.0):
+    - FirebaseCore (= 5.1.4)
+  - Firebase/Messaging (5.9.0):
+    - Firebase/CoreOnly
+    - FirebaseMessaging (= 3.1.2)
+  - FirebaseAnalytics (5.2.0):
+    - FirebaseCore (~> 5.1)
+    - FirebaseInstanceID (~> 3.2)
+    - GoogleAppMeasurement (~> 5.2)
+    - GoogleUtilities/AppDelegateSwizzler (~> 5.2)
+    - GoogleUtilities/MethodSwizzler (~> 5.2)
+    - GoogleUtilities/Network (~> 5.2)
+    - "GoogleUtilities/NSData+zlib (~> 5.2)"
+    - nanopb (~> 0.3)
+  - FirebaseCore (5.1.4):
+    - GoogleUtilities/Logger (~> 5.2)
+  - FirebaseInstanceID (3.2.2):
+    - FirebaseCore (~> 5.1)
+    - GoogleUtilities/Environment (~> 5.3)
+    - GoogleUtilities/UserDefaults (~> 5.3)
+  - FirebaseMessaging (3.1.2):
+    - FirebaseCore (~> 5.0)
+    - FirebaseInstanceID (~> 3.0)
+    - GoogleUtilities/Reachability (~> 5.2)
+    - Protobuf (~> 3.1)
+  - FolioReaderKit (1.3.0):
+    - AEXML (= 4.2.2)
+    - FontBlaster (= 4.0.1)
+    - JSQWebViewController (= 6.0.0)
+    - MenuItemKit (= 3.0.0)
+    - RealmSwift (= 3.1.1)
+    - SSZipArchive (= 2.1.1)
+    - ZFDragableModalTransition (= 0.6)
+  - FontBlaster (4.0.1)
+  - GoogleAppMeasurement (5.2.0):
+    - GoogleUtilities/AppDelegateSwizzler (~> 5.2)
+    - GoogleUtilities/MethodSwizzler (~> 5.2)
+    - GoogleUtilities/Network (~> 5.2)
+    - "GoogleUtilities/NSData+zlib (~> 5.2)"
+    - nanopb (~> 0.3)
+  - GoogleUtilities/AppDelegateSwizzler (5.3.0):
+    - GoogleUtilities/Environment
+    - GoogleUtilities/Logger
+    - GoogleUtilities/Network
+  - GoogleUtilities/Environment (5.3.0)
+  - GoogleUtilities/Logger (5.3.0):
+    - GoogleUtilities/Environment
+  - GoogleUtilities/MethodSwizzler (5.3.0):
+    - GoogleUtilities/Logger
+  - GoogleUtilities/Network (5.3.0):
+    - GoogleUtilities/Logger
+    - "GoogleUtilities/NSData+zlib"
+    - GoogleUtilities/Reachability
+  - "GoogleUtilities/NSData+zlib (5.3.0)"
+  - GoogleUtilities/Reachability (5.3.0):
+    - GoogleUtilities/Logger
+  - GoogleUtilities/UserDefaults (5.3.0):
+    - GoogleUtilities/Logger
+  - JSQWebViewController (6.0.0)
+  - Kingfisher (4.7.0)
+  - MatomoTracker (5.2.0):
+    - MatomoTracker/Core (= 5.2.0)
+  - MatomoTracker/Core (5.2.0)
+  - MBProgressHUD (1.1.0)
+  - MenuItemKit (3.0.0)
+  - MZDownloadManager (3.4)
+  - nanopb (0.3.8):
+    - nanopb/decode (= 0.3.8)
+    - nanopb/encode (= 0.3.8)
+  - nanopb/decode (0.3.8)
+  - nanopb/encode (0.3.8)
+  - OAuthSwift (1.2.2)
+  - Protobuf (3.6.1)
+  - Realm (3.1.1):
+    - Realm/Headers (= 3.1.1)
+  - Realm/Headers (3.1.1)
+  - RealmSwift (3.1.1):
+    - Realm (= 3.1.1)
+  - SideMenu (3.1.5)
+  - SSZipArchive (2.1.1)
+  - SwiftKeychainWrapper (3.0.1)
+  - Toast-Swift (3.0.1)
+  - ZFDragableModalTransition (0.6)
+
+DEPENDENCIES:
+  - Alamofire
+  - AlamofireActivityLogger
+  - Crashlytics
+  - Fabric
+  - Firebase/Core
+  - Firebase/Messaging
+  - FolioReaderKit
+  - Kingfisher
+  - MatomoTracker
+  - MBProgressHUD
+  - MZDownloadManager
+  - OAuthSwift
+  - SideMenu
+  - SwiftKeychainWrapper
+  - Toast-Swift
+
+SPEC REPOS:
+  https://github.com/cocoapods/specs.git:
+    - AEXML
+    - Alamofire
+    - AlamofireActivityLogger
+    - Crashlytics
+    - Fabric
+    - Firebase
+    - FirebaseAnalytics
+    - FirebaseCore
+    - FirebaseInstanceID
+    - FirebaseMessaging
+    - FolioReaderKit
+    - FontBlaster
+    - GoogleAppMeasurement
+    - GoogleUtilities
+    - JSQWebViewController
+    - Kingfisher
+    - MatomoTracker
+    - MBProgressHUD
+    - MenuItemKit
+    - MZDownloadManager
+    - nanopb
+    - OAuthSwift
+    - Protobuf
+    - Realm
+    - RealmSwift
+    - SideMenu
+    - SSZipArchive
+    - SwiftKeychainWrapper
+    - Toast-Swift
+    - ZFDragableModalTransition
+
+SPEC CHECKSUMS:
+  AEXML: 5ebafc1b75e0bcf0f1b09b8ca8fed2d5a199479b
+  Alamofire: f41a599bd63041760b26d393ec1069d9d7b917f4
+  AlamofireActivityLogger: bff49b2cde8886e1ccb929c699f915472867fdda
+  Crashlytics: a989ef242b66605577a425733797f810bd2725eb
+  Fabric: bb495bb9a7a7677c6d03a1f8b83d95bc49b47e41
+  Firebase: 383fa29aca93e371cab776b48a5c66544d3c2003
+  FirebaseAnalytics: 831f1f127f4a75698e9875a87bf7e2668730d953
+  FirebaseCore: 2a84b6b325792a4319ef71ee18819dcba08d2fd7
+  FirebaseInstanceID: 78ba376fcd5b94c001f9999b2cbd3d1f1e56e78d
+  FirebaseMessaging: d6feeb06218d2675b4149b0ada925a6b707a74cf
+  FolioReaderKit: 162ea6b40cdb26d54730b01bce4a7bb81d4ac961
+  FontBlaster: 84229df8e3a7a6dacb190565bc543747a0c323f9
+  GoogleAppMeasurement: 2b3a023a61239c8d002e6e4fcf4abce8eddce0e0
+  GoogleUtilities: 760ccb53b7c7f40f9c02d8c241f76f841a7a6162
+  JSQWebViewController: 51041569b75d19dbb6b7fe0b7ae053c32409a9be
+  Kingfisher: da6b005aa96d37698e3e4f1ccfe96a5b9bbf27d6
+  MatomoTracker: d178d4847626738303e7ec507425c966e5eb8e85
+  MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
+  MenuItemKit: d9b539b28fdbbd7980d6f3668b9cd6daefeabd9b
+  MZDownloadManager: 8e9f186b2e804ffaeaf782c0cd3e22f27625cb7b
+  nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
+  OAuthSwift: 27b34fe80b76b67cd8f45571e0be2432bc9f6de1
+  Protobuf: 1eb9700044745f00181c136ef21b8ff3ad5a0fd5
+  Realm: 42d1c38a5b1bbcc828b48a7ce702cb86fc68adf4
+  RealmSwift: d31937ca6a6ee54acde64ec839523c0a3c04924b
+  SideMenu: 47dbf9e4d878062d8994aed43f6e4bf6c1fea30b
+  SSZipArchive: 14401ade5f8e82aba1ff03e9f88e9de60937ae60
+  SwiftKeychainWrapper: 38952a3636320ae61bad3513cadd870929de7a4a
+  Toast-Swift: ba0b01b095aada709d915f9beb1d38184a2721c3
+  ZFDragableModalTransition: 0d294eaaba6edfcb9839595de765f9ca06a4b524
+
+PODFILE CHECKSUM: 6437ef7c58988d6cdab4898dcb7ef0e1320aa58a
+
+COCOAPODS: 1.5.3
diff --git a/iOS/Pods/AEXML/LICENSE b/iOS/Pods/AEXML/LICENSE
new file mode 100644 (file)
index 0000000..d171f3f
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2014-2017 Marko Tadić <tadija@me.com> http://tadija.net
+
+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/iOS/Pods/AEXML/README.md b/iOS/Pods/AEXML/README.md
new file mode 100644 (file)
index 0000000..c1ff677
--- /dev/null
@@ -0,0 +1,189 @@
+# AEXML
+**Simple and lightweight XML parser written in Swift**
+
+[![Language Swift 4.0](https://img.shields.io/badge/Language-Swift%204.0-orange.svg?style=flat)](https://swift.org)
+[![Platforms iOS | watchOS | tvOS | OSX](https://img.shields.io/badge/Platforms-iOS%20%7C%20watchOS%20%7C%20tvOS%20%7C%20OS%20X-lightgray.svg?style=flat)](http://www.apple.com)
+[![License MIT](https://img.shields.io/badge/License-MIT-lightgrey.svg?style=flat)](https://github.com/tadija/AEXML/blob/master/LICENSE)
+
+[![CocoaPods Version](https://img.shields.io/cocoapods/v/AEXML.svg?style=flat)](https://cocoapods.org/pods/AEXML)
+[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage)
+[![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager)
+
+> This is not a robust full featured XML parser, but rather simple,  
+lightweight and easy to use utility for casual XML handling.
+
+## Index
+- [Features](#features)
+- [Usage](#usage)
+    - [Read XML](#read-xml)
+    - [Write XML](#write-xml)
+- [Installation](#installation)
+- [License](#license)
+
+## Features
+- **Read XML** data
+- **Write XML** string
+- Covered with [unit tests](https://github.com/tadija/AEXML/blob/master/Tests/AEXMLTests.swift)
+- Covered with inline docs
+
+## Usage
+
+### Read XML
+Let's say this is some XML string you picked up somewhere and made a variable `data: Data` from that.
+
+```xml
+<?xml version="1.0" encoding="utf-8"?>
+<animals>
+    <cats>
+        <cat breed="Siberian" color="lightgray">Tinna</cat>
+        <cat breed="Domestic" color="darkgray">Rose</cat>
+        <cat breed="Domestic" color="yellow">Caesar</cat>
+        <cat></cat>
+    </cats>
+    <dogs>
+        <dog breed="Bull Terrier" color="white">Villy</dog>
+        <dog breed="Bull Terrier" color="white">Spot</dog>
+        <dog breed="Golden Retriever" color="yellow">Betty</dog>
+        <dog breed="Miniature Schnauzer" color="black">Kika</dog>
+    </dogs>
+</animals>
+```
+
+This is how you can use AEXML for working with this data:  
+(for even more examples, look at the unit tests code included in project)
+
+```swift
+guard let
+    let xmlPath = Bundle.main.path(forResource: "example", ofType: "xml"),
+    let data = try? Data(contentsOf: URL(fileURLWithPath: xmlPath))
+else { return }
+
+do {
+    let xmlDoc = try AEXMLDocument(xml: data, options: options)
+        
+    // prints the same XML structure as original
+    print(xmlDoc.xml)
+    
+    // prints cats, dogs
+    for child in xmlDoc.root.children {
+        print(child.name)
+    }
+    
+    // prints Optional("Tinna") (first element)
+    print(xmlDoc.root["cats"]["cat"].value)
+    
+    // prints Tinna (first element)
+    print(xmlDoc.root["cats"]["cat"].string)
+    
+    // prints Optional("Kika") (last element)
+    print(xmlDoc.root["dogs"]["dog"].last?.value)
+    
+    // prints Betty (3rd element)
+    print(xmlDoc.root["dogs"].children[2].string)
+    
+    // prints Tinna, Rose, Caesar
+    if let cats = xmlDoc.root["cats"]["cat"].all {
+        for cat in cats {
+            if let name = cat.value {
+                print(name)
+            }
+        }
+    }
+    
+    // prints Villy, Spot
+    for dog in xmlDoc.root["dogs"]["dog"].all! {
+        if let color = dog.attributes["color"] {
+            if color == "white" {
+                print(dog.string)
+            }
+        }
+    }
+    
+    // prints Tinna
+    if let cats = xmlDoc.root["cats"]["cat"].all(withValue: "Tinna") {
+        for cat in cats {
+            print(cat.string)
+        }
+    }
+    
+    // prints Caesar
+    if let cats = xmlDoc.root["cats"]["cat"].all(withAttributes: ["breed" : "Domestic", "color" : "yellow"]) {
+        for cat in cats {
+            print(cat.string)
+        }
+    }
+    
+    // prints 4
+    print(xmlDoc.root["cats"]["cat"].count)
+    
+    // prints Siberian
+    print(xmlDoc.root["cats"]["cat"].attributes["breed"]!)
+    
+    // prints <cat breed="Siberian" color="lightgray">Tinna</cat>
+    print(xmlDoc.root["cats"]["cat"].xmlCompact)
+    
+    // prints Optional(AEXML.AEXMLError.elementNotFound)
+    print(xmlDoc["NotExistingElement"].error)
+}
+catch {
+    print("\(error)")
+}
+```
+
+### Write XML
+Let's say this is some SOAP XML request you need to generate.  
+Well, you could just build ordinary string for that?
+
+```xml
+<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <soap:Header>
+    <m:Trans xmlns:m="http://www.w3schools.com/transaction/" soap:mustUnderstand="1">234</m:Trans>
+  </soap:Header>
+  <soap:Body>
+    <m:GetStockPrice>
+      <m:StockName>AAPL</m:StockName>
+    </m:GetStockPrice>
+  </soap:Body>
+</soap:Envelope>
+```
+
+Yes, but, you can also do it in a more structured and elegant way with AEXML:
+
+```swift
+// create XML Document
+let soapRequest = AEXMLDocument()
+let attributes = ["xmlns:xsi" : "http://www.w3.org/2001/XMLSchema-instance", "xmlns:xsd" : "http://www.w3.org/2001/XMLSchema"]
+let envelope = soapRequest.addChild(name: "soap:Envelope", attributes: attributes)
+let header = envelope.addChild(name: "soap:Header")
+let body = envelope.addChild(name: "soap:Body")
+header.addChild(name: "m:Trans", value: "234", attributes: ["xmlns:m" : "http://www.w3schools.com/transaction/", "soap:mustUnderstand" : "1"])
+let getStockPrice = body.addChild(name: "m:GetStockPrice")
+getStockPrice.addChild(name: "m:StockName", value: "AAPL")
+
+// prints the same XML structure as original
+print(soapRequest.xml)
+```
+
+## Installation
+
+- [Swift Package Manager](https://swift.org/package-manager/):
+
+       ```
+       .Package(url: "https://github.com/tadija/AEXML.git", majorVersion: 4)
+       ```
+
+- [Carthage](https://github.com/Carthage/Carthage):
+
+       ```ogdl
+       github "tadija/AEXML"
+       ```
+
+- [CocoaPods](http://cocoapods.org/):
+
+       ```ruby
+       pod 'AEXML'
+       ```
+
+## License
+AEXML is released under the MIT license. See [LICENSE](LICENSE) for details.
diff --git a/iOS/Pods/AEXML/Sources/Document.swift b/iOS/Pods/AEXML/Sources/Document.swift
new file mode 100644 (file)
index 0000000..d4e1bc3
--- /dev/null
@@ -0,0 +1,102 @@
+import Foundation
+
+/**
+    This class is inherited from `AEXMLElement` and has a few addons to represent **XML Document**.
+
+    XML Parsing is also done with this object.
+*/
+open class AEXMLDocument: AEXMLElement {
+    
+    // MARK: - Properties
+    
+    /// Root (the first child element) element of XML Document **(Empty element with error if not exists)**.
+    open var root: AEXMLElement {
+        guard let rootElement = children.first else {
+            let errorElement = AEXMLElement(name: "Error")
+            errorElement.error = AEXMLError.rootElementMissing
+            return errorElement
+        }
+        return rootElement
+    }
+    
+    open let options: AEXMLOptions
+    
+    // MARK: - Lifecycle
+    
+    /**
+        Designated initializer - Creates and returns new XML Document object.
+     
+        - parameter root: Root XML element for XML Document (defaults to `nil`).
+        - parameter options: Options for XML Document header and parser settings (defaults to `AEXMLOptions()`).
+    
+        - returns: Initialized XML Document object.
+    */
+    public init(root: AEXMLElement? = nil, options: AEXMLOptions = AEXMLOptions()) {
+        self.options = options
+        
+        let documentName = String(describing: AEXMLDocument.self)
+        super.init(name: documentName)
+        
+        // document has no parent element
+        parent = nil
+        
+        // add root element to document (if any)
+        if let rootElement = root {
+            _ = addChild(rootElement)
+        }
+    }
+    
+    /**
+        Convenience initializer - used for parsing XML data (by calling `loadXMLData:` internally).
+     
+        - parameter xmlData: XML data to parse.
+        - parameter options: Options for XML Document header and parser settings (defaults to `AEXMLOptions()`).
+    
+        - returns: Initialized XML Document object containing parsed data. Throws error if data could not be parsed.
+    */
+    public convenience init(xml: Data, options: AEXMLOptions = AEXMLOptions()) throws {
+        self.init(options: options)
+        try loadXML(xml)
+    }
+    
+    /**
+        Convenience initializer - used for parsing XML string (by calling `init(xmlData:options:)` internally).
+
+        - parameter xmlString: XML string to parse.
+        - parameter encoding: String encoding for creating `Data` from `xmlString` (defaults to `String.Encoding.utf8`)
+        - parameter options: Options for XML Document header and parser settings (defaults to `AEXMLOptions()`).
+
+        - returns: Initialized XML Document object containing parsed data. Throws error if data could not be parsed.
+    */
+    public convenience init(xml: String,
+                            encoding: String.Encoding = String.Encoding.utf8,
+                            options: AEXMLOptions = AEXMLOptions()) throws
+    {
+        guard let data = xml.data(using: encoding) else { throw AEXMLError.parsingFailed }
+        try self.init(xml: data, options: options)
+    }
+    
+    // MARK: - Parse XML
+    
+    /**
+        Creates instance of `AEXMLParser` (private class which is simple wrapper around `XMLParser`)
+        and starts parsing the given XML data. Throws error if data could not be parsed.
+    
+        - parameter data: XML which should be parsed.
+    */
+    open func loadXML(_ data: Data) throws {
+        children.removeAll(keepingCapacity: false)
+        let xmlParser = AEXMLParser(document: self, data: data)
+        try xmlParser.parse()
+    }
+    
+    // MARK: - Override
+    
+    /// Override of `xml` property of `AEXMLElement` - it just inserts XML Document header at the beginning.
+    open override var xml: String {
+        var xml =  "\(options.documentHeader.xmlString)\n"
+        xml += root.xml
+        return xml
+    }
+    
+}
diff --git a/iOS/Pods/AEXML/Sources/Element.swift b/iOS/Pods/AEXML/Sources/Element.swift
new file mode 100644 (file)
index 0000000..3728b6c
--- /dev/null
@@ -0,0 +1,327 @@
+import Foundation
+
+/**
+    This is base class for holding XML structure.
+
+    You can access its structure by using subscript like this: `element["foo"]["bar"]` which would
+    return `<bar></bar>` element from `<element><foo><bar></bar></foo></element>` XML as an `AEXMLElement` object.
+*/
+open class AEXMLElement {
+    
+    // MARK: - Properties
+    
+    /// Every `AEXMLElement` should have its parent element instead of `AEXMLDocument` which parent is `nil`.
+    open internal(set) weak var parent: AEXMLElement?
+    
+    /// Child XML elements.
+    open internal(set) var children = [AEXMLElement]()
+    
+    /// XML Element name.
+    open var name: String
+    
+    /// XML Element value.
+    open var value: String?
+    
+    /// XML Element attributes.
+    open var attributes: [String : String]
+    
+    /// Error value (`nil` if there is no error).
+    open var error: AEXMLError?
+    
+    /// String representation of `value` property (if `value` is `nil` this is empty String).
+    open var string: String { return value ?? String() }
+    
+    /// Boolean representation of `value` property (`nil` if `value` can't be represented as Bool).
+    open var bool: Bool? { return Bool(string) }
+    
+    /// Integer representation of `value` property (`nil` if `value` can't be represented as Integer).
+    open var int: Int? { return Int(string) }
+    
+    /// Double representation of `value` property (`nil` if `value` can't be represented as Double).
+    open var double: Double? { return Double(string) }
+    
+    // MARK: - Lifecycle
+    
+    /**
+        Designated initializer - all parameters are optional.
+    
+        - parameter name: XML element name.
+        - parameter value: XML element value (defaults to `nil`).
+        - parameter attributes: XML element attributes (defaults to empty dictionary).
+    
+        - returns: An initialized `AEXMLElement` object.
+    */
+    public init(name: String, value: String? = nil, attributes: [String : String] = [String : String]()) {
+        self.name = name
+        self.value = value
+        self.attributes = attributes
+    }
+    
+    // MARK: - XML Read
+    
+    /// The first element with given name **(Empty element with error if not exists)**.
+    open subscript(key: String) -> AEXMLElement {
+        guard let
+            first = children.first(where: { $0.name == key })
+        else {
+            let errorElement = AEXMLElement(name: key)
+            errorElement.error = AEXMLError.elementNotFound
+            return errorElement
+        }
+        return first
+    }
+    
+    /// Returns all of the elements with equal name as `self` **(nil if not exists)**.
+    open var all: [AEXMLElement]? { return parent?.children.filter { $0.name == self.name } }
+    
+    /// Returns the first element with equal name as `self` **(nil if not exists)**.
+    open var first: AEXMLElement? { return all?.first }
+    
+    /// Returns the last element with equal name as `self` **(nil if not exists)**.
+    open var last: AEXMLElement? { return all?.last }
+    
+    /// Returns number of all elements with equal name as `self`.
+    open var count: Int { return all?.count ?? 0 }
+    
+    /**
+        Returns all elements with given value.
+        
+        - parameter value: XML element value.
+        
+        - returns: Optional Array of found XML elements.
+    */
+    open func all(withValue value: String) -> [AEXMLElement]? {
+        let found = all?.flatMap {
+            $0.value == value ? $0 : nil
+        }
+        return found
+    }
+    
+    /**
+        Returns all elements containing given attributes.
+
+        - parameter attributes: Array of attribute names.
+
+        - returns: Optional Array of found XML elements.
+    */
+    open func all(containingAttributeKeys keys: [String]) -> [AEXMLElement]? {
+        let found = all?.flatMap { element in
+            keys.reduce(true) { (result, key) in
+                result && Array(element.attributes.keys).contains(key)
+            } ? element : nil
+        }
+        return found
+    }
+    
+    /**
+        Returns all elements with given attributes.
+    
+        - parameter attributes: Dictionary of Keys and Values of attributes.
+    
+        - returns: Optional Array of found XML elements.
+    */
+    open func all(withAttributes attributes: [String : String]) -> [AEXMLElement]? {
+        let keys = Array(attributes.keys)
+        let found = all(containingAttributeKeys: keys)?.flatMap { element in
+            attributes.reduce(true) { (result, attribute) in
+                result && element.attributes[attribute.key] == attribute.value
+            } ? element : nil
+        }
+        return found
+    }
+    
+    /**
+        Returns all descendant elements which satisfy the given predicate.
+     
+        Searching is done vertically; children are tested before siblings. Elements appear in the list
+        in the order in which they are found.
+     
+        - parameter predicate: Function which returns `true` for a desired element and `false` otherwise.
+     
+        - returns: Array of found XML elements.
+    */
+    open func allDescendants(where predicate: (AEXMLElement) -> Bool) -> [AEXMLElement] {
+        var result: [AEXMLElement] = []
+        
+        for child in children {
+            if predicate(child) {
+                result.append(child)
+            }
+            result.append(contentsOf: child.allDescendants(where: predicate))
+        }
+        
+        return result
+    }
+    
+    /**
+        Returns the first descendant element which satisfies the given predicate, or nil if no such element is found.
+     
+        Searching is done vertically; children are tested before siblings.
+     
+        - parameter predicate: Function which returns `true` for the desired element and `false` otherwise.
+     
+        - returns: Optional AEXMLElement.
+    */
+    open func firstDescendant(where predicate: (AEXMLElement) -> Bool) -> AEXMLElement? {
+        for child in children {
+            if predicate(child) {
+                return child
+            } else if let descendant = child.firstDescendant(where: predicate) {
+                return descendant
+            }
+        }
+        return nil
+    }
+    
+    /**
+        Indicates whether the element has a descendant satisfying the given predicate.
+     
+        - parameter predicate: Function which returns `true` for the desired element and `false` otherwise.
+     
+        - returns: Bool.
+    */
+    open func hasDescendant(where predicate: (AEXMLElement) -> Bool) -> Bool {
+        return firstDescendant(where: predicate) != nil
+    }
+    
+    // MARK: - XML Write
+    
+    /**
+        Adds child XML element to `self`.
+    
+        - parameter child: Child XML element to add.
+    
+        - returns: Child XML element with `self` as `parent`.
+    */
+    @discardableResult open func addChild(_ child: AEXMLElement) -> AEXMLElement {
+        child.parent = self
+        children.append(child)
+        return child
+    }
+    
+    /**
+        Adds child XML element to `self`.
+        
+        - parameter name: Child XML element name.
+        - parameter value: Child XML element value (defaults to `nil`).
+        - parameter attributes: Child XML element attributes (defaults to empty dictionary).
+        
+        - returns: Child XML element with `self` as `parent`.
+    */
+    @discardableResult open func addChild(name: String,
+                       value: String? = nil,
+                       attributes: [String : String] = [String : String]()) -> AEXMLElement
+    {
+        let child = AEXMLElement(name: name, value: value, attributes: attributes)
+        return addChild(child)
+    }
+    
+    /**
+        Adds an array of XML elements to `self`.
+    
+        - parameter children: Child XML element array to add.
+    
+        - returns: Child XML elements with `self` as `parent`.
+    */
+    @discardableResult open func addChildren(_ children: [AEXMLElement]) -> [AEXMLElement] {
+        children.forEach{ addChild($0) }
+        return children
+    }
+    
+    /// Removes `self` from `parent` XML element.
+    open func removeFromParent() {
+        parent?.removeChild(self)
+    }
+    
+    fileprivate func removeChild(_ child: AEXMLElement) {
+        if let childIndex = children.index(where: { $0 === child }) {
+            children.remove(at: childIndex)
+        }
+    }
+    
+    fileprivate var parentsCount: Int {
+        var count = 0
+        var element = self
+        
+        while let parent = element.parent {
+            count += 1
+            element = parent
+        }
+        
+        return count
+    }
+    
+    fileprivate func indent(withDepth depth: Int) -> String {
+        var count = depth
+        var indent = String()
+        
+        while count > 0 {
+            indent += "\t"
+            count -= 1
+        }
+        
+        return indent
+    }
+    
+    /// Complete hierarchy of `self` and `children` in **XML** escaped and formatted String
+    open var xml: String {
+        var xml = String()
+        
+        // open element
+        xml += indent(withDepth: parentsCount - 1)
+        xml += "<\(name)"
+        
+        if attributes.count > 0 {
+            // insert attributes
+            for (key, value) in attributes {
+                xml += " \(key)=\"\(value.xmlEscaped)\""
+            }
+        }
+        
+        if value == nil && children.count == 0 {
+            // close element
+            xml += " />"
+        } else {
+            if children.count > 0 {
+                // add children
+                xml += ">\n"
+                for child in children {
+                    xml += "\(child.xml)\n"
+                }
+                // add indentation
+                xml += indent(withDepth: parentsCount - 1)
+                xml += "</\(name)>"
+            } else {
+                // insert string value and close element
+                xml += ">\(string.xmlEscaped)</\(name)>"
+            }
+        }
+        
+        return xml
+    }
+    
+    /// Same as `xmlString` but without `\n` and `\t` characters
+    open var xmlCompact: String {
+        let chars = CharacterSet(charactersIn: "\n\t")
+        return xml.components(separatedBy: chars).joined(separator: "")
+    }
+    
+}
+
+public extension String {
+    
+    /// String representation of self with XML special characters escaped.
+    public var xmlEscaped: String {
+        // we need to make sure "&" is escaped first. Not doing this may break escaping the other characters
+        var escaped = replacingOccurrences(of: "&", with: "&amp;", options: .literal)
+        
+        // replace the other four special characters
+        let escapeChars = ["<" : "&lt;", ">" : "&gt;", "'" : "&apos;", "\"" : "&quot;"]
+        for (char, echar) in escapeChars {
+            escaped = escaped.replacingOccurrences(of: char, with: echar, options: .literal)
+        }
+        
+        return escaped
+    }
+    
+}
diff --git a/iOS/Pods/AEXML/Sources/Error.swift b/iOS/Pods/AEXML/Sources/Error.swift
new file mode 100644 (file)
index 0000000..d598c95
--- /dev/null
@@ -0,0 +1,13 @@
+import Foundation
+
+/// A type representing error value that can be thrown or inside `error` property of `AEXMLElement`.
+public enum AEXMLError: Error {
+    /// This will be inside `error` property of `AEXMLElement` when subscript is used for not-existing element.
+    case elementNotFound
+    
+    /// This will be inside `error` property of `AEXMLDocument` when there is no root element.
+    case rootElementMissing
+    
+    /// `AEXMLDocument` can throw this error on `init` or `loadXMLData` if parsing with `XMLParser` was not successful.
+    case parsingFailed
+}
diff --git a/iOS/Pods/AEXML/Sources/Options.swift b/iOS/Pods/AEXML/Sources/Options.swift
new file mode 100644 (file)
index 0000000..2aad539
--- /dev/null
@@ -0,0 +1,47 @@
+import Foundation
+
+/// Options used in `AEXMLDocument`
+public struct AEXMLOptions {
+    
+    /// Values used in XML Document header
+    public struct DocumentHeader {
+        /// Version value for XML Document header (defaults to 1.0).
+        public var version = 1.0
+        
+        /// Encoding value for XML Document header (defaults to "utf-8").
+        public var encoding = "utf-8"
+        
+        /// Standalone value for XML Document header (defaults to "no").
+        public var standalone = "no"
+        
+        /// XML Document header
+        public var xmlString: String {
+            return "<?xml version=\"\(version)\" encoding=\"\(encoding)\" standalone=\"\(standalone)\"?>"
+        }
+    }
+    
+    /// Settings used by `Foundation.XMLParser`
+    public struct ParserSettings {
+        /// Parser reports the namespaces and qualified names of elements. (defaults to `false`)
+        public var shouldProcessNamespaces = false
+        
+        /// Parser reports the prefixes indicating the scope of namespace declarations. (defaults to `false`)
+        public var shouldReportNamespacePrefixes = false
+        
+        /// Parser reports declarations of external entities. (defaults to `false`)
+        public var shouldResolveExternalEntities = false
+        
+        /// Parser should trim whitespace from text nodes. (defaults to `true`)
+        public var shouldTrimWhitespace = true
+    }
+    
+    /// Values used in XML Document header (defaults to `DocumentHeader()`)
+    public var documentHeader = DocumentHeader()
+    
+    /// Settings used by `Foundation.XMLParser` (defaults to `ParserSettings()`)
+    public var parserSettings = ParserSettings()
+    
+    /// Designated initializer - Creates and returns default `AEXMLOptions`.
+    public init() {}
+    
+}
diff --git a/iOS/Pods/AEXML/Sources/Parser.swift b/iOS/Pods/AEXML/Sources/Parser.swift
new file mode 100644 (file)
index 0000000..d596b90
--- /dev/null
@@ -0,0 +1,81 @@
+import Foundation
+
+/// Simple wrapper around `Foundation.XMLParser`.
+internal class AEXMLParser: NSObject, XMLParserDelegate {
+    
+    // MARK: - Properties
+    
+    let document: AEXMLDocument
+    let data: Data
+    
+    var currentParent: AEXMLElement?
+    var currentElement: AEXMLElement?
+    var currentValue = String()
+    
+    var parseError: Error?
+    
+    private lazy var trimWhitespace: Bool = {
+        let trim = self.document.options.parserSettings.shouldTrimWhitespace
+        return trim
+    }()
+    
+    // MARK: - Lifecycle
+    
+    init(document: AEXMLDocument, data: Data) {
+        self.document = document
+        self.data = data
+        currentParent = document
+        
+        super.init()
+    }
+    
+    // MARK: - API
+    
+    func parse() throws {
+        let parser = XMLParser(data: data)
+        parser.delegate = self
+        
+        parser.shouldProcessNamespaces = document.options.parserSettings.shouldProcessNamespaces
+        parser.shouldReportNamespacePrefixes = document.options.parserSettings.shouldReportNamespacePrefixes
+        parser.shouldResolveExternalEntities = document.options.parserSettings.shouldResolveExternalEntities
+        
+        let success = parser.parse()
+        
+        if !success {
+            guard let error = parseError else { throw AEXMLError.parsingFailed }
+            throw error
+        }
+    }
+    
+    // MARK: - XMLParserDelegate
+    
+    func parser(_ parser: XMLParser,
+                      didStartElement elementName: String,
+                      namespaceURI: String?,
+                      qualifiedName qName: String?,
+                      attributes attributeDict: [String : String])
+    {
+        currentValue = String()
+        currentElement = currentParent?.addChild(name: elementName, attributes: attributeDict)
+        currentParent = currentElement
+    }
+    
+    func parser(_ parser: XMLParser, foundCharacters string: String) {
+        currentValue.append(trimWhitespace ? string.trimmingCharacters(in: .whitespacesAndNewlines) : string)
+        currentElement?.value = currentValue.isEmpty ? nil : currentValue
+    }
+    
+    func parser(_ parser: XMLParser,
+                      didEndElement elementName: String,
+                      namespaceURI: String?,
+                      qualifiedName qName: String?)
+    {
+        currentParent = currentParent?.parent
+        currentElement = nil
+    }
+    
+    func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
+        self.parseError = parseError
+    }
+    
+}
diff --git a/iOS/Pods/Alamofire/LICENSE b/iOS/Pods/Alamofire/LICENSE
new file mode 100644 (file)
index 0000000..1654602
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2014-2017 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/iOS/Pods/Alamofire/README.md b/iOS/Pods/Alamofire/README.md
new file mode 100644 (file)
index 0000000..eb5a522
--- /dev/null
@@ -0,0 +1,236 @@
+![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/alamofire.png)
+
+[![Build Status](https://travis-ci.org/Alamofire/Alamofire.svg?branch=master)](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)](https://alamofire.github.io/Alamofire)
+[![Twitter](https://img.shields.io/badge/twitter-@AlamofireSF-blue.svg?style=flat)](http://twitter.com/AlamofireSF)
+[![Gitter](https://badges.gitter.im/Alamofire/Alamofire.svg)](https://gitter.im/Alamofire/Alamofire?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+
+Alamofire is an HTTP networking library written in Swift.
+
+- [Features](#features)
+- [Component Libraries](#component-libraries)
+- [Requirements](#requirements)
+- [Migration Guides](#migration-guides)
+- [Communication](#communication)
+- [Installation](#installation)
+- [Usage](Documentation/Usage.md)
+    - **Intro -** [Making a Request](Documentation/Usage.md#making-a-request), [Response Handling](Documentation/Usage.md#response-handling), [Response Validation](Documentation/Usage.md#response-validation), [Response Caching](Documentation/Usage.md#response-caching)
+       - **HTTP -** [HTTP Methods](Documentation/Usage.md#http-methods), [Parameter Encoding](Documentation/Usage.md#parameter-encoding), [HTTP Headers](Documentation/Usage.md#http-headers), [Authentication](Documentation/Usage.md#authentication)
+       - **Large Data -** [Downloading Data to a File](Documentation/Usage.md#downloading-data-to-a-file), [Uploading Data to a Server](Documentation/Usage.md#uploading-data-to-a-server)
+       - **Tools -** [Statistical Metrics](Documentation/Usage.md#statistical-metrics), [cURL Command Output](Documentation/Usage.md#curl-command-output)
+- [Advanced Usage](Documentation/AdvancedUsage.md)
+       - **URL Session -** [Session Manager](Documentation/AdvancedUsage.md#session-manager), [Session Delegate](Documentation/AdvancedUsage.md#session-delegate), [Request](Documentation/AdvancedUsage.md#request)
+       - **Routing -** [Routing Requests](Documentation/AdvancedUsage.md#routing-requests), [Adapting and Retrying Requests](Documentation/AdvancedUsage.md#adapting-and-retrying-requests)
+       - **Model Objects -** [Custom Response Serialization](Documentation/AdvancedUsage.md#custom-response-serialization)
+       - **Connection -** [Security](Documentation/AdvancedUsage.md#security), [Network Reachability](Documentation/AdvancedUsage.md#network-reachability)
+- [Open Radars](#open-radars)
+- [FAQ](#faq)
+- [Credits](#credits)
+- [Donations](#donations)
+- [License](#license)
+
+## Features
+
+- [x] Chainable Request / Response Methods
+- [x] URL / JSON / plist Parameter Encoding
+- [x] Upload File / Data / Stream / MultipartFormData
+- [x] Download File using Request or Resume Data
+- [x] Authentication with URLCredential
+- [x] HTTP Response Validation
+- [x] Upload and Download Progress Closures with Progress
+- [x] cURL Command Output
+- [x] Dynamically Adapt and Retry Requests
+- [x] TLS Certificate and Public Key Pinning
+- [x] Network Reachability
+- [x] Comprehensive Unit and Integration Test Coverage
+- [x] [Complete Documentation](https://alamofire.github.io/Alamofire)
+
+## 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.
+- [AlamofireNetworkActivityIndicator](https://github.com/Alamofire/AlamofireNetworkActivityIndicator) - Controls the visibility of the network activity indicator on iOS using Alamofire. It contains configurable delay timers to help mitigate flicker and can support `URLSession` instances not managed by Alamofire.
+
+## Requirements
+
+- iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
+- Xcode 8.3+
+- Swift 3.1+
+
+## Migration Guides
+
+- [Alamofire 4.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md)
+- [Alamofire 3.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md)
+- [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
+
+### CocoaPods
+
+[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
+
+```bash
+$ gem install cocoapods
+```
+
+> CocoaPods 1.1+ is required to build Alamofire 4.0+.
+
+To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`:
+
+```ruby
+source 'https://github.com/CocoaPods/Specs.git'
+platform :ios, '10.0'
+use_frameworks!
+
+target '<Your Target Name>' do
+    pod 'Alamofire', '~> 4.5'
+end
+```
+
+Then, run the following command:
+
+```bash
+$ pod install
+```
+
+### 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 Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`:
+
+```ogdl
+github "Alamofire/Alamofire" ~> 4.5
+```
+
+Run `carthage update` to build the framework and drag the built `Alamofire.framework` into your Xcode project.
+
+### Swift Package Manager
+
+The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler. It is in early development, but Alamofire does support its use on supported platforms. 
+
+Once you have your Swift package set up, adding Alamofire as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
+
+#### Swift 3
+
+```swift
+dependencies: [
+    .Package(url: "https://github.com/Alamofire/Alamofire.git", majorVersion: 4)
+]
+```
+
+#### Swift 4
+
+```swift
+dependencies: [
+    .package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.0.0")
+]
+```
+
+### Manually
+
+If you prefer not to use any of the aforementioned dependency managers, you can integrate Alamofire into your project manually.
+
+#### Embedded Framework
+
+- Open up Terminal, `cd` into your top-level project directory, and run the following command "if" your project is not initialized as a git repository:
+
+  ```bash
+  $ git init
+  ```
+
+- Add Alamofire as a git [submodule](http://git-scm.com/docs/git-submodule) by running 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`, `Alamofire macOS`, `Alamofire tvOS` or `Alamofire watchOS`.
+
+- 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.
+
+## Open Radars
+
+The following radars have some effect on the current implementation of Alamofire.
+
+- [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in test case
+- `rdar://26870455` - Background URL Session Configurations do not work in the simulator
+- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest`
+
+## Resolved Radars
+
+The following radars have been resolved over time after being filed against the Alamofire project.
+
+- [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage (Resolved on 9/1/17 in Xcode 9 beta 6).
+
+## 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.
+
+### What logic belongs in a Router vs. a Request Adapter?
+
+Simple, static data such as paths, parameters and common headers belong in the `Router`. Dynamic data such as an `Authorization` header whose value can changed based on an authentication system belongs in a `RequestAdapter`.
+
+The reason the dynamic data MUST be placed into the `RequestAdapter` is to support retry operations. When a `Request` is retried, the original request is not rebuilt meaning the `Router` will not be called again. The `RequestAdapter` is called again allowing the dynamic data to be updated on the original request before retrying the `Request`.
+
+## 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.
+
+## Donations
+
+The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise money to officially register as a federal non-profit organization. Registering will allow us members to gain some legal protections and also allow us to put donations to use, tax free. Donating to the ASF will enable us to:
+
+- Pay our legal fees to register as a federal non-profit organization
+- Pay our yearly legal fees to keep the non-profit in good status
+- Pay for our mail servers to help us stay on top of all questions and security issues
+- Potentially fund test servers to make it easier for us to test the edge cases
+- Potentially fund developers to work on one of our projects full-time
+
+The community adoption of the ASF libraries has been amazing. We are greatly humbled by your enthusiasm around the projects, and want to continue to do everything we can to move the needle forward. With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members. If you use any of our libraries for work, see if your employers would be interested in donating. Our initial goal is to raise $1000 to get all our legal ducks in a row and kickstart this campaign. Any amount you can donate today to help us reach our goal would be greatly appreciated.
+
+<a href='https://pledgie.com/campaigns/31474'><img alt='Click here to lend your support to: Alamofire Software Foundation and make a donation at pledgie.com !' src='https://pledgie.com/campaigns/31474.png?skin_name=chrome' border='0' ></a>
+
+## License
+
+Alamofire is released under the MIT license. [See LICENSE](https://github.com/Alamofire/Alamofire/blob/master/LICENSE) for details.
diff --git a/iOS/Pods/Alamofire/Source/AFError.swift b/iOS/Pods/Alamofire/Source/AFError.swift
new file mode 100644 (file)
index 0000000..585ae14
--- /dev/null
@@ -0,0 +1,460 @@
+//
+//  AFError.swift
+//
+//  Copyright (c) 2014-2017 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
+
+/// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with
+/// their own associated reasons.
+///
+/// - invalidURL:                  Returned when a `URLConvertible` type fails to create a valid `URL`.
+/// - parameterEncodingFailed:     Returned when a parameter encoding object throws an error during the encoding process.
+/// - multipartEncodingFailed:     Returned when some step in the multipart encoding process fails.
+/// - responseValidationFailed:    Returned when a `validate()` call fails.
+/// - responseSerializationFailed: Returned when a response serializer encounters an error in the serialization process.
+public enum AFError: Error {
+    /// The underlying reason the parameter encoding error occurred.
+    ///
+    /// - missingURL:                 The URL request did not have a URL to encode.
+    /// - jsonEncodingFailed:         JSON serialization failed with an underlying system error during the
+    ///                               encoding process.
+    /// - propertyListEncodingFailed: Property list serialization failed with an underlying system error during
+    ///                               encoding process.
+    public enum ParameterEncodingFailureReason {
+        case missingURL
+        case jsonEncodingFailed(error: Error)
+        case propertyListEncodingFailed(error: Error)
+    }
+
+    /// The underlying reason the multipart encoding error occurred.
+    ///
+    /// - bodyPartURLInvalid:                   The `fileURL` provided for reading an encodable body part isn't a
+    ///                                         file URL.
+    /// - bodyPartFilenameInvalid:              The filename of the `fileURL` provided has either an empty
+    ///                                         `lastPathComponent` or `pathExtension.
+    /// - bodyPartFileNotReachable:             The file at the `fileURL` provided was not reachable.
+    /// - bodyPartFileNotReachableWithError:    Attempting to check the reachability of the `fileURL` provided threw
+    ///                                         an error.
+    /// - bodyPartFileIsDirectory:              The file at the `fileURL` provided is actually a directory.
+    /// - bodyPartFileSizeNotAvailable:         The size of the file at the `fileURL` provided was not returned by
+    ///                                         the system.
+    /// - bodyPartFileSizeQueryFailedWithError: The attempt to find the size of the file at the `fileURL` provided
+    ///                                         threw an error.
+    /// - bodyPartInputStreamCreationFailed:    An `InputStream` could not be created for the provided `fileURL`.
+    /// - outputStreamCreationFailed:           An `OutputStream` could not be created when attempting to write the
+    ///                                         encoded data to disk.
+    /// - outputStreamFileAlreadyExists:        The encoded body data could not be writtent disk because a file
+    ///                                         already exists at the provided `fileURL`.
+    /// - outputStreamURLInvalid:               The `fileURL` provided for writing the encoded body data to disk is
+    ///                                         not a file URL.
+    /// - outputStreamWriteFailed:              The attempt to write the encoded body data to disk failed with an
+    ///                                         underlying error.
+    /// - inputStreamReadFailed:                The attempt to read an encoded body part `InputStream` failed with
+    ///                                         underlying system error.
+    public enum MultipartEncodingFailureReason {
+        case bodyPartURLInvalid(url: URL)
+        case bodyPartFilenameInvalid(in: URL)
+        case bodyPartFileNotReachable(at: URL)
+        case bodyPartFileNotReachableWithError(atURL: URL, error: Error)
+        case bodyPartFileIsDirectory(at: URL)
+        case bodyPartFileSizeNotAvailable(at: URL)
+        case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error)
+        case bodyPartInputStreamCreationFailed(for: URL)
+
+        case outputStreamCreationFailed(for: URL)
+        case outputStreamFileAlreadyExists(at: URL)
+        case outputStreamURLInvalid(url: URL)
+        case outputStreamWriteFailed(error: Error)
+
+        case inputStreamReadFailed(error: Error)
+    }
+
+    /// The underlying reason the response validation error occurred.
+    ///
+    /// - dataFileNil:             The data file containing the server response did not exist.
+    /// - dataFileReadFailed:      The data file containing the server response could not be read.
+    /// - missingContentType:      The response did not contain a `Content-Type` and the `acceptableContentTypes`
+    ///                            provided did not contain wildcard type.
+    /// - unacceptableContentType: The response `Content-Type` did not match any type in the provided
+    ///                            `acceptableContentTypes`.
+    /// - unacceptableStatusCode:  The response status code was not acceptable.
+    public enum ResponseValidationFailureReason {
+        case dataFileNil
+        case dataFileReadFailed(at: URL)
+        case missingContentType(acceptableContentTypes: [String])
+        case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String)
+        case unacceptableStatusCode(code: Int)
+    }
+
+    /// The underlying reason the response serialization error occurred.
+    ///
+    /// - inputDataNil:                    The server response contained no data.
+    /// - inputDataNilOrZeroLength:        The server response contained no data or the data was zero length.
+    /// - inputFileNil:                    The file containing the server response did not exist.
+    /// - inputFileReadFailed:             The file containing the server response could not be read.
+    /// - stringSerializationFailed:       String serialization failed using the provided `String.Encoding`.
+    /// - jsonSerializationFailed:         JSON serialization failed with an underlying system error.
+    /// - propertyListSerializationFailed: Property list serialization failed with an underlying system error.
+    public enum ResponseSerializationFailureReason {
+        case inputDataNil
+        case inputDataNilOrZeroLength
+        case inputFileNil
+        case inputFileReadFailed(at: URL)
+        case stringSerializationFailed(encoding: String.Encoding)
+        case jsonSerializationFailed(error: Error)
+        case propertyListSerializationFailed(error: Error)
+    }
+
+    case invalidURL(url: URLConvertible)
+    case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
+    case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
+    case responseValidationFailed(reason: ResponseValidationFailureReason)
+    case responseSerializationFailed(reason: ResponseSerializationFailureReason)
+}
+
+// MARK: - Adapt Error
+
+struct AdaptError: Error {
+    let error: Error
+}
+
+extension Error {
+    var underlyingAdaptError: Error? { return (self as? AdaptError)?.error }
+}
+
+// MARK: - Error Booleans
+
+extension AFError {
+    /// Returns whether the AFError is an invalid URL error.
+    public var isInvalidURLError: Bool {
+        if case .invalidURL = self { return true }
+        return false
+    }
+
+    /// Returns whether the AFError is a parameter encoding error. When `true`, the `underlyingError` property will
+    /// contain the associated value.
+    public var isParameterEncodingError: Bool {
+        if case .parameterEncodingFailed = self { return true }
+        return false
+    }
+
+    /// Returns whether the AFError is a multipart encoding error. When `true`, the `url` and `underlyingError` properties
+    /// will contain the associated values.
+    public var isMultipartEncodingError: Bool {
+        if case .multipartEncodingFailed = self { return true }
+        return false
+    }
+
+    /// Returns whether the `AFError` is a response validation error. When `true`, the `acceptableContentTypes`,
+    /// `responseContentType`, and `responseCode` properties will contain the associated values.
+    public var isResponseValidationError: Bool {
+        if case .responseValidationFailed = self { return true }
+        return false
+    }
+
+    /// Returns whether the `AFError` is a response serialization error. When `true`, the `failedStringEncoding` and
+    /// `underlyingError` properties will contain the associated values.
+    public var isResponseSerializationError: Bool {
+        if case .responseSerializationFailed = self { return true }
+        return false
+    }
+}
+
+// MARK: - Convenience Properties
+
+extension AFError {
+    /// The `URLConvertible` associated with the error.
+    public var urlConvertible: URLConvertible? {
+        switch self {
+        case .invalidURL(let url):
+            return url
+        default:
+            return nil
+        }
+    }
+
+    /// The `URL` associated with the error.
+    public var url: URL? {
+        switch self {
+        case .multipartEncodingFailed(let reason):
+            return reason.url
+        default:
+            return nil
+        }
+    }
+
+    /// The `Error` returned by a system framework associated with a `.parameterEncodingFailed`,
+    /// `.multipartEncodingFailed` or `.responseSerializationFailed` error.
+    public var underlyingError: Error? {
+        switch self {
+        case .parameterEncodingFailed(let reason):
+            return reason.underlyingError
+        case .multipartEncodingFailed(let reason):
+            return reason.underlyingError
+        case .responseSerializationFailed(let reason):
+            return reason.underlyingError
+        default:
+            return nil
+        }
+    }
+
+    /// The acceptable `Content-Type`s of a `.responseValidationFailed` error.
+    public var acceptableContentTypes: [String]? {
+        switch self {
+        case .responseValidationFailed(let reason):
+            return reason.acceptableContentTypes
+        default:
+            return nil
+        }
+    }
+
+    /// The response `Content-Type` of a `.responseValidationFailed` error.
+    public var responseContentType: String? {
+        switch self {
+        case .responseValidationFailed(let reason):
+            return reason.responseContentType
+        default:
+            return nil
+        }
+    }
+
+    /// The response code of a `.responseValidationFailed` error.
+    public var responseCode: Int? {
+        switch self {
+        case .responseValidationFailed(let reason):
+            return reason.responseCode
+        default:
+            return nil
+        }
+    }
+
+    /// The `String.Encoding` associated with a failed `.stringResponse()` call.
+    public var failedStringEncoding: String.Encoding? {
+        switch self {
+        case .responseSerializationFailed(let reason):
+            return reason.failedStringEncoding
+        default:
+            return nil
+        }
+    }
+}
+
+extension AFError.ParameterEncodingFailureReason {
+    var underlyingError: Error? {
+        switch self {
+        case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error):
+            return error
+        default:
+            return nil
+        }
+    }
+}
+
+extension AFError.MultipartEncodingFailureReason {
+    var url: URL? {
+        switch self {
+        case .bodyPartURLInvalid(let url), .bodyPartFilenameInvalid(let url), .bodyPartFileNotReachable(let url),
+             .bodyPartFileIsDirectory(let url), .bodyPartFileSizeNotAvailable(let url),
+             .bodyPartInputStreamCreationFailed(let url), .outputStreamCreationFailed(let url),
+             .outputStreamFileAlreadyExists(let url), .outputStreamURLInvalid(let url),
+             .bodyPartFileNotReachableWithError(let url, _), .bodyPartFileSizeQueryFailedWithError(let url, _):
+            return url
+        default:
+            return nil
+        }
+    }
+
+    var underlyingError: Error? {
+        switch self {
+        case .bodyPartFileNotReachableWithError(_, let error), .bodyPartFileSizeQueryFailedWithError(_, let error),
+             .outputStreamWriteFailed(let error), .inputStreamReadFailed(let error):
+            return error
+        default:
+            return nil
+        }
+    }
+}
+
+extension AFError.ResponseValidationFailureReason {
+    var acceptableContentTypes: [String]? {
+        switch self {
+        case .missingContentType(let types), .unacceptableContentType(let types, _):
+            return types
+        default:
+            return nil
+        }
+    }
+
+    var responseContentType: String? {
+        switch self {
+        case .unacceptableContentType(_, let responseType):
+            return responseType
+        default:
+            return nil
+        }
+    }
+
+    var responseCode: Int? {
+        switch self {
+        case .unacceptableStatusCode(let code):
+            return code
+        default:
+            return nil
+        }
+    }
+}
+
+extension AFError.ResponseSerializationFailureReason {
+    var failedStringEncoding: String.Encoding? {
+        switch self {
+        case .stringSerializationFailed(let encoding):
+            return encoding
+        default:
+            return nil
+        }
+    }
+
+    var underlyingError: Error? {
+        switch self {
+        case .jsonSerializationFailed(let error), .propertyListSerializationFailed(let error):
+            return error
+        default:
+            return nil
+        }
+    }
+}
+
+// MARK: - Error Descriptions
+
+extension AFError: LocalizedError {
+    public var errorDescription: String? {
+        switch self {
+        case .invalidURL(let url):
+            return "URL is not valid: \(url)"
+        case .parameterEncodingFailed(let reason):
+            return reason.localizedDescription
+        case .multipartEncodingFailed(let reason):
+            return reason.localizedDescription
+        case .responseValidationFailed(let reason):
+            return reason.localizedDescription
+        case .responseSerializationFailed(let reason):
+            return reason.localizedDescription
+        }
+    }
+}
+
+extension AFError.ParameterEncodingFailureReason {
+    var localizedDescription: String {
+        switch self {
+        case .missingURL:
+            return "URL request to encode was missing a URL"
+        case .jsonEncodingFailed(let error):
+            return "JSON could not be encoded because of error:\n\(error.localizedDescription)"
+        case .propertyListEncodingFailed(let error):
+            return "PropertyList could not be encoded because of error:\n\(error.localizedDescription)"
+        }
+    }
+}
+
+extension AFError.MultipartEncodingFailureReason {
+    var localizedDescription: String {
+        switch self {
+        case .bodyPartURLInvalid(let url):
+            return "The URL provided is not a file URL: \(url)"
+        case .bodyPartFilenameInvalid(let url):
+            return "The URL provided does not have a valid filename: \(url)"
+        case .bodyPartFileNotReachable(let url):
+            return "The URL provided is not reachable: \(url)"
+        case .bodyPartFileNotReachableWithError(let url, let error):
+            return (
+                "The system returned an error while checking the provided URL for " +
+                "reachability.\nURL: \(url)\nError: \(error)"
+            )
+        case .bodyPartFileIsDirectory(let url):
+            return "The URL provided is a directory: \(url)"
+        case .bodyPartFileSizeNotAvailable(let url):
+            return "Could not fetch the file size from the provided URL: \(url)"
+        case .bodyPartFileSizeQueryFailedWithError(let url, let error):
+            return (
+                "The system returned an error while attempting to fetch the file size from the " +
+                "provided URL.\nURL: \(url)\nError: \(error)"
+            )
+        case .bodyPartInputStreamCreationFailed(let url):
+            return "Failed to create an InputStream for the provided URL: \(url)"
+        case .outputStreamCreationFailed(let url):
+            return "Failed to create an OutputStream for URL: \(url)"
+        case .outputStreamFileAlreadyExists(let url):
+            return "A file already exists at the provided URL: \(url)"
+        case .outputStreamURLInvalid(let url):
+            return "The provided OutputStream URL is invalid: \(url)"
+        case .outputStreamWriteFailed(let error):
+            return "OutputStream write failed with error: \(error)"
+        case .inputStreamReadFailed(let error):
+            return "InputStream read failed with error: \(error)"
+        }
+    }
+}
+
+extension AFError.ResponseSerializationFailureReason {
+    var localizedDescription: String {
+        switch self {
+        case .inputDataNil:
+            return "Response could not be serialized, input data was nil."
+        case .inputDataNilOrZeroLength:
+            return "Response could not be serialized, input data was nil or zero length."
+        case .inputFileNil:
+            return "Response could not be serialized, input file was nil."
+        case .inputFileReadFailed(let url):
+            return "Response could not be serialized, input file could not be read: \(url)."
+        case .stringSerializationFailed(let encoding):
+            return "String could not be serialized with encoding: \(encoding)."
+        case .jsonSerializationFailed(let error):
+            return "JSON could not be serialized because of error:\n\(error.localizedDescription)"
+        case .propertyListSerializationFailed(let error):
+            return "PropertyList could not be serialized because of error:\n\(error.localizedDescription)"
+        }
+    }
+}
+
+extension AFError.ResponseValidationFailureReason {
+    var localizedDescription: String {
+        switch self {
+        case .dataFileNil:
+            return "Response could not be validated, data file was nil."
+        case .dataFileReadFailed(let url):
+            return "Response could not be validated, data file could not be read: \(url)."
+        case .missingContentType(let types):
+            return (
+                "Response Content-Type was missing and acceptable content types " +
+                "(\(types.joined(separator: ","))) do not match \"*/*\"."
+            )
+        case .unacceptableContentType(let acceptableTypes, let responseType):
+            return (
+                "Response Content-Type \"\(responseType)\" does not match any acceptable types: " +
+                "\(acceptableTypes.joined(separator: ","))."
+            )
+        case .unacceptableStatusCode(let code):
+            return "Response status code was unacceptable: \(code)."
+        }
+    }
+}
diff --git a/iOS/Pods/Alamofire/Source/Alamofire.swift b/iOS/Pods/Alamofire/Source/Alamofire.swift
new file mode 100644 (file)
index 0000000..e971613
--- /dev/null
@@ -0,0 +1,465 @@
+//
+//  Alamofire.swift
+//
+//  Copyright (c) 2014-2017 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
+
+/// Types adopting the `URLConvertible` protocol can be used to construct URLs, which are then used to construct
+/// URL requests.
+public protocol URLConvertible {
+    /// Returns a URL that conforms to RFC 2396 or throws an `Error`.
+    ///
+    /// - throws: An `Error` if the type cannot be converted to a `URL`.
+    ///
+    /// - returns: A URL or throws an `Error`.
+    func asURL() throws -> URL
+}
+
+extension String: URLConvertible {
+    /// Returns a URL if `self` represents a valid URL string that conforms to RFC 2396 or throws an `AFError`.
+    ///
+    /// - throws: An `AFError.invalidURL` if `self` is not a valid URL string.
+    ///
+    /// - returns: A URL or throws an `AFError`.
+    public func asURL() throws -> URL {
+        guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) }
+        return url
+    }
+}
+
+extension URL: URLConvertible {
+    /// Returns self.
+    public func asURL() throws -> URL { return self }
+}
+
+extension URLComponents: URLConvertible {
+    /// Returns a URL if `url` is not nil, otherwise throws an `Error`.
+    ///
+    /// - throws: An `AFError.invalidURL` if `url` is `nil`.
+    ///
+    /// - returns: A URL or throws an `AFError`.
+    public func asURL() throws -> URL {
+        guard let url = url else { throw AFError.invalidURL(url: self) }
+        return url
+    }
+}
+
+// MARK: -
+
+/// Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests.
+public protocol URLRequestConvertible {
+    /// Returns a URL request or throws if an `Error` was encountered.
+    ///
+    /// - throws: An `Error` if the underlying `URLRequest` is `nil`.
+    ///
+    /// - returns: A URL request.
+    func asURLRequest() throws -> URLRequest
+}
+
+extension URLRequestConvertible {
+    /// The URL request.
+    public var urlRequest: URLRequest? { return try? asURLRequest() }
+}
+
+extension URLRequest: URLRequestConvertible {
+    /// Returns a URL request or throws if an `Error` was encountered.
+    public func asURLRequest() throws -> URLRequest { return self }
+}
+
+// MARK: -
+
+extension URLRequest {
+    /// Creates an instance with the specified `method`, `urlString` and `headers`.
+    ///
+    /// - parameter url:     The URL.
+    /// - parameter method:  The HTTP method.
+    /// - parameter headers: The HTTP headers. `nil` by default.
+    ///
+    /// - returns: The new `URLRequest` instance.
+    public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws {
+        let url = try url.asURL()
+
+        self.init(url: url)
+
+        httpMethod = method.rawValue
+
+        if let headers = headers {
+            for (headerField, headerValue) in headers {
+                setValue(headerValue, forHTTPHeaderField: headerField)
+            }
+        }
+    }
+
+    func adapt(using adapter: RequestAdapter?) throws -> URLRequest {
+        guard let adapter = adapter else { return self }
+        return try adapter.adapt(self)
+    }
+}
+
+// MARK: - Data Request
+
+/// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of the specified `url`,
+/// `method`, `parameters`, `encoding` and `headers`.
+///
+/// - parameter url:        The URL.
+/// - parameter method:     The HTTP method. `.get` by default.
+/// - parameter parameters: The parameters. `nil` by default.
+/// - parameter encoding:   The parameter encoding. `URLEncoding.default` by default.
+/// - parameter headers:    The HTTP headers. `nil` by default.
+///
+/// - returns: The created `DataRequest`.
+@discardableResult
+public func request(
+    _ url: URLConvertible,
+    method: HTTPMethod = .get,
+    parameters: Parameters? = nil,
+    encoding: ParameterEncoding = URLEncoding.default,
+    headers: HTTPHeaders? = nil)
+    -> DataRequest
+{
+    return SessionManager.default.request(
+        url,
+        method: method,
+        parameters: parameters,
+        encoding: encoding,
+        headers: headers
+    )
+}
+
+/// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
+/// specified `urlRequest`.
+///
+/// - parameter urlRequest: The URL request
+///
+/// - returns: The created `DataRequest`.
+@discardableResult
+public func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
+    return SessionManager.default.request(urlRequest)
+}
+
+// MARK: - Download Request
+
+// MARK: URL Request
+
+/// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of the specified `url`,
+/// `method`, `parameters`, `encoding`, `headers` and save them to the `destination`.
+///
+/// If `destination` is not specified, the contents will remain in the temporary location determined by the
+/// underlying URL session.
+///
+/// - parameter url:         The URL.
+/// - parameter method:      The HTTP method. `.get` by default.
+/// - parameter parameters:  The parameters. `nil` by default.
+/// - parameter encoding:    The parameter encoding. `URLEncoding.default` by default.
+/// - parameter headers:     The HTTP headers. `nil` by default.
+/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
+///
+/// - returns: The created `DownloadRequest`.
+@discardableResult
+public func download(
+    _ url: URLConvertible,
+    method: HTTPMethod = .get,
+    parameters: Parameters? = nil,
+    encoding: ParameterEncoding = URLEncoding.default,
+    headers: HTTPHeaders? = nil,
+    to destination: DownloadRequest.DownloadFileDestination? = nil)
+    -> DownloadRequest
+{
+    return SessionManager.default.download(
+        url,
+        method: method,
+        parameters: parameters,
+        encoding: encoding,
+        headers: headers,
+        to: destination
+    )
+}
+
+/// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
+/// specified `urlRequest` and save them to the `destination`.
+///
+/// If `destination` is not specified, the contents will remain in the temporary location determined by the
+/// underlying URL session.
+///
+/// - parameter urlRequest:  The URL request.
+/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
+///
+/// - returns: The created `DownloadRequest`.
+@discardableResult
+public func download(
+    _ urlRequest: URLRequestConvertible,
+    to destination: DownloadRequest.DownloadFileDestination? = nil)
+    -> DownloadRequest
+{
+    return SessionManager.default.download(urlRequest, to: destination)
+}
+
+// MARK: Resume Data
+
+/// Creates a `DownloadRequest` using the default `SessionManager` from the `resumeData` produced from a
+/// previous request cancellation to retrieve the contents of the original request and save them to the `destination`.
+///
+/// If `destination` is not specified, the contents will remain in the temporary location determined by the
+/// underlying URL session.
+///
+/// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken
+/// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the
+/// data is written incorrectly and will always fail to resume the download. For more information about the bug and
+/// possible workarounds, please refer to the following Stack Overflow post:
+///
+///    - http://stackoverflow.com/a/39347461/1342462
+///
+/// - parameter resumeData:  The resume data. This is an opaque data blob produced by `URLSessionDownloadTask`
+///                          when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for additional
+///                          information.
+/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
+///
+/// - returns: The created `DownloadRequest`.
+@discardableResult
+public func download(
+    resumingWith resumeData: Data,
+    to destination: DownloadRequest.DownloadFileDestination? = nil)
+    -> DownloadRequest
+{
+    return SessionManager.default.download(resumingWith: resumeData, to: destination)
+}
+
+// MARK: - Upload Request
+
+// MARK: File
+
+/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers`
+/// for uploading the `file`.
+///
+/// - parameter file:    The file to upload.
+/// - parameter url:     The URL.
+/// - parameter method:  The HTTP method. `.post` by default.
+/// - parameter headers: The HTTP headers. `nil` by default.
+///
+/// - returns: The created `UploadRequest`.
+@discardableResult
+public func upload(
+    _ fileURL: URL,
+    to url: URLConvertible,
+    method: HTTPMethod = .post,
+    headers: HTTPHeaders? = nil)
+    -> UploadRequest
+{
+    return SessionManager.default.upload(fileURL, to: url, method: method, headers: headers)
+}
+
+/// Creates a `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
+/// uploading the `file`.
+///
+/// - parameter file:       The file to upload.
+/// - parameter urlRequest: The URL request.
+///
+/// - returns: The created `UploadRequest`.
+@discardableResult
+public func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest {
+    return SessionManager.default.upload(fileURL, with: urlRequest)
+}
+
+// MARK: Data
+
+/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers`
+/// for uploading the `data`.
+///
+/// - parameter data:    The data to upload.
+/// - parameter url:     The URL.
+/// - parameter method:  The HTTP method. `.post` by default.
+/// - parameter headers: The HTTP headers. `nil` by default.
+///
+/// - returns: The created `UploadRequest`.
+@discardableResult
+public func upload(
+    _ data: Data,
+    to url: URLConvertible,
+    method: HTTPMethod = .post,
+    headers: HTTPHeaders? = nil)
+    -> UploadRequest
+{
+    return SessionManager.default.upload(data, to: url, method: method, headers: headers)
+}
+
+/// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
+/// uploading the `data`.
+///
+/// - parameter data:       The data to upload.
+/// - parameter urlRequest: The URL request.
+///
+/// - returns: The created `UploadRequest`.
+@discardableResult
+public func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest {
+    return SessionManager.default.upload(data, with: urlRequest)
+}
+
+// MARK: InputStream
+
+/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers`
+/// for uploading the `stream`.
+///
+/// - parameter stream:  The stream to upload.
+/// - parameter url:     The URL.
+/// - parameter method:  The HTTP method. `.post` by default.
+/// - parameter headers: The HTTP headers. `nil` by default.
+///
+/// - returns: The created `UploadRequest`.
+@discardableResult
+public func upload(
+    _ stream: InputStream,
+    to url: URLConvertible,
+    method: HTTPMethod = .post,
+    headers: HTTPHeaders? = nil)
+    -> UploadRequest
+{
+    return SessionManager.default.upload(stream, to: url, method: method, headers: headers)
+}
+
+/// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
+/// uploading the `stream`.
+///
+/// - parameter urlRequest: The URL request.
+/// - parameter stream:     The stream to upload.
+///
+/// - returns: The created `UploadRequest`.
+@discardableResult
+public func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest {
+    return SessionManager.default.upload(stream, with: urlRequest)
+}
+
+// MARK: MultipartFormData
+
+/// Encodes `multipartFormData` using `encodingMemoryThreshold` with the default `SessionManager` and calls
+/// `encodingCompletion` with new `UploadRequest` using the `url`, `method` and `headers`.
+///
+/// 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.
+///
+/// - parameter multipartFormData:       The closure used to append body parts to the `MultipartFormData`.
+/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
+///                                      `multipartFormDataEncodingMemoryThreshold` by default.
+/// - parameter url:                     The URL.
+/// - parameter method:                  The HTTP method. `.post` by default.
+/// - parameter headers:                 The HTTP headers. `nil` by default.
+/// - parameter encodingCompletion:      The closure called when the `MultipartFormData` encoding is complete.
+public func upload(
+    multipartFormData: @escaping (MultipartFormData) -> Void,
+    usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
+    to url: URLConvertible,
+    method: HTTPMethod = .post,
+    headers: HTTPHeaders? = nil,
+    encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)
+{
+    return SessionManager.default.upload(
+        multipartFormData: multipartFormData,
+        usingThreshold: encodingMemoryThreshold,
+        to: url,
+        method: method,
+        headers: headers,
+        encodingCompletion: encodingCompletion
+    )
+}
+
+/// Encodes `multipartFormData` using `encodingMemoryThreshold` and the default `SessionManager` and
+/// calls `encodingCompletion` with new `UploadRequest` using the `urlRequest`.
+///
+/// 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.
+///
+/// - parameter multipartFormData:       The closure used to append body parts to the `MultipartFormData`.
+/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
+///                                      `multipartFormDataEncodingMemoryThreshold` by default.
+/// - parameter urlRequest:              The URL request.
+/// - parameter encodingCompletion:      The closure called when the `MultipartFormData` encoding is complete.
+public func upload(
+    multipartFormData: @escaping (MultipartFormData) -> Void,
+    usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
+    with urlRequest: URLRequestConvertible,
+    encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)
+{
+    return SessionManager.default.upload(
+        multipartFormData: multipartFormData,
+        usingThreshold: encodingMemoryThreshold,
+        with: urlRequest,
+        encodingCompletion: encodingCompletion
+    )
+}
+
+#if !os(watchOS)
+
+// MARK: - Stream Request
+
+// MARK: Hostname and Port
+
+/// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `hostname`
+/// and `port`.
+///
+/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+///
+/// - parameter hostName: The hostname of the server to connect to.
+/// - parameter port:     The port of the server to connect to.
+///
+/// - returns: The created `StreamRequest`.
+@discardableResult
+@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+public func stream(withHostName hostName: String, port: Int) -> StreamRequest {
+    return SessionManager.default.stream(withHostName: hostName, port: port)
+}
+
+// MARK: NetService
+
+/// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `netService`.
+///
+/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+///
+/// - parameter netService: The net service used to identify the endpoint.
+///
+/// - returns: The created `StreamRequest`.
+@discardableResult
+@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+public func stream(with netService: NetService) -> StreamRequest {
+    return SessionManager.default.stream(with: netService)
+}
+
+#endif
diff --git a/iOS/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift b/iOS/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift
new file mode 100644 (file)
index 0000000..9031395
--- /dev/null
@@ -0,0 +1,37 @@
+//
+//  DispatchQueue+Alamofire.swift
+//
+//  Copyright (c) 2014-2017 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 Dispatch
+import Foundation
+
+extension DispatchQueue {
+    static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) }
+    static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) }
+    static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) }
+    static var background: DispatchQueue { return DispatchQueue.global(qos: .background) }
+
+    func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
+        asyncAfter(deadline: .now() + delay, execute: closure)
+    }
+}
diff --git a/iOS/Pods/Alamofire/Source/MultipartFormData.swift b/iOS/Pods/Alamofire/Source/MultipartFormData.swift
new file mode 100644 (file)
index 0000000..ba02d24
--- /dev/null
@@ -0,0 +1,580 @@
+//
+//  MultipartFormData.swift
+//
+//  Copyright (c) 2014-2017 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) || os(tvOS)
+import MobileCoreServices
+#elseif os(macOS)
+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(forBoundaryType 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: HTTPHeaders
+        let bodyStream: InputStream
+        let bodyContentLength: UInt64
+        var hasInitialBoundary = false
+        var hasFinalBoundary = false
+
+        init(headers: HTTPHeaders, 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 lazy var contentType: String = "multipart/form-data; boundary=\(self.boundary)"
+
+    /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries.
+    public var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } }
+
+    /// The boundary used to separate the body parts in the encoded form data.
+    public let boundary: String
+
+    private var bodyParts: [BodyPart]
+    private var bodyPartError: AFError?
+    private 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.
+    public func append(_ data: Data, withName name: String) {
+        let headers = contentHeaders(withName: name)
+        let stream = InputStream(data: data)
+        let length = UInt64(data.count)
+
+        append(stream, withLength: 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.
+    public func append(_ data: Data, withName name: String, mimeType: String) {
+        let headers = contentHeaders(withName: name, mimeType: mimeType)
+        let stream = InputStream(data: data)
+        let length = UInt64(data.count)
+
+        append(stream, withLength: 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.
+    public func append(_ data: Data, withName name: String, fileName: String, mimeType: String) {
+        let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
+        let stream = InputStream(data: data)
+        let length = UInt64(data.count)
+
+        append(stream, withLength: 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.
+    public func append(_ fileURL: URL, withName name: String) {
+        let fileName = fileURL.lastPathComponent
+        let pathExtension = fileURL.pathExtension
+
+        if !fileName.isEmpty && !pathExtension.isEmpty {
+            let mime = mimeType(forPathExtension: pathExtension)
+            append(fileURL, withName: name, fileName: fileName, mimeType: mime)
+        } else {
+            setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL))
+        }
+    }
+
+    /// 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.
+    public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) {
+        let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
+
+        //============================================================
+        //                 Check 1 - is file URL?
+        //============================================================
+
+        guard fileURL.isFileURL else {
+            setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL))
+            return
+        }
+
+        //============================================================
+        //              Check 2 - is file URL reachable?
+        //============================================================
+
+        do {
+            let isReachable = try fileURL.checkPromisedItemIsReachable()
+            guard isReachable else {
+                setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL))
+                return
+            }
+        } catch {
+            setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error))
+            return
+        }
+
+        //============================================================
+        //            Check 3 - is file URL a directory?
+        //============================================================
+
+        var isDirectory: ObjCBool = false
+        let path = fileURL.path
+
+        guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else {
+            setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL))
+            return
+        }
+
+        //============================================================
+        //          Check 4 - can the file size be extracted?
+        //============================================================
+
+        let bodyContentLength: UInt64
+
+        do {
+            guard let fileSize = try FileManager.default.attributesOfItem(atPath: path)[.size] as? NSNumber else {
+                setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL))
+                return
+            }
+
+            bodyContentLength = fileSize.uint64Value
+        }
+        catch {
+            setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error))
+            return
+        }
+
+        //============================================================
+        //       Check 5 - can a stream be created from file URL?
+        //============================================================
+
+        guard let stream = InputStream(url: fileURL) else {
+            setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL))
+            return
+        }
+
+        append(stream, withLength: bodyContentLength, 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.
+    public func append(
+        _ stream: InputStream,
+        withLength length: UInt64,
+        name: String,
+        fileName: String,
+        mimeType: String)
+    {
+        let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
+        append(stream, withLength: 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.
+    public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) {
+        let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
+        bodyParts.append(bodyPart)
+    }
+
+    // MARK: - Data Encoding
+
+    /// Encodes all the appended body parts into a single `Data` value.
+    ///
+    /// 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 `AFError` if encoding encounters an error.
+    ///
+    /// - returns: The encoded `Data` if encoding is successful.
+    public func encode() throws -> Data {
+        if let bodyPartError = bodyPartError {
+            throw bodyPartError
+        }
+
+        var encoded = Data()
+
+        bodyParts.first?.hasInitialBoundary = true
+        bodyParts.last?.hasFinalBoundary = true
+
+        for bodyPart in bodyParts {
+            let encodedData = try encode(bodyPart)
+            encoded.append(encodedData)
+        }
+
+        return encoded
+    }
+
+    /// 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 `AFError` if encoding encounters an error.
+    public func writeEncodedData(to fileURL: URL) throws {
+        if let bodyPartError = bodyPartError {
+            throw bodyPartError
+        }
+
+        if FileManager.default.fileExists(atPath: fileURL.path) {
+            throw AFError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL))
+        } else if !fileURL.isFileURL {
+            throw AFError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL))
+        }
+
+        guard let outputStream = OutputStream(url: fileURL, append: false) else {
+            throw AFError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL))
+        }
+
+        outputStream.open()
+        defer { outputStream.close() }
+
+        self.bodyParts.first?.hasInitialBoundary = true
+        self.bodyParts.last?.hasFinalBoundary = true
+
+        for bodyPart in self.bodyParts {
+            try write(bodyPart, to: outputStream)
+        }
+    }
+
+    // MARK: - Private - Body Part Encoding
+
+    private func encode(_ bodyPart: BodyPart) throws -> Data {
+        var encoded = Data()
+
+        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
+        encoded.append(initialData)
+
+        let headerData = encodeHeaders(for: bodyPart)
+        encoded.append(headerData)
+
+        let bodyStreamData = try encodeBodyStream(for: bodyPart)
+        encoded.append(bodyStreamData)
+
+        if bodyPart.hasFinalBoundary {
+            encoded.append(finalBoundaryData())
+        }
+
+        return encoded
+    }
+
+    private func encodeHeaders(for 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)!
+    }
+
+    private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data {
+        let inputStream = bodyPart.bodyStream
+        inputStream.open()
+        defer { inputStream.close() }
+
+        var encoded = Data()
+
+        while inputStream.hasBytesAvailable {
+            var buffer = [UInt8](repeating: 0, count: streamBufferSize)
+            let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
+
+            if let error = inputStream.streamError {
+                throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error))
+            }
+
+            if bytesRead > 0 {
+                encoded.append(buffer, count: bytesRead)
+            } else {
+                break
+            }
+        }
+
+        return encoded
+    }
+
+    // MARK: - Private - Writing Body Part to Output Stream
+
+    private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws {
+        try writeInitialBoundaryData(for: bodyPart, to: outputStream)
+        try writeHeaderData(for: bodyPart, to: outputStream)
+        try writeBodyStream(for: bodyPart, to: outputStream)
+        try writeFinalBoundaryData(for: bodyPart, to: outputStream)
+    }
+
+    private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
+        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
+        return try write(initialData, to: outputStream)
+    }
+
+    private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
+        let headerData = encodeHeaders(for: bodyPart)
+        return try write(headerData, to: outputStream)
+    }
+
+    private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
+        let inputStream = bodyPart.bodyStream
+
+        inputStream.open()
+        defer { inputStream.close() }
+
+        while inputStream.hasBytesAvailable {
+            var buffer = [UInt8](repeating: 0, count: streamBufferSize)
+            let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
+
+            if let streamError = inputStream.streamError {
+                throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError))
+            }
+
+            if bytesRead > 0 {
+                if buffer.count != bytesRead {
+                    buffer = Array(buffer[0..<bytesRead])
+                }
+
+                try write(&buffer, to: outputStream)
+            } else {
+                break
+            }
+        }
+    }
+
+    private func writeFinalBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
+        if bodyPart.hasFinalBoundary {
+            return try write(finalBoundaryData(), to: outputStream)
+        }
+    }
+
+    // MARK: - Private - Writing Buffered Data to Output Stream
+
+    private func write(_ data: Data, to outputStream: OutputStream) throws {
+        var buffer = [UInt8](repeating: 0, count: data.count)
+        data.copyBytes(to: &buffer, count: data.count)
+
+        return try write(&buffer, to: outputStream)
+    }
+
+    private func write(_ buffer: inout [UInt8], to outputStream: OutputStream) throws {
+        var bytesToWrite = buffer.count
+
+        while bytesToWrite > 0, outputStream.hasSpaceAvailable {
+            let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite)
+
+            if let error = outputStream.streamError {
+                throw AFError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error))
+            }
+
+            bytesToWrite -= bytesWritten
+
+            if bytesToWrite > 0 {
+                buffer = Array(buffer[bytesWritten..<buffer.count])
+            }
+        }
+    }
+
+    // MARK: - Private - Mime Type
+
+    private func mimeType(forPathExtension pathExtension: String) -> 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
+
+    private func contentHeaders(withName name: String, fileName: String? = nil, mimeType: String? = nil) -> [String: String] {
+        var disposition = "form-data; name=\"\(name)\""
+        if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" }
+
+        var headers = ["Content-Disposition": disposition]
+        if let mimeType = mimeType { headers["Content-Type"] = mimeType }
+
+        return headers
+    }
+
+    // MARK: - Private - Boundary Encoding
+
+    private func initialBoundaryData() -> Data {
+        return BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary)
+    }
+
+    private func encapsulatedBoundaryData() -> Data {
+        return BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary)
+    }
+
+    private func finalBoundaryData() -> Data {
+        return BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary)
+    }
+
+    // MARK: - Private - Errors
+
+    private func setBodyPartError(withReason reason: AFError.MultipartEncodingFailureReason) {
+        guard bodyPartError == nil else { return }
+        bodyPartError = AFError.multipartEncodingFailed(reason: reason)
+    }
+}
diff --git a/iOS/Pods/Alamofire/Source/NetworkReachabilityManager.swift b/iOS/Pods/Alamofire/Source/NetworkReachabilityManager.swift
new file mode 100644 (file)
index 0000000..cdb5a02
--- /dev/null
@@ -0,0 +1,233 @@
+//
+//  NetworkReachabilityManager.swift
+//
+//  Copyright (c) 2014-2017 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 !os(watchOS)
+
+import Foundation
+import SystemConfiguration
+
+/// The `NetworkReachabilityManager` class listens for reachability changes of hosts 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 retry
+/// network requests 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.
+public class NetworkReachabilityManager {
+    /// Defines the various states of network reachability.
+    ///
+    /// - unknown:      It is unknown whether the network is reachable.
+    /// - notReachable: The network is not reachable.
+    /// - reachable:    The network is reachable.
+    public enum NetworkReachabilityStatus {
+        case unknown
+        case notReachable
+        case reachable(ConnectionType)
+    }
+
+    /// Defines the various connection types detected by reachability flags.
+    ///
+    /// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi.
+    /// - wwan:           The connection type is a WWAN connection.
+    public enum ConnectionType {
+        case ethernetOrWiFi
+        case wwan
+    }
+
+    /// A closure executed when the network reachability status changes. The closure takes a single argument: the
+    /// network reachability status.
+    public typealias Listener = (NetworkReachabilityStatus) -> Void
+
+    // MARK: - Properties
+
+    /// Whether the network is currently reachable.
+    public var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi }
+
+    /// Whether the network is currently reachable over the WWAN interface.
+    public var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) }
+
+    /// Whether the network is currently reachable over Ethernet or WiFi interface.
+    public var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) }
+
+    /// The current network reachability status.
+    public var networkReachabilityStatus: NetworkReachabilityStatus {
+        guard let flags = self.flags else { return .unknown }
+        return networkReachabilityStatusForFlags(flags)
+    }
+
+    /// The dispatch queue to execute the `listener` closure on.
+    public var listenerQueue: DispatchQueue = DispatchQueue.main
+
+    /// A closure executed when the network reachability status changes.
+    public var listener: Listener?
+
+    private var flags: SCNetworkReachabilityFlags? {
+        var flags = SCNetworkReachabilityFlags()
+
+        if SCNetworkReachabilityGetFlags(reachability, &flags) {
+            return flags
+        }
+
+        return nil
+    }
+
+    private let reachability: SCNetworkReachability
+    private var previousFlags: SCNetworkReachabilityFlags
+
+    // MARK: - Initialization
+
+    /// Creates a `NetworkReachabilityManager` instance with the specified host.
+    ///
+    /// - parameter host: The host used to evaluate network reachability.
+    ///
+    /// - returns: The new `NetworkReachabilityManager` instance.
+    public convenience init?(host: String) {
+        guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil }
+        self.init(reachability: reachability)
+    }
+
+    /// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0.
+    ///
+    /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing
+    /// status of the device, both IPv4 and IPv6.
+    ///
+    /// - returns: The new `NetworkReachabilityManager` instance.
+    public convenience init?() {
+        var address = sockaddr_in()
+        address.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
+        address.sin_family = sa_family_t(AF_INET)
+
+        guard let reachability = withUnsafePointer(to: &address, { pointer in
+            return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout<sockaddr>.size) {
+                return SCNetworkReachabilityCreateWithAddress(nil, $0)
+            }
+        }) else { return nil }
+
+        self.init(reachability: reachability)
+    }
+
+    private init(reachability: SCNetworkReachability) {
+        self.reachability = reachability
+        self.previousFlags = SCNetworkReachabilityFlags()
+    }
+
+    deinit {
+        stopListening()
+    }
+
+    // MARK: - Listening
+
+    /// Starts listening for changes in network reachability status.
+    ///
+    /// - returns: `true` if listening was started successfully, `false` otherwise.
+    @discardableResult
+    public func startListening() -> Bool {
+        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
+        context.info = Unmanaged.passUnretained(self).toOpaque()
+
+        let callbackEnabled = SCNetworkReachabilitySetCallback(
+            reachability,
+            { (_, flags, info) in
+                let reachability = Unmanaged<NetworkReachabilityManager>.fromOpaque(info!).takeUnretainedValue()
+                reachability.notifyListener(flags)
+            },
+            &context
+        )
+
+        let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue)
+
+        listenerQueue.async {
+            self.previousFlags = SCNetworkReachabilityFlags()
+            self.notifyListener(self.flags ?? SCNetworkReachabilityFlags())
+        }
+
+        return callbackEnabled && queueEnabled
+    }
+
+    /// Stops listening for changes in network reachability status.
+    public func stopListening() {
+        SCNetworkReachabilitySetCallback(reachability, nil, nil)
+        SCNetworkReachabilitySetDispatchQueue(reachability, nil)
+    }
+
+    // MARK: - Internal - Listener Notification
+
+    func notifyListener(_ flags: SCNetworkReachabilityFlags) {
+        guard previousFlags != flags else { return }
+        previousFlags = flags
+
+        listener?(networkReachabilityStatusForFlags(flags))
+    }
+
+    // MARK: - Internal - Network Reachability Status
+
+    func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus {
+        guard isNetworkReachable(with: flags) else { return .notReachable }
+
+        var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi)
+
+    #if os(iOS)
+        if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) }
+    #endif
+
+        return networkStatus
+    }
+
+    func isNetworkReachable(with flags: SCNetworkReachabilityFlags) -> Bool {
+        let isReachable = flags.contains(.reachable)
+        let needsConnection = flags.contains(.connectionRequired)
+        let canConnectAutomatically = flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic)
+        let canConnectWithoutUserInteraction = canConnectAutomatically && !flags.contains(.interventionRequired)
+
+        return isReachable && (!needsConnection || canConnectWithoutUserInteraction)
+    }
+}
+
+// MARK: -
+
+extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {}
+
+/// Returns whether the two network reachability status values are equal.
+///
+/// - parameter lhs: The left-hand side value to compare.
+/// - parameter rhs: The right-hand side value to compare.
+///
+/// - returns: `true` if the two values are equal, `false` otherwise.
+public func ==(
+    lhs: NetworkReachabilityManager.NetworkReachabilityStatus,
+    rhs: NetworkReachabilityManager.NetworkReachabilityStatus)
+    -> Bool
+{
+    switch (lhs, rhs) {
+    case (.unknown, .unknown):
+        return true
+    case (.notReachable, .notReachable):
+        return true
+    case let (.reachable(lhsConnectionType), .reachable(rhsConnectionType)):
+        return lhsConnectionType == rhsConnectionType
+    default:
+        return false
+    }
+}
+
+#endif
diff --git a/iOS/Pods/Alamofire/Source/Notifications.swift b/iOS/Pods/Alamofire/Source/Notifications.swift
new file mode 100644 (file)
index 0000000..df41505
--- /dev/null
@@ -0,0 +1,52 @@
+//
+//  Notifications.swift
+//
+//  Copyright (c) 2014-2017 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 Notification.Name {
+    /// Used as a namespace for all `URLSessionTask` related notifications.
+    public struct Task {
+        /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`.
+        public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume")
+
+        /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`.
+        public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend")
+
+        /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`.
+        public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel")
+
+        /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`.
+        public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete")
+    }
+}
+
+// MARK: -
+
+extension Notification {
+    /// Used as a namespace for all `Notification` user info dictionary keys.
+    public struct Key {
+        /// User info dictionary key representing the `URLSessionTask` associated with the notification.
+        public static let Task = "org.alamofire.notification.key.task"
+    }
+}
diff --git a/iOS/Pods/Alamofire/Source/ParameterEncoding.swift b/iOS/Pods/Alamofire/Source/ParameterEncoding.swift
new file mode 100644 (file)
index 0000000..dabb562
--- /dev/null
@@ -0,0 +1,432 @@
+//
+//  ParameterEncoding.swift
+//
+//  Copyright (c) 2014-2017 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 HTTPMethod: String {
+    case options = "OPTIONS"
+    case get     = "GET"
+    case head    = "HEAD"
+    case post    = "POST"
+    case put     = "PUT"
+    case patch   = "PATCH"
+    case delete  = "DELETE"
+    case trace   = "TRACE"
+    case connect = "CONNECT"
+}
+
+// MARK: -
+
+/// A dictionary of parameters to apply to a `URLRequest`.
+public typealias Parameters = [String: Any]
+
+/// A type used to define how a set of parameters are applied to a `URLRequest`.
+public protocol ParameterEncoding {
+    /// 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.
+    ///
+    /// - throws: An `AFError.parameterEncodingFailed` error if encoding fails.
+    ///
+    /// - returns: The encoded request.
+    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest
+}
+
+// MARK: -
+
+/// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP
+/// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as
+/// the HTTP body depends on the destination of the encoding.
+///
+/// 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`).
+public struct URLEncoding: ParameterEncoding {
+
+    // MARK: Helper Types
+
+    /// Defines whether the url-encoded query string is applied to the existing query string or HTTP body of the
+    /// resulting URL request.
+    ///
+    /// - methodDependent: Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE`
+    ///                    requests and sets as the HTTP body for requests with any other HTTP method.
+    /// - queryString:     Sets or appends encoded query string result to existing query string.
+    /// - httpBody:        Sets encoded query string result as the HTTP body of the URL request.
+    public enum Destination {
+        case methodDependent, queryString, httpBody
+    }
+
+    // MARK: Properties
+
+    /// Returns a default `URLEncoding` instance.
+    public static var `default`: URLEncoding { return URLEncoding() }
+
+    /// Returns a `URLEncoding` instance with a `.methodDependent` destination.
+    public static var methodDependent: URLEncoding { return URLEncoding() }
+
+    /// Returns a `URLEncoding` instance with a `.queryString` destination.
+    public static var queryString: URLEncoding { return URLEncoding(destination: .queryString) }
+
+    /// Returns a `URLEncoding` instance with an `.httpBody` destination.
+    public static var httpBody: URLEncoding { return URLEncoding(destination: .httpBody) }
+
+    /// The destination defining where the encoded query string is to be applied to the URL request.
+    public let destination: Destination
+
+    // MARK: Initialization
+
+    /// Creates a `URLEncoding` instance using the specified destination.
+    ///
+    /// - parameter destination: The destination defining where the encoded query string is to be applied.
+    ///
+    /// - returns: The new `URLEncoding` instance.
+    public init(destination: Destination = .methodDependent) {
+        self.destination = destination
+    }
+
+    // MARK: Encoding
+
+    /// 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.
+    ///
+    /// - throws: An `Error` if the encoding process encounters an error.
+    ///
+    /// - returns: The encoded request.
+    public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
+        var urlRequest = try urlRequest.asURLRequest()
+
+        guard let parameters = parameters else { return urlRequest }
+
+        if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
+            guard let url = urlRequest.url else {
+                throw AFError.parameterEncodingFailed(reason: .missingURL)
+            }
+
+            if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
+                let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
+                urlComponents.percentEncodedQuery = percentEncodedQuery
+                urlRequest.url = urlComponents.url
+            }
+        } else {
+            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
+                urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
+            }
+
+            urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
+        }
+
+        return urlRequest
+    }
+
+    /// 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(fromKey key: String, value: Any) -> [(String, String)] {
+        var components: [(String, String)] = []
+
+        if let dictionary = value as? [String: Any] {
+            for (nestedKey, value) in dictionary {
+                components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
+            }
+        } else if let array = value as? [Any] {
+            for value in array {
+                components += queryComponents(fromKey: "\(key)[]", value: value)
+            }
+        } else if let value = value as? NSNumber {
+            if value.isBool {
+                components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
+            } else {
+                components.append((escape(key), escape("\(value)")))
+            }
+        } else if let bool = value as? Bool {
+            components.append((escape(key), escape((bool ? "1" : "0"))))
+        } 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 = "!$&'()*+,;="
+
+        var allowedCharacterSet = CharacterSet.urlQueryAllowed
+        allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
+
+        var escaped = ""
+
+        //==========================================================================================================
+        //
+        //  Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few
+        //  hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no
+        //  longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more
+        //  info, please refer to:
+        //
+        //      - https://github.com/Alamofire/Alamofire/issues/206
+        //
+        //==========================================================================================================
+
+        if #available(iOS 8.3, *) {
+            escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
+        } else {
+            let batchSize = 50
+            var index = string.startIndex
+
+            while index != string.endIndex {
+                let startIndex = index
+                let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex
+                let range = startIndex..<endIndex
+
+                let substring = string[range]
+
+                escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? String(substring)
+
+                index = endIndex
+            }
+        }
+
+        return escaped
+    }
+
+    private func query(_ parameters: [String: Any]) -> String {
+        var components: [(String, String)] = []
+
+        for key in parameters.keys.sorted(by: <) {
+            let value = parameters[key]!
+            components += queryComponents(fromKey: key, value: value)
+        }
+        return components.map { "\($0)=\($1)" }.joined(separator: "&")
+    }
+
+    private func encodesParametersInURL(with method: HTTPMethod) -> Bool {
+        switch destination {
+        case .queryString:
+            return true
+        case .httpBody:
+            return false
+        default:
+            break
+        }
+
+        switch method {
+        case .get, .head, .delete:
+            return true
+        default:
+            return false
+        }
+    }
+}
+
+// MARK: -
+
+/// Uses `JSONSerialization` 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`.
+public struct JSONEncoding: ParameterEncoding {
+
+    // MARK: Properties
+
+    /// Returns a `JSONEncoding` instance with default writing options.
+    public static var `default`: JSONEncoding { return JSONEncoding() }
+
+    /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options.
+    public static var prettyPrinted: JSONEncoding { return JSONEncoding(options: .prettyPrinted) }
+
+    /// The options for writing the parameters as JSON data.
+    public let options: JSONSerialization.WritingOptions
+
+    // MARK: Initialization
+
+    /// Creates a `JSONEncoding` instance using the specified options.
+    ///
+    /// - parameter options: The options for writing the parameters as JSON data.
+    ///
+    /// - returns: The new `JSONEncoding` instance.
+    public init(options: JSONSerialization.WritingOptions = []) {
+        self.options = options
+    }
+
+    // MARK: Encoding
+
+    /// 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.
+    ///
+    /// - throws: An `Error` if the encoding process encounters an error.
+    ///
+    /// - returns: The encoded request.
+    public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
+        var urlRequest = try urlRequest.asURLRequest()
+
+        guard let parameters = parameters else { return urlRequest }
+
+        do {
+            let data = try JSONSerialization.data(withJSONObject: parameters, options: options)
+
+            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
+                urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
+            }
+
+            urlRequest.httpBody = data
+        } catch {
+            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
+        }
+
+        return urlRequest
+    }
+
+    /// Creates a URL request by encoding the JSON object and setting the resulting data on the HTTP body.
+    ///
+    /// - parameter urlRequest: The request to apply the JSON object to.
+    /// - parameter jsonObject: The JSON object to apply to the request.
+    ///
+    /// - throws: An `Error` if the encoding process encounters an error.
+    ///
+    /// - returns: The encoded request.
+    public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest {
+        var urlRequest = try urlRequest.asURLRequest()
+
+        guard let jsonObject = jsonObject else { return urlRequest }
+
+        do {
+            let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options)
+
+            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
+                urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
+            }
+
+            urlRequest.httpBody = data
+        } catch {
+            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
+        }
+
+        return urlRequest
+    }
+}
+
+// MARK: -
+
+/// Uses `PropertyListSerialization` 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`.
+public struct PropertyListEncoding: ParameterEncoding {
+
+    // MARK: Properties
+
+    /// Returns a default `PropertyListEncoding` instance.
+    public static var `default`: PropertyListEncoding { return PropertyListEncoding() }
+
+    /// Returns a `PropertyListEncoding` instance with xml formatting and default writing options.
+    public static var xml: PropertyListEncoding { return PropertyListEncoding(format: .xml) }
+
+    /// Returns a `PropertyListEncoding` instance with binary formatting and default writing options.
+    public static var binary: PropertyListEncoding { return PropertyListEncoding(format: .binary) }
+
+    /// The property list serialization format.
+    public let format: PropertyListSerialization.PropertyListFormat
+
+    /// The options for writing the parameters as plist data.
+    public let options: PropertyListSerialization.WriteOptions
+
+    // MARK: Initialization
+
+    /// Creates a `PropertyListEncoding` instance using the specified format and options.
+    ///
+    /// - parameter format:  The property list serialization format.
+    /// - parameter options: The options for writing the parameters as plist data.
+    ///
+    /// - returns: The new `PropertyListEncoding` instance.
+    public init(
+        format: PropertyListSerialization.PropertyListFormat = .xml,
+        options: PropertyListSerialization.WriteOptions = 0)
+    {
+        self.format = format
+        self.options = options
+    }
+
+    // MARK: Encoding
+
+    /// 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.
+    ///
+    /// - throws: An `Error` if the encoding process encounters an error.
+    ///
+    /// - returns: The encoded request.
+    public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
+        var urlRequest = try urlRequest.asURLRequest()
+
+        guard let parameters = parameters else { return urlRequest }
+
+        do {
+            let data = try PropertyListSerialization.data(
+                fromPropertyList: parameters,
+                format: format,
+                options: options
+            )
+
+            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
+                urlRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type")
+            }
+
+            urlRequest.httpBody = data
+        } catch {
+            throw AFError.parameterEncodingFailed(reason: .propertyListEncodingFailed(error: error))
+        }
+
+        return urlRequest
+    }
+}
+
+// MARK: -
+
+extension NSNumber {
+    fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) }
+}
diff --git a/iOS/Pods/Alamofire/Source/Request.swift b/iOS/Pods/Alamofire/Source/Request.swift
new file mode 100644 (file)
index 0000000..d93c52d
--- /dev/null
@@ -0,0 +1,653 @@
+//
+//  Request.swift
+//
+//  Copyright (c) 2014-2017 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
+
+/// A type that can inspect and optionally adapt a `URLRequest` in some manner if necessary.
+public protocol RequestAdapter {
+    /// Inspects and adapts the specified `URLRequest` in some manner if necessary and returns the result.
+    ///
+    /// - parameter urlRequest: The URL request to adapt.
+    ///
+    /// - throws: An `Error` if the adaptation encounters an error.
+    ///
+    /// - returns: The adapted `URLRequest`.
+    func adapt(_ urlRequest: URLRequest) throws -> URLRequest
+}
+
+// MARK: -
+
+/// A closure executed when the `RequestRetrier` determines whether a `Request` should be retried or not.
+public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void
+
+/// A type that determines whether a request should be retried after being executed by the specified session manager
+/// and encountering an error.
+public protocol RequestRetrier {
+    /// Determines whether the `Request` should be retried by calling the `completion` closure.
+    ///
+    /// This operation is fully asynchronous. Any amount of time can be taken to determine whether the request needs
+    /// to be retried. The one requirement is that the completion closure is called to ensure the request is properly
+    /// cleaned up after.
+    ///
+    /// - parameter manager:    The session manager the request was executed on.
+    /// - parameter request:    The request that failed due to the encountered error.
+    /// - parameter error:      The error encountered when executing the request.
+    /// - parameter completion: The completion closure to be executed when retry decision has been determined.
+    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
+}
+
+// MARK: -
+
+protocol TaskConvertible {
+    func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask
+}
+
+/// A dictionary of headers to apply to a `URLRequest`.
+public typealias HTTPHeaders = [String: String]
+
+// MARK: -
+
+/// Responsible for sending a request and receiving the response and associated data from the server, as well as
+/// managing its underlying `URLSessionTask`.
+open class Request {
+
+    // MARK: Helper Types
+
+    /// A closure executed when monitoring upload or download progress of a request.
+    public typealias ProgressHandler = (Progress) -> Void
+
+    enum RequestTask {
+        case data(TaskConvertible?, URLSessionTask?)
+        case download(TaskConvertible?, URLSessionTask?)
+        case upload(TaskConvertible?, URLSessionTask?)
+        case stream(TaskConvertible?, URLSessionTask?)
+    }
+
+    // MARK: Properties
+
+    /// The delegate for the underlying task.
+    open internal(set) var delegate: TaskDelegate {
+        get {
+            taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
+            return taskDelegate
+        }
+        set {
+            taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
+            taskDelegate = newValue
+        }
+    }
+
+    /// 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: URLRequest? { return task?.originalRequest }
+
+    /// The response received from the server, if any.
+    open var response: HTTPURLResponse? { return task?.response as? HTTPURLResponse }
+
+    /// The number of times the request has been retried.
+    open internal(set) var retryCount: UInt = 0
+
+    let originalTask: TaskConvertible?
+
+    var startTime: CFAbsoluteTime?
+    var endTime: CFAbsoluteTime?
+
+    var validations: [() -> Void] = []
+
+    private var taskDelegate: TaskDelegate
+    private var taskDelegateLock = NSLock()
+
+    // MARK: Lifecycle
+
+    init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
+        self.session = session
+
+        switch requestTask {
+        case .data(let originalTask, let task):
+            taskDelegate = DataTaskDelegate(task: task)
+            self.originalTask = originalTask
+        case .download(let originalTask, let task):
+            taskDelegate = DownloadTaskDelegate(task: task)
+            self.originalTask = originalTask
+        case .upload(let originalTask, let task):
+            taskDelegate = UploadTaskDelegate(task: task)
+            self.originalTask = originalTask
+        case .stream(let originalTask, let task):
+            taskDelegate = TaskDelegate(task: task)
+            self.originalTask = originalTask
+        }
+
+        delegate.error = error
+        delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
+    }
+
+    // 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.
+    @discardableResult
+    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.
+    @discardableResult
+    open func authenticate(usingCredential credential: URLCredential) -> Self {
+        delegate.credential = credential
+        return self
+    }
+
+    /// Returns a base64 encoded basic authentication credential as an authorization header tuple.
+    ///
+    /// - parameter user:     The user.
+    /// - parameter password: The password.
+    ///
+    /// - returns: A tuple with Authorization header and credential value if encoding succeeds, `nil` otherwise.
+    open static func authorizationHeader(user: String, password: String) -> (key: String, value: String)? {
+        guard let data = "\(user):\(password)".data(using: .utf8) else { return nil }
+
+        let credential = data.base64EncodedString(options: [])
+
+        return (key: "Authorization", value: "Basic \(credential)")
+    }
+
+    // MARK: State
+
+    /// Resumes the request.
+    open func resume() {
+        guard let task = task else { delegate.queue.isSuspended = false ; return }
+
+        if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }
+
+        task.resume()
+
+        NotificationCenter.default.post(
+            name: Notification.Name.Task.DidResume,
+            object: self,
+            userInfo: [Notification.Key.Task: task]
+        )
+    }
+
+    /// Suspends the request.
+    open func suspend() {
+        guard let task = task else { return }
+
+        task.suspend()
+
+        NotificationCenter.default.post(
+            name: Notification.Name.Task.DidSuspend,
+            object: self,
+            userInfo: [Notification.Key.Task: task]
+        )
+    }
+
+    /// Cancels the request.
+    open func cancel() {
+        guard let task = task else { return }
+
+        task.cancel()
+
+        NotificationCenter.default.post(
+            name: Notification.Name.Task.DidCancel,
+            object: self,
+            userInfo: [Notification.Key.Task: task]
+        )
+    }
+}
+
+// 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.
+    open 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 {
+    /// The textual representation used when written to an output stream, in the form of a cURL command.
+    open var debugDescription: String {
+        return cURLRepresentation()
+    }
+
+    func cURLRepresentation() -> String {
+        var components = ["$ curl -v"]
+
+        guard let request = self.request,
+              let url = request.url,
+              let host = url.host
+        else {
+            return "$ curl command could not be created"
+        }
+
+        if let httpMethod = request.httpMethod, httpMethod != "GET" {
+            components.append("-X \(httpMethod)")
+        }
+
+        if let credentialStorage = self.session.configuration.urlCredentialStorage {
+            let protectionSpace = URLProtectionSpace(
+                host: host,
+                port: url.port ?? 0,
+                protocol: url.scheme,
+                realm: host,
+                authenticationMethod: NSURLAuthenticationMethodHTTPBasic
+            )
+
+            if let credentials = credentialStorage.credentials(for: protectionSpace)?.values {
+                for credential in credentials {
+                    guard let user = credential.user, let password = credential.password else { continue }
+                    components.append("-u \(user):\(password)")
+                }
+            } else {
+                if let credential = delegate.credential, let user = credential.user, let password = credential.password {
+                    components.append("-u \(user):\(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);" }
+
+            #if swift(>=3.2)
+                components.append("-b \"\(string[..<string.index(before: string.endIndex)])\"")
+            #else
+                components.append("-b \"\(string.substring(to: string.characters.index(before: string.endIndex)))\"")
+            #endif
+            }
+        }
+
+        var headers: [AnyHashable: Any] = [:]
+
+        if let additionalHeaders = session.configuration.httpAdditionalHeaders {
+            for (field, value) in additionalHeaders where field != AnyHashable("Cookie") {
+                headers[field] = value
+            }
+        }
+
+        if let headerFields = request.allHTTPHeaderFields {
+            for (field, value) in headerFields where field != "Cookie" {
+                headers[field] = value
+            }
+        }
+
+        for (field, value) in headers {
+            components.append("-H \"\(field): \(value)\"")
+        }
+
+        if let httpBodyData = request.httpBody, let httpBody = String(data: httpBodyData, encoding: .utf8) {
+            var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"")
+            escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"")
+
+            components.append("-d \"\(escapedBody)\"")
+        }
+
+        components.append("\"\(url.absoluteString)\"")
+
+        return components.joined(separator: " \\\n\t")
+    }
+}
+
+// MARK: -
+
+/// Specific type of `Request` that manages an underlying `URLSessionDataTask`.
+open class DataRequest: Request {
+
+    // MARK: Helper Types
+
+    struct Requestable: TaskConvertible {
+        let urlRequest: URLRequest
+
+        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
+            do {
+                let urlRequest = try self.urlRequest.adapt(using: adapter)
+                return queue.sync { session.dataTask(with: urlRequest) }
+            } catch {
+                throw AdaptError(error: error)
+            }
+        }
+    }
+
+    // MARK: Properties
+
+    /// The request sent or to be sent to the server.
+    open override var request: URLRequest? {
+        if let request = super.request { return request }
+        if let requestable = originalTask as? Requestable { return requestable.urlRequest }
+
+        return nil
+    }
+
+    /// The progress of fetching the response data from the server for the request.
+    open var progress: Progress { return dataDelegate.progress }
+
+    var dataDelegate: DataTaskDelegate { return delegate as! DataTaskDelegate }
+
+    // MARK: Stream
+
+    /// 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 server data in any `Response` object will be `nil`.
+    ///
+    /// - parameter closure: The code to be executed periodically during the lifecycle of the request.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    open func stream(closure: ((Data) -> Void)? = nil) -> Self {
+        dataDelegate.dataStream = closure
+        return self
+    }
+
+    // MARK: Progress
+
+    /// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
+    ///
+    /// - parameter queue:   The dispatch queue to execute the closure on.
+    /// - parameter closure: The code to be executed periodically as data is read from the server.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
+        dataDelegate.progressHandler = (closure, queue)
+        return self
+    }
+}
+
+// MARK: -
+
+/// Specific type of `Request` that manages an underlying `URLSessionDownloadTask`.
+open class DownloadRequest: Request {
+
+    // MARK: Helper Types
+
+    /// A collection of options to be executed prior to moving a downloaded file from the temporary URL to the
+    /// destination URL.
+    public struct DownloadOptions: OptionSet {
+        /// Returns the raw bitmask value of the option and satisfies the `RawRepresentable` protocol.
+        public let rawValue: UInt
+
+        /// A `DownloadOptions` flag that creates intermediate directories for the destination URL if specified.
+        public static let createIntermediateDirectories = DownloadOptions(rawValue: 1 << 0)
+
+        /// A `DownloadOptions` flag that removes a previous file from the destination URL if specified.
+        public static let removePreviousFile = DownloadOptions(rawValue: 1 << 1)
+
+        /// Creates a `DownloadFileDestinationOptions` instance with the specified raw value.
+        ///
+        /// - parameter rawValue: The raw bitmask value for the option.
+        ///
+        /// - returns: A new log level instance.
+        public init(rawValue: UInt) {
+            self.rawValue = rawValue
+        }
+    }
+
+    /// A closure executed once a download 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 two arguments: the file URL where the temporary file should be moved and
+    /// the options defining how the file should be moved.
+    public typealias DownloadFileDestination = (
+        _ temporaryURL: URL,
+        _ response: HTTPURLResponse)
+        -> (destinationURL: URL, options: DownloadOptions)
+
+    enum Downloadable: TaskConvertible {
+        case request(URLRequest)
+        case resumeData(Data)
+
+        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
+            do {
+                let task: URLSessionTask
+
+                switch self {
+                case let .request(urlRequest):
+                    let urlRequest = try urlRequest.adapt(using: adapter)
+                    task = queue.sync { session.downloadTask(with: urlRequest) }
+                case let .resumeData(resumeData):
+                    task = queue.sync { session.downloadTask(withResumeData: resumeData) }
+                }
+
+                return task
+            } catch {
+                throw AdaptError(error: error)
+            }
+        }
+    }
+
+    // MARK: Properties
+
+    /// The request sent or to be sent to the server.
+    open override var request: URLRequest? {
+        if let request = super.request { return request }
+
+        if let downloadable = originalTask as? Downloadable, case let .request(urlRequest) = downloadable {
+            return urlRequest
+        }
+
+        return nil
+    }
+
+    /// The resume data of the underlying download task if available after a failure.
+    open var resumeData: Data? { return downloadDelegate.resumeData }
+
+    /// The progress of downloading the response data from the server for the request.
+    open var progress: Progress { return downloadDelegate.progress }
+
+    var downloadDelegate: DownloadTaskDelegate { return delegate as! DownloadTaskDelegate }
+
+    // MARK: State
+
+    /// Cancels the request.
+    open override func cancel() {
+        downloadDelegate.downloadTask.cancel { self.downloadDelegate.resumeData = $0 }
+
+        NotificationCenter.default.post(
+            name: Notification.Name.Task.DidCancel,
+            object: self,
+            userInfo: [Notification.Key.Task: task as Any]
+        )
+    }
+
+    // MARK: Progress
+
+    /// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
+    ///
+    /// - parameter queue:   The dispatch queue to execute the closure on.
+    /// - parameter closure: The code to be executed periodically as data is read from the server.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
+        downloadDelegate.progressHandler = (closure, queue)
+        return self
+    }
+
+    // MARK: Destination
+
+    /// 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.
+    open class func suggestedDownloadDestination(
+        for directory: FileManager.SearchPathDirectory = .documentDirectory,
+        in domain: FileManager.SearchPathDomainMask = .userDomainMask)
+        -> DownloadFileDestination
+    {
+        return { temporaryURL, response in
+            let directoryURLs = FileManager.default.urls(for: directory, in: domain)
+
+            if !directoryURLs.isEmpty {
+                return (directoryURLs[0].appendingPathComponent(response.suggestedFilename!), [])
+            }
+
+            return (temporaryURL, [])
+        }
+    }
+}
+
+// MARK: -
+
+/// Specific type of `Request` that manages an underlying `URLSessionUploadTask`.
+open class UploadRequest: DataRequest {
+
+    // MARK: Helper Types
+
+    enum Uploadable: TaskConvertible {
+        case data(Data, URLRequest)
+        case file(URL, URLRequest)
+        case stream(InputStream, URLRequest)
+
+        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
+            do {
+                let task: URLSessionTask
+
+                switch self {
+                case let .data(data, urlRequest):
+                    let urlRequest = try urlRequest.adapt(using: adapter)
+                    task = queue.sync { session.uploadTask(with: urlRequest, from: data) }
+                case let .file(url, urlRequest):
+                    let urlRequest = try urlRequest.adapt(using: adapter)
+                    task = queue.sync { session.uploadTask(with: urlRequest, fromFile: url) }
+                case let .stream(_, urlRequest):
+                    let urlRequest = try urlRequest.adapt(using: adapter)
+                    task = queue.sync { session.uploadTask(withStreamedRequest: urlRequest) }
+                }
+
+                return task
+            } catch {
+                throw AdaptError(error: error)
+            }
+        }
+    }
+
+    // MARK: Properties
+
+    /// The request sent or to be sent to the server.
+    open override var request: URLRequest? {
+        if let request = super.request { return request }
+
+        guard let uploadable = originalTask as? Uploadable else { return nil }
+
+        switch uploadable {
+        case .data(_, let urlRequest), .file(_, let urlRequest), .stream(_, let urlRequest):
+            return urlRequest
+        }
+    }
+
+    /// The progress of uploading the payload to the server for the upload request.
+    open var uploadProgress: Progress { return uploadDelegate.uploadProgress }
+
+    var uploadDelegate: UploadTaskDelegate { return delegate as! UploadTaskDelegate }
+
+    // MARK: Upload Progress
+
+    /// Sets a closure to be called periodically during the lifecycle of the `UploadRequest` as data is sent to
+    /// the server.
+    ///
+    /// After the data is sent to the server, the `progress(queue:closure:)` APIs can be used to monitor the progress
+    /// of data being read from the server.
+    ///
+    /// - parameter queue:   The dispatch queue to execute the closure on.
+    /// - parameter closure: The code to be executed periodically as data is sent to the server.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    open func uploadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
+        uploadDelegate.uploadProgressHandler = (closure, queue)
+        return self
+    }
+}
+
+// MARK: -
+
+#if !os(watchOS)
+
+/// Specific type of `Request` that manages an underlying `URLSessionStreamTask`.
+@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+open class StreamRequest: Request {
+    enum Streamable: TaskConvertible {
+        case stream(hostName: String, port: Int)
+        case netService(NetService)
+
+        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
+            let task: URLSessionTask
+
+            switch self {
+            case let .stream(hostName, port):
+                task = queue.sync { session.streamTask(withHostName: hostName, port: port) }
+            case let .netService(netService):
+                task = queue.sync { session.streamTask(with: netService) }
+            }
+
+            return task
+        }
+    }
+}
+
+#endif
diff --git a/iOS/Pods/Alamofire/Source/Response.swift b/iOS/Pods/Alamofire/Source/Response.swift
new file mode 100644 (file)
index 0000000..814662c
--- /dev/null
@@ -0,0 +1,567 @@
+//
+//  Response.swift
+//
+//  Copyright (c) 2014-2017 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 store all data associated with an non-serialized response of a data or upload request.
+public struct DefaultDataResponse {
+    /// The URL request sent to the server.
+    public let request: URLRequest?
+
+    /// The server's response to the URL request.
+    public let response: HTTPURLResponse?
+
+    /// The data returned by the server.
+    public let data: Data?
+
+    /// The error encountered while executing or validating the request.
+    public let error: Error?
+
+    /// The timeline of the complete lifecycle of the request.
+    public let timeline: Timeline
+
+    var _metrics: AnyObject?
+
+    /// Creates a `DefaultDataResponse` instance from the specified parameters.
+    ///
+    /// - Parameters:
+    ///   - request:  The URL request sent to the server.
+    ///   - response: The server's response to the URL request.
+    ///   - data:     The data returned by the server.
+    ///   - error:    The error encountered while executing or validating the request.
+    ///   - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
+    ///   - metrics:  The task metrics containing the request / response statistics. `nil` by default.
+    public init(
+        request: URLRequest?,
+        response: HTTPURLResponse?,
+        data: Data?,
+        error: Error?,
+        timeline: Timeline = Timeline(),
+        metrics: AnyObject? = nil)
+    {
+        self.request = request
+        self.response = response
+        self.data = data
+        self.error = error
+        self.timeline = timeline
+    }
+}
+
+// MARK: -
+
+/// Used to store all data associated with a serialized response of a data or upload request.
+public struct DataResponse<Value> {
+    /// The URL request sent to the server.
+    public let request: URLRequest?
+
+    /// The server's response to the URL request.
+    public let response: HTTPURLResponse?
+
+    /// The data returned by the server.
+    public let data: Data?
+
+    /// The result of response serialization.
+    public let result: Result<Value>
+
+    /// The timeline of the complete lifecycle of the request.
+    public let timeline: Timeline
+
+    /// Returns the associated value of the result if it is a success, `nil` otherwise.
+    public var value: Value? { return result.value }
+
+    /// Returns the associated error value if the result if it is a failure, `nil` otherwise.
+    public var error: Error? { return result.error }
+
+    var _metrics: AnyObject?
+
+    /// Creates a `DataResponse` instance with the specified parameters derived from response serialization.
+    ///
+    /// - parameter request:  The URL request sent to the server.
+    /// - parameter response: The server's response to the URL request.
+    /// - parameter data:     The data returned by the server.
+    /// - parameter result:   The result of response serialization.
+    /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
+    ///
+    /// - returns: The new `DataResponse` instance.
+    public init(
+        request: URLRequest?,
+        response: HTTPURLResponse?,
+        data: Data?,
+        result: Result<Value>,
+        timeline: Timeline = Timeline())
+    {
+        self.request = request
+        self.response = response
+        self.data = data
+        self.result = result
+        self.timeline = timeline
+    }
+}
+
+// MARK: -
+
+extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible {
+    /// The textual representation used when written to an output stream, which includes whether the result was a
+    /// success or failure.
+    public var description: String {
+        return result.debugDescription
+    }
+
+    /// The debug textual representation used when written to an output stream, which includes the URL request, the URL
+    /// response, the server data, the response serialization result and the timeline.
+    public var debugDescription: String {
+        var output: [String] = []
+
+        output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil")
+        output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")
+        output.append("[Data]: \(data?.count ?? 0) bytes")
+        output.append("[Result]: \(result.debugDescription)")
+        output.append("[Timeline]: \(timeline.debugDescription)")
+
+        return output.joined(separator: "\n")
+    }
+}
+
+// MARK: -
+
+extension DataResponse {
+    /// Evaluates the specified closure when the result of this `DataResponse` is a success, passing the unwrapped
+    /// result value as a parameter.
+    ///
+    /// Use the `map` method with a closure that does not throw. For example:
+    ///
+    ///     let possibleData: DataResponse<Data> = ...
+    ///     let possibleInt = possibleData.map { $0.count }
+    ///
+    /// - parameter transform: A closure that takes the success value of the instance's result.
+    ///
+    /// - returns: A `DataResponse` whose result wraps the value returned by the given closure. If this instance's
+    ///            result is a failure, returns a response wrapping the same failure.
+    public func map<T>(_ transform: (Value) -> T) -> DataResponse<T> {
+        var response = DataResponse<T>(
+            request: request,
+            response: self.response,
+            data: data,
+            result: result.map(transform),
+            timeline: timeline
+        )
+
+        response._metrics = _metrics
+
+        return response
+    }
+
+    /// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result
+    /// value as a parameter.
+    ///
+    /// Use the `flatMap` method with a closure that may throw an error. For example:
+    ///
+    ///     let possibleData: DataResponse<Data> = ...
+    ///     let possibleObject = possibleData.flatMap {
+    ///         try JSONSerialization.jsonObject(with: $0)
+    ///     }
+    ///
+    /// - parameter transform: A closure that takes the success value of the instance's result.
+    ///
+    /// - returns: A success or failure `DataResponse` depending on the result of the given closure. If this instance's
+    ///            result is a failure, returns the same failure.
+    public func flatMap<T>(_ transform: (Value) throws -> T) -> DataResponse<T> {
+        var response = DataResponse<T>(
+            request: request,
+            response: self.response,
+            data: data,
+            result: result.flatMap(transform),
+            timeline: timeline
+        )
+
+        response._metrics = _metrics
+
+        return response
+    }
+
+    /// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter.
+    ///
+    /// Use the `mapError` function with a closure that does not throw. For example:
+    ///
+    ///     let possibleData: DataResponse<Data> = ...
+    ///     let withMyError = possibleData.mapError { MyError.error($0) }
+    ///
+    /// - Parameter transform: A closure that takes the error of the instance.
+    /// - Returns: A `DataResponse` instance containing the result of the transform.
+    public func mapError<E: Error>(_ transform: (Error) -> E) -> DataResponse {
+        var response = DataResponse(
+            request: request,
+            response: self.response,
+            data: data,
+            result: result.mapError(transform),
+            timeline: timeline
+        )
+
+        response._metrics = _metrics
+
+        return response
+    }
+
+    /// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter.
+    ///
+    /// Use the `flatMapError` function with a closure that may throw an error. For example:
+    ///
+    ///     let possibleData: DataResponse<Data> = ...
+    ///     let possibleObject = possibleData.flatMapError {
+    ///         try someFailableFunction(taking: $0)
+    ///     }
+    ///
+    /// - Parameter transform: A throwing closure that takes the error of the instance.
+    ///
+    /// - Returns: A `DataResponse` instance containing the result of the transform.
+    public func flatMapError<E: Error>(_ transform: (Error) throws -> E) -> DataResponse {
+        var response = DataResponse(
+            request: request,
+            response: self.response,
+            data: data,
+            result: result.flatMapError(transform),
+            timeline: timeline
+        )
+
+        response._metrics = _metrics
+
+        return response
+    }
+}
+
+// MARK: -
+
+/// Used to store all data associated with an non-serialized response of a download request.
+public struct DefaultDownloadResponse {
+    /// The URL request sent to the server.
+    public let request: URLRequest?
+
+    /// The server's response to the URL request.
+    public let response: HTTPURLResponse?
+
+    /// The temporary destination URL of the data returned from the server.
+    public let temporaryURL: URL?
+
+    /// The final destination URL of the data returned from the server if it was moved.
+    public let destinationURL: URL?
+
+    /// The resume data generated if the request was cancelled.
+    public let resumeData: Data?
+
+    /// The error encountered while executing or validating the request.
+    public let error: Error?
+
+    /// The timeline of the complete lifecycle of the request.
+    public let timeline: Timeline
+
+    var _metrics: AnyObject?
+
+    /// Creates a `DefaultDownloadResponse` instance from the specified parameters.
+    ///
+    /// - Parameters:
+    ///   - request:        The URL request sent to the server.
+    ///   - response:       The server's response to the URL request.
+    ///   - temporaryURL:   The temporary destination URL of the data returned from the server.
+    ///   - destinationURL: The final destination URL of the data returned from the server if it was moved.
+    ///   - resumeData:     The resume data generated if the request was cancelled.
+    ///   - error:          The error encountered while executing or validating the request.
+    ///   - timeline:       The timeline of the complete lifecycle of the request. `Timeline()` by default.
+    ///   - metrics:        The task metrics containing the request / response statistics. `nil` by default.
+    public init(
+        request: URLRequest?,
+        response: HTTPURLResponse?,
+        temporaryURL: URL?,
+        destinationURL: URL?,
+        resumeData: Data?,
+        error: Error?,
+        timeline: Timeline = Timeline(),
+        metrics: AnyObject? = nil)
+    {
+        self.request = request
+        self.response = response
+        self.temporaryURL = temporaryURL
+        self.destinationURL = destinationURL
+        self.resumeData = resumeData
+        self.error = error
+        self.timeline = timeline
+    }
+}
+
+// MARK: -
+
+/// Used to store all data associated with a serialized response of a download request.
+public struct DownloadResponse<Value> {
+    /// The URL request sent to the server.
+    public let request: URLRequest?
+
+    /// The server's response to the URL request.
+    public let response: HTTPURLResponse?
+
+    /// The temporary destination URL of the data returned from the server.
+    public let temporaryURL: URL?
+
+    /// The final destination URL of the data returned from the server if it was moved.
+    public let destinationURL: URL?
+
+    /// The resume data generated if the request was cancelled.
+    public let resumeData: Data?
+
+    /// The result of response serialization.
+    public let result: Result<Value>
+
+    /// The timeline of the complete lifecycle of the request.
+    public let timeline: Timeline
+
+    /// Returns the associated value of the result if it is a success, `nil` otherwise.
+    public var value: Value? { return result.value }
+
+    /// Returns the associated error value if the result if it is a failure, `nil` otherwise.
+    public var error: Error? { return result.error }
+
+    var _metrics: AnyObject?
+
+    /// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization.
+    ///
+    /// - parameter request:        The URL request sent to the server.
+    /// - parameter response:       The server's response to the URL request.
+    /// - parameter temporaryURL:   The temporary destination URL of the data returned from the server.
+    /// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved.
+    /// - parameter resumeData:     The resume data generated if the request was cancelled.
+    /// - parameter result:         The result of response serialization.
+    /// - parameter timeline:       The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
+    ///
+    /// - returns: The new `DownloadResponse` instance.
+    public init(
+        request: URLRequest?,
+        response: HTTPURLResponse?,
+        temporaryURL: URL?,
+        destinationURL: URL?,
+        resumeData: Data?,
+        result: Result<Value>,
+        timeline: Timeline = Timeline())
+    {
+        self.request = request
+        self.response = response
+        self.temporaryURL = temporaryURL
+        self.destinationURL = destinationURL
+        self.resumeData = resumeData
+        self.result = result
+        self.timeline = timeline
+    }
+}
+
+// MARK: -
+
+extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible {
+    /// The textual representation used when written to an output stream, which includes whether the result was a
+    /// success or failure.
+    public var description: String {
+        return result.debugDescription
+    }
+
+    /// The debug textual representation used when written to an output stream, which includes the URL request, the URL
+    /// response, the temporary and destination URLs, the resume data, the response serialization result and the
+    /// timeline.
+    public var debugDescription: String {
+        var output: [String] = []
+
+        output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil")
+        output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")
+        output.append("[TemporaryURL]: \(temporaryURL?.path ?? "nil")")
+        output.append("[DestinationURL]: \(destinationURL?.path ?? "nil")")
+        output.append("[ResumeData]: \(resumeData?.count ?? 0) bytes")
+        output.append("[Result]: \(result.debugDescription)")
+        output.append("[Timeline]: \(timeline.debugDescription)")
+
+        return output.joined(separator: "\n")
+    }
+}
+
+// MARK: -
+
+extension DownloadResponse {
+    /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped
+    /// result value as a parameter.
+    ///
+    /// Use the `map` method with a closure that does not throw. For example:
+    ///
+    ///     let possibleData: DownloadResponse<Data> = ...
+    ///     let possibleInt = possibleData.map { $0.count }
+    ///
+    /// - parameter transform: A closure that takes the success value of the instance's result.
+    ///
+    /// - returns: A `DownloadResponse` whose result wraps the value returned by the given closure. If this instance's
+    ///            result is a failure, returns a response wrapping the same failure.
+    public func map<T>(_ transform: (Value) -> T) -> DownloadResponse<T> {
+        var response = DownloadResponse<T>(
+            request: request,
+            response: self.response,
+            temporaryURL: temporaryURL,
+            destinationURL: destinationURL,
+            resumeData: resumeData,
+            result: result.map(transform),
+            timeline: timeline
+        )
+
+        response._metrics = _metrics
+
+        return response
+    }
+
+    /// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped
+    /// result value as a parameter.
+    ///
+    /// Use the `flatMap` method with a closure that may throw an error. For example:
+    ///
+    ///     let possibleData: DownloadResponse<Data> = ...
+    ///     let possibleObject = possibleData.flatMap {
+    ///         try JSONSerialization.jsonObject(with: $0)
+    ///     }
+    ///
+    /// - parameter transform: A closure that takes the success value of the instance's result.
+    ///
+    /// - returns: A success or failure `DownloadResponse` depending on the result of the given closure. If this
+    /// instance's result is a failure, returns the same failure.
+    public func flatMap<T>(_ transform: (Value) throws -> T) -> DownloadResponse<T> {
+        var response = DownloadResponse<T>(
+            request: request,
+            response: self.response,
+            temporaryURL: temporaryURL,
+            destinationURL: destinationURL,
+            resumeData: resumeData,
+            result: result.flatMap(transform),
+            timeline: timeline
+        )
+
+        response._metrics = _metrics
+
+        return response
+    }
+
+    /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter.
+    ///
+    /// Use the `mapError` function with a closure that does not throw. For example:
+    ///
+    ///     let possibleData: DownloadResponse<Data> = ...
+    ///     let withMyError = possibleData.mapError { MyError.error($0) }
+    ///
+    /// - Parameter transform: A closure that takes the error of the instance.
+    /// - Returns: A `DownloadResponse` instance containing the result of the transform.
+    public func mapError<E: Error>(_ transform: (Error) -> E) -> DownloadResponse {
+        var response = DownloadResponse(
+            request: request,
+            response: self.response,
+            temporaryURL: temporaryURL,
+            destinationURL: destinationURL,
+            resumeData: resumeData,
+            result: result.mapError(transform),
+            timeline: timeline
+        )
+
+        response._metrics = _metrics
+
+        return response
+    }
+
+    /// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter.
+    ///
+    /// Use the `flatMapError` function with a closure that may throw an error. For example:
+    ///
+    ///     let possibleData: DownloadResponse<Data> = ...
+    ///     let possibleObject = possibleData.flatMapError {
+    ///         try someFailableFunction(taking: $0)
+    ///     }
+    ///
+    /// - Parameter transform: A throwing closure that takes the error of the instance.
+    ///
+    /// - Returns: A `DownloadResponse` instance containing the result of the transform.
+    public func flatMapError<E: Error>(_ transform: (Error) throws -> E) -> DownloadResponse {
+        var response = DownloadResponse(
+            request: request,
+            response: self.response,
+            temporaryURL: temporaryURL,
+            destinationURL: destinationURL,
+            resumeData: resumeData,
+            result: result.flatMapError(transform),
+            timeline: timeline
+        )
+
+        response._metrics = _metrics
+
+        return response
+    }
+}
+
+// MARK: -
+
+protocol Response {
+    /// The task metrics containing the request / response statistics.
+    var _metrics: AnyObject? { get set }
+    mutating func add(_ metrics: AnyObject?)
+}
+
+extension Response {
+    mutating func add(_ metrics: AnyObject?) {
+        #if !os(watchOS)
+            guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return }
+            guard let metrics = metrics as? URLSessionTaskMetrics else { return }
+
+            _metrics = metrics
+        #endif
+    }
+}
+
+// MARK: -
+
+@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
+extension DefaultDataResponse: Response {
+#if !os(watchOS)
+    /// The task metrics containing the request / response statistics.
+    public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
+#endif
+}
+
+@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
+extension DataResponse: Response {
+#if !os(watchOS)
+    /// The task metrics containing the request / response statistics.
+    public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
+#endif
+}
+
+@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
+extension DefaultDownloadResponse: Response {
+#if !os(watchOS)
+    /// The task metrics containing the request / response statistics.
+    public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
+#endif
+}
+
+@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
+extension DownloadResponse: Response {
+#if !os(watchOS)
+    /// The task metrics containing the request / response statistics.
+    public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
+#endif
+}
diff --git a/iOS/Pods/Alamofire/Source/ResponseSerialization.swift b/iOS/Pods/Alamofire/Source/ResponseSerialization.swift
new file mode 100644 (file)
index 0000000..1f29ed8
--- /dev/null
@@ -0,0 +1,715 @@
+//
+//  ResponseSerialization.swift
+//
+//  Copyright (c) 2014-2017 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 type in which all data response serializers must conform to in order to serialize a response.
+public protocol DataResponseSerializerProtocol {
+    /// The type of serialized object to be created by this `DataResponseSerializerType`.
+    associatedtype SerializedObject
+
+    /// A closure used by response handlers that takes a request, response, data and error and returns a result.
+    var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<SerializedObject> { get }
+}
+
+// MARK: -
+
+/// A generic `DataResponseSerializerType` used to serialize a request, response, and data into a serialized object.
+public struct DataResponseSerializer<Value>: DataResponseSerializerProtocol {
+    /// The type of serialized object to be created by this `DataResponseSerializer`.
+    public typealias SerializedObject = Value
+
+    /// A closure used by response handlers that takes a request, response, data and error and returns a result.
+    public var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>
+
+    /// Initializes the `ResponseSerializer` 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 (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>) {
+        self.serializeResponse = serializeResponse
+    }
+}
+
+// MARK: -
+
+/// The type in which all download response serializers must conform to in order to serialize a response.
+public protocol DownloadResponseSerializerProtocol {
+    /// The type of serialized object to be created by this `DownloadResponseSerializerType`.
+    associatedtype SerializedObject
+
+    /// A closure used by response handlers that takes a request, response, url and error and returns a result.
+    var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<SerializedObject> { get }
+}
+
+// MARK: -
+
+/// A generic `DownloadResponseSerializerType` used to serialize a request, response, and data into a serialized object.
+public struct DownloadResponseSerializer<Value>: DownloadResponseSerializerProtocol {
+    /// The type of serialized object to be created by this `DownloadResponseSerializer`.
+    public typealias SerializedObject = Value
+
+    /// A closure used by response handlers that takes a request, response, url and error and returns a result.
+    public var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>
+
+    /// Initializes the `ResponseSerializer` 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 (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>) {
+        self.serializeResponse = serializeResponse
+    }
+}
+
+// MARK: - Timeline
+
+extension Request {
+    var timeline: Timeline {
+        let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent()
+        let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
+        let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
+
+        return Timeline(
+            requestStartTime: requestStartTime,
+            initialResponseTime: initialResponseTime,
+            requestCompletedTime: requestCompletedTime,
+            serializationCompletedTime: CFAbsoluteTimeGetCurrent()
+        )
+    }
+}
+
+// MARK: - Default
+
+extension DataRequest {
+    /// 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.
+    @discardableResult
+    public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self {
+        delegate.queue.addOperation {
+            (queue ?? DispatchQueue.main).async {
+                var dataResponse = DefaultDataResponse(
+                    request: self.request,
+                    response: self.response,
+                    data: self.delegate.data,
+                    error: self.delegate.error,
+                    timeline: self.timeline
+                )
+
+                dataResponse.add(self.delegate.metrics)
+
+                completionHandler(dataResponse)
+            }
+        }
+
+        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.
+    @discardableResult
+    public func response<T: DataResponseSerializerProtocol>(
+        queue: DispatchQueue? = nil,
+        responseSerializer: T,
+        completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
+        -> Self
+    {
+        delegate.queue.addOperation {
+            let result = responseSerializer.serializeResponse(
+                self.request,
+                self.response,
+                self.delegate.data,
+                self.delegate.error
+            )
+
+            var dataResponse = DataResponse<T.SerializedObject>(
+                request: self.request,
+                response: self.response,
+                data: self.delegate.data,
+                result: result,
+                timeline: self.timeline
+            )
+
+            dataResponse.add(self.delegate.metrics)
+
+            (queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }
+        }
+
+        return self
+    }
+}
+
+extension DownloadRequest {
+    /// 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.
+    @discardableResult
+    public func response(
+        queue: DispatchQueue? = nil,
+        completionHandler: @escaping (DefaultDownloadResponse) -> Void)
+        -> Self
+    {
+        delegate.queue.addOperation {
+            (queue ?? DispatchQueue.main).async {
+                var downloadResponse = DefaultDownloadResponse(
+                    request: self.request,
+                    response: self.response,
+                    temporaryURL: self.downloadDelegate.temporaryURL,
+                    destinationURL: self.downloadDelegate.destinationURL,
+                    resumeData: self.downloadDelegate.resumeData,
+                    error: self.downloadDelegate.error,
+                    timeline: self.timeline
+                )
+
+                downloadResponse.add(self.delegate.metrics)
+
+                completionHandler(downloadResponse)
+            }
+        }
+
+        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 contained in the destination url.
+    /// - parameter completionHandler:  The code to be executed once the request has finished.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    public func response<T: DownloadResponseSerializerProtocol>(
+        queue: DispatchQueue? = nil,
+        responseSerializer: T,
+        completionHandler: @escaping (DownloadResponse<T.SerializedObject>) -> Void)
+        -> Self
+    {
+        delegate.queue.addOperation {
+            let result = responseSerializer.serializeResponse(
+                self.request,
+                self.response,
+                self.downloadDelegate.fileURL,
+                self.downloadDelegate.error
+            )
+
+            var downloadResponse = DownloadResponse<T.SerializedObject>(
+                request: self.request,
+                response: self.response,
+                temporaryURL: self.downloadDelegate.temporaryURL,
+                destinationURL: self.downloadDelegate.destinationURL,
+                resumeData: self.downloadDelegate.resumeData,
+                result: result,
+                timeline: self.timeline
+            )
+
+            downloadResponse.add(self.delegate.metrics)
+
+            (queue ?? DispatchQueue.main).async { completionHandler(downloadResponse) }
+        }
+
+        return self
+    }
+}
+
+// MARK: - Data
+
+extension Request {
+    /// Returns a result data type that contains the response data as-is.
+    ///
+    /// - parameter response: The response from the server.
+    /// - parameter data:     The data returned from the server.
+    /// - parameter error:    The error already encountered if it exists.
+    ///
+    /// - returns: The result data type.
+    public static func serializeResponseData(response: HTTPURLResponse?, data: Data?, error: Error?) -> Result<Data> {
+        guard error == nil else { return .failure(error!) }
+
+        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(Data()) }
+
+        guard let validData = data else {
+            return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
+        }
+
+        return .success(validData)
+    }
+}
+
+extension DataRequest {
+    /// Creates a response serializer that returns the associated data as-is.
+    ///
+    /// - returns: A data response serializer.
+    public static func dataResponseSerializer() -> DataResponseSerializer<Data> {
+        return DataResponseSerializer { _, response, data, error in
+            return Request.serializeResponseData(response: response, data: data, error: error)
+        }
+    }
+
+    /// 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.
+    @discardableResult
+    public func responseData(
+        queue: DispatchQueue? = nil,
+        completionHandler: @escaping (DataResponse<Data>) -> Void)
+        -> Self
+    {
+        return response(
+            queue: queue,
+            responseSerializer: DataRequest.dataResponseSerializer(),
+            completionHandler: completionHandler
+        )
+    }
+}
+
+extension DownloadRequest {
+    /// Creates a response serializer that returns the associated data as-is.
+    ///
+    /// - returns: A data response serializer.
+    public static func dataResponseSerializer() -> DownloadResponseSerializer<Data> {
+        return DownloadResponseSerializer { _, response, fileURL, error in
+            guard error == nil else { return .failure(error!) }
+
+            guard let fileURL = fileURL else {
+                return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
+            }
+
+            do {
+                let data = try Data(contentsOf: fileURL)
+                return Request.serializeResponseData(response: response, data: data, error: error)
+            } catch {
+                return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
+            }
+        }
+    }
+
+    /// 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.
+    @discardableResult
+    public func responseData(
+        queue: DispatchQueue? = nil,
+        completionHandler: @escaping (DownloadResponse<Data>) -> Void)
+        -> Self
+    {
+        return response(
+            queue: queue,
+            responseSerializer: DownloadRequest.dataResponseSerializer(),
+            completionHandler: completionHandler
+        )
+    }
+}
+
+// MARK: - String
+
+extension Request {
+    /// Returns a result string type 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.
+    /// - parameter response: The response from the server.
+    /// - parameter data:     The data returned from the server.
+    /// - parameter error:    The error already encountered if it exists.
+    ///
+    /// - returns: The result data type.
+    public static func serializeResponseString(
+        encoding: String.Encoding?,
+        response: HTTPURLResponse?,
+        data: Data?,
+        error: Error?)
+        -> Result<String>
+    {
+        guard error == nil else { return .failure(error!) }
+
+        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success("") }
+
+        guard let validData = data else {
+            return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
+        }
+
+        var convertedEncoding = encoding
+
+        if let encodingName = response?.textEncodingName as CFString!, convertedEncoding == nil {
+            convertedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(
+                CFStringConvertIANACharSetNameToEncoding(encodingName))
+            )
+        }
+
+        let actualEncoding = convertedEncoding ?? String.Encoding.isoLatin1
+
+        if let string = String(data: validData, encoding: actualEncoding) {
+            return .success(string)
+        } else {
+            return .failure(AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)))
+        }
+    }
+}
+
+extension DataRequest {
+    /// Creates a response serializer that returns a result string type 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) -> DataResponseSerializer<String> {
+        return DataResponseSerializer { _, response, data, error in
+            return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: 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.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    public func responseString(
+        queue: DispatchQueue? = nil,
+        encoding: String.Encoding? = nil,
+        completionHandler: @escaping (DataResponse<String>) -> Void)
+        -> Self
+    {
+        return response(
+            queue: queue,
+            responseSerializer: DataRequest.stringResponseSerializer(encoding: encoding),
+            completionHandler: completionHandler
+        )
+    }
+}
+
+extension DownloadRequest {
+    /// Creates a response serializer that returns a result string type 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) -> DownloadResponseSerializer<String> {
+        return DownloadResponseSerializer { _, response, fileURL, error in
+            guard error == nil else { return .failure(error!) }
+
+            guard let fileURL = fileURL else {
+                return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
+            }
+
+            do {
+                let data = try Data(contentsOf: fileURL)
+                return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error)
+            } catch {
+                return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
+            }
+        }
+    }
+
+    /// 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.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    public func responseString(
+        queue: DispatchQueue? = nil,
+        encoding: String.Encoding? = nil,
+        completionHandler: @escaping (DownloadResponse<String>) -> Void)
+        -> Self
+    {
+        return response(
+            queue: queue,
+            responseSerializer: DownloadRequest.stringResponseSerializer(encoding: encoding),
+            completionHandler: completionHandler
+        )
+    }
+}
+
+// MARK: - JSON
+
+extension Request {
+    /// Returns a JSON object contained in a result type constructed from the response data using `JSONSerialization`
+    /// with the specified reading options.
+    ///
+    /// - parameter options:  The JSON serialization reading options. Defaults to `.allowFragments`.
+    /// - parameter response: The response from the server.
+    /// - parameter data:     The data returned from the server.
+    /// - parameter error:    The error already encountered if it exists.
+    ///
+    /// - returns: The result data type.
+    public static func serializeResponseJSON(
+        options: JSONSerialization.ReadingOptions,
+        response: HTTPURLResponse?,
+        data: Data?,
+        error: Error?)
+        -> Result<Any>
+    {
+        guard error == nil else { return .failure(error!) }
+
+        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
+
+        guard let validData = data, validData.count > 0 else {
+            return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
+        }
+
+        do {
+            let json = try JSONSerialization.jsonObject(with: validData, options: options)
+            return .success(json)
+        } catch {
+            return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)))
+        }
+    }
+}
+
+extension DataRequest {
+    /// Creates a response serializer that returns a JSON object result type constructed from the response data using
+    /// `JSONSerialization` with the specified reading options.
+    ///
+    /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
+    ///
+    /// - returns: A JSON object response serializer.
+    public static func jsonResponseSerializer(
+        options: JSONSerialization.ReadingOptions = .allowFragments)
+        -> DataResponseSerializer<Any>
+    {
+        return DataResponseSerializer { _, response, data, error in
+            return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
+        }
+    }
+
+    /// Adds a handler to be called once the request has finished.
+    ///
+    /// - parameter options:           The JSON serialization reading options. Defaults to `.allowFragments`.
+    /// - parameter completionHandler: A closure to be executed once the request has finished.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    public func responseJSON(
+        queue: DispatchQueue? = nil,
+        options: JSONSerialization.ReadingOptions = .allowFragments,
+        completionHandler: @escaping (DataResponse<Any>) -> Void)
+        -> Self
+    {
+        return response(
+            queue: queue,
+            responseSerializer: DataRequest.jsonResponseSerializer(options: options),
+            completionHandler: completionHandler
+        )
+    }
+}
+
+extension DownloadRequest {
+    /// Creates a response serializer that returns a JSON object result type constructed from the response data using
+    /// `JSONSerialization` with the specified reading options.
+    ///
+    /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
+    ///
+    /// - returns: A JSON object response serializer.
+    public static func jsonResponseSerializer(
+        options: JSONSerialization.ReadingOptions = .allowFragments)
+        -> DownloadResponseSerializer<Any>
+    {
+        return DownloadResponseSerializer { _, response, fileURL, error in
+            guard error == nil else { return .failure(error!) }
+
+            guard let fileURL = fileURL else {
+                return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
+            }
+
+            do {
+                let data = try Data(contentsOf: fileURL)
+                return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
+            } catch {
+                return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
+            }
+        }
+    }
+
+    /// Adds a handler to be called once the request has finished.
+    ///
+    /// - parameter options:           The JSON serialization reading options. Defaults to `.allowFragments`.
+    /// - parameter completionHandler: A closure to be executed once the request has finished.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    public func responseJSON(
+        queue: DispatchQueue? = nil,
+        options: JSONSerialization.ReadingOptions = .allowFragments,
+        completionHandler: @escaping (DownloadResponse<Any>) -> Void)
+        -> Self
+    {
+        return response(
+            queue: queue,
+            responseSerializer: DownloadRequest.jsonResponseSerializer(options: options),
+            completionHandler: completionHandler
+        )
+    }
+}
+
+// MARK: - Property List
+
+extension Request {
+    /// Returns a plist object contained in a result type constructed from the response data using
+    /// `PropertyListSerialization` with the specified reading options.
+    ///
+    /// - parameter options:  The property list reading options. Defaults to `[]`.
+    /// - parameter response: The response from the server.
+    /// - parameter data:     The data returned from the server.
+    /// - parameter error:    The error already encountered if it exists.
+    ///
+    /// - returns: The result data type.
+    public static func serializeResponsePropertyList(
+        options: PropertyListSerialization.ReadOptions,
+        response: HTTPURLResponse?,
+        data: Data?,
+        error: Error?)
+        -> Result<Any>
+    {
+        guard error == nil else { return .failure(error!) }
+
+        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
+
+        guard let validData = data, validData.count > 0 else {
+            return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
+        }
+
+        do {
+            let plist = try PropertyListSerialization.propertyList(from: validData, options: options, format: nil)
+            return .success(plist)
+        } catch {
+            return .failure(AFError.responseSerializationFailed(reason: .propertyListSerializationFailed(error: error)))
+        }
+    }
+}
+
+extension DataRequest {
+    /// Creates a response serializer that returns an object constructed from the response data using
+    /// `PropertyListSerialization` with the specified reading options.
+    ///
+    /// - parameter options: The property list reading options. Defaults to `[]`.
+    ///
+    /// - returns: A property list object response serializer.
+    public static func propertyListResponseSerializer(
+        options: PropertyListSerialization.ReadOptions = [])
+        -> DataResponseSerializer<Any>
+    {
+        return DataResponseSerializer { _, response, data, error in
+            return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error)
+        }
+    }
+
+    /// Adds a handler to be called once the request has finished.
+    ///
+    /// - parameter options:           The property list reading options. Defaults to `[]`.
+    /// - parameter completionHandler: A closure to be executed once the request has finished.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    public func responsePropertyList(
+        queue: DispatchQueue? = nil,
+        options: PropertyListSerialization.ReadOptions = [],
+        completionHandler: @escaping (DataResponse<Any>) -> Void)
+        -> Self
+    {
+        return response(
+            queue: queue,
+            responseSerializer: DataRequest.propertyListResponseSerializer(options: options),
+            completionHandler: completionHandler
+        )
+    }
+}
+
+extension DownloadRequest {
+    /// Creates a response serializer that returns an object constructed from the response data using
+    /// `PropertyListSerialization` with the specified reading options.
+    ///
+    /// - parameter options: The property list reading options. Defaults to `[]`.
+    ///
+    /// - returns: A property list object response serializer.
+    public static func propertyListResponseSerializer(
+        options: PropertyListSerialization.ReadOptions = [])
+        -> DownloadResponseSerializer<Any>
+    {
+        return DownloadResponseSerializer { _, response, fileURL, error in
+            guard error == nil else { return .failure(error!) }
+
+            guard let fileURL = fileURL else {
+                return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
+            }
+
+            do {
+                let data = try Data(contentsOf: fileURL)
+                return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error)
+            } catch {
+                return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
+            }
+        }
+    }
+
+    /// Adds a handler to be called once the request has finished.
+    ///
+    /// - parameter options:           The property list reading options. Defaults to `[]`.
+    /// - parameter completionHandler: A closure to be executed once the request has finished.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    public func responsePropertyList(
+        queue: DispatchQueue? = nil,
+        options: PropertyListSerialization.ReadOptions = [],
+        completionHandler: @escaping (DownloadResponse<Any>) -> Void)
+        -> Self
+    {
+        return response(
+            queue: queue,
+            responseSerializer: DownloadRequest.propertyListResponseSerializer(options: options),
+            completionHandler: completionHandler
+        )
+    }
+}
+
+/// A set of HTTP response status code that do not contain response data.
+private let emptyDataStatusCodes: Set<Int> = [204, 205]
diff --git a/iOS/Pods/Alamofire/Source/Result.swift b/iOS/Pods/Alamofire/Source/Result.swift
new file mode 100644 (file)
index 0000000..bbd3c61
--- /dev/null
@@ -0,0 +1,300 @@
+//
+//  Result.swift
+//
+//  Copyright (c) 2014-2017 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<Value> {
+    case success(Value)
+    case failure(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 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 {
+    /// The textual representation used when written to an output stream, which includes whether the result was a
+    /// success or failure.
+    public var description: String {
+        switch self {
+        case .success:
+            return "SUCCESS"
+        case .failure:
+            return "FAILURE"
+        }
+    }
+}
+
+// MARK: - CustomDebugStringConvertible
+
+extension Result: CustomDebugStringConvertible {
+    /// The debug textual representation used when written to an output stream, which includes whether the result was a
+    /// success or failure in addition to the value or error.
+    public var debugDescription: String {
+        switch self {
+        case .success(let value):
+            return "SUCCESS: \(value)"
+        case .failure(let error):
+            return "FAILURE: \(error)"
+        }
+    }
+}
+
+// MARK: - Functional APIs
+
+extension Result {
+    /// Creates a `Result` instance from the result of a closure.
+    ///
+    /// A failure result is created when the closure throws, and a success result is created when the closure
+    /// succeeds without throwing an error.
+    ///
+    ///     func someString() throws -> String { ... }
+    ///
+    ///     let result = Result(value: {
+    ///         return try someString()
+    ///     })
+    ///
+    ///     // The type of result is Result<String>
+    ///
+    /// The trailing closure syntax is also supported:
+    ///
+    ///     let result = Result { try someString() }
+    ///
+    /// - parameter value: The closure to execute and create the result for.
+    public init(value: () throws -> Value) {
+        do {
+            self = try .success(value())
+        } catch {
+            self = .failure(error)
+        }
+    }
+
+    /// Returns the success value, or throws the failure error.
+    ///
+    ///     let possibleString: Result<String> = .success("success")
+    ///     try print(possibleString.unwrap())
+    ///     // Prints "success"
+    ///
+    ///     let noString: Result<String> = .failure(error)
+    ///     try print(noString.unwrap())
+    ///     // Throws error
+    public func unwrap() throws -> Value {
+        switch self {
+        case .success(let value):
+            return value
+        case .failure(let error):
+            throw error
+        }
+    }
+
+    /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
+    ///
+    /// Use the `map` method with a closure that does not throw. For example:
+    ///
+    ///     let possibleData: Result<Data> = .success(Data())
+    ///     let possibleInt = possibleData.map { $0.count }
+    ///     try print(possibleInt.unwrap())
+    ///     // Prints "0"
+    ///
+    ///     let noData: Result<Data> = .failure(error)
+    ///     let noInt = noData.map { $0.count }
+    ///     try print(noInt.unwrap())
+    ///     // Throws error
+    ///
+    /// - parameter transform: A closure that takes the success value of the `Result` instance.
+    ///
+    /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the
+    ///            same failure.
+    public func map<T>(_ transform: (Value) -> T) -> Result<T> {
+        switch self {
+        case .success(let value):
+            return .success(transform(value))
+        case .failure(let error):
+            return .failure(error)
+        }
+    }
+
+    /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
+    ///
+    /// Use the `flatMap` method with a closure that may throw an error. For example:
+    ///
+    ///     let possibleData: Result<Data> = .success(Data(...))
+    ///     let possibleObject = possibleData.flatMap {
+    ///         try JSONSerialization.jsonObject(with: $0)
+    ///     }
+    ///
+    /// - parameter transform: A closure that takes the success value of the instance.
+    ///
+    /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the
+    ///            same failure.
+    public func flatMap<T>(_ transform: (Value) throws -> T) -> Result<T> {
+        switch self {
+        case .success(let value):
+            do {
+                return try .success(transform(value))
+            } catch {
+                return .failure(error)
+            }
+        case .failure(let error):
+            return .failure(error)
+        }
+    }
+
+    /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter.
+    ///
+    /// Use the `mapError` function with a closure that does not throw. For example:
+    ///
+    ///     let possibleData: Result<Data> = .failure(someError)
+    ///     let withMyError: Result<Data> = possibleData.mapError { MyError.error($0) }
+    ///
+    /// - Parameter transform: A closure that takes the error of the instance.
+    /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns
+    ///            the same instance.
+    public func mapError<T: Error>(_ transform: (Error) -> T) -> Result {
+        switch self {
+        case .failure(let error):
+            return .failure(transform(error))
+        case .success:
+            return self
+        }
+    }
+
+    /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter.
+    ///
+    /// Use the `flatMapError` function with a closure that may throw an error. For example:
+    ///
+    ///     let possibleData: Result<Data> = .success(Data(...))
+    ///     let possibleObject = possibleData.flatMapError {
+    ///         try someFailableFunction(taking: $0)
+    ///     }
+    ///
+    /// - Parameter transform: A throwing closure that takes the error of the instance.
+    ///
+    /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns
+    ///            the same instance.
+    public func flatMapError<T: Error>(_ transform: (Error) throws -> T) -> Result {
+        switch self {
+        case .failure(let error):
+            do {
+                return try .failure(transform(error))
+            } catch {
+                return .failure(error)
+            }
+        case .success:
+            return self
+        }
+    }
+
+    /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
+    ///
+    /// Use the `withValue` function to evaluate the passed closure without modifying the `Result` instance.
+    ///
+    /// - Parameter closure: A closure that takes the success value of this instance.
+    /// - Returns: This `Result` instance, unmodified.
+    @discardableResult
+    public func withValue(_ closure: (Value) -> Void) -> Result {
+        if case let .success(value) = self { closure(value) }
+
+        return self
+    }
+
+    /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter.
+    ///
+    /// Use the `withError` function to evaluate the passed closure without modifying the `Result` instance.
+    ///
+    /// - Parameter closure: A closure that takes the success value of this instance.
+    /// - Returns: This `Result` instance, unmodified.
+    @discardableResult
+    public func withError(_ closure: (Error) -> Void) -> Result {
+        if case let .failure(error) = self { closure(error) }
+
+        return self
+    }
+
+    /// Evaluates the specified closure when the `Result` is a success.
+    ///
+    /// Use the `ifSuccess` function to evaluate the passed closure without modifying the `Result` instance.
+    ///
+    /// - Parameter closure: A `Void` closure.
+    /// - Returns: This `Result` instance, unmodified.
+    @discardableResult
+    public func ifSuccess(_ closure: () -> Void) -> Result {
+        if isSuccess { closure() }
+
+        return self
+    }
+
+    /// Evaluates the specified closure when the `Result` is a failure.
+    ///
+    /// Use the `ifFailure` function to evaluate the passed closure without modifying the `Result` instance.
+    ///
+    /// - Parameter closure: A `Void` closure.
+    /// - Returns: This `Result` instance, unmodified.
+    @discardableResult
+    public func ifFailure(_ closure: () -> Void) -> Result {
+        if isFailure { closure() }
+
+        return self
+    }
+}
diff --git a/iOS/Pods/Alamofire/Source/ServerTrustPolicy.swift b/iOS/Pods/Alamofire/Source/ServerTrustPolicy.swift
new file mode 100644 (file)
index 0000000..1ad3530
--- /dev/null
@@ -0,0 +1,307 @@
+//
+//  ServerTrustPolicy.swift
+//
+//  Copyright (c) 2014-2017 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 serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
+        return policies[host]
+    }
+}
+
+// MARK: -
+
+extension URLSession {
+    private struct AssociatedKeys {
+        static var managerKey = "URLSession.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.
+///
+/// - performRevokedEvaluation: Uses the default and revoked server trust evaluations allowing you to control whether to
+///                             validate the host provided by the challenge as well as specify the revocation flags for
+///                             testing for revoked certificates. Apple platforms did not start testing for revoked
+///                             certificates automatically until iOS 10.1, macOS 10.12 and tvOS 10.1 which is
+///                             demonstrated in our TLS tests. 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 performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags)
+    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 certificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] {
+        var certificates: [SecCertificate] = []
+
+        let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in
+            bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil)
+        }.joined())
+
+        for path in paths {
+            if
+                let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData,
+                let certificate = SecCertificateCreateWithData(nil, certificateData)
+            {
+                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 publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] {
+        var publicKeys: [SecKey] = []
+
+        for certificate in certificates(in: bundle) {
+            if let publicKey = publicKey(for: 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 evaluate(_ serverTrust: SecTrust, forHost 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 .performRevokedEvaluation(validateHost, revocationFlags):
+            let defaultPolicy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
+            let revokedPolicy = SecPolicyCreateRevocation(revocationFlags)
+            SecTrustSetPolicies(serverTrust, [defaultPolicy, revokedPolicy] as CFTypeRef)
+
+            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 = certificateData(for: serverTrust)
+                let pinnedCertificatesDataArray = certificateData(for: 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.publicKeys(for: 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
+
+    private func trustIsValid(_ trust: SecTrust) -> Bool {
+        var isValid = false
+
+        var result = SecTrustResultType.invalid
+        let status = SecTrustEvaluate(trust, &result)
+
+        if status == errSecSuccess {
+            let unspecified = SecTrustResultType.unspecified
+            let proceed = SecTrustResultType.proceed
+
+
+            isValid = result == unspecified || result == proceed
+        }
+
+        return isValid
+    }
+
+    // MARK: - Private - Certificate Data
+
+    private func certificateData(for trust: SecTrust) -> [Data] {
+        var certificates: [SecCertificate] = []
+
+        for index in 0..<SecTrustGetCertificateCount(trust) {
+            if let certificate = SecTrustGetCertificateAtIndex(trust, index) {
+                certificates.append(certificate)
+            }
+        }
+
+        return certificateData(for: certificates)
+    }
+
+    private func certificateData(for certificates: [SecCertificate]) -> [Data] {
+        return certificates.map { SecCertificateCopyData($0) as Data }
+    }
+
+    // MARK: - Private - Public Key Extraction
+
+    private static func publicKeys(for trust: SecTrust) -> [SecKey] {
+        var publicKeys: [SecKey] = []
+
+        for index in 0..<SecTrustGetCertificateCount(trust) {
+            if
+                let certificate = SecTrustGetCertificateAtIndex(trust, index),
+                let publicKey = publicKey(for: certificate)
+            {
+                publicKeys.append(publicKey)
+            }
+        }
+
+        return publicKeys
+    }
+
+    private static func publicKey(for certificate: SecCertificate) -> 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/iOS/Pods/Alamofire/Source/SessionDelegate.swift b/iOS/Pods/Alamofire/Source/SessionDelegate.swift
new file mode 100644 (file)
index 0000000..d38c253
--- /dev/null
@@ -0,0 +1,719 @@
+//
+//  SessionDelegate.swift
+//
+//  Copyright (c) 2014-2017 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 handling all delegate callbacks for the underlying session.
+open class SessionDelegate: NSObject {
+
+    // MARK: URLSessionDelegate Overrides
+
+    /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`.
+    open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
+
+    /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
+    open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
+
+    /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`.
+    open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
+
+    /// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`.
+    open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?
+
+    // MARK: URLSessionTaskDelegate Overrides
+
+    /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`.
+    open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
+
+    /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and
+    /// requires the caller to call the `completionHandler`.
+    open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)?
+
+    /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`.
+    open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
+
+    /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and
+    /// requires the caller to call the `completionHandler`.
+    open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
+
+    /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`.
+    open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
+
+    /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and
+    /// requires the caller to call the `completionHandler`.
+    open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)?
+
+    /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`.
+    open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
+
+    /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didCompleteWithError:)`.
+    open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
+
+    // MARK: URLSessionDataDelegate Overrides
+
+    /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)`.
+    open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
+
+    /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and
+    /// requires caller to call the `completionHandler`.
+    open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)?
+
+    /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`.
+    open var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
+
+    /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:)`.
+    open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
+
+    /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`.
+    open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
+
+    /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and
+    /// requires caller to call the `completionHandler`.
+    open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)?
+
+    // MARK: URLSessionDownloadDelegate Overrides
+
+    /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didFinishDownloadingTo:)`.
+    open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)?
+
+    /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`.
+    open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
+
+    /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)`.
+    open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
+
+    // MARK: URLSessionStreamDelegate Overrides
+
+#if !os(watchOS)
+
+    /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:readClosedFor:)`.
+    @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+    open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
+        get {
+            return _streamTaskReadClosed as? (URLSession, URLSessionStreamTask) -> Void
+        }
+        set {
+            _streamTaskReadClosed = newValue
+        }
+    }
+
+    /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:writeClosedFor:)`.
+    @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+    open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
+        get {
+            return _streamTaskWriteClosed as? (URLSession, URLSessionStreamTask) -> Void
+        }
+        set {
+            _streamTaskWriteClosed = newValue
+        }
+    }
+
+    /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:betterRouteDiscoveredFor:)`.
+    @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+    open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)? {
+        get {
+            return _streamTaskBetterRouteDiscovered as? (URLSession, URLSessionStreamTask) -> Void
+        }
+        set {
+            _streamTaskBetterRouteDiscovered = newValue
+        }
+    }
+
+    /// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:streamTask:didBecome:outputStream:)`.
+    @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+    open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)? {
+        get {
+            return _streamTaskDidBecomeInputStream as? (URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void
+        }
+        set {
+            _streamTaskDidBecomeInputStream = newValue
+        }
+    }
+
+    var _streamTaskReadClosed: Any?
+    var _streamTaskWriteClosed: Any?
+    var _streamTaskBetterRouteDiscovered: Any?
+    var _streamTaskDidBecomeInputStream: Any?
+
+#endif
+
+    // MARK: Properties
+
+    var retrier: RequestRetrier?
+    weak var sessionManager: SessionManager?
+
+    private var requests: [Int: Request] = [:]
+    private let lock = NSLock()
+
+    /// Access the task delegate for the specified task in a thread-safe manner.
+    open subscript(task: URLSessionTask) -> Request? {
+        get {
+            lock.lock() ; defer { lock.unlock() }
+            return requests[task.taskIdentifier]
+        }
+        set {
+            lock.lock() ; defer { lock.unlock() }
+            requests[task.taskIdentifier] = newValue
+        }
+    }
+
+    // MARK: Lifecycle
+
+    /// Initializes the `SessionDelegate` instance.
+    ///
+    /// - returns: The new `SessionDelegate` instance.
+    public override init() {
+        super.init()
+    }
+
+    // MARK: NSObject Overrides
+
+    /// Returns a `Bool` indicating whether the `SessionDelegate` implements or inherits a method that can respond
+    /// to a specified message.
+    ///
+    /// - parameter selector: A selector that identifies a message.
+    ///
+    /// - returns: `true` if the receiver implements or inherits a method that can respond to selector, otherwise `false`.
+    open override func responds(to selector: Selector) -> Bool {
+        #if !os(macOS)
+            if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
+                return sessionDidFinishEventsForBackgroundURLSession != nil
+            }
+        #endif
+
+        #if !os(watchOS)
+            if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) {
+                switch selector {
+                case #selector(URLSessionStreamDelegate.urlSession(_:readClosedFor:)):
+                    return streamTaskReadClosed != nil
+                case #selector(URLSessionStreamDelegate.urlSession(_:writeClosedFor:)):
+                    return streamTaskWriteClosed != nil
+                case #selector(URLSessionStreamDelegate.urlSession(_:betterRouteDiscoveredFor:)):
+                    return streamTaskBetterRouteDiscovered != nil
+                case #selector(URLSessionStreamDelegate.urlSession(_:streamTask:didBecome:outputStream:)):
+                    return streamTaskDidBecomeInputAndOutputStreams != nil
+                default:
+                    break
+                }
+            }
+        #endif
+
+        switch selector {
+        case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)):
+            return sessionDidBecomeInvalidWithError != nil
+        case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)):
+            return (sessionDidReceiveChallenge != nil  || sessionDidReceiveChallengeWithCompletion != nil)
+        case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)):
+            return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil)
+        case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)):
+            return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil)
+        default:
+            return type(of: self).instancesRespond(to: selector)
+        }
+    }
+}
+
+// MARK: - URLSessionDelegate
+
+extension SessionDelegate: URLSessionDelegate {
+    /// 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.
+    open 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.
+    open func urlSession(
+        _ session: URLSession,
+        didReceive challenge: URLAuthenticationChallenge,
+        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
+    {
+        guard sessionDidReceiveChallengeWithCompletion == nil else {
+            sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler)
+            return
+        }
+
+        var disposition: 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?.serverTrustPolicy(forHost: host),
+                let serverTrust = challenge.protectionSpace.serverTrust
+            {
+                if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
+                    disposition = .useCredential
+                    credential = URLCredential(trust: serverTrust)
+                } else {
+                    disposition = .cancelAuthenticationChallenge
+                }
+            }
+        }
+
+        completionHandler(disposition, credential)
+    }
+
+#if !os(macOS)
+
+    /// Tells the delegate that all messages enqueued for a session have been delivered.
+    ///
+    /// - parameter session: The session that no longer has any outstanding requests.
+    open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
+        sessionDidFinishEventsForBackgroundURLSession?(session)
+    }
+
+#endif
+}
+
+// MARK: - URLSessionTaskDelegate
+
+extension SessionDelegate: URLSessionTaskDelegate {
+    /// 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.
+    open func urlSession(
+        _ session: URLSession,
+        task: URLSessionTask,
+        willPerformHTTPRedirection response: HTTPURLResponse,
+        newRequest request: URLRequest,
+        completionHandler: @escaping (URLRequest?) -> Void)
+    {
+        guard taskWillPerformHTTPRedirectionWithCompletion == nil else {
+            taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler)
+            return
+        }
+
+        var redirectRequest: 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.
+    open func urlSession(
+        _ session: URLSession,
+        task: URLSessionTask,
+        didReceive challenge: URLAuthenticationChallenge,
+        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
+    {
+        guard taskDidReceiveChallengeWithCompletion == nil else {
+            taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
+            return
+        }
+
+        if let taskDidReceiveChallenge = taskDidReceiveChallenge {
+            let result = taskDidReceiveChallenge(session, task, challenge)
+            completionHandler(result.0, result.1)
+        } else if let delegate = self[task]?.delegate {
+            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.
+    open func urlSession(
+        _ session: URLSession,
+        task: URLSessionTask,
+        needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
+    {
+        guard taskNeedNewBodyStreamWithCompletion == nil else {
+            taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler)
+            return
+        }
+
+        if let taskNeedNewBodyStream = taskNeedNewBodyStream {
+            completionHandler(taskNeedNewBodyStream(session, task))
+        } else if let delegate = self[task]?.delegate {
+            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.
+    open 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]?.delegate as? UploadTaskDelegate {
+            delegate.URLSession(
+                session,
+                task: task,
+                didSendBodyData: bytesSent,
+                totalBytesSent: totalBytesSent,
+                totalBytesExpectedToSend: totalBytesExpectedToSend
+            )
+        }
+    }
+
+#if !os(watchOS)
+
+    /// Tells the delegate that the session finished collecting metrics for the task.
+    ///
+    /// - parameter session: The session collecting the metrics.
+    /// - parameter task:    The task whose metrics have been collected.
+    /// - parameter metrics: The collected metrics.
+    @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
+    @objc(URLSession:task:didFinishCollectingMetrics:)
+    open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
+        self[task]?.delegate.metrics = metrics
+    }
+
+#endif
+
+    /// 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.
+    open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
+        /// Executed after it is determined that the request is not going to be retried
+        let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
+            guard let strongSelf = self else { return }
+
+            strongSelf.taskDidComplete?(session, task, error)
+
+            strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
+
+            NotificationCenter.default.post(
+                name: Notification.Name.Task.DidComplete,
+                object: strongSelf,
+                userInfo: [Notification.Key.Task: task]
+            )
+
+            strongSelf[task] = nil
+        }
+
+        guard let request = self[task], let sessionManager = sessionManager else {
+            completeTask(session, task, error)
+            return
+        }
+
+        // Run all validations on the request before checking if an error occurred
+        request.validations.forEach { $0() }
+
+        // Determine whether an error has occurred
+        var error: Error? = error
+
+        if request.delegate.error != nil {
+            error = request.delegate.error
+        }
+
+        /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
+        /// should be retried. Otherwise, complete the task by notifying the task delegate.
+        if let retrier = retrier, let error = error {
+            retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
+                guard shouldRetry else { completeTask(session, task, error) ; return }
+
+                DispatchQueue.utility.after(timeDelay) { [weak self] in
+                    guard let strongSelf = self else { return }
+
+                    let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false
+
+                    if retrySucceeded, let task = request.task {
+                        strongSelf[task] = request
+                        return
+                    } else {
+                        completeTask(session, task, error)
+                    }
+                }
+            }
+        } else {
+            completeTask(session, task, error)
+        }
+    }
+}
+
+// MARK: - URLSessionDataDelegate
+
+extension SessionDelegate: URLSessionDataDelegate {
+    /// 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.
+    open func urlSession(
+        _ session: URLSession,
+        dataTask: URLSessionDataTask,
+        didReceive response: URLResponse,
+        completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
+    {
+        guard dataTaskDidReceiveResponseWithCompletion == nil else {
+            dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler)
+            return
+        }
+
+        var disposition: 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.
+    open func urlSession(
+        _ session: URLSession,
+        dataTask: URLSessionDataTask,
+        didBecome downloadTask: URLSessionDownloadTask)
+    {
+        if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
+            dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
+        } else {
+            self[downloadTask]?.delegate = DownloadTaskDelegate(task: downloadTask)
+        }
+    }
+
+    /// 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.
+    open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
+        if let dataTaskDidReceiveData = dataTaskDidReceiveData {
+            dataTaskDidReceiveData(session, dataTask, data)
+        } else if let delegate = self[dataTask]?.delegate as? 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.
+    open func urlSession(
+        _ session: URLSession,
+        dataTask: URLSessionDataTask,
+        willCacheResponse proposedResponse: CachedURLResponse,
+        completionHandler: @escaping (CachedURLResponse?) -> Void)
+    {
+        guard dataTaskWillCacheResponseWithCompletion == nil else {
+            dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler)
+            return
+        }
+
+        if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
+            completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
+        } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
+            delegate.urlSession(
+                session,
+                dataTask: dataTask,
+                willCacheResponse: proposedResponse,
+                completionHandler: completionHandler
+            )
+        } else {
+            completionHandler(proposedResponse)
+        }
+    }
+}
+
+// MARK: - URLSessionDownloadDelegate
+
+extension SessionDelegate: URLSessionDownloadDelegate {
+    /// 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.
+    open func urlSession(
+        _ session: URLSession,
+        downloadTask: URLSessionDownloadTask,
+        didFinishDownloadingTo location: URL)
+    {
+        if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
+            downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
+        } else if let delegate = self[downloadTask]?.delegate as? 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`.
+    open 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]?.delegate as? 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.
+    open 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]?.delegate as? DownloadTaskDelegate {
+            delegate.urlSession(
+                session,
+                downloadTask: downloadTask,
+                didResumeAtOffset: fileOffset,
+                expectedTotalBytes: expectedTotalBytes
+            )
+        }
+    }
+}
+
+// MARK: - URLSessionStreamDelegate
+
+#if !os(watchOS)
+
+@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+extension SessionDelegate: URLSessionStreamDelegate {
+    /// Tells the delegate that the read side of the connection has been closed.
+    ///
+    /// - parameter session:    The session.
+    /// - parameter streamTask: The stream task.
+    open 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.
+    open 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.
+    open 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.
+    open func urlSession(
+        _ session: URLSession,
+        streamTask: URLSessionStreamTask,
+        didBecome inputStream: InputStream,
+        outputStream: OutputStream)
+    {
+        streamTaskDidBecomeInputAndOutputStreams?(session, streamTask, inputStream, outputStream)
+    }
+}
+
+#endif
diff --git a/iOS/Pods/Alamofire/Source/SessionManager.swift b/iOS/Pods/Alamofire/Source/SessionManager.swift
new file mode 100644 (file)
index 0000000..0ff677b
--- /dev/null
@@ -0,0 +1,892 @@
+//
+//  SessionManager.swift
+//
+//  Copyright (c) 2014-2017 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 SessionManager {
+
+    // MARK: - Helper Types
+
+    /// 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 `UploadRequest` 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: UploadRequest, streamingFromDisk: Bool, streamFileURL: URL?)
+        case failure(Error)
+    }
+
+    // MARK: - Properties
+
+    /// A default instance of `SessionManager`, used by top-level Alamofire request methods, and suitable for use
+    /// directly for any ad hoc requests.
+    open static let `default`: SessionManager = {
+        let configuration = URLSessionConfiguration.default
+        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
+
+        return SessionManager(configuration: configuration)
+    }()
+
+    /// Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers.
+    open static let defaultHTTPHeaders: HTTPHeaders = {
+        // 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 = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
+            let quality = 1.0 - (Double(index) * 0.1)
+            return "\(languageCode);q=\(quality)"
+        }.joined(separator: ", ")
+
+        // User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3
+        // Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0`
+        let userAgent: String = {
+            if let info = Bundle.main.infoDictionary {
+                let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
+                let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
+                let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown"
+                let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown"
+
+                let osNameVersion: String = {
+                    let version = ProcessInfo.processInfo.operatingSystemVersion
+                    let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
+
+                    let osName: String = {
+                        #if os(iOS)
+                            return "iOS"
+                        #elseif os(watchOS)
+                            return "watchOS"
+                        #elseif os(tvOS)
+                            return "tvOS"
+                        #elseif os(macOS)
+                            return "OS X"
+                        #elseif os(Linux)
+                            return "Linux"
+                        #else
+                            return "Unknown"
+                        #endif
+                    }()
+
+                    return "\(osName) \(versionString)"
+                }()
+
+                let alamofireVersion: String = {
+                    guard
+                        let afInfo = Bundle(for: SessionManager.self).infoDictionary,
+                        let build = afInfo["CFBundleShortVersionString"]
+                    else { return "Unknown" }
+
+                    return "Alamofire/\(build)"
+                }()
+
+                return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"
+            }
+
+            return "Alamofire"
+        }()
+
+        return [
+            "Accept-Encoding": acceptEncoding,
+            "Accept-Language": acceptLanguage,
+            "User-Agent": userAgent
+        ]
+    }()
+
+    /// Default memory threshold used when encoding `MultipartFormData` in bytes.
+    open static let multipartFormDataEncodingMemoryThreshold: UInt64 = 10_000_000
+
+    /// 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 request adapter called each time a new request is created.
+    open var adapter: RequestAdapter?
+
+    /// The request retrier called each time a request encounters an error to determine whether to retry the request.
+    open var retrier: RequestRetrier? {
+        get { return delegate.retrier }
+        set { delegate.retrier = newValue }
+    }
+
+    /// 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)?
+
+    let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)
+
+    // MARK: - Lifecycle
+
+    /// Creates an instance with the specified `configuration`, `delegate` and `serverTrustPolicyManager`.
+    ///
+    /// - parameter configuration:            The configuration used to construct the managed session.
+    ///                                       `URLSessionConfiguration.default` by default.
+    /// - parameter delegate:                 The delegate used when initializing the session. `SessionDelegate()` by
+    ///                                       default.
+    /// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
+    ///                                       challenges. `nil` by default.
+    ///
+    /// - returns: The new `SessionManager` instance.
+    public init(
+        configuration: URLSessionConfiguration = URLSessionConfiguration.default,
+        delegate: SessionDelegate = SessionDelegate(),
+        serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
+    {
+        self.delegate = delegate
+        self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
+
+        commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
+    }
+
+    /// Creates an instance with the specified `session`, `delegate` and `serverTrustPolicyManager`.
+    ///
+    /// - parameter session:                  The URL session.
+    /// - parameter delegate:                 The delegate of the URL session. Must equal the URL session's delegate.
+    /// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
+    ///                                       challenges. `nil` by default.
+    ///
+    /// - returns: The new `SessionManager` instance if the URL session's delegate matches; `nil` otherwise.
+    public init?(
+        session: URLSession,
+        delegate: SessionDelegate,
+        serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
+    {
+        guard delegate === session.delegate else { return nil }
+
+        self.delegate = delegate
+        self.session = session
+
+        commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
+    }
+
+    private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
+        session.serverTrustPolicyManager = serverTrustPolicyManager
+
+        delegate.sessionManager = self
+
+        delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
+            guard let strongSelf = self else { return }
+            DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
+        }
+    }
+
+    deinit {
+        session.invalidateAndCancel()
+    }
+
+    // MARK: - Data Request
+
+    /// Creates a `DataRequest` to retrieve the contents of the specified `url`, `method`, `parameters`, `encoding`
+    /// and `headers`.
+    ///
+    /// - parameter url:        The URL.
+    /// - parameter method:     The HTTP method. `.get` by default.
+    /// - parameter parameters: The parameters. `nil` by default.
+    /// - parameter encoding:   The parameter encoding. `URLEncoding.default` by default.
+    /// - parameter headers:    The HTTP headers. `nil` by default.
+    ///
+    /// - returns: The created `DataRequest`.
+    @discardableResult
+    open func request(
+        _ url: URLConvertible,
+        method: HTTPMethod = .get,
+        parameters: Parameters? = nil,
+        encoding: ParameterEncoding = URLEncoding.default,
+        headers: HTTPHeaders? = nil)
+        -> DataRequest
+    {
+        var originalRequest: URLRequest?
+
+        do {
+            originalRequest = try URLRequest(url: url, method: method, headers: headers)
+            let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
+            return request(encodedURLRequest)
+        } catch {
+            return request(originalRequest, failedWith: error)
+        }
+    }
+
+    /// Creates a `DataRequest` to retrieve the contents of a URL based on the specified `urlRequest`.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// - parameter urlRequest: The URL request.
+    ///
+    /// - returns: The created `DataRequest`.
+    @discardableResult
+    open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
+        var originalRequest: URLRequest?
+
+        do {
+            originalRequest = try urlRequest.asURLRequest()
+            let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
+
+            let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
+            let request = DataRequest(session: session, requestTask: .data(originalTask, task))
+
+            delegate[task] = request
+
+            if startRequestsImmediately { request.resume() }
+
+            return request
+        } catch {
+            return request(originalRequest, failedWith: error)
+        }
+    }
+
+    // MARK: Private - Request Implementation
+
+    private func request(_ urlRequest: URLRequest?, failedWith error: Error) -> DataRequest {
+        var requestTask: Request.RequestTask = .data(nil, nil)
+
+        if let urlRequest = urlRequest {
+            let originalTask = DataRequest.Requestable(urlRequest: urlRequest)
+            requestTask = .data(originalTask, nil)
+        }
+
+        let underlyingError = error.underlyingAdaptError ?? error
+        let request = DataRequest(session: session, requestTask: requestTask, error: underlyingError)
+
+        if let retrier = retrier, error is AdaptError {
+            allowRetrier(retrier, toRetry: request, with: underlyingError)
+        } else {
+            if startRequestsImmediately { request.resume() }
+        }
+
+        return request
+    }
+
+    // MARK: - Download Request
+
+    // MARK: URL Request
+
+    /// Creates a `DownloadRequest` to retrieve the contents the specified `url`, `method`, `parameters`, `encoding`,
+    /// `headers` and save them to the `destination`.
+    ///
+    /// If `destination` is not specified, the contents will remain in the temporary location determined by the
+    /// underlying URL session.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// - parameter url:         The URL.
+    /// - parameter method:      The HTTP method. `.get` by default.
+    /// - parameter parameters:  The parameters. `nil` by default.
+    /// - parameter encoding:    The parameter encoding. `URLEncoding.default` by default.
+    /// - parameter headers:     The HTTP headers. `nil` by default.
+    /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
+    ///
+    /// - returns: The created `DownloadRequest`.
+    @discardableResult
+    open func download(
+        _ url: URLConvertible,
+        method: HTTPMethod = .get,
+        parameters: Parameters? = nil,
+        encoding: ParameterEncoding = URLEncoding.default,
+        headers: HTTPHeaders? = nil,
+        to destination: DownloadRequest.DownloadFileDestination? = nil)
+        -> DownloadRequest
+    {
+        do {
+            let urlRequest = try URLRequest(url: url, method: method, headers: headers)
+            let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
+            return download(encodedURLRequest, to: destination)
+        } catch {
+            return download(nil, to: destination, failedWith: error)
+        }
+    }
+
+    /// Creates a `DownloadRequest` to retrieve the contents of a URL based on the specified `urlRequest` and save
+    /// them to the `destination`.
+    ///
+    /// If `destination` is not specified, the contents will remain in the temporary location determined by the
+    /// underlying URL session.
+    ///
+    /// 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. `nil` by default.
+    ///
+    /// - returns: The created `DownloadRequest`.
+    @discardableResult
+    open func download(
+        _ urlRequest: URLRequestConvertible,
+        to destination: DownloadRequest.DownloadFileDestination? = nil)
+        -> DownloadRequest
+    {
+        do {
+            let urlRequest = try urlRequest.asURLRequest()
+            return download(.request(urlRequest), to: destination)
+        } catch {
+            return download(nil, to: destination, failedWith: error)
+        }
+    }
+
+    // MARK: Resume Data
+
+    /// Creates a `DownloadRequest` from the `resumeData` produced from a previous request cancellation to retrieve
+    /// the contents of the original request and save them to the `destination`.
+    ///
+    /// If `destination` is not specified, the contents will remain in the temporary location determined by the
+    /// underlying URL session.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken
+    /// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the
+    /// data is written incorrectly and will always fail to resume the download. For more information about the bug and
+    /// possible workarounds, please refer to the following Stack Overflow post:
+    ///
+    ///    - http://stackoverflow.com/a/39347461/1342462
+    ///
+    /// - parameter resumeData:  The resume data. This is an opaque data blob produced by `URLSessionDownloadTask`
+    ///                          when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for
+    ///                          additional information.
+    /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
+    ///
+    /// - returns: The created `DownloadRequest`.
+    @discardableResult
+    open func download(
+        resumingWith resumeData: Data,
+        to destination: DownloadRequest.DownloadFileDestination? = nil)
+        -> DownloadRequest
+    {
+        return download(.resumeData(resumeData), to: destination)
+    }
+
+    // MARK: Private - Download Implementation
+
+    private func download(
+        _ downloadable: DownloadRequest.Downloadable,
+        to destination: DownloadRequest.DownloadFileDestination?)
+        -> DownloadRequest
+    {
+        do {
+            let task = try downloadable.task(session: session, adapter: adapter, queue: queue)
+            let download = DownloadRequest(session: session, requestTask: .download(downloadable, task))
+
+            download.downloadDelegate.destination = destination
+
+            delegate[task] = download
+
+            if startRequestsImmediately { download.resume() }
+
+            return download
+        } catch {
+            return download(downloadable, to: destination, failedWith: error)
+        }
+    }
+
+    private func download(
+        _ downloadable: DownloadRequest.Downloadable?,
+        to destination: DownloadRequest.DownloadFileDestination?,
+        failedWith error: Error)
+        -> DownloadRequest
+    {
+        var downloadTask: Request.RequestTask = .download(nil, nil)
+
+        if let downloadable = downloadable {
+            downloadTask = .download(downloadable, nil)
+        }
+
+        let underlyingError = error.underlyingAdaptError ?? error
+
+        let download = DownloadRequest(session: session, requestTask: downloadTask, error: underlyingError)
+        download.downloadDelegate.destination = destination
+
+        if let retrier = retrier, error is AdaptError {
+            allowRetrier(retrier, toRetry: download, with: underlyingError)
+        } else {
+            if startRequestsImmediately { download.resume() }
+        }
+
+        return download
+    }
+
+    // MARK: - Upload Request
+
+    // MARK: File
+
+    /// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `file`.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// - parameter file:    The file to upload.
+    /// - parameter url:     The URL.
+    /// - parameter method:  The HTTP method. `.post` by default.
+    /// - parameter headers: The HTTP headers. `nil` by default.
+    ///
+    /// - returns: The created `UploadRequest`.
+    @discardableResult
+    open func upload(
+        _ fileURL: URL,
+        to url: URLConvertible,
+        method: HTTPMethod = .post,
+        headers: HTTPHeaders? = nil)
+        -> UploadRequest
+    {
+        do {
+            let urlRequest = try URLRequest(url: url, method: method, headers: headers)
+            return upload(fileURL, with: urlRequest)
+        } catch {
+            return upload(nil, failedWith: error)
+        }
+    }
+
+    /// Creates a `UploadRequest` from the specified `urlRequest` for uploading the `file`.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// - parameter file:       The file to upload.
+    /// - parameter urlRequest: The URL request.
+    ///
+    /// - returns: The created `UploadRequest`.
+    @discardableResult
+    open func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest {
+        do {
+            let urlRequest = try urlRequest.asURLRequest()
+            return upload(.file(fileURL, urlRequest))
+        } catch {
+            return upload(nil, failedWith: error)
+        }
+    }
+
+    // MARK: Data
+
+    /// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `data`.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// - parameter data:    The data to upload.
+    /// - parameter url:     The URL.
+    /// - parameter method:  The HTTP method. `.post` by default.
+    /// - parameter headers: The HTTP headers. `nil` by default.
+    ///
+    /// - returns: The created `UploadRequest`.
+    @discardableResult
+    open func upload(
+        _ data: Data,
+        to url: URLConvertible,
+        method: HTTPMethod = .post,
+        headers: HTTPHeaders? = nil)
+        -> UploadRequest
+    {
+        do {
+            let urlRequest = try URLRequest(url: url, method: method, headers: headers)
+            return upload(data, with: urlRequest)
+        } catch {
+            return upload(nil, failedWith: error)
+        }
+    }
+
+    /// Creates an `UploadRequest` from the specified `urlRequest` for uploading the `data`.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// - parameter data:       The data to upload.
+    /// - parameter urlRequest: The URL request.
+    ///
+    /// - returns: The created `UploadRequest`.
+    @discardableResult
+    open func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest {
+        do {
+            let urlRequest = try urlRequest.asURLRequest()
+            return upload(.data(data, urlRequest))
+        } catch {
+            return upload(nil, failedWith: error)
+        }
+    }
+
+    // MARK: InputStream
+
+    /// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `stream`.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// - parameter stream:  The stream to upload.
+    /// - parameter url:     The URL.
+    /// - parameter method:  The HTTP method. `.post` by default.
+    /// - parameter headers: The HTTP headers. `nil` by default.
+    ///
+    /// - returns: The created `UploadRequest`.
+    @discardableResult
+    open func upload(
+        _ stream: InputStream,
+        to url: URLConvertible,
+        method: HTTPMethod = .post,
+        headers: HTTPHeaders? = nil)
+        -> UploadRequest
+    {
+        do {
+            let urlRequest = try URLRequest(url: url, method: method, headers: headers)
+            return upload(stream, with: urlRequest)
+        } catch {
+            return upload(nil, failedWith: error)
+        }
+    }
+
+    /// Creates an `UploadRequest` from the specified `urlRequest` for uploading the `stream`.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// - parameter stream:     The stream to upload.
+    /// - parameter urlRequest: The URL request.
+    ///
+    /// - returns: The created `UploadRequest`.
+    @discardableResult
+    open func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest {
+        do {
+            let urlRequest = try urlRequest.asURLRequest()
+            return upload(.stream(stream, urlRequest))
+        } catch {
+            return upload(nil, failedWith: error)
+        }
+    }
+
+    // MARK: MultipartFormData
+
+    /// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new
+    /// `UploadRequest` using the `url`, `method` and `headers`.
+    ///
+    /// 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 multipartFormData:       The closure used to append body parts to the `MultipartFormData`.
+    /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
+    ///                                      `multipartFormDataEncodingMemoryThreshold` by default.
+    /// - parameter url:                     The URL.
+    /// - parameter method:                  The HTTP method. `.post` by default.
+    /// - parameter headers:                 The HTTP headers. `nil` by default.
+    /// - parameter encodingCompletion:      The closure called when the `MultipartFormData` encoding is complete.
+    open func upload(
+        multipartFormData: @escaping (MultipartFormData) -> Void,
+        usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
+        to url: URLConvertible,
+        method: HTTPMethod = .post,
+        headers: HTTPHeaders? = nil,
+        encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
+    {
+        do {
+            let urlRequest = try URLRequest(url: url, method: method, headers: headers)
+
+            return upload(
+                multipartFormData: multipartFormData,
+                usingThreshold: encodingMemoryThreshold,
+                with: urlRequest,
+                encodingCompletion: encodingCompletion
+            )
+        } catch {
+            DispatchQueue.main.async { encodingCompletion?(.failure(error)) }
+        }
+    }
+
+    /// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new
+    /// `UploadRequest` using the `urlRequest`.
+    ///
+    /// 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 multipartFormData:       The closure used to append body parts to the `MultipartFormData`.
+    /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
+    ///                                      `multipartFormDataEncodingMemoryThreshold` by default.
+    /// - parameter urlRequest:              The URL request.
+    /// - parameter encodingCompletion:      The closure called when the `MultipartFormData` encoding is complete.
+    open func upload(
+        multipartFormData: @escaping (MultipartFormData) -> Void,
+        usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
+        with urlRequest: URLRequestConvertible,
+        encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
+    {
+        DispatchQueue.global(qos: .utility).async {
+            let formData = MultipartFormData()
+            multipartFormData(formData)
+
+            var tempFileURL: URL?
+
+            do {
+                var urlRequestWithContentType = try urlRequest.asURLRequest()
+                urlRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type")
+
+                let isBackgroundSession = self.session.configuration.identifier != nil
+
+                if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession {
+                    let data = try formData.encode()
+
+                    let encodingResult = MultipartFormDataEncodingResult.success(
+                        request: self.upload(data, with: urlRequestWithContentType),
+                        streamingFromDisk: false,
+                        streamFileURL: nil
+                    )
+
+                    DispatchQueue.main.async { encodingCompletion?(encodingResult) }
+                } else {
+                    let fileManager = FileManager.default
+                    let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory())
+                    let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data")
+                    let fileName = UUID().uuidString
+                    let fileURL = directoryURL.appendingPathComponent(fileName)
+
+                    tempFileURL = fileURL
+
+                    var directoryError: Error?
+
+                    // Create directory inside serial queue to ensure two threads don't do this in parallel
+                    self.queue.sync {
+                        do {
+                            try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
+                        } catch {
+                            directoryError = error
+                        }
+                    }
+
+                    if let directoryError = directoryError { throw directoryError }
+
+                    try formData.writeEncodedData(to: fileURL)
+
+                    let upload = self.upload(fileURL, with: urlRequestWithContentType)
+
+                    // Cleanup the temp file once the upload is complete
+                    upload.delegate.queue.addOperation {
+                        do {
+                            try FileManager.default.removeItem(at: fileURL)
+                        } catch {
+                            // No-op
+                        }
+                    }
+
+                    DispatchQueue.main.async {
+                        let encodingResult = MultipartFormDataEncodingResult.success(
+                            request: upload,
+                            streamingFromDisk: true,
+                            streamFileURL: fileURL
+                        )
+
+                        encodingCompletion?(encodingResult)
+                    }
+                }
+            } catch {
+                // Cleanup the temp file in the event that the multipart form data encoding failed
+                if let tempFileURL = tempFileURL {
+                    do {
+                        try FileManager.default.removeItem(at: tempFileURL)
+                    } catch {
+                        // No-op
+                    }
+                }
+
+                DispatchQueue.main.async { encodingCompletion?(.failure(error)) }
+            }
+        }
+    }
+
+    // MARK: Private - Upload Implementation
+
+    private func upload(_ uploadable: UploadRequest.Uploadable) -> UploadRequest {
+        do {
+            let task = try uploadable.task(session: session, adapter: adapter, queue: queue)
+            let upload = UploadRequest(session: session, requestTask: .upload(uploadable, task))
+
+            if case let .stream(inputStream, _) = uploadable {
+                upload.delegate.taskNeedNewBodyStream = { _, _ in inputStream }
+            }
+
+            delegate[task] = upload
+
+            if startRequestsImmediately { upload.resume() }
+
+            return upload
+        } catch {
+            return upload(uploadable, failedWith: error)
+        }
+    }
+
+    private func upload(_ uploadable: UploadRequest.Uploadable?, failedWith error: Error) -> UploadRequest {
+        var uploadTask: Request.RequestTask = .upload(nil, nil)
+
+        if let uploadable = uploadable {
+            uploadTask = .upload(uploadable, nil)
+        }
+
+        let underlyingError = error.underlyingAdaptError ?? error
+        let upload = UploadRequest(session: session, requestTask: uploadTask, error: underlyingError)
+
+        if let retrier = retrier, error is AdaptError {
+            allowRetrier(retrier, toRetry: upload, with: underlyingError)
+        } else {
+            if startRequestsImmediately { upload.resume() }
+        }
+
+        return upload
+    }
+
+#if !os(watchOS)
+
+    // MARK: - Stream Request
+
+    // MARK: Hostname and Port
+
+    /// Creates a `StreamRequest` for bidirectional streaming using the `hostname` and `port`.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// - parameter hostName: The hostname of the server to connect to.
+    /// - parameter port:     The port of the server to connect to.
+    ///
+    /// - returns: The created `StreamRequest`.
+    @discardableResult
+    @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+    open func stream(withHostName hostName: String, port: Int) -> StreamRequest {
+        return stream(.stream(hostName: hostName, port: port))
+    }
+
+    // MARK: NetService
+
+    /// Creates a `StreamRequest` for bidirectional streaming using the `netService`.
+    ///
+    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+    ///
+    /// - parameter netService: The net service used to identify the endpoint.
+    ///
+    /// - returns: The created `StreamRequest`.
+    @discardableResult
+    @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+    open func stream(with netService: NetService) -> StreamRequest {
+        return stream(.netService(netService))
+    }
+
+    // MARK: Private - Stream Implementation
+
+    @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+    private func stream(_ streamable: StreamRequest.Streamable) -> StreamRequest {
+        do {
+            let task = try streamable.task(session: session, adapter: adapter, queue: queue)
+            let request = StreamRequest(session: session, requestTask: .stream(streamable, task))
+
+            delegate[task] = request
+
+            if startRequestsImmediately { request.resume() }
+
+            return request
+        } catch {
+            return stream(failedWith: error)
+        }
+    }
+
+    @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
+    private func stream(failedWith error: Error) -> StreamRequest {
+        let stream = StreamRequest(session: session, requestTask: .stream(nil, nil), error: error)
+        if startRequestsImmediately { stream.resume() }
+        return stream
+    }
+
+#endif
+
+    // MARK: - Internal - Retry Request
+
+    func retry(_ request: Request) -> Bool {
+        guard let originalTask = request.originalTask else { return false }
+
+        do {
+            let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
+
+            request.delegate.task = task // resets all task delegate data
+
+            request.retryCount += 1
+            request.startTime = CFAbsoluteTimeGetCurrent()
+            request.endTime = nil
+
+            task.resume()
+
+            return true
+        } catch {
+            request.delegate.error = error.underlyingAdaptError ?? error
+            return false
+        }
+    }
+
+    private func allowRetrier(_ retrier: RequestRetrier, toRetry request: Request, with error: Error) {
+        DispatchQueue.utility.async { [weak self] in
+            guard let strongSelf = self else { return }
+
+            retrier.should(strongSelf, retry: request, with: error) { shouldRetry, timeDelay in
+                guard let strongSelf = self else { return }
+
+                guard shouldRetry else {
+                    if strongSelf.startRequestsImmediately { request.resume() }
+                    return
+                }
+
+                DispatchQueue.utility.after(timeDelay) {
+                    guard let strongSelf = self else { return }
+
+                    let retrySucceeded = strongSelf.retry(request)
+
+                    if retrySucceeded, let task = request.task {
+                        strongSelf.delegate[task] = request
+                    } else {
+                        if strongSelf.startRequestsImmediately { request.resume() }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/iOS/Pods/Alamofire/Source/TaskDelegate.swift b/iOS/Pods/Alamofire/Source/TaskDelegate.swift
new file mode 100644 (file)
index 0000000..0607758
--- /dev/null
@@ -0,0 +1,466 @@
+//
+//  TaskDelegate.swift
+//
+//  Copyright (c) 2014-2017 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 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 {
+
+    // MARK: Properties
+
+    /// The serial operation queue used to execute all operations after the task completes.
+    open let queue: OperationQueue
+
+    /// The data returned by the server.
+    public var data: Data? { return nil }
+
+    /// The error generated throughout the lifecyle of the task.
+    public var error: Error?
+
+    var task: URLSessionTask? {
+        set {
+            taskLock.lock(); defer { taskLock.unlock() }
+            _task = newValue
+        }
+        get {
+            taskLock.lock(); defer { taskLock.unlock() }
+            return _task
+        }
+    }
+
+    var initialResponseTime: CFAbsoluteTime?
+    var credential: URLCredential?
+    var metrics: AnyObject? // URLSessionTaskMetrics
+
+    private var _task: URLSessionTask? {
+        didSet { reset() }
+    }
+
+    private let taskLock = NSLock()
+
+    // MARK: Lifecycle
+
+    init(task: URLSessionTask?) {
+        _task = task
+
+        self.queue = {
+            let operationQueue = OperationQueue()
+
+            operationQueue.maxConcurrentOperationCount = 1
+            operationQueue.isSuspended = true
+            operationQueue.qualityOfService = .utility
+
+            return operationQueue
+        }()
+    }
+
+    func reset() {
+        error = nil
+        initialResponseTime = nil
+    }
+
+    // MARK: URLSessionTaskDelegate
+
+    var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
+    var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
+    var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
+    var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)?
+
+    @objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)
+    func urlSession(
+        _ session: URLSession,
+        task: URLSessionTask,
+        willPerformHTTPRedirection response: HTTPURLResponse,
+        newRequest request: URLRequest,
+        completionHandler: @escaping (URLRequest?) -> Void)
+    {
+        var redirectRequest: URLRequest? = request
+
+        if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
+            redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
+        }
+
+        completionHandler(redirectRequest)
+    }
+
+    @objc(URLSession:task:didReceiveChallenge:completionHandler:)
+    func urlSession(
+        _ session: URLSession,
+        task: URLSessionTask,
+        didReceive challenge: URLAuthenticationChallenge,
+        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
+    {
+        var disposition: 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?.serverTrustPolicy(forHost: host),
+                let serverTrust = challenge.protectionSpace.serverTrust
+            {
+                if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
+                    disposition = .useCredential
+                    credential = URLCredential(trust: serverTrust)
+                } else {
+                    disposition = .cancelAuthenticationChallenge
+                }
+            }
+        } else {
+            if challenge.previousFailureCount > 0 {
+                disposition = .rejectProtectionSpace
+            } else {
+                credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
+
+                if credential != nil {
+                    disposition = .useCredential
+                }
+            }
+        }
+
+        completionHandler(disposition, credential)
+    }
+
+    @objc(URLSession:task:needNewBodyStream:)
+    func urlSession(
+        _ session: URLSession,
+        task: URLSessionTask,
+        needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
+    {
+        var bodyStream: InputStream?
+
+        if let taskNeedNewBodyStream = taskNeedNewBodyStream {
+            bodyStream = taskNeedNewBodyStream(session, task)
+        }
+
+        completionHandler(bodyStream)
+    }
+
+    @objc(URLSession:task:didCompleteWithError:)
+    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
+        if let taskDidCompleteWithError = taskDidCompleteWithError {
+            taskDidCompleteWithError(session, task, error)
+        } else {
+            if let error = error {
+                if self.error == nil { self.error = error }
+
+                if
+                    let downloadDelegate = self as? DownloadTaskDelegate,
+                    let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
+                {
+                    downloadDelegate.resumeData = resumeData
+                }
+            }
+
+            queue.isSuspended = false
+        }
+    }
+}
+
+// MARK: -
+
+class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate {
+
+    // MARK: Properties
+
+    var dataTask: URLSessionDataTask { return task as! URLSessionDataTask }
+
+    override var data: Data? {
+        if dataStream != nil {
+            return nil
+        } else {
+            return mutableData
+        }
+    }
+
+    var progress: Progress
+    var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
+
+    var dataStream: ((_ data: Data) -> Void)?
+
+    private var totalBytesReceived: Int64 = 0
+    private var mutableData: Data
+
+    private var expectedContentLength: Int64?
+
+    // MARK: Lifecycle
+
+    override init(task: URLSessionTask?) {
+        mutableData = Data()
+        progress = Progress(totalUnitCount: 0)
+
+        super.init(task: task)
+    }
+
+    override func reset() {
+        super.reset()
+
+        progress = Progress(totalUnitCount: 0)
+        totalBytesReceived = 0
+        mutableData = Data()
+        expectedContentLength = nil
+    }
+
+    // MARK: URLSessionDataDelegate
+
+    var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
+    var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
+    var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
+    var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
+
+    func urlSession(
+        _ session: URLSession,
+        dataTask: URLSessionDataTask,
+        didReceive response: URLResponse,
+        completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
+    {
+        var disposition: 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 initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
+
+        if let dataTaskDidReceiveData = dataTaskDidReceiveData {
+            dataTaskDidReceiveData(session, dataTask, data)
+        } else {
+            if let dataStream = dataStream {
+                dataStream(data)
+            } else {
+                mutableData.append(data)
+            }
+
+            let bytesReceived = Int64(data.count)
+            totalBytesReceived += bytesReceived
+            let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
+
+            progress.totalUnitCount = totalBytesExpected
+            progress.completedUnitCount = totalBytesReceived
+
+            if let progressHandler = progressHandler {
+                progressHandler.queue.async { progressHandler.closure(self.progress) }
+            }
+        }
+    }
+
+    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: -
+
+class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate {
+
+    // MARK: Properties
+
+    var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask }
+
+    var progress: Progress
+    var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
+
+    var resumeData: Data?
+    override var data: Data? { return resumeData }
+
+    var destination: DownloadRequest.DownloadFileDestination?
+
+    var temporaryURL: URL?
+    var destinationURL: URL?
+
+    var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL }
+
+    // MARK: Lifecycle
+
+    override init(task: URLSessionTask?) {
+        progress = Progress(totalUnitCount: 0)
+        super.init(task: task)
+    }
+
+    override func reset() {
+        super.reset()
+
+        progress = Progress(totalUnitCount: 0)
+        resumeData = nil
+    }
+
+    // MARK: URLSessionDownloadDelegate
+
+    var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)?
+    var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
+    var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
+
+    func urlSession(
+        _ session: URLSession,
+        downloadTask: URLSessionDownloadTask,
+        didFinishDownloadingTo location: URL)
+    {
+        temporaryURL = location
+
+        guard
+            let destination = destination,
+            let response = downloadTask.response as? HTTPURLResponse
+        else { return }
+
+        let result = destination(location, response)
+        let destinationURL = result.destinationURL
+        let options = result.options
+
+        self.destinationURL = destinationURL
+
+        do {
+            if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) {
+                try FileManager.default.removeItem(at: destinationURL)
+            }
+
+            if options.contains(.createIntermediateDirectories) {
+                let directory = destinationURL.deletingLastPathComponent()
+                try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
+            }
+
+            try FileManager.default.moveItem(at: location, to: destinationURL)
+        } catch {
+            self.error = error
+        }
+    }
+
+    func urlSession(
+        _ session: URLSession,
+        downloadTask: URLSessionDownloadTask,
+        didWriteData bytesWritten: Int64,
+        totalBytesWritten: Int64,
+        totalBytesExpectedToWrite: Int64)
+    {
+        if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
+
+        if let downloadTaskDidWriteData = downloadTaskDidWriteData {
+            downloadTaskDidWriteData(
+                session,
+                downloadTask,
+                bytesWritten,
+                totalBytesWritten,
+                totalBytesExpectedToWrite
+            )
+        } else {
+            progress.totalUnitCount = totalBytesExpectedToWrite
+            progress.completedUnitCount = totalBytesWritten
+
+            if let progressHandler = progressHandler {
+                progressHandler.queue.async { progressHandler.closure(self.progress) }
+            }
+        }
+    }
+
+    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
+        }
+    }
+}
+
+// MARK: -
+
+class UploadTaskDelegate: DataTaskDelegate {
+
+    // MARK: Properties
+
+    var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask }
+
+    var uploadProgress: Progress
+    var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
+
+    // MARK: Lifecycle
+
+    override init(task: URLSessionTask?) {
+        uploadProgress = Progress(totalUnitCount: 0)
+        super.init(task: task)
+    }
+
+    override func reset() {
+        super.reset()
+        uploadProgress = Progress(totalUnitCount: 0)
+    }
+
+    // MARK: URLSessionTaskDelegate
+
+    var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
+
+    func URLSession(
+        _ session: URLSession,
+        task: URLSessionTask,
+        didSendBodyData bytesSent: Int64,
+        totalBytesSent: Int64,
+        totalBytesExpectedToSend: Int64)
+    {
+        if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
+
+        if let taskDidSendBodyData = taskDidSendBodyData {
+            taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
+        } else {
+            uploadProgress.totalUnitCount = totalBytesExpectedToSend
+            uploadProgress.completedUnitCount = totalBytesSent
+
+            if let uploadProgressHandler = uploadProgressHandler {
+                uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) }
+            }
+        }
+    }
+}
diff --git a/iOS/Pods/Alamofire/Source/Timeline.swift b/iOS/Pods/Alamofire/Source/Timeline.swift
new file mode 100644 (file)
index 0000000..c5dabd1
--- /dev/null
@@ -0,0 +1,136 @@
+//
+//  Timeline.swift
+//
+//  Copyright (c) 2014-2017 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 computing the timing metrics for the complete lifecycle of a `Request`.
+public struct Timeline {
+    /// The time the request was initialized.
+    public let requestStartTime: CFAbsoluteTime
+
+    /// The time the first bytes were received from or sent to the server.
+    public let initialResponseTime: CFAbsoluteTime
+
+    /// The time when the request was completed.
+    public let requestCompletedTime: CFAbsoluteTime
+
+    /// The time when the response serialization was completed.
+    public let serializationCompletedTime: CFAbsoluteTime
+
+    /// The time interval in seconds from the time the request started to the initial response from the server.
+    public let latency: TimeInterval
+
+    /// The time interval in seconds from the time the request started to the time the request completed.
+    public let requestDuration: TimeInterval
+
+    /// The time interval in seconds from the time the request completed to the time response serialization completed.
+    public let serializationDuration: TimeInterval
+
+    /// The time interval in seconds from the time the request started to the time response serialization completed.
+    public let totalDuration: TimeInterval
+
+    /// Creates a new `Timeline` instance with the specified request times.
+    ///
+    /// - parameter requestStartTime:           The time the request was initialized. Defaults to `0.0`.
+    /// - parameter initialResponseTime:        The time the first bytes were received from or sent to the server.
+    ///                                         Defaults to `0.0`.
+    /// - parameter requestCompletedTime:       The time when the request was completed. Defaults to `0.0`.
+    /// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults
+    ///                                         to `0.0`.
+    ///
+    /// - returns: The new `Timeline` instance.
+    public init(
+        requestStartTime: CFAbsoluteTime = 0.0,
+        initialResponseTime: CFAbsoluteTime = 0.0,
+        requestCompletedTime: CFAbsoluteTime = 0.0,
+        serializationCompletedTime: CFAbsoluteTime = 0.0)
+    {
+        self.requestStartTime = requestStartTime
+        self.initialResponseTime = initialResponseTime
+        self.requestCompletedTime = requestCompletedTime
+        self.serializationCompletedTime = serializationCompletedTime
+
+        self.latency = initialResponseTime - requestStartTime
+        self.requestDuration = requestCompletedTime - requestStartTime
+        self.serializationDuration = serializationCompletedTime - requestCompletedTime
+        self.totalDuration = serializationCompletedTime - requestStartTime
+    }
+}
+
+// MARK: - CustomStringConvertible
+
+extension Timeline: CustomStringConvertible {
+    /// The textual representation used when written to an output stream, which includes the latency, the request
+    /// duration and the total duration.
+    public var description: String {
+        let latency = String(format: "%.3f", self.latency)
+        let requestDuration = String(format: "%.3f", self.requestDuration)
+        let serializationDuration = String(format: "%.3f", self.serializationDuration)
+        let totalDuration = String(format: "%.3f", self.totalDuration)
+
+        // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
+        // fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
+        let timings = [
+            "\"Latency\": " + latency + " secs",
+            "\"Request Duration\": " + requestDuration + " secs",
+            "\"Serialization Duration\": " + serializationDuration + " secs",
+            "\"Total Duration\": " + totalDuration + " secs"
+        ]
+
+        return "Timeline: { " + timings.joined(separator: ", ") + " }"
+    }
+}
+
+// MARK: - CustomDebugStringConvertible
+
+extension Timeline: CustomDebugStringConvertible {
+    /// The textual representation used when written to an output stream, which includes the request start time, the
+    /// initial response time, the request completed time, the serialization completed time, the latency, the request
+    /// duration and the total duration.
+    public var debugDescription: String {
+        let requestStartTime = String(format: "%.3f", self.requestStartTime)
+        let initialResponseTime = String(format: "%.3f", self.initialResponseTime)
+        let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime)
+        let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime)
+        let latency = String(format: "%.3f", self.latency)
+        let requestDuration = String(format: "%.3f", self.requestDuration)
+        let serializationDuration = String(format: "%.3f", self.serializationDuration)
+        let totalDuration = String(format: "%.3f", self.totalDuration)
+
+        // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
+        // fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
+        let timings = [
+            "\"Request Start Time\": " + requestStartTime,
+            "\"Initial Response Time\": " + initialResponseTime,
+            "\"Request Completed Time\": " + requestCompletedTime,
+            "\"Serialization Completed Time\": " + serializationCompletedTime,
+            "\"Latency\": " + latency + " secs",
+            "\"Request Duration\": " + requestDuration + " secs",
+            "\"Serialization Duration\": " + serializationDuration + " secs",
+            "\"Total Duration\": " + totalDuration + " secs"
+        ]
+
+        return "Timeline: { " + timings.joined(separator: ", ") + " }"
+    }
+}
diff --git a/iOS/Pods/Alamofire/Source/Validation.swift b/iOS/Pods/Alamofire/Source/Validation.swift
new file mode 100644 (file)
index 0000000..989ac20
--- /dev/null
@@ -0,0 +1,315 @@
+//
+//  Validation.swift
+//
+//  Copyright (c) 2014-2017 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 {
+
+    // MARK: Helper Types
+
+    fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason
+
+    /// 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)
+    }
+
+    fileprivate struct MIMEType {
+        let type: String
+        let subtype: String
+
+        var isWildcard: Bool { return type == "*" && subtype == "*" }
+
+        init?(_ string: String) {
+            let components: [String] = {
+                let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines)
+
+            #if swift(>=3.2)
+                let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)]
+            #else
+                let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)
+            #endif
+
+                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
+            }
+        }
+    }
+
+    // MARK: Properties
+
+    fileprivate var acceptableStatusCodes: [Int] { return Array(200..<300) }
+
+    fileprivate var acceptableContentTypes: [String] {
+        if let accept = request?.value(forHTTPHeaderField: "Accept") {
+            return accept.components(separatedBy: ",")
+        }
+
+        return ["*/*"]
+    }
+
+    // MARK: Status Code
+
+    fileprivate func validate<S: Sequence>(
+        statusCode acceptableStatusCodes: S,
+        response: HTTPURLResponse)
+        -> ValidationResult
+        where S.Iterator.Element == Int
+    {
+        if acceptableStatusCodes.contains(response.statusCode) {
+            return .success
+        } else {
+            let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode)
+            return .failure(AFError.responseValidationFailed(reason: reason))
+        }
+    }
+
+    // MARK: Content Type
+
+    fileprivate func validate<S: Sequence>(
+        contentType acceptableContentTypes: S,
+        response: HTTPURLResponse,
+        data: Data?)
+        -> ValidationResult
+        where S.Iterator.Element == String
+    {
+        guard let data = data, data.count > 0 else { return .success }
+
+        guard
+            let responseContentType = response.mimeType,
+            let responseMIMEType = MIMEType(responseContentType)
+        else {
+            for contentType in acceptableContentTypes {
+                if let mimeType = MIMEType(contentType), mimeType.isWildcard {
+                    return .success
+                }
+            }
+
+            let error: AFError = {
+                let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes))
+                return AFError.responseValidationFailed(reason: reason)
+            }()
+
+            return .failure(error)
+        }
+
+        for contentType in acceptableContentTypes {
+            if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) {
+                return .success
+            }
+        }
+
+        let error: AFError = {
+            let reason: ErrorReason = .unacceptableContentType(
+                acceptableContentTypes: Array(acceptableContentTypes),
+                responseContentType: responseContentType
+            )
+
+            return AFError.responseValidationFailed(reason: reason)
+        }()
+
+        return .failure(error)
+    }
+}
+
+// MARK: -
+
+extension DataRequest {
+    /// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the
+    /// request was valid.
+    public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> 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.
+    @discardableResult
+    public func validate(_ validation: @escaping Validation) -> Self {
+        let validationExecution: () -> Void = { [unowned self] in
+            if
+                let response = self.response,
+                self.delegate.error == nil,
+                case let .failure(error) = validation(self.request, response, self.delegate.data)
+            {
+                self.delegate.error = error
+            }
+        }
+
+        validations.append(validationExecution)
+
+        return self
+    }
+
+    /// Validates that the response has a status code in the specified sequence.
+    ///
+    /// If validation fails, subsequent calls to response handlers will have an associated error.
+    ///
+    /// - parameter range: The range of acceptable status codes.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
+        return validate { [unowned self] _, response, _ in
+            return self.validate(statusCode: acceptableStatusCodes, response: response)
+        }
+    }
+
+    /// Validates that the response has a content type in the specified sequence.
+    ///
+    /// 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.
+    @discardableResult
+    public func validate<S: Sequence>(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String {
+        return validate { [unowned self] _, response, data in
+            return self.validate(contentType: acceptableContentTypes, response: response, data: data)
+        }
+    }
+
+    /// 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.
+    @discardableResult
+    public func validate() -> Self {
+        return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes)
+    }
+}
+
+// MARK: -
+
+extension DownloadRequest {
+    /// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a
+    /// destination URL, and returns whether the request was valid.
+    public typealias Validation = (
+        _ request: URLRequest?,
+        _ response: HTTPURLResponse,
+        _ temporaryURL: URL?,
+        _ destinationURL: URL?)
+        -> 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.
+    @discardableResult
+    public func validate(_ validation: @escaping Validation) -> Self {
+        let validationExecution: () -> Void = { [unowned self] in
+            let request = self.request
+            let temporaryURL = self.downloadDelegate.temporaryURL
+            let destinationURL = self.downloadDelegate.destinationURL
+
+            if
+                let response = self.response,
+                self.delegate.error == nil,
+                case let .failure(error) = validation(request, response, temporaryURL, destinationURL)
+            {
+                self.delegate.error = error
+            }
+        }
+
+        validations.append(validationExecution)
+
+        return self
+    }
+
+    /// Validates that the response has a status code in the specified sequence.
+    ///
+    /// If validation fails, subsequent calls to response handlers will have an associated error.
+    ///
+    /// - parameter range: The range of acceptable status codes.
+    ///
+    /// - returns: The request.
+    @discardableResult
+    public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
+        return validate { [unowned self] _, response, _, _ in
+            return self.validate(statusCode: acceptableStatusCodes, response: response)
+        }
+    }
+
+    /// Validates that the response has a content type in the specified sequence.
+    ///
+    /// 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.
+    @discardableResult
+    public func validate<S: Sequence>(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String {
+        return validate { [unowned self] _, response, _, _ in
+            let fileURL = self.downloadDelegate.fileURL
+
+            guard let validFileURL = fileURL else {
+                return .failure(AFError.responseValidationFailed(reason: .dataFileNil))
+            }
+
+            do {
+                let data = try Data(contentsOf: validFileURL)
+                return self.validate(contentType: acceptableContentTypes, response: response, data: data)
+            } catch {
+                return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL)))
+            }
+        }
+    }
+
+    /// 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.
+    @discardableResult
+    public func validate() -> Self {
+        return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes)
+    }
+}
diff --git a/iOS/Pods/AlamofireActivityLogger/LICENSE b/iOS/Pods/AlamofireActivityLogger/LICENSE
new file mode 100644 (file)
index 0000000..6da9fcd
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Manuel García-Estañ
+
+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/iOS/Pods/AlamofireActivityLogger/README.md b/iOS/Pods/AlamofireActivityLogger/README.md
new file mode 100644 (file)
index 0000000..97f6a7e
--- /dev/null
@@ -0,0 +1,132 @@
+# AlamofireActivityLogger
+
+A response serializer for [**Alamofire**](https://github.com/Alamofire/Alamofire) which logs both request and response. It provides 4 log levels and a few options to configure your logs.
+
+**AlamofireActivityLogger** is available for: 
+
+- iOS (from 9.0)
+- macOS (from 10.11)
+- watchOS (from 2.0)
+- tvOS (from 9.0)
+
+
+> **NOTE**: This version is written for **Alamofire 4.x** and **Swift 3**. To support **Swift 2** and **Alamofire 3.x** use the [version 1.0.1](https://github.com/ManueGE/AlamofireActivityLogger/tree/1.0.1/).
+
+## Installing AlamofireActivityLogger
+
+##### Using CocoaPods
+
+Add the following to your `Podfile`:
+
+````
+pod 'AlamofireActivityLogger'
+````
+
+Then run `$ pod install`.
+
+And finally, in the classes where you need **AlamofireActivityLogger**: 
+
+````
+import AlamofireActivityLogger
+````
+
+If you don’t have CocoaPods installed or integrated into your project, you can learn how to do so [here](http://cocoapods.org).
+
+## Log
+
+To log a request you just have to write:
+
+````
+request(.get, url)
+    .validate()
+    .log()
+}
+````
+
+Additionally, you can provide the log level and some options:
+
+````
+request(.get, url)
+    .validate()
+    .log(level: level, options: options, printer: myPrinter)
+}
+````
+
+Let's see the **levels** and **options**.
+
+### Levels
+
+Are instances of the enum `LogLevel`. The available values are:
+
+ * **`none`**: Do not log requests or responses.
+ * **`all`**: Logs HTTP method, URL, header fields, & request body for requests, and status code, URL, header fields, response string, & elapsed time for responses.
+ * **`info`**: Logs HTTP method & URL for requests, and status code, URL, & elapsed time for responses.
+ * **`error`**: Logs HTTP method & URL for requests, and status code, URL, & elapsed time for responses, but only for failed requests.
+ The default value is **`all`**.
+
+### Options
+
+Are instances of the enum `LogOption`. The available values are:
+
+* **`onlyDebug`**: Only logs if the app is in Debug mode
+* **`jsonPrettyPrint`**: Prints the JSON body on request and response 
+* **`includeSeparator`**: Include a separator string at the begining and end of each section
+
+ The default value is **`[.onlyDebug, .jsonPrettyPrint, .includeSeparator]`**.
+### Custom printers
+
+If you use a third party logger, you can integrate it with AlamofireActivityLogger. To do it, you must provide a `Printer` object. 
+
+`Printer` is a protocol which just requires one method: 
+
+````swift
+func print(_ string: String, phase: Phase)
+````
+
+As an example, let’s suppose you have [SwiftyBeaver](https://github.com/SwiftyBeaver/SwiftyBeaver) integrated in your app. We can create a `SwiftyBeaverPrinter` struct like this: 
+
+````swift
+struct SwiftyBeaverPrinter: Printer {
+    func print(_ string: String, phase: Phase) {
+        if phase.isError {
+            log.error(string)
+        }
+        else {
+            log.info(string)
+        }
+    }
+}
+````
+
+And use it this way:
+
+````swift
+request(.get, url)
+    .validate()
+    .log(printer: SwiftyBeaverPrinter())
+}
+````
+
+By default, a instance of `NativePrinter` is used. It just make use of the native `Swift.print` to print the info.
+
+## Supported requests
+
+At the moment, **AlamofireActivityLogger** has support for `DataRequest` and `DownloadRequest`. If you need to add support to any other `Request` subclass, just make it conforms the `LoggeableRequest` protocol. Take a look at the `DataRequest` implementation to know how. 
+
+
+## Contact
+
+[Manuel García-Estañ Martínez](http://github.com/ManueGE)  
+[@manueGE](https://twitter.com/ManueGE)
+
+## License
+
+AlamofireActivityLogger is available under the [MIT license](LICENSE.md).
diff --git a/iOS/Pods/AlamofireActivityLogger/alamofire_activity_logger/ActivityLogger/LoggeableRequest.swift b/iOS/Pods/AlamofireActivityLogger/alamofire_activity_logger/ActivityLogger/LoggeableRequest.swift
new file mode 100644 (file)
index 0000000..e2895d3
--- /dev/null
@@ -0,0 +1,104 @@
+//
+//  LoggeableRequest.swift
+//  alamofire_activity_logger
+//
+//  Created by Manu on 30/5/16.
+//  Copyright © 2016 manuege. All rights reserved.
+//
+import Foundation
+import Alamofire
+
+/**
+ A struct that put together the relevant info from a Response
+ */
+public struct ResponseInfo {
+    public var httpResponse: HTTPURLResponse?
+    public var data: Data?
+    public var error: Error?
+    public var elapsedTime: TimeInterval
+}
+
+/**
+ Make a Request conform this protocol to be able to log its request/response
+ */
+public protocol LoggeableRequest: AnyObject {
+    
+    /// The request sent
+    var request: URLRequest? { get }
+    
+    /**
+     Use this method to fetch the info needed to buld a `ResponseInfo` instance. Once the `ResponseInfo` has been build, you must call the `completion` parameter .
+     - parameter completion: The block that must be called when the asynchronous process has finished.
+     */
+    func fetchResponseInfo(completion: @escaping (ResponseInfo) -> Void)
+}
+
+public extension LoggeableRequest {
+    
+    /**
+     Log the request and response with the given level and options
+     */
+    public func log(level: LogLevel = .all, options: [LogOption] = LogOption.defaultOptions, printer: Printer = NativePrinter()) -> Self {
+        
+        guard level != .none else {
+            return self
+        }
+        
+        let debugOption = options.contains(.onlyDebug)
+        if debugOption && !appIsDebugMode {
+            return self
+        }
+        
+        Logger.logRequest(request: request, level: level, options: options, printer: printer)
+        fetchResponseInfo { response in
+            Logger.logResponse(request: self.request, response: response, level: level, options: options, printer: printer)
+        }
+        
+        return self
+    }
+}
+
+
+extension DataRequest: LoggeableRequest {
+    public func fetchResponseInfo(completion: @escaping (ResponseInfo) -> Void) {
+        
+        responseData { (response) in
+            
+            var error: Error? = nil
+            if case .failure(let e) = response.result {
+                error = e
+            }
+            
+            let logResponse = ResponseInfo(httpResponse: response.response,
+                                           data: response.data,
+                                           error: error,
+                                           elapsedTime:  response.timeline.requestDuration)
+            completion(logResponse)
+        }
+        
+    }
+}
+
+extension DownloadRequest: LoggeableRequest {
+    public func fetchResponseInfo(completion: @escaping (ResponseInfo) -> Void) {
+        
+        responseData { (response) in
+            
+            var error: Error? = nil
+            var data: Data? = nil
+            
+            switch response.result {
+            case let .success(value):
+                data = value
+            case let .failure(value):
+                error = value
+            }
+            
+            let logResponse = ResponseInfo(httpResponse: response.response,
+                                           data: data,
+                                           error: error,
+                                           elapsedTime:  response.timeline.requestDuration)
+            completion(logResponse)
+        }
+    }
+}
diff --git a/iOS/Pods/AlamofireActivityLogger/alamofire_activity_logger/ActivityLogger/Logger.swift b/iOS/Pods/AlamofireActivityLogger/alamofire_activity_logger/ActivityLogger/Logger.swift
new file mode 100644 (file)
index 0000000..c23cad9
--- /dev/null
@@ -0,0 +1,135 @@
+//
+//  Logger.swift
+//  alamofire_activity_logger
+//
+//  Created by Manuel García-Estañ on 14/9/16.
+//  Copyright © 2016 manuege. All rights reserved.
+//
+
+import Foundation
+
+private let nullString = "(null)"
+private let separatorString = "*******************************"
+
+/**
+ A set of static methods which logs request and responses
+ */
+internal struct Logger {
+    
+    internal static func logRequest(request: URLRequest?, level: LogLevel, options: [LogOption], printer: Printer) {
+        
+        guard let request = request else {
+            return
+        }
+        
+        let method = request.httpMethod!
+        let url = request.url?.absoluteString ?? nullString
+        let headers = prettyPrintedString(from: request.allHTTPHeaderFields) ?? nullString
+        
+        // separator
+        let openSeparator = options.contains(.includeSeparator) ? "\(separatorString)\n" : ""
+        let closeSeparator = options.contains(.includeSeparator) ? "\n\(separatorString)" : ""
+        
+        switch (level) {
+        case .all:
+            let prettyPrint = options.contains(.jsonPrettyPrint)
+            let body = string(from: request.httpBody, prettyPrint: prettyPrint) ?? nullString
+            printer.print("\(openSeparator)[Request] \(method) '\(url)':\n\n[Headers]\n\(headers)\n\n[Body]\n\(body)\(closeSeparator)", phase: .request)
+            
+        case .info:
+            printer.print("\(openSeparator)[Request] \(method) '\(url)'\(closeSeparator)", phase: .request)
+            
+        default:
+            break
+        }
+    }
+    
+    internal static func logResponse(request: URLRequest?, response: ResponseInfo, level: LogLevel, options: [LogOption], printer: Printer) {
+        
+        guard level != .none else {
+            return
+        }
+        
+        let httpResponse = response.httpResponse
+        let data = response.data
+        let elapsedTime = response.elapsedTime
+        let error = response.error
+        
+        if request == nil && response.httpResponse == nil {
+            return
+        }
+        
+        // options
+        let prettyPrint = options.contains(.jsonPrettyPrint)
+        
+        // request
+        let requestMethod = request?.httpMethod ?? nullString
+        let requestUrl = request?.url?.absoluteString ?? nullString
+        
+        // response
+        let responseStatusCode = httpResponse?.statusCode ?? 0
+        let responseHeaders = prettyPrintedString(from: httpResponse?.allHeaderFields) ?? nullString
+        let responseData = string(from: data, prettyPrint: prettyPrint) ?? nullString
+        
+        // time
+        let elapsedTimeString = String(format: "[%.4f s]", elapsedTime)
+        
+        // separator
+        let openSeparator = options.contains(.includeSeparator) ? "\(separatorString)\n" : ""
+        let closeSeparator = options.contains(.includeSeparator) ? "\n\(separatorString)" : ""
+        
+        // log
+        let success = (error == nil)
+        let responseTitle = success ? "Response" : "Response Error"
+        switch level {
+        case .all:
+            printer.print("\(openSeparator)[\(responseTitle)] \(responseStatusCode) '\(requestUrl)' \(elapsedTimeString):\n\n[Headers]:\n\(responseHeaders)\n\n[Body]\n\(responseData)\(closeSeparator)", phase: .response(success: success))
+        case .info:
+            printer.print("\(openSeparator)[\(responseTitle)] \(responseStatusCode) '\(requestUrl)' \(elapsedTimeString)\(closeSeparator)", phase: .response(success: success))
+        case .error:
+            if let error = error {
+                printer.print("\(openSeparator)[\(responseTitle)] \(requestMethod) '\(requestUrl)' \(elapsedTimeString) s: \(error)\(closeSeparator)", phase: .response(success: success))
+            }
+        default:
+            break
+        }
+    }
+    
+    // MARK: - Private helpers
+    private static func string(from data: Data?, prettyPrint: Bool) -> String? {
+        
+        guard let data = data else {
+            return nil
+        }
+        
+        var response: String? = nil
+        
+        if prettyPrint,
+            let json = try? JSONSerialization.jsonObject(with: data, options: []),
+            let prettyString = prettyPrintedString(from: json) {
+            response = prettyString
+        }
+            
+        else if let dataString = String.init(data: data, encoding: .utf8) {
+            response = dataString
+        }
+        
+        return response
+    }
+    
+    private static func prettyPrintedString(from json: Any?) -> String? {
+        guard let json = json else {
+            return nil
+        }
+        
+        var response: String? = nil
+        
+        if let data = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted),
+            let dataString = String.init(data: data, encoding: .utf8) {
+            response = dataString
+        }
+        
+        return response
+    }
+
+}
diff --git a/iOS/Pods/AlamofireActivityLogger/alamofire_activity_logger/ActivityLogger/Tools.swift b/iOS/Pods/AlamofireActivityLogger/alamofire_activity_logger/ActivityLogger/Tools.swift
new file mode 100644 (file)
index 0000000..0aaa7e4
--- /dev/null
@@ -0,0 +1,107 @@
+//
+//  Tools.swift
+//  alamofire_activity_logger
+//
+//  Created by Manuel García-Estañ on 2/11/16.
+//  Copyright © 2016 manuege. All rights reserved.
+//
+
+import Foundation
+
+internal let appIsDebugMode = _isDebugAssertConfiguration()
+
+// MARK: - Levels
+
+/**
+ Log levels
+ `none`
+ Do not log requests or responses.
+ `all`
+ Logs HTTP method, URL, header fields, & request body for requests, and status code, URL, header fields, response string, & elapsed time for responses.
+ `info`
+ Logs HTTP method & URL for requests, and status code, URL, & elapsed time for responses.
+ `error`
+ Logs HTTP method & URL for requests, and status code, URL, & elapsed time for responses, but only for failed requests.
+ */
+public enum LogLevel {
+    case none
+    case all
+    case info
+    case error
+}
+
+// MARK: - Options
+
+/**
+ Login options
+ `onlyDebug`
+ Only logs if the app is in Debug mode
+ `jsonPrettyPrint`
+ Prints the JSON body on request and response
+ `includeSeparator`
+ Include a separator string at the begining and end of each section
+ */
+public enum LogOption {
+    case onlyDebug
+    case jsonPrettyPrint
+    case includeSeparator
+    
+    public static var defaultOptions: [LogOption] {
+        return [.onlyDebug, .jsonPrettyPrint, .includeSeparator]
+    }
+}
+
+// MARK: - Printer
+
+/**
+ The different phases of a request that can be printed
+ `request`
+ The request when it is sent
+ `response`
+ The response when it is received; includes a parameter `success` that inform if the response has finished succesfully
+*/
+public enum Phase {
+    case request
+    case response(success: Bool)
+    
+    /// Tells if there is an error in the phase
+    public var isError: Bool {
+        switch self {
+        case let .response(success):
+            return !success
+        case .request:
+            return false
+        }
+    }
+}
+
+/// Instances that conforms with `Printer` protocol are able to print the information from a given request
+public protocol Printer {
+    
+    /**
+     This method is called when the printer is requested to print a string. Use it to print the information in the way you need.
+     - parameter string: The string to be printed.
+     - parameter phase: The phase of the request that needs to be printed
+     */
+    func print(_ string: String, phase: Phase)
+}
+
+/// A printer that just use the native `Swift.print` function to print the string.
+public struct NativePrinter: Printer {
+    
+    /// Creates a new instance
+    public init() {}
+    
+    public func print(_ string: String, phase: Phase) {
+        Swift.print(string)
+    }
+}
diff --git a/iOS/Pods/Crashlytics/Crashlytics.framework/README b/iOS/Pods/Crashlytics/Crashlytics.framework/README
new file mode 100644 (file)
index 0000000..3ebf767
--- /dev/null
@@ -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/iOS/Pods/Crashlytics/Crashlytics.framework/submit b/iOS/Pods/Crashlytics/Crashlytics.framework/submit
new file mode 100755 (executable)
index 0000000..b7de4e3
--- /dev/null
@@ -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/iOS/Pods/Crashlytics/README.md b/iOS/Pods/Crashlytics/README.md
new file mode 100644 (file)
index 0000000..2715a06
--- /dev/null
@@ -0,0 +1,39 @@
+![Crashlytics Header](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-crashlytics-header.png)
+
+Part of [Google Fabric](https://get.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/apple/crashlytics/overview.html)
+* [Forums](https://stackoverflow.com/questions/tagged/google-fabric)
+* [Website](http://try.crashlytics.com/)
+* Follow us on Twitter: [@fabric](https://twitter.com/fabric) and [@crashlytics](https://twitter.com/crashlytics)
diff --git a/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics
new file mode 100755 (executable)
index 0000000..a33d1cc
Binary files /dev/null and b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics differ
diff --git a/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h
new file mode 100644 (file)
index 0000000..6ec011d
--- /dev/null
@@ -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<type>
+#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary<key_type, object_key>
+#else
+#define ANS_GENERIC_NSARRAY(type) NSArray
+#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
+#endif
diff --git a/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h
new file mode 100644 (file)
index 0000000..8deacbe
--- /dev/null
@@ -0,0 +1,210 @@
+//
+//  Answers.h
+//  Crashlytics
+//
+//  Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#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 successful or unsuccessful
+ *  @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 successfully.
+ *  @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 <code>NSString</code> and values must be <code>NSNumber</code> or <code>NSString</code>.
+ *  @discussion                  How we treat <code>NSNumbers</code>:
+ *                               We will provide information about the distribution of values over time.
+ *
+ *                               How we treat <code>NSStrings</code>:
+ *                               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/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h
new file mode 100644 (file)
index 0000000..1526b0d
--- /dev/null
@@ -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<type>
+    #define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary<key_type, object_key>
+#else
+    #define CLS_GENERIC_NSARRAY(type) NSArray
+    #define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
+#endif
diff --git a/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h
new file mode 100644 (file)
index 0000000..59590d5
--- /dev/null
@@ -0,0 +1,64 @@
+//
+//  CLSLogging.h
+//  Crashlytics
+//
+//  Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
+//
+#ifdef __OBJC__
+#import "CLSAttributes.h"
+#import <Foundation/Foundation.h>
+
+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/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h
new file mode 100644 (file)
index 0000000..a8ff3b0
--- /dev/null
@@ -0,0 +1,103 @@
+//
+//  CLSReport.h
+//  Crashlytics
+//
+//  Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "CLSAttributes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * The CLSCrashReport protocol is deprecated. See the CLSReport class and the CrashyticsDelegate changes for details.
+ **/
+@protocol CLSCrashReport <NSObject>
+
+@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, readonly, nullable) 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 occurred.
+ **/
+@interface CLSReport : NSObject <CLSCrashReport>
+
+- (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/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h
new file mode 100644 (file)
index 0000000..cdb5596
--- /dev/null
@@ -0,0 +1,38 @@
+//
+//  CLSStackFrame.h
+//  Crashlytics
+//
+//  Copyright 2015 Crashlytics, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#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/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h
new file mode 100644 (file)
index 0000000..7104ca8
--- /dev/null
@@ -0,0 +1,288 @@
+//
+//  Crashlytics.h
+//  Crashlytics
+//
+//  Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+#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 <CrashlyticsDelegate> 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<CrashlyticsDelegate>)delegate;
++ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id<CrashlyticsDelegate>)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 occurred
+ *  @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 <NSObject>
+@optional
+
+
+- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:");
+- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:");
+
+/**
+ *
+ *  Called when a Crashlytics instance has determined that the last execution of the
+ *  application resulted in a saved report.  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.
+ *
+ *  Instead of using this method, you should try to make use of -crashlyticsDidDetectReportForLastExecution: 
+ *  if you can.
+ *
+ *  @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 report
+ *  @param completionHandler The completion handler to call when your logic has completed.
+ *
+ */
+- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report completionHandler:(void (^)(BOOL submit))completionHandler;
+
+/**
+ *
+ *  Called when a Crashlytics instance has determined that the last execution of the
+ *  application resulted in a saved report. This method differs from
+ *  -crashlyticsDidDetectReportForLastExecution:completionHandler: in three important ways:
+ *
+ *    - it is not called synchronously during initialization
+ *    - it does not give you the ability to prevent the report from being submitted
+ *    - the report object itself is immutable
+ *
+ *  Thanks to these limitations, making use of this method does not impact reporting 
+ *  reliabilty in any way.
+ *
+ *  @param report The read-only CLSReport object representing the last detected report
+ *
+ */
+
+- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report;
+
+/**
+ *  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/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist
new file mode 100644 (file)
index 0000000..e5719bc
Binary files /dev/null and b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist differ
diff --git a/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap
new file mode 100644 (file)
index 0000000..da0845e
--- /dev/null
@@ -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/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/run b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/run
new file mode 100755 (executable)
index 0000000..9058ea6
--- /dev/null
@@ -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/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/submit b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/submit
new file mode 100755 (executable)
index 0000000..aa41e9e
Binary files /dev/null and b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/submit differ
diff --git a/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/uploadDSYM b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/uploadDSYM
new file mode 100755 (executable)
index 0000000..b5e9f58
Binary files /dev/null and b/iOS/Pods/Crashlytics/iOS/Crashlytics.framework/uploadDSYM differ
diff --git a/iOS/Pods/Crashlytics/submit b/iOS/Pods/Crashlytics/submit
new file mode 100755 (executable)
index 0000000..aa41e9e
Binary files /dev/null and b/iOS/Pods/Crashlytics/submit differ
diff --git a/iOS/Pods/Fabric/Fabric.framework/README b/iOS/Pods/Fabric/Fabric.framework/README
new file mode 100644 (file)
index 0000000..3b1fbe2
--- /dev/null
@@ -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/iOS/Pods/Fabric/Fabric.framework/run b/iOS/Pods/Fabric/Fabric.framework/run
new file mode 100755 (executable)
index 0000000..b9edd17
--- /dev/null
@@ -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/iOS/Pods/Fabric/README.md b/iOS/Pods/Fabric/README.md
new file mode 100644 (file)
index 0000000..9eca610
--- /dev/null
@@ -0,0 +1,42 @@
+![Fabric Header](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-fabric-header.png)
+
+# Fabric
+
+## Overview
+
+[Fabric](https://get.fabric.io) provides developers with the tools they need to build the best apps. Developed and maintained by Google 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://stackoverflow.com/questions/tagged/google-fabric)
+* [Website](https://get.fabric.io)
+* Follow us on Twitter: [@fabric](https://twitter.com/fabric)
diff --git a/iOS/Pods/Fabric/iOS/Fabric.framework/Fabric b/iOS/Pods/Fabric/iOS/Fabric.framework/Fabric
new file mode 100755 (executable)
index 0000000..07246ea
Binary files /dev/null and b/iOS/Pods/Fabric/iOS/Fabric.framework/Fabric differ
diff --git a/iOS/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h b/iOS/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h
new file mode 100644 (file)
index 0000000..3a9355a
--- /dev/null
@@ -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/iOS/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h b/iOS/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h
new file mode 100644 (file)
index 0000000..ecbdb53
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#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/iOS/Pods/Fabric/iOS/Fabric.framework/Info.plist b/iOS/Pods/Fabric/iOS/Fabric.framework/Info.plist
new file mode 100644 (file)
index 0000000..302cb01
Binary files /dev/null and b/iOS/Pods/Fabric/iOS/Fabric.framework/Info.plist differ
diff --git a/iOS/Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap b/iOS/Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap
new file mode 100644 (file)
index 0000000..2a31223
--- /dev/null
@@ -0,0 +1,6 @@
+framework module Fabric {
+    umbrella header "Fabric.h"
+
+    export *
+    module * { export * }
+}
\ No newline at end of file
diff --git a/iOS/Pods/Fabric/iOS/Fabric.framework/run b/iOS/Pods/Fabric/iOS/Fabric.framework/run
new file mode 100755 (executable)
index 0000000..9058ea6
--- /dev/null
@@ -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/iOS/Pods/Fabric/iOS/Fabric.framework/uploadDSYM b/iOS/Pods/Fabric/iOS/Fabric.framework/uploadDSYM
new file mode 100755 (executable)
index 0000000..ec7b802
Binary files /dev/null and b/iOS/Pods/Fabric/iOS/Fabric.framework/uploadDSYM differ
diff --git a/iOS/Pods/Fabric/run b/iOS/Pods/Fabric/run
new file mode 100755 (executable)
index 0000000..9058ea6
--- /dev/null
@@ -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/iOS/Pods/Fabric/upload-symbols b/iOS/Pods/Fabric/upload-symbols
new file mode 100755 (executable)
index 0000000..ff6da77
Binary files /dev/null and b/iOS/Pods/Fabric/upload-symbols differ
diff --git a/iOS/Pods/Fabric/uploadDSYM b/iOS/Pods/Fabric/uploadDSYM
new file mode 100755 (executable)
index 0000000..ec7b802
Binary files /dev/null and b/iOS/Pods/Fabric/uploadDSYM differ
diff --git a/iOS/Pods/Firebase/CoreOnly/Sources/Firebase.h b/iOS/Pods/Firebase/CoreOnly/Sources/Firebase.h
new file mode 100755 (executable)
index 0000000..6461547
--- /dev/null
@@ -0,0 +1,105 @@
+#import <FirebaseCore/FirebaseCore.h>
+
+#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(<FirebaseAnalytics/FirebaseAnalytics.h>)
+    #import <FirebaseAnalytics/FirebaseAnalytics.h>
+  #else
+    #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
+      #warning "FirebaseAnalytics.framework is not included in your target. Please add \
+`Firebase/Core` to your Podfile or add FirebaseAnalytics.framework to your project to ensure \
+Firebase services work as intended."
+    #endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
+  #endif
+
+  #if __has_include(<FirebaseAuth/FirebaseAuth.h>)
+    #import <FirebaseAuth/FirebaseAuth.h>
+  #endif
+
+  #if __has_include(<FirebaseCrash/FirebaseCrash.h>)
+    #import <FirebaseCrash/FirebaseCrash.h>
+  #endif
+
+  #if __has_include(<FirebaseDatabase/FirebaseDatabase.h>)
+    #import <FirebaseDatabase/FirebaseDatabase.h>
+  #endif
+
+  #if __has_include(<FirebaseDynamicLinks/FirebaseDynamicLinks.h>)
+    #import <FirebaseDynamicLinks/FirebaseDynamicLinks.h>
+  #endif
+
+  #if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
+    #import <FirebaseFirestore/FirebaseFirestore.h>
+  #endif
+
+  #if __has_include(<FirebaseFunctions/FirebaseFunctions.h>)
+    #import <FirebaseFunctions/FirebaseFunctions.h>
+  #endif
+
+  #if __has_include(<FirebaseInAppMessaging/FirebaseInAppMessaging.h>)
+    #import <FirebaseInAppMessaging/FirebaseInAppMessaging.h>
+  #endif
+
+  #if __has_include(<FirebaseInstanceID/FirebaseInstanceID.h>)
+    #import <FirebaseInstanceID/FirebaseInstanceID.h>
+  #endif
+
+  #if __has_include(<FirebaseInvites/FirebaseInvites.h>)
+    #import <FirebaseInvites/FirebaseInvites.h>
+  #endif
+
+  #if __has_include(<FirebaseMessaging/FirebaseMessaging.h>)
+    #import <FirebaseMessaging/FirebaseMessaging.h>
+  #endif
+
+  #if __has_include(<FirebaseMLModelInterpreter/FirebaseMLModelInterpreter.h>)
+    #import <FirebaseMLModelInterpreter/FirebaseMLModelInterpreter.h>
+  #endif
+
+  #if __has_include(<FirebaseMLVision/FirebaseMLVision.h>)
+    #import <FirebaseMLVision/FirebaseMLVision.h>
+  #endif
+
+  #if __has_include(<FirebaseMLVisionBarcodeModel/FirebaseMLVisionBarcodeModel.h>)
+    #import <FirebaseMLVisionBarcodeModel/FirebaseMLVisionBarcodeModel.h>
+  #endif
+
+  #if __has_include(<FirebaseMLVisionFaceModel/FirebaseMLVisionFaceModel.h>)
+    #import <FirebaseMLVisionFaceModel/FirebaseMLVisionFaceModel.h>
+  #endif
+
+  #if __has_include(<FirebaseMLVisionLabelModel/FirebaseMLVisionLabelModel.h>)
+    #import <FirebaseMLVisionLabelModel/FirebaseMLVisionLabelModel.h>
+  #endif
+
+  #if __has_include(<FirebaseMLVisionTextModel/FirebaseMLVisionTextModel.h>)
+    #import <FirebaseMLVisionTextModel/FirebaseMLVisionTextModel.h>
+  #endif
+
+  #if __has_include(<FirebasePerformance/FirebasePerformance.h>)
+    #import <FirebasePerformance/FirebasePerformance.h>
+  #endif
+
+  #if __has_include(<FirebaseRemoteConfig/FirebaseRemoteConfig.h>)
+    #import <FirebaseRemoteConfig/FirebaseRemoteConfig.h>
+  #endif
+
+  #if __has_include(<FirebaseStorage/FirebaseStorage.h>)
+    #import <FirebaseStorage/FirebaseStorage.h>
+  #endif
+
+  #if __has_include(<GoogleMobileAds/GoogleMobileAds.h>)
+    #import <GoogleMobileAds/GoogleMobileAds.h>
+  #endif
+
+  #if __has_include(<Fabric/Fabric.h>)
+    #import <Fabric/Fabric.h>
+  #endif
+
+  #if __has_include(<Crashlytics/Crashlytics.h>)
+    #import <Crashlytics/Crashlytics.h>
+  #endif
+
+#endif  // defined(__has_include)
diff --git a/iOS/Pods/Firebase/CoreOnly/Sources/module.modulemap b/iOS/Pods/Firebase/CoreOnly/Sources/module.modulemap
new file mode 100755 (executable)
index 0000000..3685b54
--- /dev/null
@@ -0,0 +1,4 @@
+module Firebase {
+  export *
+  header "Firebase.h"
+}
\ No newline at end of file
diff --git a/iOS/Pods/Firebase/README.md b/iOS/Pods/Firebase/README.md
new file mode 100755 (executable)
index 0000000..49aa2ee
--- /dev/null
@@ -0,0 +1,87 @@
+# 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, '8.0'
+    pod 'Firebase'
+    ```
+
+4.  Save the file.
+
+5.  Open a terminal and `cd` to the directory containing the Podfile.
+
+    ```
+    $ cd <path-to-project>/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/Auth'
+    pod 'Firebase/Crash'
+    pod 'Firebase/Database'
+    pod 'Firebase/DynamicLinks'
+    pod 'Firebase/Firestore'
+    pod 'Firebase/Functions'
+    pod 'Firebase/Invites'
+    pod 'Firebase/Messaging'
+    pod 'Firebase/MLCommon'
+    pod 'Firebase/MLModelInterpreter'
+    pod 'Firebase/MLVision'
+    pod 'Firebase/MLVisionBarcodeModel'
+    pod 'Firebase/MLVisionFaceModel'
+    pod 'Firebase/MLVisionLabelModel'
+    pod 'Firebase/MLVisionTextModel'
+    pod 'Firebase/Performance'
+    pod 'Firebase/RemoteConfig'
+    pod 'Firebase/Storage'
+    ```
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector b/iOS/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector
new file mode 100755 (executable)
index 0000000..7a374f7
Binary files /dev/null and b/iOS/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector differ
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/Modules/module.modulemap b/iOS/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/Modules/module.modulemap
new file mode 100755 (executable)
index 0000000..73fb1d7
--- /dev/null
@@ -0,0 +1,5 @@
+framework module FIRAnalyticsConnector {
+  export *
+  module * { export *}
+  link framework "Security"
+  link framework "SystemConfiguration"}
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics
new file mode 100755 (executable)
index 0000000..e3d3ed8
Binary files /dev/null and b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics differ
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h
new file mode 100755 (executable)
index 0000000..d499af6
--- /dev/null
@@ -0,0 +1,62 @@
+#import <Foundation/Foundation.h>
+
+#import "FIRAnalytics.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * 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:(nullable void (^)(void))completionHandler;
+
+/**
+ * Handles the event when the app is launched by a URL.
+ *
+ * Call this method from [UIApplicationDelegate application:openURL:options:] &#40;on iOS 9.0 and
+ * above&#41;, or [UIApplicationDelegate application:openURL:sourceApplication:annotation:] &#40;on
+ * iOS 8.x and below&#41; 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
+
+NS_ASSUME_NONNULL_END
+
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h
new file mode 100755 (executable)
index 0000000..39d23f1
--- /dev/null
@@ -0,0 +1,119 @@
+#import <Foundation/Foundation.h>
+
+#import "FIREventNames.h"
+#import "FIRParameterNames.h"
+#import "FIRUserPropertyNames.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// The top level Firebase Analytics singleton that provides methods for logging events and setting
+/// user properties. See <a href="http://goo.gl/gz8SLz">the developer guides</a> for general
+/// information on using Firebase Analytics in your apps.
+NS_SWIFT_NAME(Analytics)
+@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:
+/// <ul>
+///     <li>ad_activeview</li>
+///     <li>ad_click</li>
+///     <li>ad_exposure</li>
+///     <li>ad_impression</li>
+///     <li>ad_query</li>
+///     <li>adunit_exposure</li>
+///     <li>app_clear_data</li>
+///     <li>app_remove</li>
+///     <li>app_update</li>
+///     <li>error</li>
+///     <li>first_open</li>
+///     <li>in_app_purchase</li>
+///     <li>notification_dismiss</li>
+///     <li>notification_foreground</li>
+///     <li>notification_open</li>
+///     <li>notification_receive</li>
+///     <li>os_update</li>
+///     <li>screen_view</li>
+///     <li>session_start</li>
+///     <li>user_engagement</li>
+/// </ul>
+///
+/// @param name The name of the event. Should contain 1 to 40 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_",
+///     "google_", and "ga_" prefixes are 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 40 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 100 characters long. The "firebase_",
+///     "google_", and "ga_" prefixes are reserved and should not be used for parameter names.
++ (void)logEventWithName:(NSString *)name
+              parameters:(nullable NSDictionary<NSString *, id> *)parameters
+    NS_SWIFT_NAME(logEvent(_: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:
+/// <ul>
+///     <li>first_open_time</li>
+///     <li>last_deep_link_referrer</li>
+///     <li>user_id</li>
+/// </ul>
+///
+/// @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_", "google_", and
+///     "ga_" prefixes are reserved and should not be used for user property names.
++ (void)setUserPropertyString:(nullable NSString *)value forName:(NSString *)name
+    NS_SWIFT_NAME(setUserProperty(_:forName:));
+
+/// Sets the user ID property. This feature must be used in accordance with
+/// <a href="https://www.google.com/policies/privacy">Google's Privacy Policy</a>
+///
+/// @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 256 characters long. Setting userID to nil removes the user ID.
++ (void)setUserID:(nullable NSString *)userID;
+
+/// Sets the current screen name, which specifies the current visual context in your app. This helps
+/// identify the areas in your app where users spend their time and how they interact with your app.
+/// Must be called on the main thread.
+///
+/// Note that screen reporting is enabled automatically and records the class name of the current
+/// UIViewController for you without requiring you to call this method. If you implement
+/// viewDidAppear in your UIViewController but do not call [super viewDidAppear:], that screen class
+/// will not be automatically tracked. The class name can optionally be overridden by calling this
+/// method in the viewDidAppear callback of your UIViewController and specifying the
+/// screenClassOverride parameter. setScreenName:screenClass: must be called after
+/// [super viewDidAppear:].
+///
+/// If your app does not use a distinct UIViewController for each screen, you should call this
+/// method and specify a distinct screenName each time a new screen is presented to the user.
+///
+/// The screen name and screen class remain in effect until the current UIViewController changes or
+/// a new call to setScreenName:screenClass: is made.
+///
+/// @param screenName The name of the current screen. Should contain 1 to 100 characters. Set to nil
+///     to clear the current screen name.
+/// @param screenClassOverride The name of the screen class. Should contain 1 to 100 characters. By
+///     default this is the class name of the current UIViewController. Set to nil to revert to the
+///     default class name.
++ (void)setScreenName:(nullable NSString *)screenName
+          screenClass:(nullable NSString *)screenClassOverride;
+
+/// The unique ID for this instance of the application.
++ (NSString *)appInstanceID;
+
+/// Clears all analytics data for this instance from the device and resets the app instance ID.
+/// FIRAnalyticsConfiguration values will be reset to the default values.
++ (void)resetAnalyticsData;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h
new file mode 100755 (executable)
index 0000000..c70c53e
--- /dev/null
@@ -0,0 +1,407 @@
+/// @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 40 characters long, may only contain alphanumeric
+/// characters and underscores ("_"), and must start with an alphabetic character. The "firebase_",
+/// "google_", and "ga_" prefixes are reserved and should not be used.
+
+#import <Foundation/Foundation.h>
+
+/// Add Payment Info event. This event signifies that a user has submitted their payment information
+/// to your app.
+static NSString *const kFIREventAddPaymentInfo NS_SWIFT_NAME(AnalyticsEventAddPaymentInfo) =
+    @"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 @c kFIRParameterValue parameter, you must
+/// also supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed
+/// accurately. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li>
+///     <li>@c kFIRParameterItemID (NSString)</li>
+///     <li>@c kFIRParameterItemName (NSString)</li>
+///     <li>@c kFIRParameterItemCategory (NSString)</li>
+///     <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
+///     <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterCurrency (NSString) (optional)</li>
+///     <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterOrigin (NSString) (optional)</li>
+///     <li>@c kFIRParameterDestination (NSString) (optional)</li>
+///     <li>@c kFIRParameterStartDate (NSString) (optional)</li>
+///     <li>@c kFIRParameterEndDate (NSString) (optional)</li>
+/// </ul>
+static NSString *const kFIREventAddToCart NS_SWIFT_NAME(AnalyticsEventAddToCart) = @"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
+/// @c kFIRParameterValue parameter, you must also supply the @c kFIRParameterCurrency
+/// parameter so that revenue metrics can be computed accurately. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li>
+///     <li>@c kFIRParameterItemID (NSString)</li>
+///     <li>@c kFIRParameterItemName (NSString)</li>
+///     <li>@c kFIRParameterItemCategory (NSString)</li>
+///     <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
+///     <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterCurrency (NSString) (optional)</li>
+///     <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
+/// </ul>
+static NSString *const kFIREventAddToWishlist NS_SWIFT_NAME(AnalyticsEventAddToWishlist) =
+    @"add_to_wishlist";
+
+/// App Open event. By logging this event when an App becomes active, 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 NS_SWIFT_NAME(AnalyticsEventAppOpen) = @"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 @c kFIRParameterValue
+/// parameter, you must also supply the @c kFIRParameterCurrency parameter so that revenue
+/// metrics can be computed accurately. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterCurrency (NSString) (optional)</li>
+///     <li>@c kFIRParameterTransactionID (NSString) (optional)</li>
+///     <li>@c kFIRParameterStartDate (NSString) (optional)</li>
+///     <li>@c kFIRParameterEndDate (NSString) (optional)</li>
+///     <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for
+///         hotel bookings</li>
+///     <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for
+///         hotel bookings</li>
+///     <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional)
+///         for travel bookings</li>
+///     <li>@c kFIRParameterOrigin (NSString) (optional)</li>
+///     <li>@c kFIRParameterDestination (NSString) (optional)</li>
+///     <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li>
+/// </ul>
+static NSString *const kFIREventBeginCheckout NS_SWIFT_NAME(AnalyticsEventBeginCheckout) =
+    @"begin_checkout";
+
+/// Campaign Detail event. Log this event to supply the referral details of a re-engagement
+/// campaign. Note: you must supply at least one of the required parameters kFIRParameterSource,
+/// kFIRParameterMedium or kFIRParameterCampaign. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterSource (NSString)</li>
+///     <li>@c kFIRParameterMedium (NSString)</li>
+///     <li>@c kFIRParameterCampaign (NSString)</li>
+///     <li>@c kFIRParameterTerm (NSString) (optional)</li>
+///     <li>@c kFIRParameterContent (NSString) (optional)</li>
+///     <li>@c kFIRParameterAdNetworkClickID (NSString) (optional)</li>
+///     <li>@c kFIRParameterCP1 (NSString) (optional)</li>
+/// </ul>
+static NSString *const kFIREventCampaignDetails NS_SWIFT_NAME(AnalyticsEventCampaignDetails) =
+    @"campaign_details";
+
+/// Checkout progress. Params:
+///
+/// <ul>
+///    <li>@c kFIRParameterCheckoutStep (unsigned 64-bit integer as NSNumber)</li>
+///    <li>@c kFIRParameterCheckoutOption (NSString) (optional)</li>
+/// </ul>
+static NSString *const kFIREventCheckoutProgress NS_SWIFT_NAME(AnalyticsEventCheckoutProgress) =
+    @"checkout_progress";
+
+/// Earn Virtual Currency event. This event tracks the awarding of virtual currency in your app. Log
+/// this along with @c kFIREventSpendVirtualCurrency to better understand your virtual economy.
+/// Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterVirtualCurrencyName (NSString)</li>
+///     <li>@c kFIRParameterValue (signed 64-bit integer or double as NSNumber)</li>
+/// </ul>
+static NSString *const kFIREventEarnVirtualCurrency
+    NS_SWIFT_NAME(AnalyticsEventEarnVirtualCurrency) = @"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 @c kFIRParameterValue parameter, you must also
+/// supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed
+/// accurately. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterCurrency (NSString) (optional)</li>
+///     <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterTransactionID (NSString) (optional)</li>
+///     <li>@c kFIRParameterTax (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterShipping (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterCoupon (NSString) (optional)</li>
+///     <li>@c kFIRParameterLocation (NSString) (optional)</li>
+///     <li>@c kFIRParameterStartDate (NSString) (optional)</li>
+///     <li>@c kFIRParameterEndDate (NSString) (optional)</li>
+///     <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for
+///         hotel bookings</li>
+///     <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for
+///         hotel bookings</li>
+///     <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional)
+///         for travel bookings</li>
+///     <li>@c kFIRParameterOrigin (NSString) (optional)</li>
+///     <li>@c kFIRParameterDestination (NSString) (optional)</li>
+///     <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li>
+/// </ul>
+static NSString *const kFIREventEcommercePurchase NS_SWIFT_NAME(AnalyticsEventEcommercePurchase) =
+    @"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
+/// @c kFIRParameterValue parameter, you must also supply the @c kFIRParameterCurrency
+/// parameter so that revenue metrics can be computed accurately. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterCurrency (NSString) (optional)</li>
+///     <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
+/// </ul>
+static NSString *const kFIREventGenerateLead NS_SWIFT_NAME(AnalyticsEventGenerateLead) =
+    @"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:
+///
+/// <ul>
+///     <li>@c kFIRParameterGroupID (NSString)</li>
+/// </ul>
+static NSString *const kFIREventJoinGroup NS_SWIFT_NAME(AnalyticsEventJoinGroup) = @"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:
+///
+/// <ul>
+///     <li>@c kFIRParameterLevel (signed 64-bit integer as NSNumber)</li>
+///     <li>@c kFIRParameterCharacter (NSString) (optional)</li>
+/// </ul>
+static NSString *const kFIREventLevelUp NS_SWIFT_NAME(AnalyticsEventLevelUp) = @"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 NS_SWIFT_NAME(AnalyticsEventLogin) = @"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:
+///
+/// <ul>
+///     <li>@c kFIRParameterScore (signed 64-bit integer as NSNumber)</li>
+///     <li>@c kFIRParameterLevel (signed 64-bit integer as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterCharacter (NSString) (optional)</li>
+/// </ul>
+static NSString *const kFIREventPostScore NS_SWIFT_NAME(AnalyticsEventPostScore) = @"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 @c kFIRParameterValue parameter, you must
+/// also supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed
+/// accurately. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li>
+///     <li>@c kFIRParameterItemID (NSString)</li>
+///     <li>@c kFIRParameterItemName (NSString)</li>
+///     <li>@c kFIRParameterItemCategory (NSString)</li>
+///     <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
+///     <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterCurrency (NSString) (optional)</li>
+///     <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
+/// </ul>
+static NSString *const kFIREventPresentOffer NS_SWIFT_NAME(AnalyticsEventPresentOffer) =
+    @"present_offer";
+
+/// E-Commerce Purchase Refund event. This event signifies that an item purchase was refunded.
+/// Note: If you supply the @c kFIRParameterValue parameter, you must also supply the
+/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately.
+/// Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterCurrency (NSString) (optional)</li>
+///     <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterTransactionID (NSString) (optional)</li>
+/// </ul>
+static NSString *const kFIREventPurchaseRefund NS_SWIFT_NAME(AnalyticsEventPurchaseRefund) =
+    @"purchase_refund";
+
+/// Remove from cart event. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li>
+///     <li>@c kFIRParameterItemID (NSString)</li>
+///     <li>@c kFIRParameterItemName (NSString)</li>
+///     <li>@c kFIRParameterItemCategory (NSString)</li>
+///     <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
+///     <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterCurrency (NSString) (optional)</li>
+///     <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterOrigin (NSString) (optional)</li>
+///     <li>@c kFIRParameterDestination (NSString) (optional)</li>
+///     <li>@c kFIRParameterStartDate (NSString) (optional)</li>
+///     <li>@c kFIRParameterEndDate (NSString) (optional)</li>
+/// </ul>
+static NSString *const kFIREventRemoveFromCart NS_SWIFT_NAME(AnalyticsEventRemoveFromCart) =
+    @"remove_from_cart";
+
+/// 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:
+///
+/// <ul>
+///     <li>@c kFIRParameterSearchTerm (NSString)</li>
+///     <li>@c kFIRParameterStartDate (NSString) (optional)</li>
+///     <li>@c kFIRParameterEndDate (NSString) (optional)</li>
+///     <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for
+///         hotel bookings</li>
+///     <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for
+///         hotel bookings</li>
+///     <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional)
+///         for travel bookings</li>
+///     <li>@c kFIRParameterOrigin (NSString) (optional)</li>
+///     <li>@c kFIRParameterDestination (NSString) (optional)</li>
+///     <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li>
+/// </ul>
+static NSString *const kFIREventSearch NS_SWIFT_NAME(AnalyticsEventSearch) = @"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:
+///
+/// <ul>
+///     <li>@c kFIRParameterContentType (NSString)</li>
+///     <li>@c kFIRParameterItemID (NSString)</li>
+/// </ul>
+static NSString *const kFIREventSelectContent NS_SWIFT_NAME(AnalyticsEventSelectContent) =
+    @"select_content";
+
+/// Set checkout option. Params:
+///
+/// <ul>
+///    <li>@c kFIRParameterCheckoutStep (unsigned 64-bit integer as NSNumber)</li>
+///    <li>@c kFIRParameterCheckoutOption (NSString)</li>
+/// </ul>
+static NSString *const kFIREventSetCheckoutOption NS_SWIFT_NAME(AnalyticsEventSetCheckoutOption) =
+    @"set_checkout_option";
+
+/// Share event. Apps with social features can log the Share event to identify the most viral
+/// content. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterContentType (NSString)</li>
+///     <li>@c kFIRParameterItemID (NSString)</li>
+/// </ul>
+static NSString *const kFIREventShare NS_SWIFT_NAME(AnalyticsEventShare) = @"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:
+///
+/// <ul>
+///     <li>@c kFIRParameterSignUpMethod (NSString)</li>
+/// </ul>
+static NSString *const kFIREventSignUp NS_SWIFT_NAME(AnalyticsEventSignUp) = @"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:
+///
+/// <ul>
+///     <li>@c kFIRParameterItemName (NSString)</li>
+///     <li>@c kFIRParameterVirtualCurrencyName (NSString)</li>
+///     <li>@c kFIRParameterValue (signed 64-bit integer or double as NSNumber)</li>
+/// </ul>
+static NSString *const kFIREventSpendVirtualCurrency
+    NS_SWIFT_NAME(AnalyticsEventSpendVirtualCurrency) = @"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 NS_SWIFT_NAME(AnalyticsEventTutorialBegin) =
+    @"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 NS_SWIFT_NAME(AnalyticsEventTutorialComplete) =
+    @"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:
+///
+/// <ul>
+///     <li>@c kFIRParameterAchievementID (NSString)</li>
+/// </ul>
+static NSString *const kFIREventUnlockAchievement NS_SWIFT_NAME(AnalyticsEventUnlockAchievement) =
+    @"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 @c kFIRParameterValue parameter, you must also supply the
+/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately.
+/// Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterItemID (NSString)</li>
+///     <li>@c kFIRParameterItemName (NSString)</li>
+///     <li>@c kFIRParameterItemCategory (NSString)</li>
+///     <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
+///     <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterCurrency (NSString) (optional)</li>
+///     <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
+///     <li>@c kFIRParameterStartDate (NSString) (optional)</li>
+///     <li>@c kFIRParameterEndDate (NSString) (optional)</li>
+///     <li>@c kFIRParameterFlightNumber (NSString) (optional) for travel bookings</li>
+///     <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional)
+///         for travel bookings</li>
+///     <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for
+///         travel bookings</li>
+///     <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for
+///         travel bookings</li>
+///     <li>@c kFIRParameterOrigin (NSString) (optional)</li>
+///     <li>@c kFIRParameterDestination (NSString) (optional)</li>
+///     <li>@c kFIRParameterSearchTerm (NSString) (optional) for travel bookings</li>
+///     <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li>
+/// </ul>
+static NSString *const kFIREventViewItem NS_SWIFT_NAME(AnalyticsEventViewItem) = @"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:
+///
+/// <ul>
+///     <li>@c kFIRParameterItemCategory (NSString)</li>
+/// </ul>
+static NSString *const kFIREventViewItemList NS_SWIFT_NAME(AnalyticsEventViewItemList) =
+    @"view_item_list";
+
+/// View Search Results event. Log this event when the user has been presented with the results of a
+/// search. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterSearchTerm (NSString)</li>
+/// </ul>
+static NSString *const kFIREventViewSearchResults NS_SWIFT_NAME(AnalyticsEventViewSearchResults) =
+    @"view_search_results";
+
+/// Level Start event. Log this event when the user starts a new level. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterLevelName (NSString)</li>
+/// </ul>
+static NSString *const kFIREventLevelStart NS_SWIFT_NAME(AnalyticsEventLevelStart) =
+    @"level_start";
+
+/// Level End event. Log this event when the user finishes a level. Params:
+///
+/// <ul>
+///     <li>@c kFIRParameterLevelName (NSString)</li>
+///     <li>@c kFIRParameterSuccess (NSString)</li>
+/// </ul>
+static NSString *const kFIREventLevelEnd NS_SWIFT_NAME(AnalyticsEventLevelEnd) = @"level_end";
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h
new file mode 100755 (executable)
index 0000000..4e1366c
--- /dev/null
@@ -0,0 +1,507 @@
+/// @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 40 characters long, may only contain alphanumeric
+/// characters and underscores ("_"), and must start with an alphabetic character. Param values can
+/// be up to 100 characters long. The "firebase_", "google_", and "ga_" prefixes are reserved and
+/// should not be used.
+
+#import <Foundation/Foundation.h>
+
+/// Game achievement ID (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterAchievementID : @"10_matches_won",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterAchievementID NS_SWIFT_NAME(AnalyticsParameterAchievementID) =
+    @"achievement_id";
+
+/// Ad Network Click ID (NSString). Used for network-specific click IDs which vary in format.
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterAdNetworkClickID : @"1234567",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterAdNetworkClickID
+    NS_SWIFT_NAME(AnalyticsParameterAdNetworkClickID) = @"aclid";
+
+/// The store or affiliation from which this transaction occurred (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterAffiliation : @"Google Store",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterAffiliation NS_SWIFT_NAME(AnalyticsParameterAffiliation) =
+    @"affiliation";
+
+/// The individual campaign name, slogan, promo code, etc. Some networks have pre-defined macro to
+/// capture campaign information, otherwise can be populated by developer. Highly Recommended
+/// (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterCampaign : @"winter_promotion",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterCampaign NS_SWIFT_NAME(AnalyticsParameterCampaign) =
+    @"campaign";
+
+/// Character used in game (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterCharacter : @"beat_boss",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterCharacter NS_SWIFT_NAME(AnalyticsParameterCharacter) =
+    @"character";
+
+/// The checkout step (1..N) (unsigned 64-bit integer as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterCheckoutStep : @"1",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterCheckoutStep NS_SWIFT_NAME(AnalyticsParameterCheckoutStep) =
+    @"checkout_step";
+
+/// Some option on a step in an ecommerce flow (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterCheckoutOption : @"Visa",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterCheckoutOption
+    NS_SWIFT_NAME(AnalyticsParameterCheckoutOption) = @"checkout_option";
+
+/// Campaign content (NSString).
+static NSString *const kFIRParameterContent NS_SWIFT_NAME(AnalyticsParameterContent) = @"content";
+
+/// Type of content selected (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterContentType : @"news article",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterContentType NS_SWIFT_NAME(AnalyticsParameterContentType) =
+    @"content_type";
+
+/// Coupon code for a purchasable item (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterCoupon : @"zz123",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterCoupon NS_SWIFT_NAME(AnalyticsParameterCoupon) = @"coupon";
+
+/// Campaign custom parameter (NSString). Used as a method of capturing custom data in a campaign.
+/// Use varies by network.
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterCP1 : @"custom_data",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterCP1 NS_SWIFT_NAME(AnalyticsParameterCP1) = @"cp1";
+
+/// The name of a creative used in a promotional spot (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterCreativeName : @"Summer Sale",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterCreativeName NS_SWIFT_NAME(AnalyticsParameterCreativeName) =
+    @"creative_name";
+
+/// The name of a creative slot (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterCreativeSlot : @"summer_banner2",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterCreativeSlot NS_SWIFT_NAME(AnalyticsParameterCreativeSlot) =
+    @"creative_slot";
+
+/// Purchase currency in 3-letter <a href="http://en.wikipedia.org/wiki/ISO_4217#Active_codes">
+/// ISO_4217</a> format (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterCurrency : @"USD",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterCurrency NS_SWIFT_NAME(AnalyticsParameterCurrency) =
+    @"currency";
+
+/// Flight or Travel destination (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterDestination : @"Mountain View, CA",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterDestination NS_SWIFT_NAME(AnalyticsParameterDestination) =
+    @"destination";
+
+/// The arrival date, check-out date or rental end date for the item. This should be in
+/// YYYY-MM-DD format (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterEndDate : @"2015-09-14",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterEndDate NS_SWIFT_NAME(AnalyticsParameterEndDate) = @"end_date";
+
+/// Flight number for travel events (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterFlightNumber : @"ZZ800",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterFlightNumber NS_SWIFT_NAME(AnalyticsParameterFlightNumber) =
+    @"flight_number";
+
+/// Group/clan/guild ID (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterGroupID : @"g1",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterGroupID NS_SWIFT_NAME(AnalyticsParameterGroupID) = @"group_id";
+
+/// Index of an item in a list (signed 64-bit integer as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterIndex : @(1),
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterIndex NS_SWIFT_NAME(AnalyticsParameterIndex) = @"index";
+
+/// Item brand (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterItemBrand : @"Google",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterItemBrand NS_SWIFT_NAME(AnalyticsParameterItemBrand) =
+    @"item_brand";
+
+/// Item category (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterItemCategory : @"t-shirts",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterItemCategory NS_SWIFT_NAME(AnalyticsParameterItemCategory) =
+    @"item_category";
+
+/// Item ID (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterItemID : @"p7654",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterItemID NS_SWIFT_NAME(AnalyticsParameterItemID) = @"item_id";
+
+/// The Google <a href="https://developers.google.com/places/place-id">Place ID</a> (NSString) that
+/// corresponds to the associated item. Alternatively, you can supply your own custom Location ID.
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterItemLocationID : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterItemLocationID
+    NS_SWIFT_NAME(AnalyticsParameterItemLocationID) = @"item_location_id";
+
+/// Item name (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterItemName : @"abc",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterItemName NS_SWIFT_NAME(AnalyticsParameterItemName) =
+    @"item_name";
+
+/// The list in which the item was presented to the user (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterItemList : @"Search Results",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterItemList NS_SWIFT_NAME(AnalyticsParameterItemList) =
+    @"item_list";
+
+/// Item variant (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterItemVariant : @"Red",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterItemVariant NS_SWIFT_NAME(AnalyticsParameterItemVariant) =
+    @"item_variant";
+
+/// Level in game (signed 64-bit integer as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterLevel : @(42),
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterLevel NS_SWIFT_NAME(AnalyticsParameterLevel) = @"level";
+
+/// Location (NSString). The Google <a href="https://developers.google.com/places/place-id">Place ID
+/// </a> that corresponds to the associated event. Alternatively, you can supply your own custom
+/// Location ID.
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterLocation : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterLocation NS_SWIFT_NAME(AnalyticsParameterLocation) =
+    @"location";
+
+/// The advertising or marketing medium, for example: cpc, banner, email, push. Highly recommended
+/// (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterMedium : @"email",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterMedium NS_SWIFT_NAME(AnalyticsParameterMedium) = @"medium";
+
+/// Number of nights staying at hotel (signed 64-bit integer as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterNumberOfNights : @(3),
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterNumberOfNights
+    NS_SWIFT_NAME(AnalyticsParameterNumberOfNights) = @"number_of_nights";
+
+/// Number of passengers traveling (signed 64-bit integer as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterNumberOfPassengers : @(11),
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterNumberOfPassengers
+    NS_SWIFT_NAME(AnalyticsParameterNumberOfPassengers) = @"number_of_passengers";
+
+/// Number of rooms for travel events (signed 64-bit integer as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterNumberOfRooms : @(2),
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterNumberOfRooms NS_SWIFT_NAME(AnalyticsParameterNumberOfRooms) =
+    @"number_of_rooms";
+
+/// Flight or Travel origin (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterOrigin : @"Mountain View, CA",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterOrigin NS_SWIFT_NAME(AnalyticsParameterOrigin) = @"origin";
+
+/// Purchase price (double as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterPrice : @(1.0),
+///       kFIRParameterCurrency : @"USD",  // e.g. $1.00 USD
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterPrice NS_SWIFT_NAME(AnalyticsParameterPrice) = @"price";
+
+/// Purchase quantity (signed 64-bit integer as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterQuantity : @(1),
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterQuantity NS_SWIFT_NAME(AnalyticsParameterQuantity) =
+    @"quantity";
+
+/// Score in game (signed 64-bit integer as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterScore : @(4200),
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterScore NS_SWIFT_NAME(AnalyticsParameterScore) = @"score";
+
+/// The search string/keywords used (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterSearchTerm : @"periodic table",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterSearchTerm NS_SWIFT_NAME(AnalyticsParameterSearchTerm) =
+    @"search_term";
+
+/// Shipping cost (double as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterShipping : @(9.50),
+///       kFIRParameterCurrency : @"USD",  // e.g. $9.50 USD
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterShipping NS_SWIFT_NAME(AnalyticsParameterShipping) =
+    @"shipping";
+
+/// Sign up method (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterSignUpMethod : @"google",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterSignUpMethod NS_SWIFT_NAME(AnalyticsParameterSignUpMethod) =
+    @"sign_up_method";
+
+/// The origin of your traffic, such as an Ad network (for example, google) or partner (urban
+/// airship). Identify the advertiser, site, publication, etc. that is sending traffic to your
+/// property. Highly recommended (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterSource : @"InMobi",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterSource NS_SWIFT_NAME(AnalyticsParameterSource) = @"source";
+
+/// The departure date, check-in date or rental start date for the item. This should be in
+/// YYYY-MM-DD format (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterStartDate : @"2015-09-14",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterStartDate NS_SWIFT_NAME(AnalyticsParameterStartDate) =
+    @"start_date";
+
+/// Tax amount (double as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterTax : @(1.0),
+///       kFIRParameterCurrency : @"USD",  // e.g. $1.00 USD
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterTax NS_SWIFT_NAME(AnalyticsParameterTax) = @"tax";
+
+/// If you're manually tagging keyword campaigns, you should use utm_term to specify the keyword
+/// (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterTerm : @"game",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterTerm NS_SWIFT_NAME(AnalyticsParameterTerm) = @"term";
+
+/// A single ID for a ecommerce group transaction (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterTransactionID : @"ab7236dd9823",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterTransactionID NS_SWIFT_NAME(AnalyticsParameterTransactionID) =
+    @"transaction_id";
+
+/// Travel class (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterTravelClass : @"business",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterTravelClass NS_SWIFT_NAME(AnalyticsParameterTravelClass) =
+    @"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: Values for pre-defined currency-related events
+/// (such as @c kFIREventAddToCart) should be supplied using double as NSNumber and must be
+/// accompanied by a @c kFIRParameterCurrency parameter. The valid range of accumulated values is
+/// [-9,223,372,036,854.77, 9,223,372,036,854.77]. Supplying a non-numeric value, omitting the
+/// corresponding @c kFIRParameterCurrency parameter, or supplying an invalid
+/// <a href="https://goo.gl/qqX3J2">currency code</a> for conversion events will cause that
+/// conversion to be omitted from reporting.
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterValue : @(3.99),
+///       kFIRParameterCurrency : @"USD",  // e.g. $3.99 USD
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterValue NS_SWIFT_NAME(AnalyticsParameterValue) = @"value";
+
+/// Name of virtual currency type (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterVirtualCurrencyName : @"virtual_currency_name",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterVirtualCurrencyName
+    NS_SWIFT_NAME(AnalyticsParameterVirtualCurrencyName) = @"virtual_currency_name";
+
+/// The name of a level in a game (NSString).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterLevelName : @"room_1",
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterLevelName NS_SWIFT_NAME(AnalyticsParameterLevelName) =
+    @"level_name";
+
+/// The result of an operation. Specify 1 to indicate success and 0 to indicate failure (unsigned
+/// integer as NSNumber).
+/// <pre>
+///     NSDictionary *params = @{
+///       kFIRParameterSuccess : @(1),
+///       // ...
+///     };
+/// </pre>
+static NSString *const kFIRParameterSuccess NS_SWIFT_NAME(AnalyticsParameterSuccess) = @"success";
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h
new file mode 100755 (executable)
index 0000000..f50707f
--- /dev/null
@@ -0,0 +1,17 @@
+/// @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_", "google_", and "ga_" prefixes are reserved and should not
+/// be used.
+
+#import <Foundation/Foundation.h>
+
+/// The method used to sign in. For example, "google", "facebook" or "twitter".
+static NSString *const kFIRUserPropertySignUpMethod
+    NS_SWIFT_NAME(AnalyticsUserPropertySignUpMethod) = @"sign_up_method";
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h
new file mode 100755 (executable)
index 0000000..ed7588a
--- /dev/null
@@ -0,0 +1,5 @@
+#import "FIRAnalytics+AppDelegate.h"
+#import "FIRAnalytics.h"
+#import "FIREventNames.h"
+#import "FIRParameterNames.h"
+#import "FIRUserPropertyNames.h"
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Modules/module.modulemap b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Modules/module.modulemap
new file mode 100755 (executable)
index 0000000..ef80595
--- /dev/null
@@ -0,0 +1,10 @@
+framework module FirebaseAnalytics {
+  umbrella header "FirebaseAnalytics.h"
+  export *
+  module * { export *}
+  link "sqlite3"
+  link "z"
+  link framework "Security"
+  link framework "StoreKit"
+  link framework "SystemConfiguration"
+  link framework "UIKit"}
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics
new file mode 100755 (executable)
index 0000000..0c57863
Binary files /dev/null and b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics differ
diff --git a/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/Modules/module.modulemap b/iOS/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/Modules/module.modulemap
new file mode 100755 (executable)
index 0000000..bbcb94e
--- /dev/null
@@ -0,0 +1,6 @@
+framework module FirebaseCoreDiagnostics {
+  export *
+  module * { export *}
+  link "z"
+  link framework "Security"
+  link framework "SystemConfiguration"}
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRAnalyticsConfiguration.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRAnalyticsConfiguration.m
new file mode 100644 (file)
index 0000000..33aa168
--- /dev/null
@@ -0,0 +1,69 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "FIRAnalyticsConfiguration.h"
+
+#import "Private/FIRAnalyticsConfiguration+Internal.h"
+
+@implementation FIRAnalyticsConfiguration
+
++ (FIRAnalyticsConfiguration *)sharedInstance {
+  static FIRAnalyticsConfiguration *sharedInstance = nil;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    sharedInstance = [[FIRAnalyticsConfiguration alloc] init];
+  });
+  return sharedInstance;
+}
+
+- (void)postNotificationName:(NSString *)name value:(id)value {
+  if (!name.length || !value) {
+    return;
+  }
+  [[NSNotificationCenter defaultCenter] postNotificationName:name
+                                                      object:self
+                                                    userInfo:@{name : value}];
+}
+
+- (void)setMinimumSessionInterval:(NSTimeInterval)minimumSessionInterval {
+  [self postNotificationName:kFIRAnalyticsConfigurationSetMinimumSessionIntervalNotification
+                       value:@(minimumSessionInterval)];
+}
+
+- (void)setSessionTimeoutInterval:(NSTimeInterval)sessionTimeoutInterval {
+  [self postNotificationName:kFIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification
+                       value:@(sessionTimeoutInterval)];
+}
+
+- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled {
+  [self setAnalyticsCollectionEnabled:analyticsCollectionEnabled persistSetting:YES];
+}
+
+- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled
+                       persistSetting:(BOOL)shouldPersist {
+  // Persist the measurementEnabledState. Use FIRAnalyticsEnabledState values instead of YES/NO.
+  FIRAnalyticsEnabledState analyticsEnabledState =
+      analyticsCollectionEnabled ? kFIRAnalyticsEnabledStateSetYes : kFIRAnalyticsEnabledStateSetNo;
+  if (shouldPersist) {
+    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+    [userDefaults setObject:@(analyticsEnabledState)
+                     forKey:kFIRAPersistedConfigMeasurementEnabledStateKey];
+    [userDefaults synchronize];
+  }
+
+  [self postNotificationName:kFIRAnalyticsConfigurationSetEnabledNotification
+                       value:@(analyticsCollectionEnabled)];
+}
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRApp.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRApp.m
new file mode 100644 (file)
index 0000000..2edcb07
--- /dev/null
@@ -0,0 +1,805 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <sys/utsname.h>
+
+#import "FIRApp.h"
+#import "FIRConfiguration.h"
+#import "Private/FIRAnalyticsConfiguration+Internal.h"
+#import "Private/FIRAppInternal.h"
+#import "Private/FIRBundleUtil.h"
+#import "Private/FIRComponentContainerInternal.h"
+#import "Private/FIRCoreConfigurable.h"
+#import "Private/FIRLogger.h"
+#import "Private/FIROptionsInternal.h"
+
+NSString *const kFIRServiceAdMob = @"AdMob";
+NSString *const kFIRServiceAuth = @"Auth";
+NSString *const kFIRServiceAuthUI = @"AuthUI";
+NSString *const kFIRServiceCrash = @"Crash";
+NSString *const kFIRServiceDatabase = @"Database";
+NSString *const kFIRServiceDynamicLinks = @"DynamicLinks";
+NSString *const kFIRServiceFirestore = @"Firestore";
+NSString *const kFIRServiceFunctions = @"Functions";
+NSString *const kFIRServiceInstanceID = @"InstanceID";
+NSString *const kFIRServiceInvites = @"Invites";
+NSString *const kFIRServiceMessaging = @"Messaging";
+NSString *const kFIRServiceMeasurement = @"Measurement";
+NSString *const kFIRServicePerformance = @"Performance";
+NSString *const kFIRServiceRemoteConfig = @"RemoteConfig";
+NSString *const kFIRServiceStorage = @"Storage";
+NSString *const kGGLServiceAnalytics = @"Analytics";
+NSString *const kGGLServiceSignIn = @"SignIn";
+
+NSString *const kFIRDefaultAppName = @"__FIRAPP_DEFAULT";
+NSString *const kFIRAppReadyToConfigureSDKNotification = @"FIRAppReadyToConfigureSDKNotification";
+NSString *const kFIRAppDeleteNotification = @"FIRAppDeleteNotification";
+NSString *const kFIRAppIsDefaultAppKey = @"FIRAppIsDefaultAppKey";
+NSString *const kFIRAppNameKey = @"FIRAppNameKey";
+NSString *const kFIRGoogleAppIDKey = @"FIRGoogleAppIDKey";
+
+NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat =
+    @"/google/firebase/global_data_collection_enabled:%@";
+NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey =
+    @"FirebaseDataCollectionDefaultEnabled";
+
+NSString *const kFIRAppDiagnosticsNotification = @"FIRAppDiagnosticsNotification";
+
+NSString *const kFIRAppDiagnosticsConfigurationTypeKey = @"ConfigType";
+NSString *const kFIRAppDiagnosticsErrorKey = @"Error";
+NSString *const kFIRAppDiagnosticsFIRAppKey = @"FIRApp";
+NSString *const kFIRAppDiagnosticsSDKNameKey = @"SDKName";
+NSString *const kFIRAppDiagnosticsSDKVersionKey = @"SDKVersion";
+
+// Auth internal notification notification and key.
+NSString *const FIRAuthStateDidChangeInternalNotification =
+    @"FIRAuthStateDidChangeInternalNotification";
+NSString *const FIRAuthStateDidChangeInternalNotificationAppKey =
+    @"FIRAuthStateDidChangeInternalNotificationAppKey";
+NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey =
+    @"FIRAuthStateDidChangeInternalNotificationTokenKey";
+NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey =
+    @"FIRAuthStateDidChangeInternalNotificationUIDKey";
+
+/**
+ * The URL to download plist files.
+ */
+static NSString *const kPlistURL = @"https://console.firebase.google.com/";
+
+/**
+ * An array of all classes that registered as `FIRCoreConfigurable` in order to receive lifecycle
+ * events from Core.
+ */
+static NSMutableArray<Class<FIRCoreConfigurable>> *gRegisteredAsConfigurable;
+
+@interface FIRApp ()
+
+@property(nonatomic) BOOL alreadySentConfigureNotification;
+
+@property(nonatomic) BOOL alreadySentDeleteNotification;
+
+#ifdef DEBUG
+@property(nonatomic) BOOL alreadyOutputDataCollectionFlag;
+#endif  // DEBUG
+
+@end
+
+@implementation FIRApp
+
+// This is necessary since our custom getter prevents `_options` from being created.
+@synthesize options = _options;
+
+static NSMutableDictionary *sAllApps;
+static FIRApp *sDefaultApp;
+static NSMutableDictionary *sLibraryVersions;
+
++ (void)configure {
+  FIROptions *options = [FIROptions defaultOptions];
+  if (!options) {
+    [[NSNotificationCenter defaultCenter]
+        postNotificationName:kFIRAppDiagnosticsNotification
+                      object:nil
+                    userInfo:@{
+                      kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeCore),
+                      kFIRAppDiagnosticsErrorKey : [FIRApp errorForMissingOptions]
+                    }];
+    [NSException raise:kFirebaseCoreErrorDomain
+                format:
+                    @"`[FIRApp configure];` (`FirebaseApp.configure()` in Swift) could not find "
+                    @"a valid GoogleService-Info.plist in your project. Please download one "
+                    @"from %@.",
+                    kPlistURL];
+  }
+  [FIRApp configureDefaultAppWithOptions:options sendingNotifications:YES];
+#if TARGET_OS_OSX || TARGET_OS_TV
+  FIRLogNotice(kFIRLoggerCore, @"I-COR000028",
+               @"tvOS and macOS SDK support is not part of the official Firebase product. "
+               @"Instead they are community supported. Details at "
+               @"https://github.com/firebase/firebase-ios-sdk/blob/master/README.md.");
+#endif
+}
+
++ (void)configureWithOptions:(FIROptions *)options {
+  if (!options) {
+    [NSException raise:kFirebaseCoreErrorDomain
+                format:@"Options is nil. Please pass a valid options."];
+  }
+  [FIRApp configureDefaultAppWithOptions:options sendingNotifications:YES];
+}
+
++ (void)configureDefaultAppWithOptions:(FIROptions *)options
+                  sendingNotifications:(BOOL)sendNotifications {
+  if (sDefaultApp) {
+    // FIRApp sets up FirebaseAnalytics and does plist validation, but does not cause it
+    // to fire notifications. So, if the default app already exists, but has not sent out
+    // configuration notifications, then continue re-initializing it.
+    if (!sendNotifications || sDefaultApp.alreadySentConfigureNotification) {
+      [NSException raise:kFirebaseCoreErrorDomain
+                  format:@"Default app has already been configured."];
+    }
+  }
+  @synchronized(self) {
+    FIRLogDebug(kFIRLoggerCore, @"I-COR000001", @"Configuring the default app.");
+    sDefaultApp = [[FIRApp alloc] initInstanceWithName:kFIRDefaultAppName options:options];
+    [FIRApp addAppToAppDictionary:sDefaultApp];
+    if (!sDefaultApp.alreadySentConfigureNotification && sendNotifications) {
+      [FIRApp sendNotificationsToSDKs:sDefaultApp];
+      sDefaultApp.alreadySentConfigureNotification = YES;
+    }
+  }
+}
+
++ (void)configureWithName:(NSString *)name options:(FIROptions *)options {
+  if (!name || !options) {
+    [NSException raise:kFirebaseCoreErrorDomain format:@"Neither name nor options can be nil."];
+  }
+  if (name.length == 0) {
+    [NSException raise:kFirebaseCoreErrorDomain format:@"Name cannot be empty."];
+  }
+  if ([name isEqualToString:kFIRDefaultAppName]) {
+    [NSException raise:kFirebaseCoreErrorDomain format:@"Name cannot be __FIRAPP_DEFAULT."];
+  }
+  for (NSUInteger charIndex = 0; charIndex < name.length; charIndex++) {
+    char character = [name characterAtIndex:charIndex];
+    if (!((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') ||
+          (character >= '0' && character <= '9') || character == '_' || character == '-')) {
+      [NSException raise:kFirebaseCoreErrorDomain
+                  format:
+                      @"App name should only contain Letters, "
+                      @"Numbers, Underscores, and Dashes."];
+    }
+  }
+
+  if (sAllApps && sAllApps[name]) {
+    [NSException raise:kFirebaseCoreErrorDomain
+                format:@"App named %@ has already been configured.", name];
+  }
+
+  @synchronized(self) {
+    FIRLogDebug(kFIRLoggerCore, @"I-COR000002", @"Configuring app named %@", name);
+    FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
+    [FIRApp addAppToAppDictionary:app];
+    if (!app.alreadySentConfigureNotification) {
+      [FIRApp sendNotificationsToSDKs:app];
+      app.alreadySentConfigureNotification = YES;
+    }
+  }
+}
+
++ (FIRApp *)defaultApp {
+  if (sDefaultApp) {
+    return sDefaultApp;
+  }
+  FIRLogError(kFIRLoggerCore, @"I-COR000003",
+              @"The default Firebase app has not yet been "
+              @"configured. Add `[FIRApp configure];` (`FirebaseApp.configure()` in Swift) to your "
+              @"application initialization. Read more: https://goo.gl/ctyzm8.");
+  return nil;
+}
+
++ (FIRApp *)appNamed:(NSString *)name {
+  @synchronized(self) {
+    if (sAllApps) {
+      FIRApp *app = sAllApps[name];
+      if (app) {
+        return app;
+      }
+    }
+    FIRLogError(kFIRLoggerCore, @"I-COR000004", @"App with name %@ does not exist.", name);
+    return nil;
+  }
+}
+
++ (NSDictionary *)allApps {
+  @synchronized(self) {
+    if (!sAllApps) {
+      FIRLogError(kFIRLoggerCore, @"I-COR000005", @"No app has been configured yet.");
+    }
+    NSDictionary *dict = [NSDictionary dictionaryWithDictionary:sAllApps];
+    return dict;
+  }
+}
+
+// Public only for tests
++ (void)resetApps {
+  sDefaultApp = nil;
+  [sAllApps removeAllObjects];
+  sAllApps = nil;
+  [sLibraryVersions removeAllObjects];
+  sLibraryVersions = nil;
+}
+
+- (void)deleteApp:(FIRAppVoidBoolCallback)completion {
+  @synchronized([self class]) {
+    if (sAllApps && sAllApps[self.name]) {
+      FIRLogDebug(kFIRLoggerCore, @"I-COR000006", @"Deleting app named %@", self.name);
+
+      // Remove all cached instances from the container before deleting the app.
+      [self.container removeAllCachedInstances];
+
+      [sAllApps removeObjectForKey:self.name];
+      [self clearDataCollectionSwitchFromUserDefaults];
+      if ([self.name isEqualToString:kFIRDefaultAppName]) {
+        sDefaultApp = nil;
+      }
+      if (!self.alreadySentDeleteNotification) {
+        NSDictionary *appInfoDict = @{kFIRAppNameKey : self.name};
+        [[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppDeleteNotification
+                                                            object:[self class]
+                                                          userInfo:appInfoDict];
+        self.alreadySentDeleteNotification = YES;
+      }
+      completion(YES);
+    } else {
+      FIRLogError(kFIRLoggerCore, @"I-COR000007", @"App does not exist.");
+      completion(NO);
+    }
+  }
+}
+
++ (void)addAppToAppDictionary:(FIRApp *)app {
+  if (!sAllApps) {
+    sAllApps = [NSMutableDictionary dictionary];
+  }
+  if ([app configureCore]) {
+    sAllApps[app.name] = app;
+    [[NSNotificationCenter defaultCenter]
+        postNotificationName:kFIRAppDiagnosticsNotification
+                      object:nil
+                    userInfo:@{
+                      kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeCore),
+                      kFIRAppDiagnosticsFIRAppKey : app
+                    }];
+  } else {
+    [NSException raise:kFirebaseCoreErrorDomain
+                format:
+                    @"Configuration fails. It may be caused by an invalid GOOGLE_APP_ID in "
+                    @"GoogleService-Info.plist or set in the customized options."];
+  }
+}
+
+- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options {
+  self = [super init];
+  if (self) {
+    _name = [name copy];
+    _options = [options copy];
+    _options.editingLocked = YES;
+    _isDefaultApp = [name isEqualToString:kFIRDefaultAppName];
+    _container = [[FIRComponentContainer alloc] initWithApp:self];
+
+    FIRApp *app = sAllApps[name];
+    _alreadySentConfigureNotification = app.alreadySentConfigureNotification;
+    _alreadySentDeleteNotification = app.alreadySentDeleteNotification;
+  }
+  return self;
+}
+
+- (void)getTokenForcingRefresh:(BOOL)forceRefresh withCallback:(FIRTokenCallback)callback {
+  if (!_getTokenImplementation) {
+    callback(nil, nil);
+    return;
+  }
+
+  _getTokenImplementation(forceRefresh, callback);
+}
+
+- (BOOL)configureCore {
+  [self checkExpectedBundleID];
+  if (![self isAppIDValid]) {
+    if (_options.usingOptionsFromDefaultPlist) {
+      [[NSNotificationCenter defaultCenter]
+          postNotificationName:kFIRAppDiagnosticsNotification
+                        object:nil
+                      userInfo:@{
+                        kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeCore),
+                        kFIRAppDiagnosticsErrorKey : [FIRApp errorForInvalidAppID],
+                      }];
+    }
+    return NO;
+  }
+
+#if TARGET_OS_IOS
+  // Initialize the Analytics once there is a valid options under default app. Analytics should
+  // always initialize first by itself before the other SDKs.
+  if ([self.name isEqualToString:kFIRDefaultAppName]) {
+    Class firAnalyticsClass = NSClassFromString(@"FIRAnalytics");
+    if (!firAnalyticsClass) {
+      FIRLogWarning(kFIRLoggerCore, @"I-COR000022",
+                    @"Firebase Analytics is not available. To add it, include Firebase/Core in the "
+                    @"Podfile or add FirebaseAnalytics.framework to the Link Build Phase");
+    } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+      SEL startWithConfigurationSelector = @selector(startWithConfiguration:options:);
+#pragma clang diagnostic pop
+      if ([firAnalyticsClass respondsToSelector:startWithConfigurationSelector]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+        [firAnalyticsClass performSelector:startWithConfigurationSelector
+                                withObject:[FIRConfiguration sharedInstance].analyticsConfiguration
+                                withObject:_options];
+#pragma clang diagnostic pop
+      }
+    }
+  }
+#endif
+
+  return YES;
+}
+
+- (FIROptions *)options {
+  return [_options copy];
+}
+
+- (void)setDataCollectionDefaultEnabled:(BOOL)dataCollectionDefaultEnabled {
+#ifdef DEBUG
+  FIRLogDebug(kFIRLoggerCore, @"I-COR000034", @"Explicitly %@ data collection flag.",
+              dataCollectionDefaultEnabled ? @"enabled" : @"disabled");
+  self.alreadyOutputDataCollectionFlag = YES;
+#endif  // DEBUG
+
+  NSString *key =
+      [NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, self.name];
+  [[NSUserDefaults standardUserDefaults] setBool:dataCollectionDefaultEnabled forKey:key];
+
+  // Core also controls the FirebaseAnalytics flag, so check if the Analytics flags are set
+  // within FIROptions and change the Analytics value if necessary. Analytics only works with the
+  // default app, so return if this isn't the default app.
+  if (self != sDefaultApp) {
+    return;
+  }
+
+  // Check if the Analytics flag is explicitly set. If so, no further actions are necessary.
+  if ([self.options isAnalyticsCollectionExpicitlySet]) {
+    return;
+  }
+
+  // The Analytics flag has not been explicitly set, so update with the value being set.
+  [[FIRAnalyticsConfiguration sharedInstance]
+      setAnalyticsCollectionEnabled:dataCollectionDefaultEnabled
+                     persistSetting:NO];
+}
+
+- (BOOL)isDataCollectionDefaultEnabled {
+  // Check if it's been manually set before in code, and use that as the higher priority value.
+  NSNumber *defaultsObject = [[self class] readDataCollectionSwitchFromUserDefaultsForApp:self];
+  if (defaultsObject != nil) {
+#ifdef DEBUG
+    if (!self.alreadyOutputDataCollectionFlag) {
+      FIRLogDebug(kFIRLoggerCore, @"I-COR000031", @"Data Collection flag is %@ in user defaults.",
+                  [defaultsObject boolValue] ? @"enabled" : @"disabled");
+      self.alreadyOutputDataCollectionFlag = YES;
+    }
+#endif  // DEBUG
+    return [defaultsObject boolValue];
+  }
+
+  // Read the Info.plist to see if the flag is set. If it's not set, it should default to `YES`.
+  // As per the implementation of `readDataCollectionSwitchFromPlist`, it's a cached value and has
+  // no performance impact calling multiple times.
+  NSNumber *collectionEnabledPlistValue = [[self class] readDataCollectionSwitchFromPlist];
+  if (collectionEnabledPlistValue != nil) {
+#ifdef DEBUG
+    if (!self.alreadyOutputDataCollectionFlag) {
+      FIRLogDebug(kFIRLoggerCore, @"I-COR000032", @"Data Collection flag is %@ in plist.",
+                  [collectionEnabledPlistValue boolValue] ? @"enabled" : @"disabled");
+      self.alreadyOutputDataCollectionFlag = YES;
+    }
+#endif  // DEBUG
+    return [collectionEnabledPlistValue boolValue];
+  }
+
+#ifdef DEBUG
+  if (!self.alreadyOutputDataCollectionFlag) {
+    FIRLogDebug(kFIRLoggerCore, @"I-COR000033", @"Data Collection flag is not set.");
+    self.alreadyOutputDataCollectionFlag = YES;
+  }
+#endif  // DEBUG
+  return YES;
+}
+
+#pragma mark - private
+
++ (void)sendNotificationsToSDKs:(FIRApp *)app {
+  // TODO: Remove this notification once all SDKs are registered with `FIRCoreConfigurable`.
+  NSNumber *isDefaultApp = [NSNumber numberWithBool:(app == sDefaultApp)];
+  NSDictionary *appInfoDict = @{
+    kFIRAppNameKey : app.name,
+    kFIRAppIsDefaultAppKey : isDefaultApp,
+    kFIRGoogleAppIDKey : app.options.googleAppID
+  };
+  [[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppReadyToConfigureSDKNotification
+                                                      object:self
+                                                    userInfo:appInfoDict];
+
+  // This is the new way of sending information to SDKs.
+  // TODO: Do we want this on a background thread, maybe?
+  for (Class<FIRCoreConfigurable> library in gRegisteredAsConfigurable) {
+    [library configureWithApp:app];
+  }
+}
+
++ (NSError *)errorForMissingOptions {
+  NSDictionary *errorDict = @{
+    NSLocalizedDescriptionKey :
+        @"Unable to parse GoogleService-Info.plist in order to configure services.",
+    NSLocalizedRecoverySuggestionErrorKey :
+        @"Check formatting and location of GoogleService-Info.plist."
+  };
+  return [NSError errorWithDomain:kFirebaseCoreErrorDomain
+                             code:FIRErrorCodeInvalidPlistFile
+                         userInfo:errorDict];
+}
+
++ (NSError *)errorForSubspecConfigurationFailureWithDomain:(NSString *)domain
+                                                 errorCode:(FIRErrorCode)code
+                                                   service:(NSString *)service
+                                                    reason:(NSString *)reason {
+  NSString *description =
+      [NSString stringWithFormat:@"Configuration failed for service %@.", service];
+  NSDictionary *errorDict =
+      @{NSLocalizedDescriptionKey : description, NSLocalizedFailureReasonErrorKey : reason};
+  return [NSError errorWithDomain:domain code:code userInfo:errorDict];
+}
+
++ (NSError *)errorForInvalidAppID {
+  NSDictionary *errorDict = @{
+    NSLocalizedDescriptionKey : @"Unable to validate Google App ID",
+    NSLocalizedRecoverySuggestionErrorKey :
+        @"Check formatting and location of GoogleService-Info.plist or GoogleAppID set in the "
+        @"customized options."
+  };
+  return [NSError errorWithDomain:kFirebaseCoreErrorDomain
+                             code:FIRErrorCodeInvalidAppID
+                         userInfo:errorDict];
+}
+
++ (void)registerAsConfigurable:(Class<FIRCoreConfigurable>)klass {
+  // This is called at +load time, keep the work to a minimum.
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    gRegisteredAsConfigurable = [[NSMutableArray alloc] initWithCapacity:1];
+  });
+
+  NSAssert([(Class)klass conformsToProtocol:@protocol(FIRCoreConfigurable)],
+           @"The class being registered (%@) must conform to `FIRCoreConfigurable`.", klass);
+  [gRegisteredAsConfigurable addObject:klass];
+}
+
++ (BOOL)isDefaultAppConfigured {
+  return (sDefaultApp != nil);
+}
+
++ (void)registerLibrary:(nonnull NSString *)library withVersion:(nonnull NSString *)version {
+  // Create the set of characters which aren't allowed, only if this feature is used.
+  NSMutableCharacterSet *allowedSet = [NSMutableCharacterSet alphanumericCharacterSet];
+  [allowedSet addCharactersInString:@"-_."];
+  NSCharacterSet *disallowedSet = [allowedSet invertedSet];
+  // Make sure the library name and version strings do not contain unexpected characters, and
+  // add the name/version pair to the dictionary.
+  if ([library rangeOfCharacterFromSet:disallowedSet].location == NSNotFound &&
+      [version rangeOfCharacterFromSet:disallowedSet].location == NSNotFound) {
+    if (!sLibraryVersions) {
+      sLibraryVersions = [[NSMutableDictionary alloc] init];
+    }
+    sLibraryVersions[library] = version;
+  } else {
+    FIRLogError(kFIRLoggerCore, @"I-COR000027",
+                @"The library name (%@) or version number (%@) contain illegal characters. "
+                @"Only alphanumeric, dash, underscore and period characters are allowed.",
+                library, version);
+  }
+}
+
++ (NSString *)firebaseUserAgent {
+  NSMutableArray<NSString *> *libraries =
+      [[NSMutableArray<NSString *> alloc] initWithCapacity:sLibraryVersions.count];
+  for (NSString *libraryName in sLibraryVersions) {
+    [libraries
+        addObject:[NSString stringWithFormat:@"%@/%@", libraryName, sLibraryVersions[libraryName]]];
+  }
+  [libraries sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
+  return [libraries componentsJoinedByString:@" "];
+}
+
+- (void)checkExpectedBundleID {
+  NSArray *bundles = [FIRBundleUtil relevantBundles];
+  NSString *expectedBundleID = [self expectedBundleID];
+  // The checking is only done when the bundle ID is provided in the serviceInfo dictionary for
+  // backward compatibility.
+  if (expectedBundleID != nil &&
+      ![FIRBundleUtil hasBundleIdentifier:expectedBundleID inBundles:bundles]) {
+    FIRLogError(kFIRLoggerCore, @"I-COR000008",
+                @"The project's Bundle ID is inconsistent with "
+                @"either the Bundle ID in '%@.%@', or the Bundle ID in the options if you are "
+                @"using a customized options. To ensure that everything can be configured "
+                @"correctly, you may need to make the Bundle IDs consistent. To continue with this "
+                @"plist file, you may change your app's bundle identifier to '%@'. Or you can "
+                @"download a new configuration file that matches your bundle identifier from %@ "
+                @"and replace the current one.",
+                kServiceInfoFileName, kServiceInfoFileType, expectedBundleID, kPlistURL);
+  }
+}
+
+// TODO: Remove once SDKs transition to Auth interop library.
+- (nullable NSString *)getUID {
+  if (!_getUIDImplementation) {
+    FIRLogWarning(kFIRLoggerCore, @"I-COR000025", @"FIRAuth getUID implementation wasn't set.");
+    return nil;
+  }
+  return _getUIDImplementation();
+}
+
+#pragma mark - private - App ID Validation
+
+/**
+ * Validates the format and fingerprint of the app ID contained in GOOGLE_APP_ID in the plist file.
+ * This is the main method for validating app ID.
+ *
+ * @return YES if the app ID fulfills the expected format and fingerprint, NO otherwise.
+ */
+- (BOOL)isAppIDValid {
+  NSString *appID = _options.googleAppID;
+  BOOL isValid = [FIRApp validateAppID:appID];
+  if (!isValid) {
+    NSString *expectedBundleID = [self expectedBundleID];
+    FIRLogError(kFIRLoggerCore, @"I-COR000009",
+                @"The GOOGLE_APP_ID either in the plist file "
+                @"'%@.%@' or the one set in the customized options is invalid. If you are using "
+                @"the plist file, use the iOS version of bundle identifier to download the file, "
+                @"and do not manually edit the GOOGLE_APP_ID. You may change your app's bundle "
+                @"identifier to '%@'. Or you can download a new configuration file that matches "
+                @"your bundle identifier from %@ and replace the current one.",
+                kServiceInfoFileName, kServiceInfoFileType, expectedBundleID, kPlistURL);
+  };
+  return isValid;
+}
+
++ (BOOL)validateAppID:(NSString *)appID {
+  // Failing validation only occurs when we are sure we are looking at a V2 app ID and it does not
+  // have a valid fingerprint, otherwise we just warn about the potential issue.
+  if (!appID.length) {
+    return NO;
+  }
+
+  // All app IDs must start with at least "<version number>:".
+  NSString *const versionPattern = @"^\\d+:";
+  NSRegularExpression *versionRegex =
+      [NSRegularExpression regularExpressionWithPattern:versionPattern options:0 error:NULL];
+  if (!versionRegex) {
+    return NO;
+  }
+
+  NSRange appIDRange = NSMakeRange(0, appID.length);
+  NSArray *versionMatches = [versionRegex matchesInString:appID options:0 range:appIDRange];
+  if (versionMatches.count != 1) {
+    return NO;
+  }
+
+  NSRange versionRange = [(NSTextCheckingResult *)versionMatches.firstObject range];
+  NSString *appIDVersion = [appID substringWithRange:versionRange];
+  NSArray *knownVersions = @[ @"1:" ];
+  if (![knownVersions containsObject:appIDVersion]) {
+    // Permit unknown yet properly formatted app ID versions.
+    return YES;
+  }
+
+  if (![FIRApp validateAppIDFormat:appID withVersion:appIDVersion]) {
+    return NO;
+  }
+
+  if (![FIRApp validateAppIDFingerprint:appID withVersion:appIDVersion]) {
+    return NO;
+  }
+
+  return YES;
+}
+
++ (NSString *)actualBundleID {
+  return [[NSBundle mainBundle] bundleIdentifier];
+}
+
+/**
+ * Validates that the format of the app ID string is what is expected based on the supplied version.
+ * The version must end in ":".
+ *
+ * For v1 app ids the format is expected to be
+ * '<version #>:<project number>:ios:<fingerprint of bundle id>'.
+ *
+ * This method does not verify that the contents of the app id are correct, just that they fulfill
+ * the expected format.
+ *
+ * @param appID Contents of GOOGLE_APP_ID from the plist file.
+ * @param version Indicates what version of the app id format this string should be.
+ * @return YES if provided string fufills the expected format, NO otherwise.
+ */
++ (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version {
+  if (!appID.length || !version.length) {
+    return NO;
+  }
+
+  if (![version hasSuffix:@":"]) {
+    return NO;
+  }
+
+  if (![appID hasPrefix:version]) {
+    return NO;
+  }
+
+  NSString *const pattern = @"^\\d+:ios:[a-f0-9]+$";
+  NSRegularExpression *regex =
+      [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:NULL];
+  if (!regex) {
+    return NO;
+  }
+
+  NSRange localRange = NSMakeRange(version.length, appID.length - version.length);
+  NSUInteger numberOfMatches = [regex numberOfMatchesInString:appID options:0 range:localRange];
+  if (numberOfMatches != 1) {
+    return NO;
+  }
+  return YES;
+}
+
+/**
+ * Validates that the fingerprint of the app ID string is what is expected based on the supplied
+ * version. The version must end in ":".
+ *
+ * Note that the v1 hash algorithm is not permitted on the client and cannot be fully validated.
+ *
+ * @param appID Contents of GOOGLE_APP_ID from the plist file.
+ * @param version Indicates what version of the app id format this string should be.
+ * @return YES if provided string fufills the expected fingerprint and the version is known, NO
+ *         otherwise.
+ */
++ (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)version {
+  if (!appID.length || !version.length) {
+    return NO;
+  }
+
+  if (![version hasSuffix:@":"]) {
+    return NO;
+  }
+
+  if (![appID hasPrefix:version]) {
+    return NO;
+  }
+
+  // Extract the supplied fingerprint from the supplied app ID.
+  // This assumes the app ID format is the same for all known versions below. If the app ID format
+  // changes in future versions, the tokenizing of the app ID format will need to take into account
+  // the version of the app ID.
+  NSArray *components = [appID componentsSeparatedByString:@":"];
+  if (components.count != 4) {
+    return NO;
+  }
+
+  NSString *suppliedFingerprintString = components[3];
+  if (!suppliedFingerprintString.length) {
+    return NO;
+  }
+
+  uint64_t suppliedFingerprint;
+  NSScanner *scanner = [NSScanner scannerWithString:suppliedFingerprintString];
+  if (![scanner scanHexLongLong:&suppliedFingerprint]) {
+    return NO;
+  }
+
+  if ([version isEqual:@"1:"]) {
+    // The v1 hash algorithm is not permitted on the client so the actual hash cannot be validated.
+    return YES;
+  }
+
+  // Unknown version.
+  return NO;
+}
+
+- (NSString *)expectedBundleID {
+  return _options.bundleID;
+}
+
+// end App ID validation
+
+#pragma mark - Reading From Plist & User Defaults
+
+/**
+ * Clears the data collection switch from the standard NSUserDefaults for easier testing and
+ * readability.
+ */
+- (void)clearDataCollectionSwitchFromUserDefaults {
+  NSString *key =
+      [NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, self.name];
+  [[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
+}
+
+/**
+ * Reads the data collection switch from the standard NSUserDefaults for easier testing and
+ * readability.
+ */
++ (nullable NSNumber *)readDataCollectionSwitchFromUserDefaultsForApp:(FIRApp *)app {
+  // Read the object in user defaults, and only return if it's an NSNumber.
+  NSString *key =
+      [NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, app.name];
+  id collectionEnabledDefaultsObject = [[NSUserDefaults standardUserDefaults] objectForKey:key];
+  if ([collectionEnabledDefaultsObject isKindOfClass:[NSNumber class]]) {
+    return collectionEnabledDefaultsObject;
+  }
+
+  return nil;
+}
+
+/**
+ * Reads the data collection switch from the Info.plist for easier testing and readability. Will
+ * only read once from the plist and return the cached value.
+ */
++ (nullable NSNumber *)readDataCollectionSwitchFromPlist {
+  static NSNumber *collectionEnabledPlistObject;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    // Read the data from the `Info.plist`, only assign it if it's there and an NSNumber.
+    id plistValue = [[NSBundle mainBundle]
+        objectForInfoDictionaryKey:kFIRGlobalAppDataCollectionEnabledPlistKey];
+    if (plistValue && [plistValue isKindOfClass:[NSNumber class]]) {
+      collectionEnabledPlistObject = (NSNumber *)plistValue;
+    }
+  });
+
+  return collectionEnabledPlistObject;
+}
+
+#pragma mark - Sending Logs
+
+- (void)sendLogsWithServiceName:(NSString *)serviceName
+                        version:(NSString *)version
+                          error:(NSError *)error {
+  // If the user has manually turned off data collection, return and don't send logs.
+  if (![self isDataCollectionDefaultEnabled]) {
+    return;
+  }
+
+  NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithDictionary:@{
+    kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeSDK),
+    kFIRAppDiagnosticsSDKNameKey : serviceName,
+    kFIRAppDiagnosticsSDKVersionKey : version,
+    kFIRAppDiagnosticsFIRAppKey : self
+  }];
+  if (error) {
+    userInfo[kFIRAppDiagnosticsErrorKey] = error;
+  }
+  [[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppDiagnosticsNotification
+                                                      object:nil
+                                                    userInfo:userInfo];
+}
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRAppAssociationRegistration.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRAppAssociationRegistration.m
new file mode 100644 (file)
index 0000000..2aecdab
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/FIRAppAssociationRegistration.h"
+
+#import <objc/runtime.h>
+
+@implementation FIRAppAssociationRegistration
+
++ (nullable id)registeredObjectWithHost:(id)host
+                                    key:(NSString *)key
+                          creationBlock:(id _Nullable (^)(void))creationBlock {
+  @synchronized(self) {
+    SEL dictKey = @selector(registeredObjectWithHost:key:creationBlock:);
+    NSMutableDictionary<NSString *, id> *objectsByKey = objc_getAssociatedObject(host, dictKey);
+    if (!objectsByKey) {
+      objectsByKey = [[NSMutableDictionary alloc] init];
+      objc_setAssociatedObject(host, dictKey, objectsByKey, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+    }
+    id obj = objectsByKey[key];
+    NSValue *creationBlockBeingCalled = [NSValue valueWithPointer:dictKey];
+    if (obj) {
+      if ([creationBlockBeingCalled isEqual:obj]) {
+        [NSException raise:@"Reentering registeredObjectWithHost:key:creationBlock: not allowed"
+                    format:@"host: %@ key: %@", host, key];
+      }
+      return obj;
+    }
+    objectsByKey[key] = creationBlockBeingCalled;
+    obj = creationBlock();
+    objectsByKey[key] = obj;
+    return obj;
+  }
+}
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRBundleUtil.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRBundleUtil.m
new file mode 100644 (file)
index 0000000..93ee02e
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/FIRBundleUtil.h"
+
+@implementation FIRBundleUtil
+
++ (NSArray *)relevantBundles {
+  return @[ [NSBundle mainBundle], [NSBundle bundleForClass:[self class]] ];
+}
+
++ (NSString *)optionsDictionaryPathWithResourceName:(NSString *)resourceName
+                                        andFileType:(NSString *)fileType
+                                          inBundles:(NSArray *)bundles {
+  // Loop through all bundles to find the config dict.
+  for (NSBundle *bundle in bundles) {
+    NSString *path = [bundle pathForResource:resourceName ofType:fileType];
+    // Use the first one we find.
+    if (path) {
+      return path;
+    }
+  }
+  return nil;
+}
+
++ (NSArray *)relevantURLSchemes {
+  NSMutableArray *result = [[NSMutableArray alloc] init];
+  for (NSBundle *bundle in [[self class] relevantBundles]) {
+    NSArray *urlTypes = [bundle objectForInfoDictionaryKey:@"CFBundleURLTypes"];
+    for (NSDictionary *urlType in urlTypes) {
+      [result addObjectsFromArray:urlType[@"CFBundleURLSchemes"]];
+    }
+  }
+  return result;
+}
+
++ (BOOL)hasBundleIdentifier:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles {
+  for (NSBundle *bundle in bundles) {
+    if ([bundle.bundleIdentifier isEqualToString:bundleIdentifier]) {
+      return YES;
+    }
+  }
+  return NO;
+}
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRComponent.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRComponent.m
new file mode 100644 (file)
index 0000000..2474d1a
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "Private/FIRComponent.h"
+
+#import "Private/FIRComponentContainer.h"
+#import "Private/FIRDependency.h"
+
+@interface FIRComponent ()
+
+- (instancetype)initWithProtocol:(Protocol *)protocol
+             instantiationTiming:(FIRInstantiationTiming)instantiationTiming
+                    dependencies:(NSArray<FIRDependency *> *)dependencies
+                   creationBlock:(FIRComponentCreationBlock)creationBlock;
+
+@end
+
+@implementation FIRComponent
+
++ (instancetype)componentWithProtocol:(Protocol *)protocol
+                        creationBlock:(FIRComponentCreationBlock)creationBlock {
+  return [[FIRComponent alloc] initWithProtocol:protocol
+                            instantiationTiming:FIRInstantiationTimingLazy
+                                   dependencies:@[]
+                                  creationBlock:creationBlock];
+}
+
++ (instancetype)componentWithProtocol:(Protocol *)protocol
+                  instantiationTiming:(FIRInstantiationTiming)instantiationTiming
+                         dependencies:(NSArray<FIRDependency *> *)dependencies
+                        creationBlock:(FIRComponentCreationBlock)creationBlock {
+  return [[FIRComponent alloc] initWithProtocol:protocol
+                            instantiationTiming:instantiationTiming
+                                   dependencies:dependencies
+                                  creationBlock:creationBlock];
+}
+
+- (instancetype)initWithProtocol:(Protocol *)protocol
+             instantiationTiming:(FIRInstantiationTiming)instantiationTiming
+                    dependencies:(NSArray<FIRDependency *> *)dependencies
+                   creationBlock:(FIRComponentCreationBlock)creationBlock {
+  self = [super init];
+  if (self) {
+    _protocol = protocol;
+    _instantiationTiming = instantiationTiming;
+    _dependencies = [dependencies copy];
+    _creationBlock = creationBlock;
+  }
+  return self;
+}
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRComponentContainer.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRComponentContainer.m
new file mode 100644 (file)
index 0000000..381c95c
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "Private/FIRComponentContainer.h"
+
+#import "Private/FIRAppInternal.h"
+#import "Private/FIRComponent.h"
+#import "Private/FIRComponentRegistrant.h"
+#import "Private/FIRLogger.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRComponentContainer ()
+
+/// The dictionary of components that are registered for a particular app. The key is an NSString
+/// of the protocol.
+@property(nonatomic, strong) NSMutableDictionary<NSString *, FIRComponentCreationBlock> *components;
+
+/// Cached instances of components that requested to be cached.
+@property(nonatomic, strong) NSMutableDictionary<NSString *, id> *cachedInstances;
+
+@end
+
+@implementation FIRComponentContainer
+
+// Collection of all classes that register to provide components.
+static NSMutableSet<Class> *gFIRComponentRegistrants;
+
+#pragma mark - Public Registration
+
++ (void)registerAsComponentRegistrant:(Class)klass {
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    gFIRComponentRegistrants = [[NSMutableSet<Class> alloc] init];
+  });
+
+  [self registerAsComponentRegistrant:klass inSet:gFIRComponentRegistrants];
+}
+
++ (void)registerAsComponentRegistrant:(Class)klass inSet:(NSMutableSet<Class> *)allRegistrants {
+  // Validate the array to store the components is initialized.
+  if (!allRegistrants) {
+    FIRLogWarning(kFIRLoggerCore, @"I-COR000025",
+                  @"Attempted to store registered components in an empty set.");
+    return;
+  }
+
+  // Ensure the class given conforms to the proper protocol.
+  if (![klass conformsToProtocol:@protocol(FIRComponentRegistrant)] ||
+      ![klass respondsToSelector:@selector(componentsToRegister)]) {
+    [NSException raise:NSInvalidArgumentException
+                format:
+                    @"Class %@ attempted to register components, but it does not conform to "
+                    @"`FIRComponentRegistrant` or provide a `componentsToRegister:` method.",
+                    klass];
+  }
+
+  [allRegistrants addObject:klass];
+}
+
+#pragma mark - Internal Initialization
+
+- (instancetype)initWithApp:(FIRApp *)app {
+  return [self initWithApp:app registrants:gFIRComponentRegistrants];
+}
+
+- (instancetype)initWithApp:(FIRApp *)app registrants:(NSMutableSet<Class> *)allRegistrants {
+  self = [super init];
+  if (self) {
+    _app = app;
+    _cachedInstances = [NSMutableDictionary<NSString *, id> dictionary];
+    _components = [NSMutableDictionary<NSString *, FIRComponentCreationBlock> dictionary];
+
+    [self populateComponentsFromRegisteredClasses:allRegistrants forApp:app];
+  }
+  return self;
+}
+
+- (void)populateComponentsFromRegisteredClasses:(NSSet<Class> *)classes forApp:(FIRApp *)app {
+  // Loop through the verified component registrants and populate the components array.
+  for (Class<FIRComponentRegistrant> klass in classes) {
+    // Loop through all the components being registered and store them as appropriate.
+    // Classes which do not provide functionality should use a dummy FIRComponentRegistrant
+    // protocol.
+    for (FIRComponent *component in [klass componentsToRegister]) {
+      // Check if the component has been registered before, and error out if so.
+      NSString *protocolName = NSStringFromProtocol(component.protocol);
+      if (self.components[protocolName]) {
+        FIRLogError(kFIRLoggerCore, @"I-COR000029",
+                    @"Attempted to register protocol %@, but it already has an implementation.",
+                    protocolName);
+        continue;
+      }
+
+      // Store the creation block for later usage.
+      self.components[protocolName] = component.creationBlock;
+
+      // Instantiate the
+      BOOL shouldInstantiateEager =
+          (component.instantiationTiming == FIRInstantiationTimingAlwaysEager);
+      BOOL shouldInstantiateDefaultEager =
+          (component.instantiationTiming == FIRInstantiationTimingEagerInDefaultApp &&
+           [app isDefaultApp]);
+      if (shouldInstantiateEager || shouldInstantiateDefaultEager) {
+        [self instantiateInstanceForProtocol:component.protocol withBlock:component.creationBlock];
+      }
+    }
+  }
+}
+
+#pragma mark - Instance Creation
+
+/// Instantiate an instance of a class that conforms to the specified protocol.
+/// This will:
+///   - Call the block to create an instance if possible,
+///   - Validate that the instance returned conforms to the protocol it claims to,
+///   - Cache the instance if the block requests it
+- (nullable id)instantiateInstanceForProtocol:(Protocol *)protocol
+                                    withBlock:(FIRComponentCreationBlock)creationBlock {
+  if (!creationBlock) {
+    return nil;
+  }
+
+  // Create an instance using the creation block.
+  BOOL shouldCache = NO;
+  id instance = creationBlock(self, &shouldCache);
+  if (!instance) {
+    return nil;
+  }
+
+  // An instance was created, validate that it conforms to the protocol it claims to.
+  NSString *protocolName = NSStringFromProtocol(protocol);
+  if (![instance conformsToProtocol:protocol]) {
+    FIRLogError(kFIRLoggerCore, @"I-COR000030",
+                @"An instance conforming to %@ was requested, but the instance provided does not "
+                @"conform to the protocol",
+                protocolName);
+  }
+
+  // The instance is ready to be returned, but check if it should be cached first before returning.
+  if (shouldCache) {
+    self.cachedInstances[protocolName] = instance;
+  }
+
+  return instance;
+}
+
+#pragma mark - Internal Retrieval
+
+- (nullable id)instanceForProtocol:(Protocol *)protocol {
+  // Check if there is a cached instance, and return it if so.
+  NSString *protocolName = NSStringFromProtocol(protocol);
+  id cachedInstance = self.cachedInstances[protocolName];
+  if (cachedInstance) {
+    return cachedInstance;
+  }
+
+  // Use the creation block to instantiate an instance and return it.
+  FIRComponentCreationBlock creationBlock = self.components[protocolName];
+  return [self instantiateInstanceForProtocol:protocol withBlock:creationBlock];
+}
+
+#pragma mark - Lifecycle
+
+- (void)removeAllCachedInstances {
+  // Loop through the cache and notify each instance that is a maintainer to clean up after itself.
+  for (id instance in self.cachedInstances.allValues) {
+    if ([instance conformsToProtocol:@protocol(FIRComponentLifecycleMaintainer)] &&
+        [instance respondsToSelector:@selector(appWillBeDeleted:)]) {
+      [instance appWillBeDeleted:self.app];
+    }
+  }
+
+  [self.cachedInstances removeAllObjects];
+}
+
+#pragma mark - Testing Initializers
+
+// TODO(wilsonryan): Set up a testing flag so this only is compiled in with unit tests.
+/// Initialize an instance with an app and existing components.
+- (instancetype)initWithApp:(FIRApp *)app
+                 components:(NSDictionary<NSString *, FIRComponentCreationBlock> *)components {
+  self = [self initWithApp:app registrants:[[NSMutableSet alloc] init]];
+  if (self) {
+    _components = [components mutableCopy];
+  }
+  return self;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRComponentType.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRComponentType.m
new file mode 100644 (file)
index 0000000..bdc004f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "Private/FIRComponentType.h"
+
+#import "Private/FIRComponentContainerInternal.h"
+
+@implementation FIRComponentType
+
++ (id)instanceForProtocol:(Protocol *)protocol inContainer:(FIRComponentContainer *)container {
+  // Forward the call to the container.
+  return [container instanceForProtocol:protocol];
+}
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRConfiguration.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRConfiguration.m
new file mode 100644 (file)
index 0000000..cd64862
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "FIRConfiguration.h"
+
+extern void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
+
+@implementation FIRConfiguration
+
++ (instancetype)sharedInstance {
+  static FIRConfiguration *sharedInstance = nil;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    sharedInstance = [[FIRConfiguration alloc] init];
+  });
+  return sharedInstance;
+}
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _analyticsConfiguration = [FIRAnalyticsConfiguration sharedInstance];
+  }
+  return self;
+}
+
+- (void)setLoggerLevel:(FIRLoggerLevel)loggerLevel {
+  NSAssert(loggerLevel <= FIRLoggerLevelMax && loggerLevel >= FIRLoggerLevelMin,
+           @"Invalid logger level, %ld", (long)loggerLevel);
+  FIRSetLoggerLevel(loggerLevel);
+}
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRDependency.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRDependency.m
new file mode 100644 (file)
index 0000000..f979984
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "Private/FIRDependency.h"
+
+@interface FIRDependency ()
+
+- (instancetype)initWithProtocol:(Protocol *)protocol isRequired:(BOOL)required;
+
+@end
+
+@implementation FIRDependency
+
++ (instancetype)dependencyWithProtocol:(Protocol *)protocol {
+  return [[self alloc] initWithProtocol:protocol isRequired:YES];
+}
+
++ (instancetype)dependencyWithProtocol:(Protocol *)protocol isRequired:(BOOL)required {
+  return [[self alloc] initWithProtocol:protocol isRequired:required];
+}
+
+- (instancetype)initWithProtocol:(Protocol *)protocol isRequired:(BOOL)required {
+  self = [super init];
+  if (self) {
+    _protocol = protocol;
+    _isRequired = required;
+  }
+  return self;
+}
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRErrors.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRErrors.m
new file mode 100644 (file)
index 0000000..6d6d52d
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/FIRErrors.h"
+
+NSString *const kFirebaseErrorDomain = @"com.firebase";
+NSString *const kFirebaseAdMobErrorDomain = @"com.firebase.admob";
+NSString *const kFirebaseAppInviteErrorDomain = @"com.firebase.appinvite";
+NSString *const kFirebaseAuthErrorDomain = @"com.firebase.auth";
+NSString *const kFirebaseCloudMessagingErrorDomain = @"com.firebase.cloudmessaging";
+NSString *const kFirebaseConfigErrorDomain = @"com.firebase.config";
+NSString *const kFirebaseCoreErrorDomain = @"com.firebase.core";
+NSString *const kFirebaseCrashReportingErrorDomain = @"com.firebase.crashreporting";
+NSString *const kFirebaseDatabaseErrorDomain = @"com.firebase.database";
+NSString *const kFirebaseDurableDeepLinkErrorDomain = @"com.firebase.durabledeeplink";
+NSString *const kFirebaseInstanceIDErrorDomain = @"com.firebase.instanceid";
+NSString *const kFirebasePerfErrorDomain = @"com.firebase.perf";
+NSString *const kFirebaseStorageErrorDomain = @"com.firebase.storage";
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRLogger.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRLogger.m
new file mode 100644 (file)
index 0000000..d1e3b37
--- /dev/null
@@ -0,0 +1,185 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/FIRLogger.h"
+
+#import <FirebaseCore/FIRLoggerLevel.h>
+#import <GoogleUtilities/GULAppEnvironmentUtil.h>
+#import <GoogleUtilities/GULLogger.h>
+
+#import "Private/FIRVersion.h"
+
+FIRLoggerService kFIRLoggerABTesting = @"[Firebase/ABTesting]";
+FIRLoggerService kFIRLoggerAdMob = @"[Firebase/AdMob]";
+FIRLoggerService kFIRLoggerAnalytics = @"[Firebase/Analytics]";
+FIRLoggerService kFIRLoggerAuth = @"[Firebase/Auth]";
+FIRLoggerService kFIRLoggerCore = @"[Firebase/Core]";
+FIRLoggerService kFIRLoggerCrash = @"[Firebase/Crash]";
+FIRLoggerService kFIRLoggerDatabase = @"[Firebase/Database]";
+FIRLoggerService kFIRLoggerDynamicLinks = @"[Firebase/DynamicLinks]";
+FIRLoggerService kFIRLoggerFirestore = @"[Firebase/Firestore]";
+FIRLoggerService kFIRLoggerInstanceID = @"[Firebase/InstanceID]";
+FIRLoggerService kFIRLoggerInvites = @"[Firebase/Invites]";
+FIRLoggerService kFIRLoggerMLKit = @"[Firebase/MLKit]";
+FIRLoggerService kFIRLoggerMessaging = @"[Firebase/Messaging]";
+FIRLoggerService kFIRLoggerPerf = @"[Firebase/Performance]";
+FIRLoggerService kFIRLoggerRemoteConfig = @"[Firebase/RemoteConfig]";
+FIRLoggerService kFIRLoggerStorage = @"[Firebase/Storage]";
+FIRLoggerService kFIRLoggerSwizzler = @"[FirebaseSwizzlingUtilities]";
+
+/// Arguments passed on launch.
+NSString *const kFIRDisableDebugModeApplicationArgument = @"-FIRDebugDisabled";
+NSString *const kFIREnableDebugModeApplicationArgument = @"-FIRDebugEnabled";
+NSString *const kFIRLoggerForceSDTERRApplicationArgument = @"-FIRLoggerForceSTDERR";
+
+/// Key for the debug mode bit in NSUserDefaults.
+NSString *const kFIRPersistedDebugModeKey = @"/google/firebase/debug_mode";
+
+/// NSUserDefaults that should be used to store and read variables. If nil, `standardUserDefaults`
+/// will be used.
+static NSUserDefaults *sFIRLoggerUserDefaults;
+
+static dispatch_once_t sFIRLoggerOnceToken;
+
+// The sFIRAnalyticsDebugMode flag is here to support the -FIRDebugEnabled/-FIRDebugDisabled
+// flags used by Analytics. Users who use those flags expect Analytics to log verbosely,
+// while the rest of Firebase logs at the default level. This flag is introduced to support
+// that behavior.
+static BOOL sFIRAnalyticsDebugMode;
+
+#ifdef DEBUG
+/// The regex pattern for the message code.
+static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$";
+static NSRegularExpression *sMessageCodeRegex;
+#endif
+
+void FIRLoggerInitializeASL() {
+  dispatch_once(&sFIRLoggerOnceToken, ^{
+    // Register Firebase Version with GULLogger.
+    GULLoggerRegisterVersion(FIRVersionString);
+
+    // Override the aslOptions to ASL_OPT_STDERR if the override argument is passed in.
+    NSArray *arguments = [NSProcessInfo processInfo].arguments;
+    BOOL overrideSTDERR = [arguments containsObject:kFIRLoggerForceSDTERRApplicationArgument];
+
+    // Use the standard NSUserDefaults if it hasn't been explicitly set.
+    if (sFIRLoggerUserDefaults == nil) {
+      sFIRLoggerUserDefaults = [NSUserDefaults standardUserDefaults];
+    }
+
+    BOOL forceDebugMode = NO;
+    BOOL debugMode = [sFIRLoggerUserDefaults boolForKey:kFIRPersistedDebugModeKey];
+    if ([arguments containsObject:kFIRDisableDebugModeApplicationArgument]) {  // Default mode
+      [sFIRLoggerUserDefaults removeObjectForKey:kFIRPersistedDebugModeKey];
+    } else if ([arguments containsObject:kFIREnableDebugModeApplicationArgument] ||
+               debugMode) {  // Debug mode
+      [sFIRLoggerUserDefaults setBool:YES forKey:kFIRPersistedDebugModeKey];
+      forceDebugMode = YES;
+    }
+    GULLoggerInitializeASL();
+    if (overrideSTDERR) {
+      GULLoggerEnableSTDERR();
+    }
+    if (forceDebugMode) {
+      GULLoggerForceDebug();
+    }
+  });
+}
+
+__attribute__((no_sanitize("thread"))) void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode) {
+  sFIRAnalyticsDebugMode = analyticsDebugMode;
+}
+
+void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel) {
+  FIRLoggerInitializeASL();
+  GULSetLoggerLevel((GULLoggerLevel)loggerLevel);
+}
+
+#ifdef DEBUG
+void FIRResetLogger() {
+  extern void GULResetLogger(void);
+  sFIRLoggerOnceToken = 0;
+  [sFIRLoggerUserDefaults removeObjectForKey:kFIRPersistedDebugModeKey];
+  sFIRLoggerUserDefaults = nil;
+  GULResetLogger();
+}
+
+void FIRSetLoggerUserDefaults(NSUserDefaults *defaults) {
+  sFIRLoggerUserDefaults = defaults;
+}
+#endif
+
+/**
+ * Check if the level is high enough to be loggable.
+ *
+ * Analytics can override the log level with an intentional race condition.
+ * Add the attribute to get a clean thread sanitizer run.
+ */
+__attribute__((no_sanitize("thread"))) BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel,
+                                                               BOOL analyticsComponent) {
+  FIRLoggerInitializeASL();
+  if (sFIRAnalyticsDebugMode && analyticsComponent) {
+    return YES;
+  }
+  return GULIsLoggableLevel((GULLoggerLevel)loggerLevel);
+}
+
+void FIRLogBasic(FIRLoggerLevel level,
+                 FIRLoggerService service,
+                 NSString *messageCode,
+                 NSString *message,
+                 va_list args_ptr) {
+  FIRLoggerInitializeASL();
+  GULLogBasic((GULLoggerLevel)level, service,
+              sFIRAnalyticsDebugMode && [kFIRLoggerAnalytics isEqualToString:service], messageCode,
+              message, args_ptr);
+}
+
+/**
+ * Generates the logging functions using macros.
+ *
+ * Calling FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configure %@ failed.", @"blah") shows:
+ * yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Error> [Firebase/Core][I-COR000001] Configure blah failed.
+ * Calling FIRLogDebug(kFIRLoggerCore, @"I-COR000001", @"Configure succeed.") shows:
+ * yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Debug> [Firebase/Core][I-COR000001] Configure succeed.
+ */
+#define FIR_LOGGING_FUNCTION(level)                                                             \
+  void FIRLog##level(FIRLoggerService service, NSString *messageCode, NSString *message, ...) { \
+    va_list args_ptr;                                                                           \
+    va_start(args_ptr, message);                                                                \
+    FIRLogBasic(FIRLoggerLevel##level, service, messageCode, message, args_ptr);                \
+    va_end(args_ptr);                                                                           \
+  }
+
+FIR_LOGGING_FUNCTION(Error)
+FIR_LOGGING_FUNCTION(Warning)
+FIR_LOGGING_FUNCTION(Notice)
+FIR_LOGGING_FUNCTION(Info)
+FIR_LOGGING_FUNCTION(Debug)
+
+#undef FIR_MAKE_LOGGER
+
+#pragma mark - FIRLoggerWrapper
+
+@implementation FIRLoggerWrapper
+
++ (void)logWithLevel:(FIRLoggerLevel)level
+         withService:(FIRLoggerService)service
+            withCode:(NSString *)messageCode
+         withMessage:(NSString *)message
+            withArgs:(va_list)args {
+  FIRLogBasic(level, service, messageCode, message, args);
+}
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIROptions.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIROptions.m
new file mode 100644 (file)
index 0000000..8cbc7a2
--- /dev/null
@@ -0,0 +1,444 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/FIRAppInternal.h"
+#import "Private/FIRBundleUtil.h"
+#import "Private/FIRErrors.h"
+#import "Private/FIRLogger.h"
+#import "Private/FIROptionsInternal.h"
+
+// Keys for the strings in the plist file.
+NSString *const kFIRAPIKey = @"API_KEY";
+NSString *const kFIRTrackingID = @"TRACKING_ID";
+NSString *const kFIRGoogleAppID = @"GOOGLE_APP_ID";
+NSString *const kFIRClientID = @"CLIENT_ID";
+NSString *const kFIRGCMSenderID = @"GCM_SENDER_ID";
+NSString *const kFIRAndroidClientID = @"ANDROID_CLIENT_ID";
+NSString *const kFIRDatabaseURL = @"DATABASE_URL";
+NSString *const kFIRStorageBucket = @"STORAGE_BUCKET";
+// The key to locate the expected bundle identifier in the plist file.
+NSString *const kFIRBundleID = @"BUNDLE_ID";
+// The key to locate the project identifier in the plist file.
+NSString *const kFIRProjectID = @"PROJECT_ID";
+
+NSString *const kFIRIsMeasurementEnabled = @"IS_MEASUREMENT_ENABLED";
+NSString *const kFIRIsAnalyticsCollectionEnabled = @"FIREBASE_ANALYTICS_COLLECTION_ENABLED";
+NSString *const kFIRIsAnalyticsCollectionDeactivated = @"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED";
+
+NSString *const kFIRIsAnalyticsEnabled = @"IS_ANALYTICS_ENABLED";
+NSString *const kFIRIsSignInEnabled = @"IS_SIGNIN_ENABLED";
+
+// Library version ID.
+NSString *const kFIRLibraryVersionID =
+    @"5"     // Major version (one or more digits)
+    @"01"    // Minor version (exactly 2 digits)
+    @"04"    // Build number (exactly 2 digits)
+    @"000";  // Fixed "000"
+// Plist file name.
+NSString *const kServiceInfoFileName = @"GoogleService-Info";
+// Plist file type.
+NSString *const kServiceInfoFileType = @"plist";
+
+// Exception raised from attempting to modify a FIROptions after it's been copied to a FIRApp.
+NSString *const kFIRExceptionBadModification =
+    @"Attempted to modify options after it's set on FIRApp. Please modify all properties before "
+    @"initializing FIRApp.";
+
+@interface FIROptions ()
+
+/**
+ * This property maintains the actual configuration key-value pairs.
+ */
+@property(nonatomic, readwrite) NSMutableDictionary *optionsDictionary;
+
+/**
+ * Calls `analyticsOptionsDictionaryWithInfoDictionary:` using [NSBundle mainBundle].infoDictionary.
+ * It combines analytics options from both the infoDictionary and the GoogleService-Info.plist.
+ * Values which are present in the main plist override values from the GoogleService-Info.plist.
+ */
+@property(nonatomic, readonly) NSDictionary *analyticsOptionsDictionary;
+
+/**
+ * Combination of analytics options from both the infoDictionary and the GoogleService-Info.plist.
+ * Values which are present in the infoDictionary override values from the GoogleService-Info.plist.
+ */
+- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary;
+
+/**
+ * Throw exception if editing is locked when attempting to modify an option.
+ */
+- (void)checkEditingLocked;
+
+@end
+
+@implementation FIROptions {
+  /// Backing variable for self.analyticsOptionsDictionary.
+  NSDictionary *_analyticsOptionsDictionary;
+}
+
+static FIROptions *sDefaultOptions = nil;
+static NSDictionary *sDefaultOptionsDictionary = nil;
+
+#pragma mark - Public only for internal class methods
+
++ (FIROptions *)defaultOptions {
+  if (sDefaultOptions != nil) {
+    return sDefaultOptions;
+  }
+
+  NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
+  if (defaultOptionsDictionary == nil) {
+    return nil;
+  }
+
+  sDefaultOptions = [[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
+  return sDefaultOptions;
+}
+
+#pragma mark - Private class methods
+
++ (void)initialize {
+  // Report FirebaseCore version for useragent string
+  NSRange major = NSMakeRange(0, 1);
+  NSRange minor = NSMakeRange(1, 2);
+  NSRange patch = NSMakeRange(3, 2);
+  [FIRApp
+      registerLibrary:@"fire-ios"
+          withVersion:[NSString stringWithFormat:@"%@.%d.%d",
+                                                 [kFIRLibraryVersionID substringWithRange:major],
+                                                 [[kFIRLibraryVersionID substringWithRange:minor]
+                                                     intValue],
+                                                 [[kFIRLibraryVersionID substringWithRange:patch]
+                                                     intValue]]];
+  NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
+  NSString *xcodeVersion = info[@"DTXcodeBuild"];
+  NSString *sdkVersion = info[@"DTSDKBuild"];
+  if (xcodeVersion) {
+    [FIRApp registerLibrary:@"xcode" withVersion:xcodeVersion];
+  }
+  if (sdkVersion) {
+    [FIRApp registerLibrary:@"apple-sdk" withVersion:sdkVersion];
+  }
+}
+
++ (NSDictionary *)defaultOptionsDictionary {
+  if (sDefaultOptionsDictionary != nil) {
+    return sDefaultOptionsDictionary;
+  }
+  NSString *plistFilePath = [FIROptions plistFilePathWithName:kServiceInfoFileName];
+  if (plistFilePath == nil) {
+    return nil;
+  }
+  sDefaultOptionsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
+  if (sDefaultOptionsDictionary == nil) {
+    FIRLogError(kFIRLoggerCore, @"I-COR000011",
+                @"The configuration file is not a dictionary: "
+                @"'%@.%@'.",
+                kServiceInfoFileName, kServiceInfoFileType);
+  }
+  return sDefaultOptionsDictionary;
+}
+
+// Returns the path of the plist file with a given file name.
++ (NSString *)plistFilePathWithName:(NSString *)fileName {
+  NSArray *bundles = [FIRBundleUtil relevantBundles];
+  NSString *plistFilePath =
+      [FIRBundleUtil optionsDictionaryPathWithResourceName:fileName
+                                               andFileType:kServiceInfoFileType
+                                                 inBundles:bundles];
+  if (plistFilePath == nil) {
+    FIRLogError(kFIRLoggerCore, @"I-COR000012", @"Could not locate configuration file: '%@.%@'.",
+                fileName, kServiceInfoFileType);
+  }
+  return plistFilePath;
+}
+
++ (void)resetDefaultOptions {
+  sDefaultOptions = nil;
+  sDefaultOptionsDictionary = nil;
+}
+
+#pragma mark - Private instance methods
+
+- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)optionsDictionary {
+  self = [super init];
+  if (self) {
+    _optionsDictionary = [optionsDictionary mutableCopy];
+    _usingOptionsFromDefaultPlist = YES;
+  }
+  return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+  FIROptions *newOptions = [[[self class] allocWithZone:zone] init];
+  if (newOptions) {
+    newOptions.optionsDictionary = self.optionsDictionary;
+    newOptions.deepLinkURLScheme = self.deepLinkURLScheme;
+    newOptions.editingLocked = self.isEditingLocked;
+    newOptions.usingOptionsFromDefaultPlist = self.usingOptionsFromDefaultPlist;
+  }
+  return newOptions;
+}
+
+#pragma mark - Public instance methods
+
+- (instancetype)initWithContentsOfFile:(NSString *)plistPath {
+  self = [super init];
+  if (self) {
+    if (plistPath == nil) {
+      FIRLogError(kFIRLoggerCore, @"I-COR000013", @"The plist file path is nil.");
+      return nil;
+    }
+    _optionsDictionary = [[NSDictionary dictionaryWithContentsOfFile:plistPath] mutableCopy];
+    if (_optionsDictionary == nil) {
+      FIRLogError(kFIRLoggerCore, @"I-COR000014",
+                  @"The configuration file at %@ does not exist or "
+                  @"is not a well-formed plist file.",
+                  plistPath);
+      return nil;
+    }
+    // TODO: Do we want to validate the dictionary here? It says we do that already in
+    // the public header.
+  }
+  return self;
+}
+
+- (instancetype)initWithGoogleAppID:(NSString *)googleAppID GCMSenderID:(NSString *)GCMSenderID {
+  self = [super init];
+  if (self) {
+    NSMutableDictionary *mutableOptionsDict = [NSMutableDictionary dictionary];
+    [mutableOptionsDict setValue:googleAppID forKey:kFIRGoogleAppID];
+    [mutableOptionsDict setValue:GCMSenderID forKey:kFIRGCMSenderID];
+    [mutableOptionsDict setValue:[[NSBundle mainBundle] bundleIdentifier] forKey:kFIRBundleID];
+    self.optionsDictionary = mutableOptionsDict;
+  }
+  return self;
+}
+
+- (NSString *)APIKey {
+  return self.optionsDictionary[kFIRAPIKey];
+}
+
+- (void)checkEditingLocked {
+  if (self.isEditingLocked) {
+    [NSException raise:kFirebaseCoreErrorDomain format:kFIRExceptionBadModification];
+  }
+}
+
+- (void)setAPIKey:(NSString *)APIKey {
+  [self checkEditingLocked];
+  _optionsDictionary[kFIRAPIKey] = [APIKey copy];
+}
+
+- (NSString *)clientID {
+  return self.optionsDictionary[kFIRClientID];
+}
+
+- (void)setClientID:(NSString *)clientID {
+  [self checkEditingLocked];
+  _optionsDictionary[kFIRClientID] = [clientID copy];
+}
+
+- (NSString *)trackingID {
+  return self.optionsDictionary[kFIRTrackingID];
+}
+
+- (void)setTrackingID:(NSString *)trackingID {
+  [self checkEditingLocked];
+  _optionsDictionary[kFIRTrackingID] = [trackingID copy];
+}
+
+- (NSString *)GCMSenderID {
+  return self.optionsDictionary[kFIRGCMSenderID];
+}
+
+- (void)setGCMSenderID:(NSString *)GCMSenderID {
+  [self checkEditingLocked];
+  _optionsDictionary[kFIRGCMSenderID] = [GCMSenderID copy];
+}
+
+- (NSString *)projectID {
+  return self.optionsDictionary[kFIRProjectID];
+}
+
+- (void)setProjectID:(NSString *)projectID {
+  [self checkEditingLocked];
+  _optionsDictionary[kFIRProjectID] = [projectID copy];
+}
+
+- (NSString *)androidClientID {
+  return self.optionsDictionary[kFIRAndroidClientID];
+}
+
+- (void)setAndroidClientID:(NSString *)androidClientID {
+  [self checkEditingLocked];
+  _optionsDictionary[kFIRAndroidClientID] = [androidClientID copy];
+}
+
+- (NSString *)googleAppID {
+  return self.optionsDictionary[kFIRGoogleAppID];
+}
+
+- (void)setGoogleAppID:(NSString *)googleAppID {
+  [self checkEditingLocked];
+  _optionsDictionary[kFIRGoogleAppID] = [googleAppID copy];
+}
+
+- (NSString *)libraryVersionID {
+  return kFIRLibraryVersionID;
+}
+
+- (void)setLibraryVersionID:(NSString *)libraryVersionID {
+  _optionsDictionary[kFIRLibraryVersionID] = [libraryVersionID copy];
+}
+
+- (NSString *)databaseURL {
+  return self.optionsDictionary[kFIRDatabaseURL];
+}
+
+- (void)setDatabaseURL:(NSString *)databaseURL {
+  [self checkEditingLocked];
+
+  _optionsDictionary[kFIRDatabaseURL] = [databaseURL copy];
+}
+
+- (NSString *)storageBucket {
+  return self.optionsDictionary[kFIRStorageBucket];
+}
+
+- (void)setStorageBucket:(NSString *)storageBucket {
+  [self checkEditingLocked];
+  _optionsDictionary[kFIRStorageBucket] = [storageBucket copy];
+}
+
+- (void)setDeepLinkURLScheme:(NSString *)deepLinkURLScheme {
+  [self checkEditingLocked];
+  _deepLinkURLScheme = [deepLinkURLScheme copy];
+}
+
+- (NSString *)bundleID {
+  return self.optionsDictionary[kFIRBundleID];
+}
+
+- (void)setBundleID:(NSString *)bundleID {
+  [self checkEditingLocked];
+  _optionsDictionary[kFIRBundleID] = [bundleID copy];
+}
+
+#pragma mark - Internal instance methods
+
+- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary {
+  if (_analyticsOptionsDictionary == nil) {
+    NSMutableDictionary *tempAnalyticsOptions = [[NSMutableDictionary alloc] init];
+    NSArray *measurementKeys = @[
+      kFIRIsMeasurementEnabled, kFIRIsAnalyticsCollectionEnabled,
+      kFIRIsAnalyticsCollectionDeactivated
+    ];
+    for (NSString *key in measurementKeys) {
+      id value = infoDictionary[key] ?: self.optionsDictionary[key] ?: nil;
+      if (!value) {
+        continue;
+      }
+      tempAnalyticsOptions[key] = value;
+    }
+    _analyticsOptionsDictionary = tempAnalyticsOptions;
+  }
+  return _analyticsOptionsDictionary;
+}
+
+- (NSDictionary *)analyticsOptionsDictionary {
+  return [self analyticsOptionsDictionaryWithInfoDictionary:[NSBundle mainBundle].infoDictionary];
+}
+
+/**
+ * Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
+ * GoogleService-Info.plist. This uses the old plist flag IS_MEASUREMENT_ENABLED, which should still
+ * be supported.
+ */
+- (BOOL)isMeasurementEnabled {
+  if (self.isAnalyticsCollectionDeactivated) {
+    return NO;
+  }
+  NSNumber *value = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
+  if (value == nil) {
+    // TODO: This could probably be cleaned up since FIROptions shouldn't know about FIRApp or have
+    //       to check if it's the default app. The FIROptions instance can't be modified after
+    //       `+configure` is called, so it's not a good place to copy it either in case the flag is
+    //       changed at runtime.
+
+    // If no values are set for Analytics, fall back to the global collection switch in FIRApp.
+    // Analytics only supports the default FIRApp, so check that first.
+    if (![FIRApp isDefaultAppConfigured]) {
+      return NO;
+    }
+
+    // Fall back to the default app's collection switch when the key is not in the dictionary.
+    return [FIRApp defaultApp].isDataCollectionDefaultEnabled;
+  }
+  return [value boolValue];
+}
+
+- (BOOL)isAnalyticsCollectionExpicitlySet {
+  // If it's de-activated, it classifies as explicity set. If not, it's not a good enough indication
+  // that the developer wants FirebaseAnalytics enabled so continue checking.
+  if (self.isAnalyticsCollectionDeactivated) {
+    return YES;
+  }
+
+  // Check if the current Analytics flag is set.
+  id collectionEnabledObject = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
+  if (collectionEnabledObject && [collectionEnabledObject isKindOfClass:[NSNumber class]]) {
+    // It doesn't matter what the value is, it's explicitly set.
+    return YES;
+  }
+
+  // Check if the old measurement flag is set.
+  id measurementEnabledObject = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
+  if (measurementEnabledObject && [measurementEnabledObject isKindOfClass:[NSNumber class]]) {
+    // It doesn't matter what the value is, it's explicitly set.
+    return YES;
+  }
+
+  // No flags are set to explicitly enable or disable FirebaseAnalytics.
+  return NO;
+}
+
+- (BOOL)isAnalyticsCollectionEnabled {
+  if (self.isAnalyticsCollectionDeactivated) {
+    return NO;
+  }
+  NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
+  if (value == nil) {
+    return self.isMeasurementEnabled;  // Fall back to older plist flag.
+  }
+  return [value boolValue];
+}
+
+- (BOOL)isAnalyticsCollectionDeactivated {
+  NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionDeactivated];
+  if (value == nil) {
+    return NO;  // Analytics Collection is not deactivated when the key is not in the dictionary.
+  }
+  return [value boolValue];
+}
+
+- (BOOL)isAnalyticsEnabled {
+  return [self.optionsDictionary[kFIRIsAnalyticsEnabled] boolValue];
+}
+
+- (BOOL)isSignInEnabled {
+  return [self.optionsDictionary[kFIRIsSignInEnabled] boolValue];
+}
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/FIRVersion.m b/iOS/Pods/FirebaseCore/Firebase/Core/FIRVersion.m
new file mode 100644 (file)
index 0000000..ec0f6ba
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Firebase_VERSION
+#error "Firebase_VERSION is not defined: add -DFirebase_VERSION=... to the build invocation"
+#endif
+
+#ifndef FIRCore_VERSION
+#error "FIRCore_VERSION is not defined: add -DFIRCore_VERSION=... to the build invocation"
+#endif
+
+// The following two macros supply the incantation so that the C
+// preprocessor does not try to parse the version as a floating
+// point number. See
+// https://www.guyrutenberg.com/2008/12/20/expanding-macros-into-string-constants-in-c/
+#define STR(x) STR_EXPAND(x)
+#define STR_EXPAND(x) #x
+
+const char *const FIRVersionString = (const char *const)STR(Firebase_VERSION);
+const char *const FIRCoreVersionString = (const char *const)STR(FIRCore_VERSION);
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h
new file mode 100644 (file)
index 0000000..be624b4
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRAnalyticsConfiguration.h"
+
+/// Values stored in analyticsEnabledState. Never alter these constants since they must match with
+/// values persisted to disk.
+typedef NS_ENUM(int64_t, FIRAnalyticsEnabledState) {
+  // 0 is the default value for keys not found stored in persisted config, so it cannot represent
+  // kFIRAnalyticsEnabledStateSetNo. It must represent kFIRAnalyticsEnabledStateNotSet.
+  kFIRAnalyticsEnabledStateNotSet = 0,
+  kFIRAnalyticsEnabledStateSetYes = 1,
+  kFIRAnalyticsEnabledStateSetNo = 2,
+};
+
+/// The user defaults key for the persisted measurementEnabledState value. FIRAPersistedConfig reads
+/// measurementEnabledState using this same key.
+static NSString *const kFIRAPersistedConfigMeasurementEnabledStateKey =
+    @"/google/measurement/measurement_enabled_state";
+
+static NSString *const kFIRAnalyticsConfigurationSetEnabledNotification =
+    @"FIRAnalyticsConfigurationSetEnabledNotification";
+static NSString *const kFIRAnalyticsConfigurationSetMinimumSessionIntervalNotification =
+    @"FIRAnalyticsConfigurationSetMinimumSessionIntervalNotification";
+static NSString *const kFIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification =
+    @"FIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification";
+
+@interface FIRAnalyticsConfiguration (Internal)
+
+/// Sets whether analytics collection is enabled for this app on this device, and a flag to persist
+/// the value or not. The setting should not be persisted if being set by the global data collection
+/// flag.
+- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled
+                       persistSetting:(BOOL)shouldPersist;
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRAppAssociationRegistration.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRAppAssociationRegistration.h
new file mode 100644 (file)
index 0000000..3fc69c6
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+//  TODO: Remove this once Auth moves over to Core's instance registration system.
+/** @class FIRAppAssociationRegistration
+    @brief Manages object associations as a singleton-dependent: At most one object is
+        registered for any given host/key pair, and the object shall be created on-the-fly when
+        asked for.
+ */
+@interface FIRAppAssociationRegistration<ObjectType> : NSObject
+
+/** @fn registeredObjectWithHost:key:creationBlock:
+    @brief Retrieves the registered object with a particular host and key.
+    @param host The host object.
+    @param key The key to specify the registered object on the host.
+    @param creationBlock The block to return the object to be registered if not already.
+        The block is executed immediately before this method returns if it is executed at all.
+        It can also be executed multiple times across different method invocations if previous
+        execution of the block returns @c nil.
+    @return The registered object for the host/key pair, or @c nil if no object is registered
+        and @c creationBlock returns @c nil.
+    @remarks The method is thread-safe but non-reentrant in the sense that attempting to call this
+        method again within the @c creationBlock with the same host/key pair raises an exception.
+        The registered object is retained by the host.
+ */
++ (nullable ObjectType)registeredObjectWithHost:(id)host
+                                            key:(NSString *)key
+                                  creationBlock:(ObjectType _Nullable (^)(void))creationBlock;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRAppInternal.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRAppInternal.h
new file mode 100644 (file)
index 0000000..f9fc539
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRApp.h"
+#import "FIRErrors.h"
+
+@class FIRComponentContainer;
+@protocol FIRCoreConfigurable;
+
+/**
+ * The internal interface to FIRApp. This is meant for first-party integrators, who need to receive
+ * FIRApp notifications, log info about the success or failure of their configuration, and access
+ * other internal functionality of FIRApp.
+ *
+ * TODO(b/28296561): Restructure this header.
+ */
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSInteger, FIRConfigType) {
+  FIRConfigTypeCore = 1,
+  FIRConfigTypeSDK = 2,
+};
+
+/**
+ * Names of services provided by Firebase.
+ */
+extern NSString *const kFIRServiceAdMob;
+extern NSString *const kFIRServiceAuth;
+extern NSString *const kFIRServiceAuthUI;
+extern NSString *const kFIRServiceCrash;
+extern NSString *const kFIRServiceDatabase;
+extern NSString *const kFIRServiceDynamicLinks;
+extern NSString *const kFIRServiceInstanceID;
+extern NSString *const kFIRServiceInvites;
+extern NSString *const kFIRServiceMessaging;
+extern NSString *const kFIRServiceMeasurement;
+extern NSString *const kFIRServiceRemoteConfig;
+extern NSString *const kFIRServiceStorage;
+
+/**
+ * Names of services provided by the Google pod, but logged by the Firebase pod.
+ */
+extern NSString *const kGGLServiceAnalytics;
+extern NSString *const kGGLServiceSignIn;
+
+extern NSString *const kFIRDefaultAppName;
+extern NSString *const kFIRAppReadyToConfigureSDKNotification;
+extern NSString *const kFIRAppDeleteNotification;
+extern NSString *const kFIRAppIsDefaultAppKey;
+extern NSString *const kFIRAppNameKey;
+extern NSString *const kFIRGoogleAppIDKey;
+
+/**
+ * The format string for the User Defaults key used for storing the data collection enabled flag.
+ * This includes formatting to append the Firebase App's name.
+ */
+extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat;
+
+/**
+ * The plist key used for storing the data collection enabled flag.
+ */
+extern NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey;
+
+/**
+ * A notification fired containing diagnostic information when SDK errors occur.
+ */
+extern NSString *const kFIRAppDiagnosticsNotification;
+
+/** @var FIRAuthStateDidChangeInternalNotification
+ @brief The name of the @c NSNotificationCenter notification which is posted when the auth state
+ changes (e.g. a new token has been produced, a user logs in or out). The object parameter of
+ the notification is a dictionary possibly containing the key:
+ @c FIRAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not
+ contain this key it indicates a sign-out event took place.
+ */
+extern NSString *const FIRAuthStateDidChangeInternalNotification;
+
+/** @var FIRAuthStateDidChangeInternalNotificationTokenKey
+ @brief A key present in the dictionary object parameter of the
+ @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
+ key will contain the new access token.
+ */
+extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey;
+
+/** @var FIRAuthStateDidChangeInternalNotificationAppKey
+ @brief A key present in the dictionary object parameter of the
+ @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
+ key will contain the FIRApp associated with the auth instance.
+ */
+extern NSString *const FIRAuthStateDidChangeInternalNotificationAppKey;
+
+/** @var FIRAuthStateDidChangeInternalNotificationUIDKey
+ @brief A key present in the dictionary object parameter of the
+ @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
+ key will contain the new user's UID (or nil if there is no longer a user signed in).
+ */
+extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
+
+/** @typedef FIRTokenCallback
+    @brief The type of block which gets called when a token is ready.
+ */
+typedef void (^FIRTokenCallback)(NSString *_Nullable token, NSError *_Nullable error);
+
+/** @typedef FIRAppGetTokenImplementation
+    @brief The type of block which can provide an implementation for the @c getTokenWithCallback:
+        method.
+    @param forceRefresh Forces the token to be refreshed.
+    @param callback The block which should be invoked when the async call completes.
+ */
+typedef void (^FIRAppGetTokenImplementation)(BOOL forceRefresh, FIRTokenCallback callback);
+
+/** @typedef FIRAppGetUID
+    @brief The type of block which can provide an implementation for the @c getUID method.
+ */
+typedef NSString *_Nullable (^FIRAppGetUIDImplementation)(void);
+
+@interface FIRApp ()
+
+/**
+ * A flag indicating if this is the default app.
+ */
+@property(nonatomic, readonly) BOOL isDefaultApp;
+
+/** @property getTokenImplementation
+    @brief Gets or sets the block to use for the implementation of
+        @c getTokenForcingRefresh:withCallback:
+ */
+@property(nonatomic, copy) FIRAppGetTokenImplementation getTokenImplementation;
+
+/** @property getUIDImplementation
+    @brief Gets or sets the block to use for the implementation of @c getUID.
+ */
+@property(nonatomic, copy) FIRAppGetUIDImplementation getUIDImplementation;
+
+/*
+ * The container of interop SDKs for this app.
+ */
+@property(nonatomic) FIRComponentContainer *container;
+
+/**
+ * Creates an error for failing to configure a subspec service. This method is called by each
+ * FIRApp notification listener.
+ */
++ (NSError *)errorForSubspecConfigurationFailureWithDomain:(NSString *)domain
+                                                 errorCode:(FIRErrorCode)code
+                                                   service:(NSString *)service
+                                                    reason:(NSString *)reason;
+/**
+ * Checks if the default app is configured without trying to configure it.
+ */
++ (BOOL)isDefaultAppConfigured;
+
+/**
+ * Register a class that conforms to `FIRCoreConfigurable`. Each SDK should have one class that
+ * registers in order to provide critical information for interoperability and lifecycle events.
+ * TODO(wilsonryan): Write more documentation.
+ */
++ (void)registerAsConfigurable:(Class<FIRCoreConfigurable>)klass;
+
+/**
+ * Registers a given third-party library with the given version number to be reported for
+ * analyitcs.
+ *
+ * @param library Name of the library
+ * @param version Version of the library
+ */
+// clang-format off
++ (void)registerLibrary:(NSString *)library
+            withVersion:(NSString *)version NS_SWIFT_NAME(registerLibrary(_:version:));
+// clang-format on
+
+/**
+ * A concatenated string representing all the third-party libraries and version numbers.
+ */
++ (NSString *)firebaseUserAgent;
+
+/**
+ * Used by each SDK to send logs about SDK configuration status to Clearcut.
+ */
+- (void)sendLogsWithServiceName:(NSString *)serviceName
+                        version:(NSString *)version
+                          error:(NSError *)error;
+
+/**
+ * Can be used by the unit tests in eack SDK to reset FIRApp. This method is thread unsafe.
+ */
++ (void)resetApps;
+
+/**
+ * Can be used by the unit tests in each SDK to set customized options.
+ */
+- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options;
+
+/** @fn getTokenForcingRefresh:withCallback:
+    @brief Retrieves the Firebase authentication token, possibly refreshing it.
+    @param forceRefresh Forces a token refresh. Useful if the token becomes invalid for some reason
+        other than an expiration.
+    @param callback The block to invoke when the token is available.
+ */
+- (void)getTokenForcingRefresh:(BOOL)forceRefresh withCallback:(FIRTokenCallback)callback;
+
+/**
+ * Expose the UID of the current user for Firestore.
+ */
+- (nullable NSString *)getUID;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRBundleUtil.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRBundleUtil.h
new file mode 100644 (file)
index 0000000..c458a2c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+/**
+ * This class provides utilities for accessing resources in bundles.
+ */
+@interface FIRBundleUtil : NSObject
+
+/**
+ * Finds all relevant bundles, starting with [NSBundle mainBundle].
+ */
++ (NSArray *)relevantBundles;
+
+/**
+ * Reads the options dictionary from one of the provided bundles.
+ *
+ * @param resourceName The resource name, e.g. @"GoogleService-Info".
+ * @param fileType The file type (extension), e.g. @"plist".
+ * @param bundles The bundles to expect, in priority order. See also
+ * +[FIRBundleUtil relevantBundles].
+ */
++ (NSString *)optionsDictionaryPathWithResourceName:(NSString *)resourceName
+                                        andFileType:(NSString *)fileType
+                                          inBundles:(NSArray *)bundles;
+
+/**
+ * Finds URL schemes defined in all relevant bundles, starting with those from
+ * [NSBundle mainBundle].
+ */
++ (NSArray *)relevantURLSchemes;
+
+/**
+ * Checks if the bundle identifier exists in the given bundles.
+ */
++ (BOOL)hasBundleIdentifier:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles;
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponent.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponent.h
new file mode 100644 (file)
index 0000000..cb51ee7
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@class FIRApp;
+@class FIRComponentContainer;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// Provides a system to clean up cached instances returned from the component system.
+NS_SWIFT_NAME(ComponentLifecycleMaintainer)
+@protocol FIRComponentLifecycleMaintainer
+/// The associated app will be deleted, clean up any resources as they are about to be deallocated.
+- (void)appWillBeDeleted:(FIRApp *)app;
+@end
+
+typedef _Nullable id (^FIRComponentCreationBlock)(FIRComponentContainer *container,
+                                                  BOOL *isCacheable)
+    NS_SWIFT_NAME(ComponentCreationBlock);
+
+@class FIRDependency;
+
+/// Describes the timing of instantiation. Note: new components should default to lazy unless there
+/// is a strong reason to be eager.
+typedef NS_ENUM(NSInteger, FIRInstantiationTiming) {
+  FIRInstantiationTimingLazy,
+  FIRInstantiationTimingAlwaysEager,
+  FIRInstantiationTimingEagerInDefaultApp
+} NS_SWIFT_NAME(InstantiationTiming);
+
+/// A component that can be used from other Firebase SDKs.
+NS_SWIFT_NAME(Component)
+@interface FIRComponent : NSObject
+
+/// The protocol describing functionality provided from the Component.
+@property(nonatomic, strong, readonly) Protocol *protocol;
+
+/// The timing of instantiation.
+@property(nonatomic, readonly) FIRInstantiationTiming instantiationTiming;
+
+/// An array of dependencies for the component.
+@property(nonatomic, copy, readonly) NSArray<FIRDependency *> *dependencies;
+
+/// A block to instantiate an instance of the component with the appropriate dependencies.
+@property(nonatomic, copy, readonly) FIRComponentCreationBlock creationBlock;
+
+// There's an issue with long NS_SWIFT_NAMES that causes compilation to fail, disable clang-format
+// for the next two methods.
+// clang-format off
+
+/// Creates a component with no dependencies that will be lazily initialized.
++ (instancetype)componentWithProtocol:(Protocol *)protocol
+                        creationBlock:(FIRComponentCreationBlock)creationBlock
+NS_SWIFT_NAME(init(_:creationBlock:));
+
+/// Creates a component to be registered with the component container.
+///
+/// @param protocol - The protocol describing functionality provided by the component.
+/// @param instantiationTiming - When the component should be initialized. Use .lazy unless there's
+///                              a good reason to be instantiated earlier.
+/// @param dependencies - Any dependencies the `implementingClass` has, optional or required.
+/// @param creationBlock - A block to instantiate the component with a container, and if
+/// @return A component that can be registered with the component container.
++ (instancetype)componentWithProtocol:(Protocol *)protocol
+                  instantiationTiming:(FIRInstantiationTiming)instantiationTiming
+                         dependencies:(NSArray<FIRDependency *> *)dependencies
+                        creationBlock:(FIRComponentCreationBlock)creationBlock
+NS_SWIFT_NAME(init(_:instantiationTiming:dependencies:creationBlock:));
+
+// clang-format on
+
+/// Unavailable.
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentContainer.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentContainer.h
new file mode 100644 (file)
index 0000000..10e2255
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#import "FIRComponentType.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// A type-safe macro to retrieve a component from a container. This should be used to retrieve
+/// components instead of using the container directly.
+#define FIR_COMPONENT(type, container) \
+  [FIRComponentType<id<type>> instanceForProtocol:@protocol(type) inContainer:container]
+
+@class FIRApp;
+
+/// A container that holds different components that are registered via the
+/// `registerAsComponentRegistrant:` call. These classes should conform to `FIRComponentRegistrant`
+/// in order to properly register components for Core.
+NS_SWIFT_NAME(FirebaseComponentContainer)
+@interface FIRComponentContainer : NSObject
+
+/// A weak reference to the app that an instance of the container belongs to.
+@property(nonatomic, weak, readonly) FIRApp *app;
+
+/// Unavailable. Use the `container` property on `FIRApp`.
+- (instancetype)init NS_UNAVAILABLE;
+
+/// Register a class to provide components for the interoperability system. The class should conform
+/// to `FIRComponentRegistrant` and provide an array of `FIRComponent` objects.
++ (void)registerAsComponentRegistrant:(Class)klass;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentContainerInternal.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentContainerInternal.h
new file mode 100644 (file)
index 0000000..bb73e7b
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#import "FIRComponent.h"
+#import "FIRComponentContainer.h"
+
+@class FIRApp;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRComponentContainer (Private)
+
+/// Initializes a contain for a given app. This should only be called by the app itself.
+- (instancetype)initWithApp:(FIRApp *)app;
+
+/// Retrieves an instance that conforms to the specified protocol. This will return `nil` if the
+/// protocol wasn't registered, or if the instance couldn't instantiate for the provided app.
+- (nullable id)instanceForProtocol:(Protocol *)protocol NS_SWIFT_NAME(instance(for:));
+
+/// Remove all of the cached instances stored and allow them to clean up after themselves.
+- (void)removeAllCachedInstances;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentRegistrant.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentRegistrant.h
new file mode 100644 (file)
index 0000000..ad2cad2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 FIRComponentRegistrant_h
+#define FIRComponentRegistrant_h
+
+#import <Foundation/Foundation.h>
+
+@class FIRComponent;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// Describes functionality for SDKs registering components in the `FIRComponentContainer`.
+NS_SWIFT_NAME(ComponentRegistrant)
+@protocol FIRComponentRegistrant
+
+/// Returns one or more FIRComponents that will be registered in
+/// FIRApp and participate in dependency resolution and injection.
++ (NSArray<FIRComponent *> *)componentsToRegister;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif /* FIRComponentRegistrant_h */
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentType.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRComponentType.h
new file mode 100644 (file)
index 0000000..6f2aca7
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@class FIRComponentContainer;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// Do not use directly. A placeholder type in order to provide a macro that will warn users of
+/// mis-matched protocols.
+NS_SWIFT_NAME(ComponentType)
+@interface FIRComponentType<__covariant T> : NSObject
+
+/// Do not use directly. A factory method to retrieve an instance that provides a specific
+/// functionality.
++ (T)instanceForProtocol:(Protocol *)protocol inContainer:(FIRComponentContainer *)container;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRCoreConfigurable.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRCoreConfigurable.h
new file mode 100644 (file)
index 0000000..6c2b077
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 FIRCoreConfigurable_h
+#define FIRCoreConfigurable_h
+
+#import <Foundation/Foundation.h>
+
+@class FIRApp;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// Provides an interface to set up an SDK once a `FIRApp` is configured.
+NS_SWIFT_NAME(CoreConfigurable)
+@protocol FIRCoreConfigurable
+
+/// Configure the SDK if needed ahead of time. This method is called when the developer calls
+/// `FirebaseApp.configure()`.
++ (void)configureWithApp:(FIRApp *)app;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif /* FIRCoreConfigurable_h */
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRDependency.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRDependency.h
new file mode 100644 (file)
index 0000000..46e9b7e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// A dependency on a specific protocol's functionality.
+NS_SWIFT_NAME(Dependency)
+@interface FIRDependency : NSObject
+
+/// The protocol describing functionality being depended on.
+@property(nonatomic, strong, readonly) Protocol *protocol;
+
+/// A flag to specify if the dependency is required or not.
+@property(nonatomic, readonly) BOOL isRequired;
+
+/// Initializes a dependency that is required. Calls `initWithProtocol:isRequired` with `YES` for
+/// the required parameter.
+/// Creates a required dependency on the specified protocol's functionality.
++ (instancetype)dependencyWithProtocol:(Protocol *)protocol;
+
+/// Creates a dependency on the specified protocol's functionality and specify if it's required for
+/// the class's functionality.
++ (instancetype)dependencyWithProtocol:(Protocol *)protocol isRequired:(BOOL)required;
+
+/// Use `dependencyWithProtocol:isRequired:` instead.
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRErrorCode.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRErrorCode.h
new file mode 100644 (file)
index 0000000..01d3c56
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** Error codes in Firebase error domain. */
+typedef NS_ENUM(NSInteger, FIRErrorCode) {
+  /**
+   * Unknown error.
+   */
+  FIRErrorCodeUnknown = 0,
+  /**
+   * 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.
+   */
+  FIRErrorCodeInvalidPlistFile = -100,
+
+  /**
+   * Validating the Google App ID format failed.
+   */
+  FIRErrorCodeInvalidAppID = -101,
+
+  /**
+   * Error code for failing to configure a specific service.
+   */
+  FIRErrorCodeAdMobFailed = -110,
+  FIRErrorCodeAppInviteFailed = -112,
+  FIRErrorCodeCloudMessagingFailed = -113,
+  FIRErrorCodeConfigFailed = -114,
+  FIRErrorCodeDatabaseFailed = -115,
+  FIRErrorCodeCrashReportingFailed = -118,
+  FIRErrorCodeDurableDeepLinkFailed = -119,
+  FIRErrorCodeAuthFailed = -120,
+  FIRErrorCodeInstanceIDFailed = -121,
+  FIRErrorCodeStorageFailed = -123,
+
+  /**
+   * Error codes returned by Dynamic Links
+   */
+  FIRErrorCodeDynamicLinksStrongMatchNotAvailable = -124,
+  FIRErrorCodeDynamicLinksManualRetrievalNotEnabled = -125,
+  FIRErrorCodeDynamicLinksPendingLinkOnlyAvailableAtFirstLaunch = -126,
+  FIRErrorCodeDynamicLinksPendingLinkRetrievalAlreadyRunning = -127,
+};
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRErrors.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRErrors.h
new file mode 100644 (file)
index 0000000..cf69252
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#include "FIRErrorCode.h"
+
+extern NSString *const kFirebaseErrorDomain;
+extern NSString *const kFirebaseAdMobErrorDomain;
+extern NSString *const kFirebaseAppInviteErrorDomain;
+extern NSString *const kFirebaseAuthErrorDomain;
+extern NSString *const kFirebaseCloudMessagingErrorDomain;
+extern NSString *const kFirebaseConfigErrorDomain;
+extern NSString *const kFirebaseCoreErrorDomain;
+extern NSString *const kFirebaseCrashReportingErrorDomain;
+extern NSString *const kFirebaseDatabaseErrorDomain;
+extern NSString *const kFirebaseDurableDeepLinkErrorDomain;
+extern NSString *const kFirebaseInstanceIDErrorDomain;
+extern NSString *const kFirebasePerfErrorDomain;
+extern NSString *const kFirebaseStorageErrorDomain;
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRLogger.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRLogger.h
new file mode 100644 (file)
index 0000000..a538199
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#import "FIRLoggerLevel.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * The Firebase services used in Firebase logger.
+ */
+typedef NSString *const FIRLoggerService;
+
+extern FIRLoggerService kFIRLoggerABTesting;
+extern FIRLoggerService kFIRLoggerAdMob;
+extern FIRLoggerService kFIRLoggerAnalytics;
+extern FIRLoggerService kFIRLoggerAuth;
+extern FIRLoggerService kFIRLoggerCore;
+extern FIRLoggerService kFIRLoggerCrash;
+extern FIRLoggerService kFIRLoggerDatabase;
+extern FIRLoggerService kFIRLoggerDynamicLinks;
+extern FIRLoggerService kFIRLoggerFirestore;
+extern FIRLoggerService kFIRLoggerInstanceID;
+extern FIRLoggerService kFIRLoggerInvites;
+extern FIRLoggerService kFIRLoggerMLKit;
+extern FIRLoggerService kFIRLoggerMessaging;
+extern FIRLoggerService kFIRLoggerPerf;
+extern FIRLoggerService kFIRLoggerRemoteConfig;
+extern FIRLoggerService kFIRLoggerStorage;
+extern FIRLoggerService kFIRLoggerSwizzler;
+
+/**
+ * The key used to store the logger's error count.
+ */
+extern NSString *const kFIRLoggerErrorCountKey;
+
+/**
+ * The key used to store the logger's warning count.
+ */
+extern NSString *const kFIRLoggerWarningCountKey;
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+/**
+ * Enables or disables Analytics debug mode.
+ * If set to YES, the logging level for Analytics will be set to FIRLoggerLevelDebug.
+ * Enabling the debug mode has no effect if the app is running from App Store.
+ * (required) analytics debug mode flag.
+ */
+void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode);
+
+/**
+ * Changes the default logging level of FIRLoggerLevelNotice to a user-specified level.
+ * The default level cannot be set above FIRLoggerLevelNotice if the app is running from App Store.
+ * (required) log level (one of the FIRLoggerLevel enum values).
+ */
+void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
+
+/**
+ * Checks if the specified logger level is loggable given the current settings.
+ * (required) log level (one of the FIRLoggerLevel enum values).
+ * (required) whether or not this function is called from the Analytics component.
+ */
+BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel, BOOL analyticsComponent);
+
+/**
+ * Logs a message to the Xcode console and the device log. If running from AppStore, will
+ * not log any messages with a level higher than FIRLoggerLevelNotice to avoid log spamming.
+ * (required) log level (one of the FIRLoggerLevel enum values).
+ * (required) service name of type FIRLoggerService.
+ * (required) message code starting with "I-" which means iOS, followed by a capitalized
+ *            three-character service identifier and a six digit integer message ID that is unique
+ *            within the service.
+ *            An example of the message code is @"I-COR000001".
+ * (required) message string which can be a format string.
+ * (optional) variable arguments list obtained from calling va_start, used when message is a format
+ *            string.
+ */
+extern void FIRLogBasic(FIRLoggerLevel level,
+                        FIRLoggerService service,
+                        NSString *messageCode,
+                        NSString *message,
+// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable
+// See: http://stackoverflow.com/q/29095469
+#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX
+                        va_list args_ptr
+#else
+                        va_list _Nullable args_ptr
+#endif
+);
+
+/**
+ * The following functions accept the following parameters in order:
+ * (required) service name of type FIRLoggerService.
+ * (required) message code starting from "I-" which means iOS, followed by a capitalized
+ *            three-character service identifier and a six digit integer message ID that is unique
+ *            within the service.
+ *            An example of the message code is @"I-COR000001".
+ *            See go/firebase-log-proposal for details.
+ * (required) message string which can be a format string.
+ * (optional) the list of arguments to substitute into the format string.
+ * Example usage:
+ * FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
+ */
+extern void FIRLogError(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
+    NS_FORMAT_FUNCTION(3, 4);
+extern void FIRLogWarning(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
+    NS_FORMAT_FUNCTION(3, 4);
+extern void FIRLogNotice(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
+    NS_FORMAT_FUNCTION(3, 4);
+extern void FIRLogInfo(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
+    NS_FORMAT_FUNCTION(3, 4);
+extern void FIRLogDebug(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
+    NS_FORMAT_FUNCTION(3, 4);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+@interface FIRLoggerWrapper : NSObject
+
+/**
+ * Objective-C wrapper for FIRLogBasic to allow weak linking to FIRLogger
+ * (required) log level (one of the FIRLoggerLevel enum values).
+ * (required) service name of type FIRLoggerService.
+ * (required) message code starting with "I-" which means iOS, followed by a capitalized
+ *            three-character service identifier and a six digit integer message ID that is unique
+ *            within the service.
+ *            An example of the message code is @"I-COR000001".
+ * (required) message string which can be a format string.
+ * (optional) variable arguments list obtained from calling va_start, used when message is a format
+ *            string.
+ */
+
++ (void)logWithLevel:(FIRLoggerLevel)level
+         withService:(FIRLoggerService)service
+            withCode:(NSString *)messageCode
+         withMessage:(NSString *)message
+            withArgs:(va_list)args;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIROptionsInternal.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIROptionsInternal.h
new file mode 100644 (file)
index 0000000..7bb40fc
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIROptions.h"
+
+/**
+ * Keys for the strings in the plist file.
+ */
+extern NSString *const kFIRAPIKey;
+extern NSString *const kFIRTrackingID;
+extern NSString *const kFIRGoogleAppID;
+extern NSString *const kFIRClientID;
+extern NSString *const kFIRGCMSenderID;
+extern NSString *const kFIRAndroidClientID;
+extern NSString *const kFIRDatabaseURL;
+extern NSString *const kFIRStorageBucket;
+extern NSString *const kFIRBundleID;
+extern NSString *const kFIRProjectID;
+
+/**
+ * Keys for the plist file name
+ */
+extern NSString *const kServiceInfoFileName;
+
+extern NSString *const kServiceInfoFileType;
+
+/**
+ * This header file exposes the initialization of FIROptions to internal use.
+ */
+@interface FIROptions ()
+
+/**
+ * resetDefaultOptions and initInternalWithOptionsDictionary: are exposed only for unit tests.
+ */
++ (void)resetDefaultOptions;
+
+/**
+ * Initializes the options with dictionary. The above strings are the keys of the dictionary.
+ * This is the designated initializer.
+ */
+- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary;
+
+/**
+ * defaultOptions and defaultOptionsDictionary are exposed in order to be used in FIRApp and
+ * other first party services.
+ */
++ (FIROptions *)defaultOptions;
+
++ (NSDictionary *)defaultOptionsDictionary;
+
+/**
+ * Indicates whether or not Analytics collection was explicitly enabled via a plist flag or at
+ * runtime.
+ */
+@property(nonatomic, readonly) BOOL isAnalyticsCollectionExpicitlySet;
+
+/**
+ * Whether or not Analytics Collection was enabled. Analytics Collection is enabled unless
+ * explicitly disabled in GoogleService-Info.plist.
+ */
+@property(nonatomic, readonly) BOOL isAnalyticsCollectionEnabled;
+
+/**
+ * Whether or not Analytics Collection was completely disabled. If YES, then
+ * isAnalyticsCollectionEnabled will be NO.
+ */
+@property(nonatomic, readonly) BOOL isAnalyticsCollectionDeactivated;
+
+/**
+ * The version ID of the client library, e.g. @"1100000".
+ */
+@property(nonatomic, readonly, copy) NSString *libraryVersionID;
+
+/**
+ * The flag indicating whether this object was constructed with the values in the default plist
+ * file.
+ */
+@property(nonatomic) BOOL usingOptionsFromDefaultPlist;
+
+/**
+ * Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
+ * GoogleService-Info.plist.
+ */
+@property(nonatomic, readonly) BOOL isMeasurementEnabled;
+
+/**
+ * Whether or not Analytics was enabled in the developer console.
+ */
+@property(nonatomic, readonly) BOOL isAnalyticsEnabled;
+
+/**
+ * Whether or not SignIn was enabled in the developer console.
+ */
+@property(nonatomic, readonly) BOOL isSignInEnabled;
+
+/**
+ * Whether or not editing is locked. This should occur after FIROptions has been set on a FIRApp.
+ */
+@property(nonatomic, getter=isEditingLocked) BOOL editingLocked;
+
+@end
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRVersion.h b/iOS/Pods/FirebaseCore/Firebase/Core/Private/FIRVersion.h
new file mode 100644 (file)
index 0000000..226efb1
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+/** The version of the Firebase SDK. */
+FOUNDATION_EXPORT const char *const FIRVersionString;
+
+/** The version of the FirebaseCore Component. */
+FOUNDATION_EXPORT const char *const FIRCoreVersionString;
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRAnalyticsConfiguration.h b/iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRAnalyticsConfiguration.h
new file mode 100644 (file)
index 0000000..ca1d32c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This class provides configuration fields for Firebase Analytics.
+ */
+NS_SWIFT_NAME(AnalyticsConfiguration)
+@interface FIRAnalyticsConfiguration : NSObject
+
+/**
+ * Returns the shared instance of FIRAnalyticsConfiguration.
+ */
++ (FIRAnalyticsConfiguration *)sharedInstance NS_SWIFT_NAME(shared());
+
+/**
+ * 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;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRApp.h b/iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRApp.h
new file mode 100644 (file)
index 0000000..e0dd6d6
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@class FIROptions;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** A block that takes a BOOL and has no return value. */
+typedef void (^FIRAppVoidBoolCallback)(BOOL success) NS_SWIFT_NAME(FirebaseAppVoidBoolCallback);
+
+/**
+ * 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.
+ *
+ * It is also possible to change the default logging level in code by calling setLoggerLevel: on
+ * the FIRConfiguration interface.
+ */
+NS_SWIFT_NAME(FirebaseApp)
+@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 and contains synchronous file I/O
+ * (reading GoogleService-Info.plist from disk).
+ */
++ (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 NS_SWIFT_NAME(configure(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.
+ */
+// clang-format off
++ (void)configureWithName:(NSString *)name
+                  options:(FIROptions *)options NS_SWIFT_NAME(configure(name:options:));
+// clang-format on
+
+/**
+ * Returns the default app, or nil if the default app does not exist.
+ */
++ (nullable FIRApp *)defaultApp NS_SWIFT_NAME(app());
+
+/**
+ * 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 NS_SWIFT_NAME(app(name:));
+
+/**
+ * Returns the set of all extant FIRApp instances, or nil if there are no FIRApp instances. This
+ * method is thread safe.
+ */
+@property(class, readonly, nullable) NSDictionary<NSString *, FIRApp *> *allApps;
+
+/**
+ * Cleans up the current FIRApp, freeing associated data and returning its name to the pool for
+ * future use. This method is thread safe.
+ */
+- (void)deleteApp:(FIRAppVoidBoolCallback)completion;
+
+/**
+ * FIRApp instances should not be initialized directly. Call +[FIRApp configure],
+ * +[FIRApp configureWithOptions:], or +[FIRApp configureWithNames:options:] directly.
+ */
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ * Gets the name of this app.
+ */
+@property(nonatomic, copy, readonly) NSString *name;
+
+/**
+ * Gets a copy of the options for this app. These are non-modifiable.
+ */
+@property(nonatomic, copy, readonly) FIROptions *options;
+
+/**
+ * Gets or sets whether automatic data collection is enabled for all products. Defaults to `YES`
+ * unless `FirebaseDataCollectionDefaultEnabled` is set to `NO` in your app's Info.plist. This value
+ * is persisted across runs of the app so that it can be set once when users have consented to
+ * collection.
+ */
+@property(nonatomic, readwrite, getter=isDataCollectionDefaultEnabled)
+    BOOL dataCollectionDefaultEnabled;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRConfiguration.h b/iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRConfiguration.h
new file mode 100644 (file)
index 0000000..95bba5e
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#import "FIRAnalyticsConfiguration.h"
+#import "FIRLoggerLevel.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This interface provides global level properties that the developer can tweak, and the singleton
+ * of the Firebase Analytics configuration class.
+ */
+NS_SWIFT_NAME(FirebaseConfiguration)
+@interface FIRConfiguration : NSObject
+
+/** Returns the shared configuration object. */
+@property(class, nonatomic, readonly) FIRConfiguration *sharedInstance NS_SWIFT_NAME(shared);
+
+/** The configuration class for Firebase Analytics. */
+@property(nonatomic, readwrite) FIRAnalyticsConfiguration *analyticsConfiguration;
+
+/**
+ * Sets the logging level for internal Firebase logging. Firebase will only log messages
+ * that are logged at or below loggerLevel. The messages are logged both to the Xcode
+ * console and to the device's log. Note that if an app is running from AppStore, it will
+ * never log above FIRLoggerLevelNotice even if loggerLevel is set to a higher (more verbose)
+ * setting.
+ *
+ * @param loggerLevel The maximum logging level. The default level is set to FIRLoggerLevelNotice.
+ */
+- (void)setLoggerLevel:(FIRLoggerLevel)loggerLevel;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRLoggerLevel.h b/iOS/Pods/FirebaseCore/Firebase/Core/Public/FIRLoggerLevel.h
new file mode 100644 (file)
index 0000000..dca3aa0
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Note that importing GULLoggerLevel.h will lead to a non-modular header
+// import error.
+
+/**
+ * The log levels used by internal logging.
+ */
+typedef NS_ENUM(NSInteger, FIRLoggerLevel) {
+  /** Error level, matches ASL_LEVEL_ERR. */
+  FIRLoggerLevelError = 3,
+  /** Warning level, matches ASL_LEVEL_WARNING. */
+  FIRLoggerLevelWarning = 4,
+  /** Notice level, matches ASL_LEVEL_NOTICE. */
+  FIRLoggerLevelNotice = 5,
+  /** Info level, matches ASL_LEVEL_INFO. */
+  FIRLoggerLevelInfo = 6,
+  /** Debug level, matches ASL_LEVEL_DEBUG. */
+  FIRLoggerLevelDebug = 7,
+  /** Minimum log level. */
+  FIRLoggerLevelMin = FIRLoggerLevelError,
+  /** Maximum log level. */
+  FIRLoggerLevelMax = FIRLoggerLevelDebug
+} NS_SWIFT_NAME(FirebaseLoggerLevel);
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Public/FIROptions.h b/iOS/Pods/FirebaseCore/Firebase/Core/Public/FIROptions.h
new file mode 100644 (file)
index 0000000..87a01dd
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This class provides constant fields of Google APIs.
+ */
+NS_SWIFT_NAME(FirebaseOptions)
+@interface FIROptions : NSObject <NSCopying>
+
+/**
+ * Returns the default options. The first time this is called it synchronously reads
+ * GoogleService-Info.plist from disk.
+ */
++ (nullable FIROptions *)defaultOptions NS_SWIFT_NAME(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, copy, nullable) NSString *APIKey NS_SWIFT_NAME(apiKey);
+
+/**
+ * The bundle ID for the application. Defaults to `[[NSBundle mainBundle] bundleID]` when not set
+ * manually or in a plist.
+ */
+@property(nonatomic, copy) NSString *bundleID;
+
+/**
+ * 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, copy, nullable) NSString *clientID;
+
+/**
+ * The tracking ID for Google Analytics, e.g. @"UA-12345678-1", used to configure Google Analytics.
+ */
+@property(nonatomic, copy, nullable) NSString *trackingID;
+
+/**
+ * The Project Number from the Google Developer's console, for example @"012345678901", used to
+ * configure Google Cloud Messaging.
+ */
+@property(nonatomic, copy) NSString *GCMSenderID NS_SWIFT_NAME(gcmSenderID);
+
+/**
+ * The Project ID from the Firebase console, for example @"abc-xyz-123".
+ */
+@property(nonatomic, copy, nullable) NSString *projectID;
+
+/**
+ * The Android client ID used in Google AppInvite when an iOS app has its Android version, for
+ * example @"12345.apps.googleusercontent.com".
+ */
+@property(nonatomic, copy, nullable) NSString *androidClientID;
+
+/**
+ * The Google App ID that is used to uniquely identify an instance of an app.
+ */
+@property(nonatomic, copy) NSString *googleAppID;
+
+/**
+ * The database root URL, e.g. @"http://abc-xyz-123.firebaseio.com".
+ */
+@property(nonatomic, copy, nullable) NSString *databaseURL;
+
+/**
+ * The URL scheme used to set up Durable Deep Link service.
+ */
+@property(nonatomic, copy, nullable) NSString *deepLinkURLScheme;
+
+/**
+ * The Google Cloud Storage bucket name, e.g. @"abc-xyz-123.storage.firebase.com".
+ */
+@property(nonatomic, copy, nullable) NSString *storageBucket;
+
+/**
+ * Initializes a customized instance of FIROptions from the file at the given plist file path. This
+ * will read the file synchronously from disk.
+ * 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.
+ */
+- (nullable instancetype)initWithContentsOfFile:(NSString *)plistPath;
+
+/**
+ * Initializes a customized instance of FIROptions with required fields. Use the mutable properties
+ * to modify fields for configuring specific services.
+ */
+// clang-format off
+- (instancetype)initWithGoogleAppID:(NSString *)googleAppID
+                        GCMSenderID:(NSString *)GCMSenderID
+    NS_SWIFT_NAME(init(googleAppID:gcmSenderID:));
+// clang-format on
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseCore/Firebase/Core/Public/FirebaseCore.h b/iOS/Pods/FirebaseCore/Firebase/Core/Public/FirebaseCore.h
new file mode 100644 (file)
index 0000000..fa26f69
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRAnalyticsConfiguration.h"
+#import "FIRApp.h"
+#import "FIRConfiguration.h"
+#import "FIRLoggerLevel.h"
+#import "FIROptions.h"
diff --git a/iOS/Pods/FirebaseCore/LICENSE b/iOS/Pods/FirebaseCore/LICENSE
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -0,0 +1,202 @@
+
+                                 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/iOS/Pods/FirebaseCore/README.md b/iOS/Pods/FirebaseCore/README.md
new file mode 100644 (file)
index 0000000..eb6ea33
--- /dev/null
@@ -0,0 +1,180 @@
+# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
+
+This repository contains a subset of the Firebase iOS SDK source. It currently
+includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
+FirebaseFunctions, FirebaseInAppMessagingDisplay, FirebaseMessaging and
+FirebaseStorage.
+
+The repository also includes GoogleUtilities source. The
+[GoogleUtilities](GoogleUtilities/README.md) pod is
+a set of utilities used by Firebase and other Google products.
+
+Firebase is an app development platform with tools to help you build, grow and
+monetize your app. More information about Firebase can be found at
+[https://firebase.google.com](https://firebase.google.com).
+
+## Installation
+
+See the three subsections for details about three different installation methods.
+1. [Standard pod install](README.md#standard-pod-install)
+1. [Installing from the GitHub repo](README.md#installing-from-github)
+1. [Experimental Carthage](README.md#carthage-ios-only)
+
+### Standard pod install
+
+Go to
+[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
+
+### Installing from GitHub
+
+For releases starting with 5.0.0, the source for each release is also deployed
+to CocoaPods master and available via standard
+[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
+
+These instructions can be used to access the Firebase repo at other branches,
+tags, or commits.
+
+#### Background
+
+See
+[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
+for instructions and options about overriding pod source locations.
+
+#### Accessing Firebase Source Snapshots
+
+All of the official releases are tagged in this repo and available via CocoaPods. To access a local
+source snapshot or unreleased branch, use Podfile directives like the following:
+
+To access FirebaseFirestore via a branch:
+```
+pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
+pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
+```
+
+To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
+
+```
+pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
+pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
+```
+
+### Carthage (iOS only)
+
+An experimental Carthage distribution is now available. See
+[Carthage](Carthage.md).
+
+## Development
+
+Follow the subsequent instructions to develop, debug, unit test, run integration
+tests, and try out reference samples:
+
+```
+$ git clone git@github.com:firebase/firebase-ios-sdk.git
+$ cd firebase-ios-sdk/Example
+$ pod update
+$ open Firebase.xcworkspace
+```
+
+Firestore and Functions have self contained Xcode projects. See
+[Firestore/README.md](Firestore/README.md) and
+[Functions/README.md](Functions/README.md).
+
+### Running Unit Tests
+
+Select a scheme and press Command-u to build a component and run its unit tests.
+
+### Running Sample Apps
+In order to run the sample apps and integration tests, you'll need valid
+`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
+files without real values, but can be replaced with real plist files. To get your own
+`GoogleService-Info.plist` files:
+
+1. Go to the [Firebase Console](https://console.firebase.google.com/)
+2. Create a new Firebase project, if you don't already have one
+3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
+identifier (e.g. `com.google.Database-Example`)
+4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
+(e.g. in [Example/Database/App/](Example/Database/App/));
+
+Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
+special Apple capabilities, and you will have to change the sample app to use a unique bundle
+identifier that you can control in your own Apple Developer account.
+
+## Specific Component Instructions
+See the sections below for any special instructions for those components.
+
+### Firebase Auth
+
+If you're doing specific Firebase Auth development, see
+[the Auth Sample README](Example/Auth/README.md) for instructions about
+building and running the FirebaseAuth pod along with various samples and tests.
+
+### Firebase Database
+
+To run the Database Integration tests, make your database authentication rules
+[public](https://firebase.google.com/docs/database/security/quickstart).
+
+### Firebase Storage
+
+To run the Storage Integration tests, follow the instructions in
+[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
+
+#### Push Notifications
+
+Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
+In order to actually test receiving push notifications, you will need to:
+
+1. Change the bundle identifier of the sample app to something you own in your Apple Developer
+account, and enable that App ID for push notifications.
+2. You'll also need to
+[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
+at **Project Settings > Cloud Messaging > [Your Firebase App]**.
+3. Ensure your iOS device is added to your Apple Developer portal as a test device.
+
+#### iOS Simulator
+
+The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
+In order to receive push notifications, you'll have to follow the steps above and run the app on a
+physical device.
+
+## Community Supported Efforts
+
+We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
+very grateful!  We'd like to empower as many developers as we can to be able to use Firebase and
+participate in the Firebase community.
+
+### macOS and tvOS
+FirebaseAuth, FirebaseCore, FirebaseDatabase and FirebaseStorage now compile, run unit tests, and
+work on macOS and tvOS, thanks to contributions from the community. There are a few tweaks needed,
+like ensuring iOS-only, macOS-only, or tvOS-only code is correctly guarded with checks for
+`TARGET_OS_IOS`, `TARGET_OS_OSX` and `TARGET_OS_TV`.
+
+For tvOS, checkout the [Sample](Example/tvOSSample).
+
+Keep in mind that macOS and tvOS are not officially supported by Firebase, and this repository is
+actively developed primarily for iOS. While we can catch basic unit test issues with Travis, there
+may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter
+this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
+
+For installation instructions, see [above](README.md#accessing-firebase-source-snapshots).
+
+Note that the Firebase pod is not available for macOS and tvOS. Install a selection of the
+`FirebaseAuth`, `FirebaseCore`, `FirebaseDatabase` and `FirebaseStorage` CocoaPods.
+
+## Roadmap
+
+See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
+plans and directions.
+
+## Contributing
+
+See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
+iOS SDK.
+
+## License
+
+The contents of this repository is licensed under the
+[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
+
+Your use of Firebase is governed by the
+[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
diff --git a/iOS/Pods/FirebaseInstanceID/CHANGELOG.md b/iOS/Pods/FirebaseInstanceID/CHANGELOG.md
new file mode 100755 (executable)
index 0000000..b2f5eac
--- /dev/null
@@ -0,0 +1,128 @@
+# 2018-09-25 -- v3.2.2
+- Fixed a crash caused by NSUserDefaults being called on background thread.
+
+# 2018-08-14 -- v3.2.1
+- Fixed an issue that checkin is not cached properly when app first started. (#1561)
+
+# 2018-07-31 -- v3.2.0
+- Added support for global Firebase data collection flag. (#1219)
+- Improved message tracking sent by server API.
+- Fixed an issue that InstanceID doesn't compile in app extensions, allowing its
+dependents like remote config to be working inside the app extensions.
+
+# 2018-06-19 -- v3.1.1
+- Ensure the checkin and tokens are refreshed if firebase project changed.
+- Fixed an issue that checkin should be turned off when FCM's autoInitEnabled flag is off.
+
+# 2018-06-12 -- v3.1.0
+- Added a new API to fetch InstanceID and Token with a completion handler. The completion handler returns a FIRInstanceIDResult with a instanceID and a token properties.
+- Deprecated the token method.
+- Added support to log a new customized label provided by developer.
+
+# 2018-05-08 -- v3.0.0
+- Removed deprecated method `setAPNSToken:type` defined in FIRInstanceID, please use `setAPNSToken:type` defined in FIRMessaging instead.
+- Removed deprecated enum `FIRInstanceIDAPNSTokenType` defined in FIRInstanceID, please use `FIRMessagingAPNSTokenType` defined in FIRMessaging instead.
+- Fixed an issue that FCM scheduled messages were not tracked successfully.
+
+# 2018-03-06 -- v2.0.10
+- Improved documentation on InstanceID usage for GDPR.
+- Improved the keypair handling during GCM to FCM migration. If you are migrating from GCM to FCM, we encourage you to update to this version and above.
+
+# 2018-02-06 -- v2.0.9
+- Improved support for language targeting for FCM service. Server updates happen more efficiently when language changes.
+- Improved support for FCM token auto generation enable/disable functions.
+
+# 2017-12-11 -- v2.0.8
+- Fixed a crash caused by a reflection call during logging.
+- Updating server with the latest parameters and deprecating old ones.
+
+# 2017-11-27 -- v2.0.7
+- Improve identity reset process, ensuring all information is reset during Identity deletion.
+
+# 2017-11-06 -- v2.0.6
+- Make token refresh weekly.
+- Fixed a crash when performing token operation.
+
+# 2017-10-11 -- v2.0.5
+- Improved support for working in shared Keychain environments.
+
+# 2017-09-26 -- v2.0.4
+- Fixed an issue where the FCM token was not associating correctly with an APNs
+  device token, depending on when the APNs device token was made available.
+- Fixed an issue where FCM tokens for different Sender IDs were not associating
+  correctly with an APNs device token.
+- Fixed an issue that was preventing the FCM direct channel from being
+  established on the first start after 24 hours of being opened.
+
+# 2017-09-13 -- v2.0.3
+- Fixed a race condition where a token was not being generated on first start,
+  if Firebase Messaging was included and the app did not register for remote
+  notifications.
+
+# 2017-08-25 -- v2.0.2
+- Fixed a startup performance regression, removing a call which was blocking the
+  main thread.
+
+# 2017-08-07 -- v2.0.1
+- Fixed issues with token and app identifier being inaccessible when the device
+  is locked.
+- Fixed a crash if bundle identifier is nil, which is possible in some testing
+  environments.
+- Fixed a small memory leak fetching a new token.
+- Moved to a new and simplified token storage system.
+- Moved to a new queuing system for token fetches and deletes.
+- Simplified logic and code around configuration and logging.
+- Added clarification about the 'apns_sandbox' parameter, in header comments.
+
+# 2017-05-08 -- v2.0.0
+- Introduced an improved interface for Swift 3 developers
+- Deprecated some methods and properties after moving their logic to the
+  Firebase Cloud Messaging SDK
+- Fixed an intermittent stability issue when a debug build of an app was
+  replaced with a release build of the same version
+- Removed swizzling logic that was sometimes resulting in developers receiving
+  a validation notice about enabling push notification capabilities, even though
+  they weren't using push notifications
+- Fixed a notification that would sometimes fire twice in quick succession
+  during the first run of an app
+
+# 2017-03-31 -- v1.0.10
+
+- Improvements to token-fetching logic
+- Fixed some warnings in Instance ID
+- Improved error messages if Instance ID couldn't be initialized properly
+- Improvements to console logging
+
+# 2017-01-31 -- v1.0.9
+
+- Removed an error being mistakenly logged to the console.
+
+# 2016-07-06 -- v1.0.8
+
+- Don't store InstanceID plists in Documents folder.
+
+# 2016-06-19 -- v1.0.7
+
+- Fix remote-notifications warning on app submission.
+
+# 2016-05-16 -- v1.0.6
+
+- Fix CocoaPod linter issues for InstanceID pod.
+
+# 2016-05-13 -- v1.0.5
+
+- Fix Authorization errors for InstanceID tokens.
+
+# 2016-05-11 -- v1.0.4
+
+- Reduce wait for InstanceID token during parallel requests.
+
+# 2016-04-18 -- v1.0.3
+
+- Change flag to disable swizzling to *FirebaseAppDelegateProxyEnabled*.
+- Fix incessant Keychain errors while accessing InstanceID.
+- Fix max retries for fetching IID token.
+
+# 2016-04-18 -- v1.0.2
+
+- Register for remote notifications on iOS8+ in the SDK itself.
diff --git a/iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/FirebaseInstanceID b/iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/FirebaseInstanceID
new file mode 100755 (executable)
index 0000000..9fbc00b
Binary files /dev/null and b/iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/FirebaseInstanceID differ
diff --git a/iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/Headers/FIRInstanceID.h b/iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/Headers/FIRInstanceID.h
new file mode 100755 (executable)
index 0000000..97777e1
--- /dev/null
@@ -0,0 +1,304 @@
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class FIRInstanceIDResult;
+/**
+ *  @memberof FIRInstanceID
+ *
+ *  The scope to be used when fetching/deleting a token for Firebase Messaging.
+ */
+FOUNDATION_EXPORT NSString *const kFIRInstanceIDScopeFirebaseMessaging
+    NS_SWIFT_NAME(InstanceIDScopeFirebaseMessaging);
+
+#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
+/**
+ *  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 const NSNotificationName kFIRInstanceIDTokenRefreshNotification
+    NS_SWIFT_NAME(InstanceIDTokenRefresh);
+#else
+/**
+ *  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 *const kFIRInstanceIDTokenRefreshNotification
+    NS_SWIFT_NAME(InstanceIDTokenRefreshNotification);
+#endif  // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
+
+/**
+ *  @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)
+    NS_SWIFT_NAME(InstanceIDTokenHandler);
+
+/**
+ *  @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 *error)
+    NS_SWIFT_NAME(InstanceIDDeleteTokenHandler);
+
+/**
+ *  @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)
+    NS_SWIFT_NAME(InstanceIDHandler);
+
+/**
+ *  @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)
+    NS_SWIFT_NAME(InstanceIDDeleteHandler);
+
+/**
+ *  @related FIRInstanceID
+ *
+ *  The completion handler invoked when the app identity and token are fetched. If the
+ *  identity wasn't created for some reason we return the appropriate error code.
+ *
+ *  @param result   The result containing an identity for the app instance and a valid token,
+ *                  nil if there was an error while creating the result.
+ *  @param error    The error if fetching the identity or token fails else nil.
+ */
+typedef void (^FIRInstanceIDResultHandler)(FIRInstanceIDResult *__nullable result,
+                                           NSError *__nullable error)
+    NS_SWIFT_NAME(InstanceIDResultHandler);
+
+/**
+ * Public errors produced by InstanceID.
+ */
+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,
+} NS_SWIFT_NAME(InstanceIDError);
+
+/**
+ * A class contains the results of InstanceID and token query.
+ */
+NS_SWIFT_NAME(InstanceIDResult)
+@interface FIRInstanceIDResult : NSObject <NSCopying>
+
+/**
+ * An instanceID uniquely identifies the app instance.
+ */
+@property(nonatomic, readonly, copy) NSString *instanceID;
+
+/*
+ * Returns a Firebase Messaging scoped token for the firebase app.
+ */
+@property(nonatomic, readonly, copy) NSString *token;
+
+@end
+
+/**
+ *  Instance ID provides a unique identifier for each app instance and a mechanism
+ *  to authenticate and authorize actions (for example, sending an FCM message).
+ *
+ *  Once an InstanceID is generated, the library periodically sends information about the
+ *  application and the device where it's running to the Firebase backend. To stop this. see
+ *  `[FIRInstanceID deleteIDWithHandler:]`.
+ *
+ *  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 via
+ *  `kFIRInstanceIDTokenRefreshNotification`.
+ *
+ *  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]`.
+ */
+NS_SWIFT_NAME(InstanceID)
+@interface FIRInstanceID : NSObject
+
+/**
+ *  FIRInstanceID.
+ *
+ *  @return A shared instance of FIRInstanceID.
+ */
++ (instancetype)instanceID NS_SWIFT_NAME(instanceID());
+
+/**
+ *  Unavailable. Use +instanceID instead.
+ */
+- (instancetype)init __attribute__((unavailable("Use +instanceID instead.")));
+
+#pragma mark - Tokens
+
+/**
+ * Returns a result of app instance identifier InstanceID and a Firebase Messaging scoped token.
+ * param handler    The callback handler invoked when an app instanceID and a default token
+ *                  are generated and returned. If instanceID and token fetching fail for some
+ *                  reason the callback is invoked with nil `result` and the appropriate error.
+ */
+- (void)instanceIDWithHandler:(FIRInstanceIDResultHandler)handler;
+
+/**
+ *  Returns a Firebase Messaging scoped token for the firebase app.
+ *
+ *  @return Returns the stored token if the device has registered with Firebase Messaging, otherwise
+ *          returns nil.
+ */
+- (nullable NSString *)token __deprecated_msg("Use instanceIDWithHandler: instead.");
+
+/**
+ *  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.
+ *
+ *  This generates an Instance ID if it does not exist yet, which starts periodically sending
+ *  information to the Firebase backend (see `[FIRInstanceID getIDWithHandler:]`).
+ *
+ *  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 the UIApplicationDelegate's
+ *                          `didRegisterForRemoteNotificationsWithDeviceToken` method.
+ *                          The value for `apns_sandbox` should be a boolean (or an
+ *                          NSNumber representing a BOOL in Objective C) set to true if
+ *                          your app is a debug build, which means that the APNs
+ *                          device token is for the sandbox environment. It should be
+ *                          set to false otherwise. If the `apns_sandbox` key is not
+ *                          provided, an automatically-detected value shall be used.
+ *  @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:(NSString *)authorizedEntity
+                            scope:(NSString *)scope
+                          options:(nullable NSDictionary *)options
+                          handler:(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:(NSString *)authorizedEntity
+                                  scope:(NSString *)scope
+                                handler:(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.
+ *
+ *  Once an InstanceID is generated, the library periodically sends information about the
+ *  application and the device where it's running to the Firebase backend. To stop this. see
+ *  `[FIRInstanceID deleteIDWithHandler:]`.
+ *
+ *  @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:(FIRInstanceIDHandler)handler NS_SWIFT_NAME(getID(handler:));
+
+/**
+ *  Resets Instance ID and revokes all tokens.
+ *
+ *  This method also triggers a request to fetch a new Instance ID and Firebase Messaging scope
+ *  token. Please listen to kFIRInstanceIDTokenRefreshNotification when the new ID and token are
+ *  ready.
+ *
+ *  This stops the periodic sending of data to the Firebase backend that began when the Instance ID
+ *  was generated. No more data is sent until another library calls Instance ID internally again
+ *  (like FCM, RemoteConfig or Analytics) or user explicitly calls Instance ID APIs to get an
+ *  Instance ID and token again.
+ */
+- (void)deleteIDWithHandler:(FIRInstanceIDDeleteHandler)handler NS_SWIFT_NAME(deleteID(handler:));
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/Headers/FirebaseInstanceID.h b/iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/Headers/FirebaseInstanceID.h
new file mode 100755 (executable)
index 0000000..053ec2b
--- /dev/null
@@ -0,0 +1 @@
+#import "FIRInstanceID.h"
diff --git a/iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/Modules/module.modulemap b/iOS/Pods/FirebaseInstanceID/Frameworks/FirebaseInstanceID.framework/Modules/module.modulemap
new file mode 100755 (executable)
index 0000000..2058956
--- /dev/null
@@ -0,0 +1,6 @@
+framework module FirebaseInstanceID {
+  umbrella header "FirebaseInstanceID.h"
+  export *
+  module * { export *}
+  link framework "Security"
+  link framework "SystemConfiguration"}
diff --git a/iOS/Pods/FirebaseInstanceID/README.md b/iOS/Pods/FirebaseInstanceID/README.md
new file mode 100755 (executable)
index 0000000..25fe219
--- /dev/null
@@ -0,0 +1,10 @@
+# InstanceID SDK for iOS
+
+Instance ID provides a unique ID per instance of your apps and also provides a
+mechanism to authenticate and authorize actions, like sending messages via
+Firebase Cloud Messaging (FCM).
+
+
+Please visit [our developer
+site](https://developers.google.com/instance-id/) for integration instructions,
+documentation, support information, and terms of service.
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMMessageCode.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMMessageCode.h
new file mode 100644 (file)
index 0000000..0d7e027
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+typedef NS_ENUM(NSInteger, FIRMessagingMessageCode) {
+  // FIRMessaging+FIRApp.m
+  kFIRMessagingMessageCodeFIRApp000 = 1000,  // I-FCM001000
+  kFIRMessagingMessageCodeFIRApp001 = 1001,  // I-FCM001001
+  // FIRMessaging.m
+  kFIRMessagingMessageCodeMessagingPrintLibraryVersion = 2000,  // I-FCM002000
+  kFIRMessagingMessageCodeMessaging001 = 2001,  // I-FCM002001
+  kFIRMessagingMessageCodeMessaging002 = 2002,  // I-FCM002002 - no longer used
+  kFIRMessagingMessageCodeMessaging003 = 2003,  // I-FCM002003
+  kFIRMessagingMessageCodeMessaging004 = 2004,  // I-FCM002004
+  kFIRMessagingMessageCodeMessaging005 = 2005,  // I-FCM002005
+  kFIRMessagingMessageCodeMessaging006 = 2006,  // I-FCM002006 - no longer used
+  kFIRMessagingMessageCodeMessaging007 = 2007,  // I-FCM002007 - no longer used
+  kFIRMessagingMessageCodeMessaging008 = 2008,  // I-FCM002008 - no longer used
+  kFIRMessagingMessageCodeMessaging009 = 2009,  // I-FCM002009
+  kFIRMessagingMessageCodeMessaging010 = 2010,  // I-FCM002010
+  kFIRMessagingMessageCodeMessaging011 = 2011,  // I-FCM002011
+  kFIRMessagingMessageCodeMessaging012 = 2012,  // I-FCM002012
+  kFIRMessagingMessageCodeMessaging013 = 2013,  // I-FCM002013
+  kFIRMessagingMessageCodeMessaging014 = 2014,  // I-FCM002014
+  kFIRMessagingMessageCodeMessaging015 = 2015,  // I-FCM002015
+  kFIRMessagingMessageCodeMessaging016 = 2016,  // I-FCM002016 - no longer used
+  kFIRMessagingMessageCodeMessaging017 = 2017,  // I-FCM002017
+  kFIRMessagingMessageCodeMessaging018 = 2018,  // I-FCM002018
+  kFIRMessagingMessageCodeRemoteMessageDelegateMethodNotImplemented = 2019, // I-FCM002019
+  kFIRMessagingMessageCodeSenderIDNotSuppliedForTokenFetch = 2020, // I-FCM002020
+  kFIRMessagingMessageCodeSenderIDNotSuppliedForTokenDelete = 2021, // I-FCM002021
+  kFIRMessagingMessageCodeAPNSTokenNotAvailableDuringTokenFetch = 2022, // I-FCM002022
+  kFIRMessagingMessageCodeTokenDelegateMethodsNotImplemented = 2023, // I-FCM002023
+  kFIRMessagingMessageCodeTopicFormatIsDeprecated = 2024,
+  // FIRMessagingClient.m
+  kFIRMessagingMessageCodeClient000 = 4000,  // I-FCM004000
+  kFIRMessagingMessageCodeClient001 = 4001,  // I-FCM004001
+  kFIRMessagingMessageCodeClient002 = 4002,  // I-FCM004002
+  kFIRMessagingMessageCodeClient003 = 4003,  // I-FCM004003
+  kFIRMessagingMessageCodeClient004 = 4004,  // I-FCM004004
+  kFIRMessagingMessageCodeClient005 = 4005,  // I-FCM004005
+  kFIRMessagingMessageCodeClient006 = 4006,  // I-FCM004006
+  kFIRMessagingMessageCodeClient007 = 4007,  // I-FCM004007
+  kFIRMessagingMessageCodeClient008 = 4008,  // I-FCM004008
+  kFIRMessagingMessageCodeClient009 = 4009,  // I-FCM004009
+  kFIRMessagingMessageCodeClient010 = 4010,  // I-FCM004010
+  kFIRMessagingMessageCodeClient011 = 4011,  // I-FCM004011
+  // FIRMessagingConnection.m
+  kFIRMessagingMessageCodeConnection000 = 5000,  // I-FCM005000
+  kFIRMessagingMessageCodeConnection001 = 5001,  // I-FCM005001
+  kFIRMessagingMessageCodeConnection002 = 5002,  // I-FCM005002
+  kFIRMessagingMessageCodeConnection003 = 5003,  // I-FCM005003
+  kFIRMessagingMessageCodeConnection004 = 5004,  // I-FCM005004
+  kFIRMessagingMessageCodeConnection005 = 5005,  // I-FCM005005
+  kFIRMessagingMessageCodeConnection006 = 5006,  // I-FCM005006
+  kFIRMessagingMessageCodeConnection007 = 5007,  // I-FCM005007
+  kFIRMessagingMessageCodeConnection008 = 5008,  // I-FCM005008
+  kFIRMessagingMessageCodeConnection009 = 5009,  // I-FCM005009
+  kFIRMessagingMessageCodeConnection010 = 5010,  // I-FCM005010
+  kFIRMessagingMessageCodeConnection011 = 5011,  // I-FCM005011
+  kFIRMessagingMessageCodeConnection012 = 5012,  // I-FCM005012
+  kFIRMessagingMessageCodeConnection013 = 5013,  // I-FCM005013
+  kFIRMessagingMessageCodeConnection014 = 5014,  // I-FCM005014
+  kFIRMessagingMessageCodeConnection015 = 5015,  // I-FCM005015
+  kFIRMessagingMessageCodeConnection016 = 5016,  // I-FCM005016
+  kFIRMessagingMessageCodeConnection017 = 5017,  // I-FCM005017
+  kFIRMessagingMessageCodeConnection018 = 5018,  // I-FCM005018
+  kFIRMessagingMessageCodeConnection019 = 5019,  // I-FCM005019
+  kFIRMessagingMessageCodeConnection020 = 5020,  // I-FCM005020
+  kFIRMessagingMessageCodeConnection021 = 5021,  // I-FCM005021
+  kFIRMessagingMessageCodeConnection022 = 5022,  // I-FCM005022
+  kFIRMessagingMessageCodeConnection023 = 5023,  // I-FCM005023
+  // FIRMessagingContextManagerService.m
+  kFIRMessagingMessageCodeContextManagerService000 = 6000,  // I-FCM006000
+  kFIRMessagingMessageCodeContextManagerService001 = 6001,  // I-FCM006001
+  kFIRMessagingMessageCodeContextManagerService002 = 6002,  // I-FCM006002
+  kFIRMessagingMessageCodeContextManagerService003 = 6003,  // I-FCM006003
+  kFIRMessagingMessageCodeContextManagerService004 = 6004,  // I-FCM006004
+  kFIRMessagingMessageCodeContextManagerService005 = 6005,  // I-FCM006005
+  // FIRMessagingDataMessageManager.m
+  kFIRMessagingMessageCodeDataMessageManager000 = 7000,  // I-FCM007000
+  kFIRMessagingMessageCodeDataMessageManager001 = 7001,  // I-FCM007001
+  kFIRMessagingMessageCodeDataMessageManager002 = 7002,  // I-FCM007002
+  kFIRMessagingMessageCodeDataMessageManager003 = 7003,  // I-FCM007003
+  kFIRMessagingMessageCodeDataMessageManager004 = 7004,  // I-FCM007004
+  kFIRMessagingMessageCodeDataMessageManager005 = 7005,  // I-FCM007005
+  kFIRMessagingMessageCodeDataMessageManager006 = 7006,  // I-FCM007006
+  kFIRMessagingMessageCodeDataMessageManager007 = 7007,  // I-FCM007007
+  kFIRMessagingMessageCodeDataMessageManager008 = 7008,  // I-FCM007008
+  kFIRMessagingMessageCodeDataMessageManager009 = 7009,  // I-FCM007009
+  kFIRMessagingMessageCodeDataMessageManager010 = 7010,  // I-FCM007010
+  kFIRMessagingMessageCodeDataMessageManager011 = 7011,  // I-FCM007011
+  kFIRMessagingMessageCodeDataMessageManager012 = 7012,  // I-FCM007012
+  // FIRMessagingPendingTopicsList.m
+  kFIRMessagingMessageCodePendingTopicsList000 = 8000,  // I-FCM008000
+  // FIRMessagingPubSub.m
+  kFIRMessagingMessageCodePubSub000 = 9000,  // I-FCM009000
+  kFIRMessagingMessageCodePubSub001 = 9001,  // I-FCM009001
+  kFIRMessagingMessageCodePubSub002 = 9002,  // I-FCM009002
+  kFIRMessagingMessageCodePubSub003 = 9003,  // I-FCM009003
+  // FIRMessagingReceiver.m
+  kFIRMessagingMessageCodeReceiver000 = 10000,  // I-FCM010000
+  kFIRMessagingMessageCodeReceiver001 = 10001,  // I-FCM010001
+  kFIRMessagingMessageCodeReceiver002 = 10002,  // I-FCM010002
+  kFIRMessagingMessageCodeReceiver003 = 10003,  // I-FCM010003
+  kFIRMessagingMessageCodeReceiver004 = 10004,  // I-FCM010004 - no longer used
+  kFIRMessagingMessageCodeReceiver005 = 10005,  // I-FCM010005
+  // FIRMessagingRegistrar.m
+  kFIRMessagingMessageCodeRegistrar000 = 11000,  // I-FCM011000
+  // FIRMessagingRemoteNotificationsProxy.m
+  kFIRMessagingMessageCodeRemoteNotificationsProxy000 = 12000,  // I-FCM012000
+  kFIRMessagingMessageCodeRemoteNotificationsProxy001 = 12001,  // I-FCM012001
+  kFIRMessagingMessageCodeRemoteNotificationsProxyAPNSFailed = 12002,  // I-FCM012002
+  kFIRMessagingMessageCodeRemoteNotificationsProxyMethodNotAdded = 12003,  // I-FCM012003
+  // FIRMessagingRmq2PersistentStore.m
+  kFIRMessagingMessageCodeRmq2PersistentStore000 = 13000,  // I-FCM013000
+  kFIRMessagingMessageCodeRmq2PersistentStore001 = 13001,  // I-FCM013001
+  kFIRMessagingMessageCodeRmq2PersistentStore002 = 13002,  // I-FCM013002
+  kFIRMessagingMessageCodeRmq2PersistentStore003 = 13003,  // I-FCM013003
+  kFIRMessagingMessageCodeRmq2PersistentStore004 = 13004,  // I-FCM013004
+  kFIRMessagingMessageCodeRmq2PersistentStore005 = 13005,  // I-FCM013005
+  kFIRMessagingMessageCodeRmq2PersistentStore006 = 13006,  // I-FCM013006
+  kFIRMessagingMessageCodeRmq2PersistentStoreErrorCreatingDatabase = 13007,  // I-FCM013007
+  kFIRMessagingMessageCodeRmq2PersistentStoreErrorOpeningDatabase = 13008,  // I-FCM013008
+  kFIRMessagingMessageCodeRmq2PersistentStoreInvalidRmqDirectory = 13009,  // I-FCM013009
+  kFIRMessagingMessageCodeRmq2PersistentStoreErrorCreatingTable = 13010,  // I-FCM013010
+  // FIRMessagingRmqManager.m
+  kFIRMessagingMessageCodeRmqManager000 = 14000,  // I-FCM014000
+  // FIRMessagingSecureSocket.m
+  kFIRMessagingMessageCodeSecureSocket000 = 15000,  // I-FCM015000
+  kFIRMessagingMessageCodeSecureSocket001 = 15001,  // I-FCM015001
+  kFIRMessagingMessageCodeSecureSocket002 = 15002,  // I-FCM015002
+  kFIRMessagingMessageCodeSecureSocket003 = 15003,  // I-FCM015003
+  kFIRMessagingMessageCodeSecureSocket004 = 15004,  // I-FCM015004
+  kFIRMessagingMessageCodeSecureSocket005 = 15005,  // I-FCM015005
+  kFIRMessagingMessageCodeSecureSocket006 = 15006,  // I-FCM015006
+  kFIRMessagingMessageCodeSecureSocket007 = 15007,  // I-FCM015007
+  kFIRMessagingMessageCodeSecureSocket008 = 15008,  // I-FCM015008
+  kFIRMessagingMessageCodeSecureSocket009 = 15009,  // I-FCM015009
+  kFIRMessagingMessageCodeSecureSocket010 = 15010,  // I-FCM015010
+  kFIRMessagingMessageCodeSecureSocket011 = 15011,  // I-FCM015011
+  kFIRMessagingMessageCodeSecureSocket012 = 15012,  // I-FCM015012
+  kFIRMessagingMessageCodeSecureSocket013 = 15013,  // I-FCM015013
+  kFIRMessagingMessageCodeSecureSocket014 = 15014,  // I-FCM015014
+  kFIRMessagingMessageCodeSecureSocket015 = 15015,  // I-FCM015015
+  kFIRMessagingMessageCodeSecureSocket016 = 15016,  // I-FCM015016
+  // FIRMessagingSyncMessageManager.m
+  kFIRMessagingMessageCodeSyncMessageManager000 = 16000,  // I-FCM016000
+  kFIRMessagingMessageCodeSyncMessageManager001 = 16001,  // I-FCM016001
+  kFIRMessagingMessageCodeSyncMessageManager002 = 16002,  // I-FCM016002
+  kFIRMessagingMessageCodeSyncMessageManager003 = 16003,  // I-FCM016003
+  kFIRMessagingMessageCodeSyncMessageManager004 = 16004,  // I-FCM016004
+  kFIRMessagingMessageCodeSyncMessageManager005 = 16005,  // I-FCM016005
+  kFIRMessagingMessageCodeSyncMessageManager006 = 16006,  // I-FCM016006
+  kFIRMessagingMessageCodeSyncMessageManager007 = 16007,  // I-FCM016007
+  kFIRMessagingMessageCodeSyncMessageManager008 = 16008,  // I-FCM016008
+  // FIRMessagingTopicOperation.m
+  kFIRMessagingMessageCodeTopicOption000 = 17000,  // I-FCM017000
+  kFIRMessagingMessageCodeTopicOption001 = 17001,  // I-FCM017001
+  kFIRMessagingMessageCodeTopicOption002 = 17002,  // I-FCM017002
+  kFIRMessagingMessageCodeTopicOptionTopicEncodingFailed = 17003,  // I-FCM017003
+  kFIRMessagingMessageCodeTopicOperationEmptyResponse = 17004,  // I-FCM017004
+  // FIRMessagingUtilities.m
+  kFIRMessagingMessageCodeUtilities000 = 18000,  // I-FCM018000
+  kFIRMessagingMessageCodeUtilities001 = 18001,  // I-FCM018001
+  kFIRMessagingMessageCodeUtilities002 = 18002,  // I-FCM018002
+};
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessaging.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessaging.m
new file mode 100644 (file)
index 0000000..2da0e60
--- /dev/null
@@ -0,0 +1,1191 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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  !__has_feature(objc_arc)
+#error FIRMessagingLib should be compiled with ARC.
+#endif
+
+#import "FIRMessaging.h"
+#import "FIRMessaging_Private.h"
+
+#import <UIKit/UIKit.h>
+
+#import "FIRMessagingClient.h"
+#import "FIRMessagingConstants.h"
+#import "FIRMessagingContextManagerService.h"
+#import "FIRMessagingDataMessageManager.h"
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingPubSub.h"
+#import "FIRMessagingReceiver.h"
+#import "FIRMessagingRemoteNotificationsProxy.h"
+#import "FIRMessagingRmqManager.h"
+#import "FIRMessagingSyncMessageManager.h"
+#import "FIRMessagingUtilities.h"
+#import "FIRMessagingVersionUtilities.h"
+#import "FIRMessaging_Private.h"
+
+#import <FirebaseCore/FIRAppInternal.h>
+#import <FirebaseInstanceID/FirebaseInstanceID.h>
+#import <GoogleUtilities/GULReachabilityChecker.h>
+
+#import "NSError+FIRMessaging.h"
+
+static NSString *const kFIRMessagingMessageViaAPNSRootKey = @"aps";
+static NSString *const kFIRMessagingReachabilityHostname = @"www.google.com";
+static NSString *const kFIRMessagingDefaultTokenScope = @"*";
+static NSString *const kFIRMessagingFCMTokenFetchAPNSOption = @"apns_token";
+
+#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
+const NSNotificationName FIRMessagingSendSuccessNotification =
+    @"com.firebase.messaging.notif.send-success";
+const NSNotificationName FIRMessagingSendErrorNotification =
+    @"com.firebase.messaging.notif.send-error";
+const NSNotificationName FIRMessagingMessagesDeletedNotification =
+    @"com.firebase.messaging.notif.messages-deleted";
+const NSNotificationName FIRMessagingConnectionStateChangedNotification =
+    @"com.firebase.messaging.notif.connection-state-changed";
+const NSNotificationName FIRMessagingRegistrationTokenRefreshedNotification =
+    @"com.firebase.messaging.notif.fcm-token-refreshed";
+#else
+NSString *const FIRMessagingSendSuccessNotification =
+    @"com.firebase.messaging.notif.send-success";
+NSString *const FIRMessagingSendErrorNotification =
+    @"com.firebase.messaging.notif.send-error";
+NSString * const FIRMessagingMessagesDeletedNotification =
+    @"com.firebase.messaging.notif.messages-deleted";
+NSString * const FIRMessagingConnectionStateChangedNotification =
+    @"com.firebase.messaging.notif.connection-state-changed";
+NSString * const FIRMessagingRegistrationTokenRefreshedNotification =
+    @"com.firebase.messaging.notif.fcm-token-refreshed";
+#endif  // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
+
+NSString *const kFIRMessagingUserDefaultsKeyAutoInitEnabled =
+    @"com.firebase.messaging.auto-init.enabled";  // Auto Init Enabled key stored in NSUserDefaults
+
+NSString *const kFIRMessagingAPNSTokenType = @"APNSTokenType"; // APNS Token type key stored in user info.
+
+NSString *const kFIRMessagingPlistAutoInitEnabled =
+    @"FirebaseMessagingAutoInitEnabled";  // Auto Init Enabled key stored in Info.plist
+
+@interface FIRMessagingMessageInfo ()
+
+@property(nonatomic, readwrite, assign) FIRMessagingMessageStatus status;
+
+@end
+
+@implementation FIRMessagingMessageInfo
+
+- (instancetype)init {
+  FIRMessagingInvalidateInitializer();
+}
+
+- (instancetype)initWithStatus:(FIRMessagingMessageStatus)status {
+  self = [super init];
+  if (self) {
+    _status = status;
+  }
+  return self;
+}
+
+@end
+
+#pragma mark - for iOS 10 compatibility
+@implementation FIRMessagingRemoteMessage
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _appData = [[NSMutableDictionary alloc] init];
+  }
+
+  return self;
+}
+
+- (instancetype)initWithMessage:(FIRMessagingRemoteMessage *)message {
+  self = [self init];
+  if (self) {
+    _appData = [message.appData copy];
+  }
+
+  return self;
+}
+
+@end
+
+@interface FIRMessaging ()<FIRMessagingClientDelegate, FIRMessagingReceiverDelegate,
+                           GULReachabilityDelegate>
+
+// FIRApp properties
+@property(nonatomic, readwrite, strong) NSData *apnsTokenData;
+@property(nonatomic, readwrite, strong) NSString *defaultFcmToken;
+
+@property(nonatomic, readwrite, strong) FIRInstanceID *instanceID;
+
+@property(nonatomic, readwrite, assign) BOOL isClientSetup;
+
+@property(nonatomic, readwrite, strong) FIRMessagingClient *client;
+@property(nonatomic, readwrite, strong) GULReachabilityChecker *reachability;
+@property(nonatomic, readwrite, strong) FIRMessagingDataMessageManager *dataMessageManager;
+@property(nonatomic, readwrite, strong) FIRMessagingPubSub *pubsub;
+@property(nonatomic, readwrite, strong) FIRMessagingRmqManager *rmq2Manager;
+@property(nonatomic, readwrite, strong) FIRMessagingReceiver *receiver;
+@property(nonatomic, readwrite, strong) FIRMessagingSyncMessageManager *syncMessageManager;
+@property(nonatomic, readwrite, strong) NSUserDefaults *messagingUserDefaults;
+
+/// Message ID's logged for analytics. This prevents us from logging the same message twice
+/// which can happen if the user inadvertently calls `appDidReceiveMessage` along with us
+/// calling it implicitly during swizzling.
+@property(nonatomic, readwrite, strong) NSMutableSet *loggedMessageIDs;
+
+- (instancetype)initWithInstanceID:(FIRInstanceID *)instanceID
+                      userDefaults:(NSUserDefaults *)defaults NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation FIRMessaging
+
++ (FIRMessaging *)messaging {
+  static FIRMessaging *messaging;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    messaging = [[FIRMessaging alloc] initPrivately];
+    [messaging start];
+  });
+  return messaging;
+}
+
+- (instancetype)initWithInstanceID:(FIRInstanceID *)instanceID
+                      userDefaults:(NSUserDefaults *)defaults {
+  self = [super init];
+  if (self != nil) {
+    _loggedMessageIDs = [NSMutableSet set];
+    _instanceID = instanceID;
+    _messagingUserDefaults = defaults;
+  }
+  return self;
+}
+
+- (instancetype)initPrivately {
+  return [self initWithInstanceID:[FIRInstanceID instanceID]
+                     userDefaults:[NSUserDefaults standardUserDefaults]];
+}
+
+- (void)dealloc {
+  [self.reachability stop];
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
+  [self teardown];
+}
+
+#pragma mark - Config
+
++ (void)load {
+  [[NSNotificationCenter defaultCenter] addObserver:self
+                                           selector:@selector(didReceiveConfigureSDKNotification:)
+                                               name:kFIRAppReadyToConfigureSDKNotification
+                                             object:nil];
+}
+
++ (void)didReceiveConfigureSDKNotification:(NSNotification *)notification {
+  NSDictionary *appInfoDict = notification.userInfo;
+  NSNumber *isDefaultApp = appInfoDict[kFIRAppIsDefaultAppKey];
+  if (![isDefaultApp boolValue]) {
+    // Only configure for the default FIRApp.
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeFIRApp001,
+                            @"Firebase Messaging only works with the default app.");
+    return;
+  }
+
+  NSString *appName = appInfoDict[kFIRAppNameKey];
+  FIRApp *app = [FIRApp appNamed:appName];
+  [[FIRMessaging messaging] configureMessaging:app];
+}
+
+- (void)configureMessaging:(FIRApp *)app {
+  // Swizzle remote-notification-related methods (app delegate and UNUserNotificationCenter)
+  if ([FIRMessagingRemoteNotificationsProxy canSwizzleMethods]) {
+    NSString *docsURLString = @"https://firebase.google.com/docs/cloud-messaging/ios/client"
+                              @"#method_swizzling_in_firebase_messaging";
+    FIRMessagingLoggerNotice(kFIRMessagingMessageCodeFIRApp000,
+                             @"FIRMessaging Remote Notifications proxy enabled, will swizzle "
+                             @"remote notification receiver handlers. If you'd prefer to manually "
+                             @"integrate Firebase Messaging, add \"%@\" to your Info.plist, "
+                             @"and set it to NO. Follow the instructions at:\n%@\nto ensure "
+                             @"proper integration.",
+                             kFIRMessagingRemoteNotificationsProxyEnabledInfoPlistKey,
+                             docsURLString);
+    [FIRMessagingRemoteNotificationsProxy swizzleMethods];
+  }
+}
+
+- (void)start {
+  // Print the library version for logging.
+  NSString *currentLibraryVersion = FIRMessagingCurrentLibraryVersion();
+  FIRMessagingLoggerInfo(kFIRMessagingMessageCodeMessagingPrintLibraryVersion,
+                         @"FIRMessaging library version %@",
+                         currentLibraryVersion);
+
+  [self setupReceiver];
+
+  NSString *hostname = kFIRMessagingReachabilityHostname;
+  self.reachability = [[GULReachabilityChecker alloc] initWithReachabilityDelegate:self
+                                                                          withHost:hostname];
+  [self.reachability start];
+
+  [self setupApplicationSupportSubDirectory];
+  // setup FIRMessaging objects
+  [self setupRmqManager];
+  [self setupClient];
+  [self setupSyncMessageManager];
+  [self setupDataMessageManager];
+  [self setupTopics];
+
+  self.isClientSetup = YES;
+  [self setupNotificationListeners];
+}
+
+- (void)setupApplicationSupportSubDirectory {
+  NSString *messagingSubDirectory = kFIRMessagingApplicationSupportSubDirectory;
+  if (![[self class] hasApplicationSupportSubDirectory:messagingSubDirectory]) {
+    [[self class] createApplicationSupportSubDirectory:messagingSubDirectory];
+  }
+}
+
+- (void)setupNotificationListeners {
+  // To prevent multiple notifications remove self as observer for all events.
+  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+  [center removeObserver:self];
+
+  [center addObserver:self
+             selector:@selector(didReceiveDefaultInstanceIDToken:)
+                 name:kFIRMessagingFCMTokenNotification
+               object:nil];
+  [center addObserver:self
+             selector:@selector(defaultInstanceIDTokenWasRefreshed:)
+                 name:kFIRMessagingRegistrationTokenRefreshNotification
+               object:nil];
+  [center addObserver:self
+             selector:@selector(applicationStateChanged)
+                 name:UIApplicationDidBecomeActiveNotification
+               object:nil];
+  [center addObserver:self
+             selector:@selector(applicationStateChanged)
+                 name:UIApplicationDidEnterBackgroundNotification
+               object:nil];
+}
+
+- (void)setupReceiver {
+  self.receiver = [[FIRMessagingReceiver alloc] init];
+  self.receiver.delegate = self;
+}
+
+- (void)setupClient {
+  self.client = [[FIRMessagingClient alloc] initWithDelegate:self
+                                                reachability:self.reachability
+                                                 rmq2Manager:self.rmq2Manager];
+}
+
+- (void)setupDataMessageManager {
+  self.dataMessageManager =
+      [[FIRMessagingDataMessageManager alloc] initWithDelegate:self.receiver
+                                                        client:self.client
+                                                   rmq2Manager:self.rmq2Manager
+                                            syncMessageManager:self.syncMessageManager];
+
+  [self.dataMessageManager refreshDelayedMessages];
+  [self.client setDataMessageManager:self.dataMessageManager];
+}
+
+- (void)setupRmqManager {
+  self.rmq2Manager = [[FIRMessagingRmqManager alloc] initWithDatabaseName:@"rmq2"];
+  [self.rmq2Manager loadRmqId];
+}
+
+- (void)setupTopics {
+  _FIRMessagingDevAssert(self.client, @"Invalid nil client before init pubsub.");
+  self.pubsub = [[FIRMessagingPubSub alloc] initWithClient:self.client];
+}
+
+- (void)setupSyncMessageManager {
+  self.syncMessageManager =
+      [[FIRMessagingSyncMessageManager alloc] initWithRmqManager:self.rmq2Manager];
+
+  // Delete the expired messages with a delay. We don't want to block startup with a somewhat
+  // expensive db call.
+  FIRMessaging_WEAKIFY(self);
+  dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
+  dispatch_after(time, dispatch_get_main_queue(), ^{
+    FIRMessaging_STRONGIFY(self);
+    [self.syncMessageManager removeExpiredSyncMessages];
+  });
+}
+
+- (void)teardown {
+  _FIRMessagingDevAssert([NSThread isMainThread],
+                         @"FIRMessaging should be called from main thread only.");
+  [self.client teardown];
+  self.pubsub = nil;
+  self.syncMessageManager = nil;
+  self.rmq2Manager = nil;
+  self.dataMessageManager = nil;
+  self.client = nil;
+  self.isClientSetup = NO;
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeMessaging001, @"Did successfully teardown");
+}
+
+#pragma mark - Messages
+
+- (FIRMessagingMessageInfo *)appDidReceiveMessage:(NSDictionary *)message {
+  if (!message.count) {
+    return [[FIRMessagingMessageInfo alloc] initWithStatus:FIRMessagingMessageStatusUnknown];
+  }
+
+  // For downstream messages that go via MCS we should strip out this key before sending
+  // the message to the device.
+  BOOL isOldMessage = NO;
+  NSString *messageID = message[kFIRMessagingMessageIDKey];
+  if ([messageID length]) {
+    [self.rmq2Manager saveS2dMessageWithRmqId:messageID];
+
+    BOOL isSyncMessage = [[self class] isAPNSSyncMessage:message];
+    if (isSyncMessage) {
+      isOldMessage = [self.syncMessageManager didReceiveAPNSSyncMessage:message];
+    }
+  }
+  // Prevent duplicates by keeping a cache of all the logged messages during each session.
+  // The duplicates only happen when the 3P app calls `appDidReceiveMessage:` along with
+  // us swizzling their implementation to call the same method implicitly.
+  if (!isOldMessage && messageID.length) {
+    isOldMessage = [self.loggedMessageIDs containsObject:messageID];
+    if (!isOldMessage) {
+      [self.loggedMessageIDs addObject:messageID];
+    }
+  }
+
+  if (!isOldMessage) {
+    Class firMessagingLogClass = NSClassFromString(@"FIRMessagingLog");
+    SEL logMessageSelector = NSSelectorFromString(@"logMessage:");
+
+    if ([firMessagingLogClass respondsToSelector:logMessageSelector]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+      [firMessagingLogClass performSelector:logMessageSelector
+                                 withObject:message];
+    }
+#pragma clang diagnostic pop
+    [self handleContextManagerMessage:message];
+    [self handleIncomingLinkIfNeededFromMessage:message];
+  }
+  return [[FIRMessagingMessageInfo alloc] initWithStatus:FIRMessagingMessageStatusNew];
+}
+
+- (BOOL)handleContextManagerMessage:(NSDictionary *)message {
+  if ([FIRMessagingContextManagerService isContextManagerMessage:message]) {
+    return [FIRMessagingContextManagerService handleContextManagerMessage:message];
+  }
+  return NO;
+}
+
++ (BOOL)isAPNSSyncMessage:(NSDictionary *)message {
+  if ([message[kFIRMessagingMessageViaAPNSRootKey] isKindOfClass:[NSDictionary class]]) {
+    NSDictionary *aps = message[kFIRMessagingMessageViaAPNSRootKey];
+    return [aps[kFIRMessagingMessageAPNSContentAvailableKey] boolValue];
+  }
+  return NO;
+}
+
+- (void)handleIncomingLinkIfNeededFromMessage:(NSDictionary *)message {
+  NSURL *url = [self linkURLFromMessage:message];
+  if (url == nil) {
+    return;
+  }
+  if (![NSThread isMainThread]) {
+    dispatch_async(dispatch_get_main_queue(), ^{
+      [self handleIncomingLinkIfNeededFromMessage:message];
+
+    });
+    return;
+  }
+  UIApplication *application = FIRMessagingUIApplication();
+  if (!application) {
+    return;
+  }
+  id<UIApplicationDelegate> appDelegate = application.delegate;
+  SEL continueUserActivitySelector =
+      @selector(application:continueUserActivity:restorationHandler:);
+  SEL openURLWithOptionsSelector = @selector(application:openURL:options:);
+  SEL openURLWithSourceApplicationSelector =
+      @selector(application:openURL:sourceApplication:annotation:);
+  SEL handleOpenURLSelector = @selector(application:handleOpenURL:);
+  // Due to FIRAAppDelegateProxy swizzling, this selector will most likely get chosen, whether or
+  // not the actual application has implemented
+  // |application:continueUserActivity:restorationHandler:|. A warning will be displayed to the user
+  // if they haven't implemented it.
+  if ([NSUserActivity class] != nil &&
+      [appDelegate respondsToSelector:continueUserActivitySelector]) {
+    NSUserActivity *userActivity =
+        [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb];
+    userActivity.webpageURL = url;
+    [appDelegate application:application
+        continueUserActivity:userActivity
+          restorationHandler:^(NSArray * _Nullable restorableObjects) {
+      // Do nothing, as we don't support the app calling this block
+    }];
+
+  } else if ([appDelegate respondsToSelector:openURLWithOptionsSelector]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+    [appDelegate application:application openURL:url options:@{}];
+#pragma clang diagnostic pop
+
+  // Similarly, |application:openURL:sourceApplication:annotation:| will also always be called, due
+  // to the default swizzling done by FIRAAppDelegateProxy in Firebase Analytics
+  } else if ([appDelegate respondsToSelector:openURLWithSourceApplicationSelector]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    [appDelegate application:application
+                     openURL:url
+           sourceApplication:FIRMessagingAppIdentifier()
+                  annotation:@{}];
+  } else if ([appDelegate respondsToSelector:handleOpenURLSelector]) {
+    [appDelegate application:application handleOpenURL:url];
+#pragma clang diagnostic pop
+  }
+}
+
+- (NSURL *)linkURLFromMessage:(NSDictionary *)message {
+  NSString *urlString = message[kFIRMessagingMessageLinkKey];
+  if (urlString == nil || ![urlString isKindOfClass:[NSString class]] || urlString.length == 0) {
+    return nil;
+  }
+  NSURL *url = [NSURL URLWithString:urlString];
+  return url;
+}
+
+#pragma mark - APNS
+
+- (NSData *)APNSToken {
+  return self.apnsTokenData;
+}
+
+- (void)setAPNSToken:(NSData *)APNSToken {
+  [self setAPNSToken:APNSToken type:FIRMessagingAPNSTokenTypeUnknown];
+}
+
+- (void)setAPNSToken:(NSData *)apnsToken type:(FIRMessagingAPNSTokenType)type {
+  if ([apnsToken isEqual:self.apnsTokenData]) {
+    return;
+  }
+  self.apnsTokenData = apnsToken;
+
+  // Notify InstanceID that APNS Token has been set.
+  NSDictionary *userInfo = @{kFIRMessagingAPNSTokenType : @(type)};
+  NSNotification *notification =
+      [NSNotification notificationWithName:kFIRMessagingAPNSTokenNotification
+                                    object:[apnsToken copy]
+                                  userInfo:userInfo];
+  [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostASAP];
+}
+
+#pragma mark - FCM
+
+- (BOOL)isAutoInitEnabled {
+  // Check storage
+  id isAutoInitEnabledObject =
+      [_messagingUserDefaults objectForKey:kFIRMessagingUserDefaultsKeyAutoInitEnabled];
+  if (isAutoInitEnabledObject) {
+    return [isAutoInitEnabledObject boolValue];
+  }
+
+  // Check Info.plist
+  isAutoInitEnabledObject =
+      [[NSBundle mainBundle] objectForInfoDictionaryKey:kFIRMessagingPlistAutoInitEnabled];
+  if (isAutoInitEnabledObject) {
+    return [isAutoInitEnabledObject boolValue];
+  }
+
+  // If none of above exists, we default to the global switch that comes from FIRApp.
+  return [[FIRApp defaultApp] isDataCollectionDefaultEnabled];
+}
+
+- (void)setAutoInitEnabled:(BOOL)autoInitEnabled {
+  BOOL isFCMAutoInitEnabled = [self isAutoInitEnabled];
+  [_messagingUserDefaults setBool:autoInitEnabled
+                           forKey:kFIRMessagingUserDefaultsKeyAutoInitEnabled];
+  [_messagingUserDefaults synchronize];
+  if (!isFCMAutoInitEnabled && autoInitEnabled) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    self.defaultFcmToken = self.instanceID.token;
+#pragma clang diagnostic pop
+  }
+}
+
+- (NSString *)FCMToken {
+  NSString *token = self.defaultFcmToken;
+  if (!token) {
+    // We may not have received it from Instance ID yet (via NSNotification), so extract it directly
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    token = self.instanceID.token;
+#pragma clang diagnostic pop
+  }
+  return token;
+}
+
+- (void)retrieveFCMTokenForSenderID:(nonnull NSString *)senderID
+                         completion:(nonnull FIRMessagingFCMTokenFetchCompletion)completion {
+  if (!senderID.length) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeSenderIDNotSuppliedForTokenFetch,
+                            @"Sender ID not supplied. It is required for a token fetch, "
+                            @"to identify the sender.");
+    if (completion) {
+      NSString *description = @"Couldn't fetch token because a Sender ID was not supplied. A valid "
+                              @"Sender ID is required to fetch an FCM token";
+      NSError *error = [NSError fcm_errorWithCode:FIRMessagingErrorInvalidRequest
+                                         userInfo:@{NSLocalizedDescriptionKey : description}];
+      completion(nil, error);
+    }
+    return;
+  }
+  NSDictionary *options = nil;
+  if (self.APNSToken) {
+    options = @{kFIRMessagingFCMTokenFetchAPNSOption : self.APNSToken};
+  } else {
+    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeAPNSTokenNotAvailableDuringTokenFetch,
+                           @"APNS device token not set before retrieving FCM Token for Sender ID "
+                           @"'%@'. Notifications to this FCM Token will not be delivered over APNS."
+                           @"Be sure to re-retrieve the FCM token once the APNS device token is "
+                           @"set.", senderID);
+  }
+  [self.instanceID tokenWithAuthorizedEntity:senderID
+                                       scope:kFIRMessagingDefaultTokenScope
+                                     options:options
+                                     handler:completion];
+}
+
+- (void)deleteFCMTokenForSenderID:(nonnull NSString *)senderID
+                       completion:(nonnull FIRMessagingDeleteFCMTokenCompletion)completion {
+  if (!senderID.length) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeSenderIDNotSuppliedForTokenDelete,
+                            @"Sender ID not supplied. It is required to delete an FCM token.");
+    if (completion) {
+      NSString *description = @"Couldn't delete token because a Sender ID was not supplied. A "
+                              @"valid Sender ID is required to delete an FCM token";
+      NSError *error = [NSError fcm_errorWithCode:FIRMessagingErrorInvalidRequest
+                                         userInfo:@{NSLocalizedDescriptionKey : description}];
+      completion(error);
+    }
+    return;
+  }
+  [self.instanceID deleteTokenWithAuthorizedEntity:senderID
+                                             scope:kFIRMessagingDefaultTokenScope
+                                           handler:completion];
+}
+
+#pragma mark - FIRMessagingDelegate helper methods
+- (void)setDelegate:(id<FIRMessagingDelegate>)delegate {
+  _delegate = delegate;
+  [self validateDelegateConformsToTokenAvailabilityMethods];
+}
+
+// Check if the delegate conforms to |didReceiveRegistrationToken:|
+// and display a warning to the developer if not.
+// NOTE: Once |didReceiveRegistrationToken:| can be made a required method, this
+// check can be removed.
+- (void)validateDelegateConformsToTokenAvailabilityMethods {
+  if (self.delegate &&
+      ![self.delegate respondsToSelector:@selector(messaging:didReceiveRegistrationToken:)]) {
+    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTokenDelegateMethodsNotImplemented,
+                           @"The object %@ does not respond to "
+                           @"-messaging:didReceiveRegistrationToken:. Please implement "
+                           @"-messaging:didReceiveRegistrationToken: to be provided with an FCM "
+                           @"token.", self.delegate.description);
+  }
+}
+
+- (void)notifyDelegateOfFCMTokenAvailability {
+  __weak FIRMessaging *weakSelf = self;
+  if (![NSThread isMainThread]) {
+    dispatch_async(dispatch_get_main_queue(), ^{
+      [weakSelf notifyDelegateOfFCMTokenAvailability];
+    });
+    return;
+  }
+  if ([self.delegate respondsToSelector:@selector(messaging:didReceiveRegistrationToken:)]) {
+    [self.delegate messaging:self didReceiveRegistrationToken:self.defaultFcmToken];
+  }
+}
+
+#pragma mark - Application State Changes
+
+- (void)applicationStateChanged {
+  if (self.shouldEstablishDirectChannel) {
+    [self updateAutomaticClientConnection];
+  }
+}
+
+#pragma mark - Direct Channel
+
+- (void)setShouldEstablishDirectChannel:(BOOL)shouldEstablishDirectChannel {
+  if (_shouldEstablishDirectChannel == shouldEstablishDirectChannel) {
+    return;
+  }
+  _shouldEstablishDirectChannel = shouldEstablishDirectChannel;
+  [self updateAutomaticClientConnection];
+}
+
+- (BOOL)isDirectChannelEstablished {
+  return self.client.isConnectionActive;
+}
+
+- (BOOL)shouldBeConnectedAutomatically {
+  // We require a token from Instance ID
+  NSString *token = self.defaultFcmToken;
+  // Only on foreground connections
+  UIApplication *application = FIRMessagingUIApplication();
+  if (!application) {
+    return NO;
+  }
+  UIApplicationState applicationState = application.applicationState;
+  BOOL shouldBeConnected = _shouldEstablishDirectChannel &&
+                           (token.length > 0) &&
+                           applicationState == UIApplicationStateActive;
+  return shouldBeConnected;
+}
+
+- (void)updateAutomaticClientConnection {
+  if (![NSThread isMainThread]) {
+    // Call this method from the main thread
+    dispatch_async(dispatch_get_main_queue(), ^{
+      [self updateAutomaticClientConnection];
+    });
+    return;
+  }
+  BOOL shouldBeConnected = [self shouldBeConnectedAutomatically];
+  if (shouldBeConnected && !self.client.isConnected) {
+    [self.client connectWithHandler:^(NSError *error) {
+      if (!error) {
+        // It means we connected. Fire connection change notification
+        [self notifyOfDirectChannelConnectionChange];
+      }
+    }];
+  } else if (!shouldBeConnected && self.client.isConnected) {
+    [self.client disconnect];
+    [self notifyOfDirectChannelConnectionChange];
+  }
+}
+
+- (void)notifyOfDirectChannelConnectionChange {
+  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+  [center postNotificationName:FIRMessagingConnectionStateChangedNotification object:self];
+}
+
+#pragma mark - Connect
+
+- (void)connectWithCompletion:(FIRMessagingConnectCompletion)handler {
+  _FIRMessagingDevAssert([NSThread isMainThread],
+                         @"FIRMessaging connect should be called from main thread only.");
+  _FIRMessagingDevAssert(self.isClientSetup, @"FIRMessaging client not setup.");
+  [self.client connectWithHandler:^(NSError *error) {
+    if (handler) {
+      handler(error);
+    }
+    if (!error) {
+      // It means we connected. Fire connection change notification
+      [self notifyOfDirectChannelConnectionChange];
+    }
+  }];
+
+}
+
+- (void)disconnect {
+  _FIRMessagingDevAssert([NSThread isMainThread],
+                         @"FIRMessaging should be called from main thread only.");
+  if ([self.client isConnected]) {
+    [self.client disconnect];
+    [self notifyOfDirectChannelConnectionChange];
+  }
+}
+
+#pragma mark - Topics
+
++ (NSString *)normalizeTopic:(NSString *)topic {
+  if (!topic.length) {
+    return nil;
+  }
+  if (![FIRMessagingPubSub hasTopicsPrefix:topic]) {
+    topic = [FIRMessagingPubSub addPrefixToTopic:topic];
+  }
+  if ([FIRMessagingPubSub isValidTopicWithPrefix:topic]) {
+    return [topic copy];
+  }
+  return nil;
+}
+
+- (void)subscribeToTopic:(NSString *)topic {
+  [self subscribeToTopic:topic completion:nil];
+}
+
+- (void)subscribeToTopic:(NSString *)topic
+              completion:(nullable FIRMessagingTopicOperationCompletion)completion {
+  if ([FIRMessagingPubSub hasTopicsPrefix:topic]) {
+    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicFormatIsDeprecated,
+                           @"Format '%@' is deprecated. Only '%@' should be used in "
+                           @"subscribeToTopic.",
+                           topic, [FIRMessagingPubSub removePrefixFromTopic:topic]);
+  }
+  if (!self.defaultFcmToken.length) {
+    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeMessaging010,
+                           @"The subscription operation is suspended because you don't have a "
+                           @"token. The operation will resume once you get an FCM token.");
+  }
+  NSString *normalizeTopic = [[self class] normalizeTopic:topic];
+  if (normalizeTopic.length) {
+    [self.pubsub subscribeToTopic:normalizeTopic handler:completion];
+    return;
+  }
+  FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging009,
+                          @"Cannot parse topic name %@. Will not subscribe.", topic);
+}
+
+- (void)unsubscribeFromTopic:(NSString *)topic {
+  [self unsubscribeFromTopic:topic completion:nil];
+}
+
+- (void)unsubscribeFromTopic:(NSString *)topic
+                  completion:(nullable FIRMessagingTopicOperationCompletion)completion {
+  if ([FIRMessagingPubSub hasTopicsPrefix:topic]) {
+    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicFormatIsDeprecated,
+                           @"Format '%@' is deprecated. Only '%@' should be used in "
+                           @"unsubscribeFromTopic.",
+                           topic, [FIRMessagingPubSub removePrefixFromTopic:topic]);
+  }
+  if (!self.defaultFcmToken.length) {
+    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeMessaging012,
+                           @"The unsubscription operation is suspended because you don't have a "
+                           @"token. The operation will resume once you get an FCM token.");
+  }
+  NSString *normalizeTopic = [[self class] normalizeTopic:topic];
+  if (normalizeTopic.length) {
+    [self.pubsub unsubscribeFromTopic:normalizeTopic handler:completion];
+    return;
+  }
+  FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging011,
+                          @"Cannot parse topic name %@. Will not unsubscribe.", topic);
+}
+
+#pragma mark - Send
+
+- (void)sendMessage:(NSDictionary *)message
+                 to:(NSString *)to
+      withMessageID:(NSString *)messageID
+         timeToLive:(int64_t)ttl {
+  _FIRMessagingDevAssert([to length] != 0, @"Invalid receiver id for FIRMessaging-message");
+
+  NSMutableDictionary *fcmMessage = [[self class] createFIRMessagingMessageWithMessage:message
+                                                                           to:to
+                                                                       withID:messageID
+                                                                   timeToLive:ttl
+                                                                        delay:0];
+  FIRMessagingLoggerInfo(kFIRMessagingMessageCodeMessaging013, @"Sending message: %@ with id: %@",
+                         message, messageID);
+  [self.dataMessageManager sendDataMessageStanza:fcmMessage];
+}
+
++ (NSMutableDictionary *)createFIRMessagingMessageWithMessage:(NSDictionary *)message
+                                                  to:(NSString *)to
+                                              withID:(NSString *)msgID
+                                          timeToLive:(int64_t)ttl
+                                               delay:(int)delay {
+  NSMutableDictionary *fcmMessage = [NSMutableDictionary dictionary];
+  fcmMessage[kFIRMessagingSendTo] = [to copy];
+  fcmMessage[kFIRMessagingSendMessageID] = msgID ? [msgID copy] : @"";
+  fcmMessage[kFIRMessagingSendTTL] = @(ttl);
+  fcmMessage[kFIRMessagingSendDelay] = @(delay);
+  fcmMessage[KFIRMessagingSendMessageAppData] =
+      [NSMutableDictionary dictionaryWithDictionary:message];
+  return fcmMessage;
+}
+
+#pragma mark - IID dependencies
+
++ (NSString *)FIRMessagingSDKVersion {
+  return FIRMessagingCurrentLibraryVersion();
+}
+
++ (NSString *)FIRMessagingSDKCurrentLocale {
+  return [self currentLocale];
+}
+
+#pragma mark - FIRMessagingReceiverDelegate
+
+- (void)receiver:(FIRMessagingReceiver *)receiver
+      receivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
+  if ([self.delegate respondsToSelector:@selector(messaging:didReceiveMessage:)]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+    [self.delegate messaging:self didReceiveMessage:remoteMessage];
+#pragma pop
+  } else {
+    // Delegate methods weren't implemented, so messages are being dropped, log a warning
+    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeRemoteMessageDelegateMethodNotImplemented,
+                           @"FIRMessaging received data-message, but FIRMessagingDelegate's"
+                           @"-messaging:didReceiveMessage: not implemented");
+  }
+}
+
+#pragma mark - GULReachabilityDelegate
+
+- (void)reachability:(GULReachabilityChecker *)reachability
+       statusChanged:(GULReachabilityStatus)status {
+  [self onNetworkStatusChanged];
+}
+
+#pragma mark - Network
+
+- (void)onNetworkStatusChanged {
+  if (![self.client isConnected] && [self isNetworkAvailable]) {
+    if (self.client.shouldStayConnected) {
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeMessaging014,
+                              @"Attempting to establish direct channel.");
+      [self.client retryConnectionImmediately:YES];
+    }
+    [self.pubsub scheduleSync:YES];
+  }
+}
+
+- (BOOL)isNetworkAvailable {
+  GULReachabilityStatus status = self.reachability.reachabilityStatus;
+  return (status == kGULReachabilityViaCellular || status == kGULReachabilityViaWifi);
+}
+
+- (FIRMessagingNetworkStatus)networkType {
+  GULReachabilityStatus status = self.reachability.reachabilityStatus;
+  if (![self isNetworkAvailable]) {
+    return kFIRMessagingReachabilityNotReachable;
+  } else if (status == kGULReachabilityViaCellular) {
+    return kFIRMessagingReachabilityReachableViaWWAN;
+  } else {
+    return kFIRMessagingReachabilityReachableViaWiFi;
+  }
+}
+
+#pragma mark - Notifications
+
+- (void)didReceiveDefaultInstanceIDToken:(NSNotification *)notification {
+  if (notification.object && ![notification.object isKindOfClass:[NSString class]]) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeMessaging015,
+                            @"Invalid default FCM token type %@",
+                            NSStringFromClass([notification.object class]));
+    return;
+  }
+  NSString *oldToken = self.defaultFcmToken;
+  self.defaultFcmToken = [(NSString *)notification.object copy];
+  if (self.defaultFcmToken && ![self.defaultFcmToken isEqualToString:oldToken]) {
+    [self notifyDelegateOfFCMTokenAvailability];
+  }
+  [self.pubsub scheduleSync:YES];
+  if (self.shouldEstablishDirectChannel) {
+    [self updateAutomaticClientConnection];
+  }
+}
+
+- (void)defaultInstanceIDTokenWasRefreshed:(NSNotification *)notification {
+  // Retrieve the Instance ID default token, and if it is non-nil, post it
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+  NSString *token = self.instanceID.token;
+#pragma clang diagnostic pop
+  // Sometimes Instance ID doesn't yet have a token, so wait until the default
+  // token is fetched, and then notify. This ensures that this token should not
+  // be nil when the developer accesses it.
+  if (token != nil) {
+    NSString *oldToken = self.defaultFcmToken;
+    self.defaultFcmToken = [token copy];
+    if (self.defaultFcmToken && ![self.defaultFcmToken isEqualToString:oldToken]) {
+      [self notifyDelegateOfFCMTokenAvailability];
+    }
+    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+    [center postNotificationName:FIRMessagingRegistrationTokenRefreshedNotification object:nil];
+  }
+}
+
+#pragma mark - Application Support Directory
+
++ (BOOL)hasApplicationSupportSubDirectory:(NSString *)subDirectoryName {
+  NSString *subDirectoryPath = [self pathForApplicationSupportSubDirectory:subDirectoryName];
+  BOOL isDirectory;
+  if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
+                                            isDirectory:&isDirectory]) {
+    return NO;
+  } else if (!isDirectory) {
+    return NO;
+  }
+  return YES;
+}
+
++ (NSString *)pathForApplicationSupportSubDirectory:(NSString *)subDirectoryName {
+  NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,
+                                                                NSUserDomainMask, YES);
+  NSString *applicationSupportDirPath = directoryPaths.lastObject;
+  NSArray *components = @[applicationSupportDirPath, subDirectoryName];
+  return [NSString pathWithComponents:components];
+}
+
++ (BOOL)createApplicationSupportSubDirectory:(NSString *)subDirectoryName {
+  NSString *subDirectoryPath = [self pathForApplicationSupportSubDirectory:subDirectoryName];
+  BOOL hasSubDirectory;
+
+  if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
+                                            isDirectory:&hasSubDirectory]) {
+    NSError *error;
+    [[NSFileManager defaultManager] createDirectoryAtPath:subDirectoryPath
+                              withIntermediateDirectories:YES
+                                               attributes:nil
+                                                    error:&error];
+    if (error) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging017,
+                              @"Cannot create directory %@, error: %@", subDirectoryPath, error);
+      return NO;
+    }
+  } else {
+    if (!hasSubDirectory) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging018,
+                              @"Found file instead of directory at %@", subDirectoryPath);
+      return NO;
+    }
+  }
+  return YES;
+}
+
+#pragma mark - Locales
+
++ (NSString *)currentLocale {
+  NSArray *locales = [self firebaseLocales];
+  NSArray *preferredLocalizations =
+    [NSBundle preferredLocalizationsFromArray:locales
+                               forPreferences:[NSLocale preferredLanguages]];
+  NSString *legalDocsLanguage = [preferredLocalizations firstObject];
+  // Use en as the default language
+  return legalDocsLanguage ? legalDocsLanguage : @"en";
+}
+
++ (NSArray *)firebaseLocales {
+  NSMutableArray *locales = [NSMutableArray array];
+  NSDictionary *localesMap = [self firebaselocalesMap];
+  for (NSString *key in localesMap) {
+    [locales addObjectsFromArray:localesMap[key]];
+  }
+  return locales;
+}
+
++ (NSDictionary *)firebaselocalesMap {
+  return @{
+    // Albanian
+    @"sq" : @[ @"sq_AL" ],
+    // Belarusian
+    @"be" : @[ @"be_BY" ],
+    // Bulgarian
+    @"bg" : @[ @"bg_BG" ],
+    // Catalan
+    @"ca" : @[ @"ca", @"ca_ES" ],
+    // Croatian
+    @"hr" : @[ @"hr", @"hr_HR" ],
+    // Czech
+    @"cs" : @[ @"cs", @"cs_CZ" ],
+    // Danish
+    @"da" : @[ @"da", @"da_DK" ],
+    // Estonian
+    @"et" : @[ @"et_EE" ],
+    // Finnish
+    @"fi" : @[ @"fi", @"fi_FI" ],
+    // Hebrew
+    @"he" : @[ @"he", @"iw_IL" ],
+    // Hindi
+    @"hi" : @[ @"hi_IN" ],
+    // Hungarian
+    @"hu" : @[ @"hu", @"hu_HU" ],
+    // Icelandic
+    @"is" : @[ @"is_IS" ],
+    // Indonesian
+    @"id" : @[ @"id", @"in_ID", @"id_ID" ],
+    // Irish
+    @"ga" : @[ @"ga_IE" ],
+    // Korean
+    @"ko" : @[ @"ko", @"ko_KR", @"ko-KR" ],
+    // Latvian
+    @"lv" : @[ @"lv_LV" ],
+    // Lithuanian
+    @"lt" : @[ @"lt_LT" ],
+    // Macedonian
+    @"mk" : @[ @"mk_MK" ],
+    // Malay
+    @"ms" : @[ @"ms_MY" ],
+    // Maltese
+    @"ms" : @[ @"mt_MT" ],
+    // Polish
+    @"pl" : @[ @"pl", @"pl_PL", @"pl-PL" ],
+    // Romanian
+    @"ro" : @[ @"ro", @"ro_RO" ],
+    // Russian
+    @"ru" : @[ @"ru_RU", @"ru", @"ru_BY", @"ru_KZ", @"ru-RU" ],
+    // Slovak
+    @"sk" : @[ @"sk", @"sk_SK" ],
+    // Slovenian
+    @"sl" : @[ @"sl_SI" ],
+    // Swedish
+    @"sv" : @[ @"sv", @"sv_SE", @"sv-SE" ],
+    // Turkish
+    @"tr" : @[ @"tr", @"tr-TR", @"tr_TR" ],
+    // Ukrainian
+    @"uk" : @[ @"uk", @"uk_UA" ],
+    // Vietnamese
+    @"vi" : @[ @"vi", @"vi_VN" ],
+    // The following are groups of locales or locales that sub-divide a
+    // language).
+    // Arabic
+    @"ar" : @[
+      @"ar",
+      @"ar_DZ",
+      @"ar_BH",
+      @"ar_EG",
+      @"ar_IQ",
+      @"ar_JO",
+      @"ar_KW",
+      @"ar_LB",
+      @"ar_LY",
+      @"ar_MA",
+      @"ar_OM",
+      @"ar_QA",
+      @"ar_SA",
+      @"ar_SD",
+      @"ar_SY",
+      @"ar_TN",
+      @"ar_AE",
+      @"ar_YE",
+      @"ar_GB",
+      @"ar-IQ",
+      @"ar_US"
+    ],
+    // Simplified Chinese
+    @"zh_Hans" : @[ @"zh_CN", @"zh_SG", @"zh-Hans" ],
+    // Traditional Chinese
+    @"zh_Hant" : @[ @"zh_HK", @"zh_TW", @"zh-Hant", @"zh-HK", @"zh-TW" ],
+    // Dutch
+    @"nl" : @[ @"nl", @"nl_BE", @"nl_NL", @"nl-NL" ],
+    // English
+    @"en" : @[
+      @"en",
+      @"en_AU",
+      @"en_CA",
+      @"en_IN",
+      @"en_IE",
+      @"en_MT",
+      @"en_NZ",
+      @"en_PH",
+      @"en_SG",
+      @"en_ZA",
+      @"en_GB",
+      @"en_US",
+      @"en_AE",
+      @"en-AE",
+      @"en_AS",
+      @"en-AU",
+      @"en_BD",
+      @"en-CA",
+      @"en_EG",
+      @"en_ES",
+      @"en_GB",
+      @"en-GB",
+      @"en_HK",
+      @"en_ID",
+      @"en-IN",
+      @"en_NG",
+      @"en-PH",
+      @"en_PK",
+      @"en-SG",
+      @"en-US"
+    ],
+    // French
+
+    @"fr" : @[
+      @"fr",
+      @"fr_BE",
+      @"fr_CA",
+      @"fr_FR",
+      @"fr_LU",
+      @"fr_CH",
+      @"fr-CA",
+      @"fr-FR",
+      @"fr_MA"
+    ],
+    // German
+    @"de" : @[ @"de", @"de_AT", @"de_DE", @"de_LU", @"de_CH", @"de-DE" ],
+    // Greek
+    @"el" : @[ @"el", @"el_CY", @"el_GR" ],
+    // Italian
+    @"it" : @[ @"it", @"it_IT", @"it_CH", @"it-IT" ],
+    // Japanese
+    @"ja" : @[ @"ja", @"ja_JP", @"ja_JP_JP", @"ja-JP" ],
+    // Norwegian
+    @"no" : @[ @"nb", @"no_NO", @"no_NO_NY", @"nb_NO" ],
+    // Brazilian Portuguese
+    @"pt_BR" : @[ @"pt_BR", @"pt-BR" ],
+    // European Portuguese
+    @"pt_PT" : @[ @"pt", @"pt_PT", @"pt-PT" ],
+    // Serbian
+    @"sr" : @[
+      @"sr_BA",
+      @"sr_ME",
+      @"sr_RS",
+      @"sr_Latn_BA",
+      @"sr_Latn_ME",
+      @"sr_Latn_RS"
+    ],
+    // European Spanish
+    @"es_ES" : @[ @"es", @"es_ES", @"es-ES" ],
+    // Mexican Spanish
+    @"es_MX" : @[ @"es-MX", @"es_MX", @"es_US", @"es-US" ],
+    // Latin American Spanish
+    @"es_419" : @[
+      @"es_AR",
+      @"es_BO",
+      @"es_CL",
+      @"es_CO",
+      @"es_CR",
+      @"es_DO",
+      @"es_EC",
+      @"es_SV",
+      @"es_GT",
+      @"es_HN",
+      @"es_NI",
+      @"es_PA",
+      @"es_PY",
+      @"es_PE",
+      @"es_PR",
+      @"es_UY",
+      @"es_VE",
+      @"es-AR",
+      @"es-CL",
+      @"es-CO"
+    ],
+    // Thai
+    @"th" : @[ @"th", @"th_TH", @"th_TH_TH" ],
+  };
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCheckinService.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCheckinService.h
new file mode 100644 (file)
index 0000000..155143a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+/**
+ * Register the device with Checkin Service and get back the `authID`, `secret token` etc. for the
+ * client. Checkin results are cached in the `FIRMessagingDefaultsManager` and periodically refreshed to
+ * prevent them from being stale. Each client needs to register with checkin before registering
+ * with FIRMessaging.
+ */
+@interface FIRMessagingCheckinService : NSObject
+
+@property(nonatomic, readonly, strong) NSString *deviceAuthID;
+@property(nonatomic, readonly, strong) NSString *secretToken;
+@property(nonatomic, readonly, strong) NSString *versionInfo;
+@property(nonatomic, readonly, assign) BOOL hasValidCheckinInfo;
+
+/**
+ *  Verify if valid checkin preferences have been loaded in memory.
+ *
+ *  @return YES if valid checkin preferences exist in memory else NO.
+ */
+- (BOOL)hasValidCheckinInfo;
+
+/**
+ *  Try to load prefetched checkin preferences from the cache. This supports the use case where
+ *  InstanceID library has already obtained a valid checkin and we should be using that.
+ *
+ *  This should be used as a last gasp effort to retreive any cached checkin preferences before
+ *  hitting the FIRMessaging backend to retrieve new preferences.
+ *
+ *  Note this is only required because InstanceID and FIRMessaging both require checkin preferences which
+ *  need to be synced with each other.
+ *
+ *  @return YES if successfully loaded cached checkin preferences into memory else NO.
+ */
+- (BOOL)tryToLoadPrefetchedCheckinPreferences;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCheckinService.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCheckinService.m
new file mode 100644 (file)
index 0000000..9dad847
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingCheckinService.h"
+
+#import "FIRMessagingUtilities.h"
+#import "NSError+FIRMessaging.h"
+
+@interface FIRMessagingCheckinService ()
+
+// This property is of type FIRInstanceIDCheckinPreferences, if InstanceID was directly linkable
+@property(nonatomic, readwrite, strong) id checkinPreferences;
+
+@end
+
+@implementation FIRMessagingCheckinService;
+
+#pragma mark - Reflection-Based Getter Functions
+
+// Encapsulates the -hasValidCheckinInfo method of FIRInstanceIDCheckinPreferences
+BOOL FIRMessagingCheckinService_hasValidCheckinInfo(id checkinPreferences) {
+  SEL hasValidCheckinInfoSelector = NSSelectorFromString(@"hasValidCheckinInfo");
+  if (![checkinPreferences respondsToSelector:hasValidCheckinInfoSelector]) {
+    // Can't check hasValidCheckinInfo
+    return NO;
+  }
+
+  // Since hasValidCheckinInfo returns a BOOL, use NSInvocation
+  NSMethodSignature *methodSignature =
+      [[checkinPreferences class] instanceMethodSignatureForSelector:hasValidCheckinInfoSelector];
+  NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
+  invocation.selector = hasValidCheckinInfoSelector;
+  invocation.target = checkinPreferences;
+  [invocation invoke];
+  BOOL returnValue;
+  [invocation getReturnValue:&returnValue];
+  return returnValue;
+}
+
+// Returns a non-scalar (id) object based on the property name
+id FIRMessagingCheckinService_propertyNamed(id checkinPreferences, NSString *propertyName) {
+  SEL propertyGetterSelector = NSSelectorFromString(propertyName);
+  if ([checkinPreferences respondsToSelector:propertyGetterSelector]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+    return [checkinPreferences performSelector:propertyGetterSelector];
+#pragma clang diagnostic pop
+  }
+  return nil;
+}
+
+#pragma mark - Methods
+
+- (BOOL)tryToLoadPrefetchedCheckinPreferences {
+  Class instanceIDClass = NSClassFromString(@"FIRInstanceID");
+  if (!instanceIDClass) {
+    // InstanceID is not linked
+    return NO;
+  }
+
+  // [FIRInstanceID instanceID]
+  SEL instanceIDSelector = NSSelectorFromString(@"instanceID");
+  if (![instanceIDClass respondsToSelector:instanceIDSelector]) {
+    return NO;
+  }
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+  id instanceID = [instanceIDClass performSelector:instanceIDSelector];
+#pragma clang diagnostic pop
+  if (!instanceID) {
+    // Instance ID singleton not available
+    return NO;
+  }
+
+  // [[FIRInstanceID instanceID] cachedCheckinPreferences]
+  SEL cachedCheckinPrefsSelector = NSSelectorFromString(@"cachedCheckinPreferences");
+  if (![instanceID respondsToSelector:cachedCheckinPrefsSelector]) {
+    // cachedCheckinPreferences is not accessible
+    return NO;
+  }
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+  id checkinPreferences = [instanceID performSelector:cachedCheckinPrefsSelector];
+#pragma clang diagnostic pop
+  if (!checkinPreferences) {
+    // No cached checkin prefs
+    return NO;
+  }
+
+  BOOL hasValidInfo = FIRMessagingCheckinService_hasValidCheckinInfo(checkinPreferences);
+  if (hasValidInfo) {
+    self.checkinPreferences = checkinPreferences;
+  }
+  return hasValidInfo;
+}
+
+#pragma mark - API
+
+- (NSString *)deviceAuthID {
+  return FIRMessagingCheckinService_propertyNamed(self.checkinPreferences, @"deviceID");
+}
+
+- (NSString *)secretToken {
+  return FIRMessagingCheckinService_propertyNamed(self.checkinPreferences, @"secretToken");
+}
+
+- (NSString *)versionInfo {
+  return FIRMessagingCheckinService_propertyNamed(self.checkinPreferences, @"versionInfo");
+}
+
+- (NSString *)digest {
+  return FIRMessagingCheckinService_propertyNamed(self.checkinPreferences, @"digest");
+}
+
+- (BOOL)hasValidCheckinInfo {
+  return FIRMessagingCheckinService_hasValidCheckinInfo(self.checkinPreferences);
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingClient.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingClient.h
new file mode 100644 (file)
index 0000000..98337a3
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessaging.h"
+
+@class GULReachabilityChecker;
+@class GPBMessage;
+
+@class FIRMessagingConnection;
+@class FIRMessagingDataMessageManager;
+@class FIRMessagingRmqManager;
+
+/**
+ *  Callback to handle MCS connection requests.
+ *
+ *  @param error The error object if any while trying to connect with MCS else nil.
+ */
+typedef void(^FIRMessagingConnectCompletionHandler)(NSError *error);
+
+@protocol FIRMessagingClientDelegate <NSObject>
+
+@end
+
+/**
+ * The client handles the subscribe/unsubscribe for an unregistered senderID
+ * and device. It also manages the FIRMessaging data connection, the exponential backoff
+ * algorithm in case of registration failures, sign in failures and unregister
+ * failures. It also handles the reconnect logic if the FIRMessaging connection is
+ * broken off by some error during an active session.
+ */
+@interface FIRMessagingClient : NSObject
+
+@property(nonatomic, readonly, strong) FIRMessagingConnection *connection;
+@property(nonatomic, readwrite, weak) FIRMessagingDataMessageManager *dataMessageManager;
+
+// Designated initializer
+- (instancetype)initWithDelegate:(id<FIRMessagingClientDelegate>)delegate
+                    reachability:(GULReachabilityChecker *)reachability
+                     rmq2Manager:(FIRMessagingRmqManager *)rmq2Manager;
+
+- (void)teardown;
+
+- (void)cancelAllRequests;
+
+#pragma mark - FIRMessaging subscribe
+
+/**
+ *  Update the subscription associated with the given token and topic.
+ *
+ *  For a to-be-created subscription we check if the client is already
+ *  subscribed to the topic or not. If subscribed we should have the
+ *  subscriptionID in the cache and we return from there itself, else we call
+ *  the FIRMessaging backend to create a new subscription for the topic for this client.
+ *
+ *  For delete subscription requests we delete the stored subscription in the
+ *  client and then invoke the FIRMessaging backend to delete the existing subscription
+ *  completely.
+ *
+ *  @param token        The token associated with the device.
+ *  @param topic        The topic for which the subscription should be updated.
+ *  @param options      The options to be passed in to the subscription request.
+ *  @param shouldDelete If YES this would delete the subscription from the cache
+ *                      and also let the FIRMessaging backend know that we need to delete
+ *                      the subscriptionID associated with this topic.
+ *                      If NO we try to create a new subscription for the given
+ *                      token and topic.
+ *  @param handler      The handler to invoke once the subscription request
+ *                      finishes.
+ */
+- (void)updateSubscriptionWithToken:(NSString *)token
+                              topic:(NSString *)topic
+                            options:(NSDictionary *)options
+                       shouldDelete:(BOOL)shouldDelete
+                            handler:(FIRMessagingTopicOperationCompletion)handler;
+
+#pragma mark - MCS Connection
+
+/**
+ *  Create a MCS connection.
+ *
+ *  @param handler  The handler to be invokend once the connection is setup. If
+ *                  setting up the connection fails we invoke the handler with
+ *                  an appropriate error object.
+ */
+- (void)connectWithHandler:(FIRMessagingConnectCompletionHandler)handler;
+
+/**
+ *  Disconnect the current MCS connection. If there is no valid connection this
+ *  should be a NO-OP.
+ */
+- (void)disconnect;
+
+#pragma mark - MCS Connection State
+
+/**
+ *  If we are connected to MCS or not. This doesn't take into account the fact if
+ *  the client has been signed in(verified) by MCS.
+ *
+ *  @return YES if we are signed in or connecting and trying to sign-in else NO.
+ */
+@property(nonatomic, readonly) BOOL isConnected;
+
+/**
+ *  If we have an active MCS connection
+ *
+ *  @return YES if we have an active MCS connection else NO.
+ */
+@property(nonatomic, readonly) BOOL isConnectionActive;
+
+/**
+ *  If we should be connected to MCS
+ *
+ *  @return YES if we have attempted a connection and not requested to disconect.
+ */
+@property(nonatomic, readonly) BOOL shouldStayConnected;
+
+/**
+ *  Schedule a retry to connect to MCS. If `immediately` is `YES` try to
+ *  schedule a retry now else retry with some delay.
+ *
+ *  @param immediately Should retry right now.
+ */
+- (void)retryConnectionImmediately:(BOOL)immediately;
+
+#pragma mark - Messages
+
+/**
+ *  Send a message over the MCS connection.
+ *
+ *  @param message Message to be sent.
+ */
+- (void)sendMessage:(GPBMessage *)message;
+
+/**
+ *  Send message if we have an active MCS connection. If not cache the  message
+ *  for this session and in case we are able to re-establish the connection try
+ *  again else drop it. This should only be used for TTL=0 messages for now.
+ *
+ *  @param message Message to be sent.
+ */
+- (void)sendOnConnectOrDrop:(GPBMessage *)message;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingClient.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingClient.m
new file mode 100644 (file)
index 0000000..9d8c558
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingClient.h"
+
+#import <GoogleUtilities/GULReachabilityChecker.h>
+
+#import "FIRMessaging.h"
+#import "FIRMessagingConnection.h"
+#import "FIRMessagingConstants.h"
+#import "FIRMessagingDataMessageManager.h"
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingRegistrar.h"
+#import "FIRMessagingRmqManager.h"
+#import "FIRMessagingTopicsCommon.h"
+#import "FIRMessagingUtilities.h"
+#import "NSError+FIRMessaging.h"
+
+static const NSTimeInterval kConnectTimeoutInterval = 40.0;
+static const NSTimeInterval kReconnectDelayInSeconds = 2 * 60; // 2 minutes
+
+static const NSUInteger kMaxRetryExponent = 10;  // 2^10 = 1024 seconds ~= 17 minutes
+
+static NSString *const kFIRMessagingMCSServerHost = @"mtalk.google.com";
+static NSUInteger const kFIRMessagingMCSServerPort = 5228;
+
+// register device with checkin
+typedef void(^FIRMessagingRegisterDeviceHandler)(NSError *error);
+
+static NSString *FIRMessagingServerHost() {
+  static NSString *serverHost = nil;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    NSDictionary *environment = [[NSProcessInfo processInfo] environment];
+    NSString *customServerHostAndPort = environment[@"FCM_MCS_HOST"];
+    NSString *host = [customServerHostAndPort componentsSeparatedByString:@":"].firstObject;
+    if (host) {
+      serverHost = host;
+    } else {
+      serverHost = kFIRMessagingMCSServerHost;
+    }
+  });
+  return serverHost;
+}
+
+static NSUInteger FIRMessagingServerPort() {
+  static NSUInteger serverPort = kFIRMessagingMCSServerPort;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    NSDictionary *environment = [[NSProcessInfo processInfo] environment];
+    NSString *customServerHostAndPort = environment[@"FCM_MCS_HOST"];
+    NSArray<NSString *> *components = [customServerHostAndPort componentsSeparatedByString:@":"];
+    NSUInteger port = (NSUInteger)[components.lastObject integerValue];
+    if (port != 0) {
+      serverPort = port;
+    }
+  });
+  return serverPort;
+}
+
+@interface FIRMessagingClient () <FIRMessagingConnectionDelegate>
+
+@property(nonatomic, readwrite, weak) id<FIRMessagingClientDelegate> clientDelegate;
+@property(nonatomic, readwrite, strong) FIRMessagingConnection *connection;
+@property(nonatomic, readwrite, strong) FIRMessagingRegistrar *registrar;
+
+@property(nonatomic, readwrite, strong) NSString *senderId;
+
+// FIRMessagingService owns these instances
+@property(nonatomic, readwrite, weak) FIRMessagingRmqManager *rmq2Manager;
+@property(nonatomic, readwrite, weak) GULReachabilityChecker *reachability;
+
+@property(nonatomic, readwrite, assign) int64_t lastConnectedTimestamp;
+@property(nonatomic, readwrite, assign) int64_t lastDisconnectedTimestamp;
+@property(nonatomic, readwrite, assign) NSUInteger connectRetryCount;
+
+// Should we stay connected to MCS or not. Should be YES throughout the lifetime
+// of a MCS connection. If set to NO it signifies that an existing MCS connection
+// should be disconnected.
+@property(nonatomic, readwrite, assign) BOOL stayConnected;
+@property(nonatomic, readwrite, assign) NSTimeInterval connectionTimeoutInterval;
+
+// Used if the MCS connection suddenly breaksdown in the middle and we want to reconnect
+// with some permissible delay we schedule a reconnect and set it to YES and when it's
+// scheduled this will be set back to NO.
+@property(nonatomic, readwrite, assign) BOOL didScheduleReconnect;
+
+// handlers
+@property(nonatomic, readwrite, copy) FIRMessagingConnectCompletionHandler connectHandler;
+
+@end
+
+@implementation FIRMessagingClient
+
+- (instancetype)init {
+  FIRMessagingInvalidateInitializer();
+}
+
+- (instancetype)initWithDelegate:(id<FIRMessagingClientDelegate>)delegate
+                    reachability:(GULReachabilityChecker *)reachability
+                     rmq2Manager:(FIRMessagingRmqManager *)rmq2Manager {
+  self = [super init];
+  if (self) {
+    _reachability = reachability;
+    _clientDelegate = delegate;
+    _rmq2Manager = rmq2Manager;
+    _registrar = [[FIRMessagingRegistrar alloc] init];
+    _connectionTimeoutInterval = kConnectTimeoutInterval;
+    // Listen for checkin fetch notifications, as connecting to MCS may have failed due to
+    // missing checkin info (while it was being fetched).
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(checkinFetched:)
+                                                 name:kFIRMessagingCheckinFetchedNotification
+                                               object:nil];
+  }
+  return self;
+}
+
+- (void)teardown {
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeClient000, @"");
+  self.stayConnected = NO;
+
+  // Clear all the handlers
+  self.connectHandler = nil;
+
+  [self.connection teardown];
+
+  // Stop all subscription requests
+  [self.registrar cancelAllRequests];
+
+  _FIRMessagingDevAssert(self.connection.state == kFIRMessagingConnectionNotConnected, @"Did not disconnect");
+  [NSObject cancelPreviousPerformRequestsWithTarget:self];
+
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)cancelAllRequests {
+  // Stop any checkin requests or any subscription requests
+  [self.registrar cancelAllRequests];
+
+  // Stop any future connection requests to MCS
+  if (self.stayConnected && self.isConnected && !self.isConnectionActive) {
+    self.stayConnected = NO;
+    [NSObject cancelPreviousPerformRequestsWithTarget:self];
+  }
+}
+
+#pragma mark - FIRMessaging subscribe
+
+- (void)updateSubscriptionWithToken:(NSString *)token
+                              topic:(NSString *)topic
+                            options:(NSDictionary *)options
+                       shouldDelete:(BOOL)shouldDelete
+                            handler:(FIRMessagingTopicOperationCompletion)handler {
+
+  _FIRMessagingDevAssert(handler != nil, @"Invalid handler to FIRMessaging subscribe");
+
+  FIRMessagingTopicOperationCompletion completion = ^void(NSError *error) {
+    if (error) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeClient001, @"Failed to subscribe to topic %@",
+                              error);
+    } else {
+      if (shouldDelete) {
+        FIRMessagingLoggerInfo(kFIRMessagingMessageCodeClient002,
+                               @"Successfully unsubscribed from topic %@", topic);
+      } else {
+        FIRMessagingLoggerInfo(kFIRMessagingMessageCodeClient003,
+                               @"Successfully subscribed to topic %@", topic);
+      }
+    }
+    handler(error);
+  };
+
+  [self.registrar updateSubscriptionToTopic:topic
+                                  withToken:token
+                                    options:options
+                               shouldDelete:shouldDelete
+                                    handler:completion];
+}
+
+#pragma mark - MCS Connection
+
+- (BOOL)isConnected {
+  return self.stayConnected && self.connection.state != kFIRMessagingConnectionNotConnected;
+}
+
+- (BOOL)isConnectionActive {
+  return self.stayConnected && self.connection.state == kFIRMessagingConnectionSignedIn;
+}
+
+- (BOOL)shouldStayConnected {
+  return self.stayConnected;
+}
+
+- (void)retryConnectionImmediately:(BOOL)immediately {
+  // Do not connect to an invalid host or an invalid port
+  if (!self.stayConnected || !self.connection.host || self.connection.port == 0) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeClient004,
+                            @"FIRMessaging connection will not reconnect to MCS. "
+                            @"Stay connected: %d",
+                            self.stayConnected);
+    return;
+  }
+  if (self.isConnectionActive) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeClient005,
+                            @"FIRMessaging Connection skip retry, active");
+    // already connected and logged in.
+    // Heartbeat alarm is set and will force close the connection
+    return;
+  }
+  if (self.isConnected) {
+    // already connected and logged in.
+    // Heartbeat alarm is set and will force close the connection
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeClient006,
+                            @"FIRMessaging Connection skip retry, connected");
+    return;
+  }
+
+  if (immediately) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeClient007,
+                            @"Try to connect to MCS immediately");
+    [self tryToConnect];
+  } else {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeClient008, @"Try to connect to MCS lazily");
+    // Avoid all the other logic that we have in other clients, since this would always happen
+    // when the app is in the foreground and since the FIRMessaging connection isn't shared with any other
+    // app we can be more aggressive in reconnections
+    if (!self.didScheduleReconnect) {
+      FIRMessaging_WEAKIFY(self);
+      dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
+                                   (int64_t)(kReconnectDelayInSeconds * NSEC_PER_SEC)),
+                     dispatch_get_main_queue(), ^{
+                         FIRMessaging_STRONGIFY(self);
+                         self.didScheduleReconnect = NO;
+                         [self tryToConnect];
+                     });
+
+      self.didScheduleReconnect = YES;
+    }
+  }
+}
+
+- (void)connectWithHandler:(FIRMessagingConnectCompletionHandler)handler {
+  if (self.isConnected) {
+    NSError *error = [NSError fcm_errorWithCode:kFIRMessagingErrorCodeAlreadyConnected
+                                       userInfo:@{
+        NSLocalizedFailureReasonErrorKey: @"FIRMessaging is already connected",
+        }];
+    handler(error);
+    return;
+  }
+  self.lastDisconnectedTimestamp = FIRMessagingCurrentTimestampInMilliseconds();
+  self.connectHandler = handler;
+  [self connect];
+}
+
+- (void)connect {
+  // reset retry counts
+  self.connectRetryCount = 0;
+
+  if (self.isConnected) {
+    return;
+  }
+
+  self.stayConnected = YES;
+  if (![self.registrar tryToLoadValidCheckinInfo]) {
+    // Checkin info is not available. This may be due to the checkin still being fetched.
+    if (self.connectHandler) {
+      NSError *error = [NSError errorWithFCMErrorCode:kFIRMessagingErrorCodeMissingDeviceID];
+      self.connectHandler(error);
+    }
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeClient009,
+                            @"Failed to connect to MCS. No deviceID and secret found.");
+    // Return for now. If checkin is, in fact, retrieved, the
+    // |kFIRMessagingCheckinFetchedNotification| will be fired.
+    return;
+  }
+  [self setupConnectionAndConnect];
+}
+
+- (void)disconnect {
+  // user called disconnect
+  // We don't want to connect later even if no network is available.
+  [self disconnectWithTryToConnectLater:NO];
+}
+
+/**
+ *  Disconnect the current client connection. Also explicitly stop and connction retries.
+ *
+ *  @param tryToConnectLater If YES will try to connect later when sending upstream messages
+ *                           else if NO do not connect again until user explicitly calls
+ *                           connect.
+ */
+- (void)disconnectWithTryToConnectLater:(BOOL)tryToConnectLater {
+
+  self.stayConnected = tryToConnectLater;
+  [self.connection signOut];
+  _FIRMessagingDevAssert(self.connection.state == kFIRMessagingConnectionNotConnected,
+                @"FIRMessaging connection did not disconnect");
+
+  // since we can disconnect while still trying to establish the connection it's required to
+  // cancel all performSelectors else the object might be retained
+  [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                           selector:@selector(tryToConnect)
+                                             object:nil];
+  [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                           selector:@selector(didConnectTimeout)
+                                             object:nil];
+  self.connectHandler = nil;
+}
+
+#pragma mark - Checkin Notification
+- (void)checkinFetched:(NSNotification *)notification {
+  // A failed checkin may have been the reason for the connection failure. Attempt a connection
+  // if the checkin fetched notification is fired.
+  if (self.stayConnected && !self.isConnected) {
+    [self connect];
+  }
+}
+
+#pragma mark - Messages
+
+- (void)sendMessage:(GPBMessage *)message {
+  [self.connection sendProto:message];
+}
+
+- (void)sendOnConnectOrDrop:(GPBMessage *)message {
+  [self.connection sendOnConnectOrDrop:message];
+}
+
+#pragma mark - FIRMessagingConnectionDelegate
+
+- (void)connection:(FIRMessagingConnection *)fcmConnection
+    didCloseForReason:(FIRMessagingConnectionCloseReason)reason {
+
+  self.lastDisconnectedTimestamp = FIRMessagingCurrentTimestampInMilliseconds();
+
+  if (reason == kFIRMessagingConnectionCloseReasonSocketDisconnected) {
+    // Cancel the not-yet-triggered timeout task before rescheduling, in case the previous sign in
+    // failed, due to a connection error caused by bad network.
+    [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                             selector:@selector(didConnectTimeout)
+                                               object:nil];
+  }
+  if (self.stayConnected) {
+    [self scheduleConnectRetry];
+  }
+}
+
+- (void)didLoginWithConnection:(FIRMessagingConnection *)fcmConnection {
+  // Cancel the not-yet-triggered timeout task.
+  [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                           selector:@selector(didConnectTimeout)
+                                             object:nil];
+  self.connectRetryCount = 0;
+  self.lastConnectedTimestamp = FIRMessagingCurrentTimestampInMilliseconds();
+
+
+  [self.dataMessageManager setDeviceAuthID:self.registrar.deviceAuthID
+                               secretToken:self.registrar.secretToken];
+  if (self.connectHandler) {
+    self.connectHandler(nil);
+    // notified the third party app with the registrationId.
+    // we don't want them to know about the connection status and how it changes
+    // so remove this handler
+    self.connectHandler = nil;
+  }
+}
+
+- (void)connectionDidRecieveMessage:(GtalkDataMessageStanza *)message {
+  NSDictionary *parsedMessage = [self.dataMessageManager processPacket:message];
+  if ([parsedMessage count]) {
+    [self.dataMessageManager didReceiveParsedMessage:parsedMessage];
+  }
+}
+
+- (int)connectionDidReceiveAckForRmqIds:(NSArray *)rmqIds {
+  NSSet *rmqIDSet = [NSSet setWithArray:rmqIds];
+  NSMutableArray *messagesSent = [NSMutableArray arrayWithCapacity:rmqIds.count];
+  [self.rmq2Manager scanWithRmqMessageHandler:nil
+                           dataMessageHandler:^(int64_t rmqId, GtalkDataMessageStanza *stanza) {
+                             NSString *rmqIdString = [NSString stringWithFormat:@"%lld", rmqId];
+                             if ([rmqIDSet containsObject:rmqIdString]) {
+                               [messagesSent addObject:stanza];
+                             }
+                           }];
+  for (GtalkDataMessageStanza *message in messagesSent) {
+    [self.dataMessageManager didSendDataMessageStanza:message];
+  }
+  return [self.rmq2Manager removeRmqMessagesWithRmqIds:rmqIds];
+}
+
+#pragma mark - Private
+
+- (void)setupConnectionAndConnect {
+  [self setupConnection];
+  [self tryToConnect];
+}
+
+- (void)setupConnection {
+  NSString *host = FIRMessagingServerHost();
+  NSUInteger port = FIRMessagingServerPort();
+  _FIRMessagingDevAssert([host length] > 0 && port != 0, @"Invalid port or host");
+
+  if (self.connection != nil) {
+    // if there is an old connection, explicitly sign it off.
+    [self.connection signOut];
+    self.connection.delegate = nil;
+  }
+  self.connection = [[FIRMessagingConnection alloc] initWithAuthID:self.registrar.deviceAuthID
+                                                    token:self.registrar.secretToken
+                                                     host:host
+                                                     port:port
+                                                  runLoop:[NSRunLoop mainRunLoop]
+                                              rmq2Manager:self.rmq2Manager
+                                               fcmManager:self.dataMessageManager];
+  self.connection.delegate = self;
+}
+
+- (void)tryToConnect {
+  if (!self.stayConnected) {
+    return;
+  }
+
+  // Cancel any other pending signin requests.
+  [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                           selector:@selector(tryToConnect)
+                                             object:nil];
+
+  // Do not re-sign in if there is already a connection in progress.
+  if (self.connection.state != kFIRMessagingConnectionNotConnected) {
+    return;
+  }
+
+  _FIRMessagingDevAssert(self.registrar.deviceAuthID.length > 0 &&
+                 self.registrar.secretToken.length > 0 &&
+                 self.connection != nil,
+                 @"Invalid state cannot connect");
+
+  self.connectRetryCount = MIN(kMaxRetryExponent, self.connectRetryCount + 1);
+  [self performSelector:@selector(didConnectTimeout)
+             withObject:nil
+             afterDelay:self.connectionTimeoutInterval];
+  [self.connection signIn];
+}
+
+- (void)didConnectTimeout {
+  _FIRMessagingDevAssert(self.connection.state != kFIRMessagingConnectionSignedIn,
+                @"Invalid state for MCS connection");
+
+  if (self.stayConnected) {
+    [self.connection signOut];
+    [self scheduleConnectRetry];
+  }
+}
+
+#pragma mark - Schedulers
+
+- (void)scheduleConnectRetry {
+  GULReachabilityStatus status = self.reachability.reachabilityStatus;
+  BOOL isReachable = (status == kGULReachabilityViaWifi || status == kGULReachabilityViaCellular);
+  if (!isReachable) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeClient010,
+                            @"Internet not reachable when signing into MCS during a retry");
+
+    FIRMessagingConnectCompletionHandler handler = [self.connectHandler copy];
+    // disconnect before issuing a callback
+    [self disconnectWithTryToConnectLater:YES];
+    NSError *error =
+        [NSError errorWithDomain:@"No internet available, cannot connect to FIRMessaging"
+                            code:kFIRMessagingErrorCodeNetwork
+                        userInfo:nil];
+    if (handler) {
+      handler(error);
+      self.connectHandler = nil;
+    }
+    return;
+  }
+
+  NSUInteger retryInterval = [self nextRetryInterval];
+
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeClient011,
+                          @"Failed to sign in to MCS, retry in %lu seconds",
+                          _FIRMessaging_UL(retryInterval));
+  [self performSelector:@selector(tryToConnect) withObject:nil afterDelay:retryInterval];
+}
+
+- (NSUInteger)nextRetryInterval {
+  return 1u << self.connectRetryCount;
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCodedInputStream.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCodedInputStream.h
new file mode 100644 (file)
index 0000000..8f22290
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@interface FIRMessagingCodedInputStream : NSObject
+
+@property(nonatomic, readonly, assign) size_t offset;
+
+- (instancetype)initWithData:(NSData *)data;
+- (BOOL)readTag:(int8_t *)tag;
+- (BOOL)readLength:(int32_t *)length;
+- (NSData *)readDataWithLength:(uint32_t)length;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCodedInputStream.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingCodedInputStream.m
new file mode 100644 (file)
index 0000000..82c0677
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingCodedInputStream.h"
+#import "FIRMessagingDefines.h"
+
+typedef struct {
+  const void *bytes;
+  size_t bufferSize;
+  size_t bufferPos;
+} BufferState;
+
+static BOOL CheckSize(BufferState *state, size_t size) {
+  size_t newSize = state->bufferPos + size;
+  if (newSize > state->bufferSize) {
+    return NO;
+  }
+  return YES;
+}
+
+static BOOL ReadRawByte(BufferState *state, int8_t *output) {
+  _FIRMessagingDevAssert(output != NULL && state != NULL, @"Invalid parameters");
+
+  if (CheckSize(state, sizeof(int8_t))) {
+    *output = ((int8_t *)state->bytes)[state->bufferPos++];
+    return YES;
+  }
+  return NO;
+}
+
+static BOOL ReadRawVarInt32(BufferState *state, int32_t *output) {
+  _FIRMessagingDevAssert(output != NULL && state != NULL, @"Invalid parameters");
+
+  int8_t tmp = 0;
+  if (!ReadRawByte(state, &tmp)) {
+    return NO;
+  }
+  if (tmp >= 0) {
+    *output = tmp;
+    return YES;
+  }
+  int32_t result = tmp & 0x7f;
+  if (!ReadRawByte(state, &tmp)) {
+    return NO;
+  }
+  if (tmp >= 0) {
+    result |= tmp << 7;
+  } else {
+    result |= (tmp & 0x7f) << 7;
+    if (!ReadRawByte(state, &tmp)) {
+      return NO;
+    }
+    if (tmp >= 0) {
+      result |= tmp << 14;
+    } else {
+      result |= (tmp & 0x7f) << 14;
+      if (!ReadRawByte(state, &tmp)) {
+        return NO;
+      }
+      if (tmp >= 0) {
+        result |= tmp << 21;
+      } else {
+        result |= (tmp & 0x7f) << 21;
+        if (!ReadRawByte(state, &tmp)) {
+          return NO;
+        }
+        result |= tmp << 28;
+        if (tmp < 0) {
+          // Discard upper 32 bits.
+          for (int i = 0; i < 5; ++i) {
+            if (!ReadRawByte(state, &tmp)) {
+              return NO;
+            }
+            if (tmp >= 0) {
+              *output = result;
+              return YES;
+            }
+          }
+          return NO;
+        }
+      }
+    }
+  }
+  *output = result;
+  return YES;
+}
+
+@interface FIRMessagingCodedInputStream()
+
+@property(nonatomic, readwrite, strong) NSData *buffer;
+@property(nonatomic, readwrite, assign) BufferState state;
+
+@end
+
+@implementation FIRMessagingCodedInputStream;
+
+- (instancetype)initWithData:(NSData *)data {
+  self = [super init];
+  if (self) {
+    _buffer = data;
+    _state.bytes = _buffer.bytes;
+    _state.bufferSize = _buffer.length;
+  }
+  return self;
+}
+
+- (size_t)offset {
+  return _state.bufferPos;
+}
+
+- (BOOL)readTag:(int8_t *)tag {
+  return ReadRawByte(&_state, tag);
+}
+
+- (BOOL)readLength:(int32_t *)length {
+  return ReadRawVarInt32(&_state, length);
+}
+
+- (NSData *)readDataWithLength:(uint32_t)length {
+  if (!CheckSize(&_state, length)) {
+    return nil;
+  }
+  const void *bytesToRead = _state.bytes + _state.bufferPos;
+  NSData *result = [NSData dataWithBytes:bytesToRead length:length];
+  _state.bufferPos += length;
+  return result;
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConnection.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConnection.h
new file mode 100644 (file)
index 0000000..25a018c
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@class FIRMessagingConnection;
+@class FIRMessagingDataMessageManager;
+@class FIRMessagingRmqManager;
+
+@class GtalkDataMessageStanza;
+@class GPBMessage;
+
+typedef void (^FIRMessagingMessageHandler)(NSDictionary *);
+
+typedef NS_ENUM(NSUInteger, FIRMessagingConnectionState) {
+  kFIRMessagingConnectionNotConnected = 0,
+  kFIRMessagingConnectionConnecting,
+  kFIRMessagingConnectionConnected,
+  kFIRMessagingConnectionSignedIn,
+};
+
+typedef NS_ENUM(NSUInteger, FIRMessagingConnectionCloseReason) {
+  kFIRMessagingConnectionCloseReasonSocketDisconnected = 0,
+  kFIRMessagingConnectionCloseReasonTimeout,
+  kFIRMessagingConnectionCloseReasonUserDisconnect,
+};
+
+@protocol FIRMessagingConnectionDelegate<NSObject>
+
+- (void)connection:(FIRMessagingConnection *)fcmConnection
+    didCloseForReason:(FIRMessagingConnectionCloseReason)reason;
+- (void)didLoginWithConnection:(FIRMessagingConnection *)fcmConnection;
+- (void)connectionDidRecieveMessage:(GtalkDataMessageStanza *)message;
+/**
+ * Called when a stream ACK or a selective ACK are received - this indicates the
+ * message has been received by MCS.
+ * @return The count of rmqIds deleted from the client RMQ store.
+ */
+- (int)connectionDidReceiveAckForRmqIds:(NSArray *)rmqIds;
+
+@end
+
+
+/**
+ * This class maintains the actual FIRMessaging connection that we use to receive and send messages
+ * while the app is in foreground. Once we have a registrationID from the FIRMessaging backend we
+ * are able to set up this connection which is used for any further communication with FIRMessaging
+ * backend. In case the connection breaks off while the app is still being used we try to rebuild
+ * the connection with an exponential backoff.
+ *
+ * This class also notifies the delegate about the main events happening in the lifcycle of the
+ * FIRMessaging connection (read FIRMessagingConnectionDelegate). All of the `on-the-wire`
+ * interactions with FIRMessaging are channelled through here.
+ */
+@interface FIRMessagingConnection : NSObject
+
+@property(nonatomic, readonly, assign) FIRMessagingConnectionState state;
+@property(nonatomic, readonly, copy) NSString *host;
+@property(nonatomic, readonly, assign) NSUInteger port;
+@property(nonatomic, readwrite, weak) id<FIRMessagingConnectionDelegate> delegate;
+
+- (instancetype)initWithAuthID:(NSString *)authId
+                         token:(NSString *)token
+                          host:(NSString *)host
+                          port:(NSUInteger)port
+                       runLoop:(NSRunLoop *)runLoop
+                   rmq2Manager:(FIRMessagingRmqManager *)rmq2Manager
+                    fcmManager:(FIRMessagingDataMessageManager *)dataMessageManager;
+
+- (void)signIn; // connect
+- (void)signOut; // disconnect
+
+/**
+ * Teardown the FIRMessaging connection and deallocate the resources being held up by the
+ * connection.
+ */
+- (void)teardown;
+
+/**
+ * Send proto to the wire. The message will be cached before we try to send so that in case of
+ * failure we can send it again later on when we have connection.
+ */
+- (void)sendProto:(GPBMessage *)proto;
+
+/**
+ * Send a message after the currently in progress connection succeeds, otherwise drop it.
+ *
+ * This should be used for TTL=0 messages that force a reconnect. They shouldn't be persisted
+ * in the RMQ, but they should be sent if the reconnect is successful.
+ */
+- (void)sendOnConnectOrDrop:(GPBMessage *)message;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConnection.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConnection.m
new file mode 100644 (file)
index 0000000..8694326
--- /dev/null
@@ -0,0 +1,708 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingConnection.h"
+
+#import "Protos/GtalkCore.pbobjc.h"
+#import "Protos/GtalkExtensions.pbobjc.h"
+
+#import "FIRMessaging.h"
+#import "FIRMessagingDataMessageManager.h"
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingRmqManager.h"
+#import "FIRMessagingSecureSocket.h"
+#import "FIRMessagingUtilities.h"
+#import "FIRMessagingVersionUtilities.h"
+#import "FIRMessaging_Private.h"
+
+static NSInteger const kIqSelectiveAck = 12;
+static NSInteger const kIqStreamAck = 13;
+static int const kInvalidStreamId = -1;
+// Threshold for number of messages removed that we will ack, for short lived connections
+static int const kMessageRemoveAckThresholdCount = 5;
+
+static NSTimeInterval const kHeartbeatInterval = 30.0;
+static NSTimeInterval const kConnectionTimeout = 20.0;
+static int32_t const kAckingInterval = 10;
+
+static NSString *const kUnackedS2dIdKey = @"FIRMessagingUnackedS2dIdKey";
+static NSString *const kAckedS2dIdMapKey = @"FIRMessagingAckedS2dIdMapKey";
+
+static NSString *const kRemoteFromAddress = @"from";
+
+@interface FIRMessagingD2SInfo : NSObject
+
+@property(nonatomic, readwrite, assign) int streamId;
+@property(nonatomic, readwrite, strong) NSString *d2sID;
+- (instancetype)initWithStreamId:(int)streamId d2sId:(NSString *)d2sID;
+
+@end
+
+@implementation FIRMessagingD2SInfo
+
+- (instancetype)initWithStreamId:(int)streamId d2sId:(NSString *)d2sID {
+  self = [super init];
+  if (self) {
+    _streamId = streamId;
+    _d2sID = [d2sID copy];
+  }
+  return self;
+}
+
+- (BOOL)isEqual:(id)object {
+  if ([object isKindOfClass:[self class]]) {
+    FIRMessagingD2SInfo *other = (FIRMessagingD2SInfo *)object;
+    return self.streamId == other.streamId && [self.d2sID isEqualToString:other.d2sID];
+  }
+  return NO;
+}
+
+- (NSUInteger)hash {
+  return [self.d2sID hash];
+}
+
+@end
+
+@interface FIRMessagingConnection ()<FIRMessagingSecureSocketDelegate>
+
+@property(nonatomic, readwrite, weak) FIRMessagingRmqManager *rmq2Manager;
+@property(nonatomic, readwrite, weak) FIRMessagingDataMessageManager *dataMessageManager;
+
+@property(nonatomic, readwrite, assign) FIRMessagingConnectionState state;
+@property(nonatomic, readwrite, copy) NSString *host;
+@property(nonatomic, readwrite, assign) NSUInteger port;
+
+@property(nonatomic, readwrite, strong) NSString *authId;
+@property(nonatomic, readwrite, strong) NSString *token;
+
+@property(nonatomic, readwrite, strong) FIRMessagingSecureSocket *socket;
+
+@property(nonatomic, readwrite, assign) int64_t lastLoginServerTimestamp;
+@property(nonatomic, readwrite, assign) int lastStreamIdAcked;
+@property(nonatomic, readwrite, assign) int inStreamId;
+@property(nonatomic, readwrite, assign) int outStreamId;
+
+@property(nonatomic, readwrite, strong) NSMutableArray *unackedS2dIds;
+@property(nonatomic, readwrite, strong) NSMutableDictionary *ackedS2dMap;
+@property(nonatomic, readwrite, strong) NSMutableArray *d2sInfos;
+// ttl=0 messages that need to be sent as soon as we establish a connection
+@property(nonatomic, readwrite, strong) NSMutableArray *sendOnConnectMessages;
+
+@property(nonatomic, readwrite, strong) NSRunLoop *runLoop;
+
+@end
+
+
+@implementation FIRMessagingConnection;
+
+- (instancetype)initWithAuthID:(NSString *)authId
+                         token:(NSString *)token
+                          host:(NSString *)host
+                          port:(NSUInteger)port
+                       runLoop:(NSRunLoop *)runLoop
+                   rmq2Manager:(FIRMessagingRmqManager *)rmq2Manager
+                    fcmManager:(FIRMessagingDataMessageManager *)dataMessageManager {
+  self = [super init];
+  if (self) {
+    _authId = [authId copy];
+    _token = [token copy];
+    _host = [host copy];
+    _port = port;
+    _runLoop = runLoop;
+    _rmq2Manager = rmq2Manager;
+    _dataMessageManager = dataMessageManager;
+
+    _d2sInfos = [NSMutableArray array];
+
+    _unackedS2dIds = [NSMutableArray arrayWithArray:[_rmq2Manager unackedS2dRmqIds]];
+    _ackedS2dMap = [NSMutableDictionary dictionary];
+    _sendOnConnectMessages = [NSMutableArray array];
+  }
+  return self;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"host: %@, port: %lu, stream id in: %d, stream id out: %d",
+          self.host,
+          _FIRMessaging_UL(self.port),
+          self.inStreamId,
+          self.outStreamId];
+}
+
+- (void)signIn {
+  _FIRMessagingDevAssert(self.state == kFIRMessagingConnectionNotConnected, @"Invalid connection state.");
+  if (self.state != kFIRMessagingConnectionNotConnected) {
+    return;
+  }
+
+  // break it up for testing
+  [self setupConnectionSocket];
+  [self connectToSocket:self.socket];
+}
+
+- (void)setupConnectionSocket {
+  self.socket = [[FIRMessagingSecureSocket alloc] init];
+  self.socket.delegate = self;
+}
+
+- (void)connectToSocket:(FIRMessagingSecureSocket *)socket {
+  self.state = kFIRMessagingConnectionConnecting;
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection000,
+                          @"Start connecting to FIRMessaging service.");
+  [socket connectToHost:self.host port:self.port onRunLoop:self.runLoop];
+}
+
+- (void)signOut {
+  // Clear the list of messages to be sent on connect. This will only
+  // have messages in it if an error happened before receiving the LoginResponse.
+  [self.sendOnConnectMessages removeAllObjects];
+
+  if (self.state == kFIRMessagingConnectionSignedIn) {
+    [self sendClose];
+  }
+  if (self.state != kFIRMessagingConnectionNotConnected) {
+    [self disconnect];
+  }
+}
+
+- (void)teardown {
+  if (self.state != kFIRMessagingConnectionNotConnected) {
+    [self disconnect];
+  }
+}
+
+#pragma mark - FIRMessagingSecureSocketDelegate
+
+- (void)secureSocketDidConnect:(FIRMessagingSecureSocket *)socket {
+  self.state = kFIRMessagingConnectionConnected;
+  self.lastStreamIdAcked = 0;
+  self.inStreamId = 0;
+  self.outStreamId = 0;
+
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection001,
+                          @"Connected to FIRMessaging service.");
+  [self resetUnconfirmedAcks];
+  [self sendLoginRequest:self.authId token:self.token];
+}
+
+- (void)didDisconnectWithSecureSocket:(FIRMessagingSecureSocket *)socket {
+  _FIRMessagingDevAssert(self.socket == socket, @"Invalid socket");
+  _FIRMessagingDevAssert(self.socket.state == kFIRMessagingSecureSocketClosed, @"Socket already closed");
+
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection002,
+                          @"Secure socket disconnected from FIRMessaging service.");
+  [self disconnect];
+  [self.delegate connection:self didCloseForReason:kFIRMessagingConnectionCloseReasonSocketDisconnected];
+}
+
+- (void)secureSocket:(FIRMessagingSecureSocket *)socket
+      didReceiveData:(NSData *)data
+             withTag:(int8_t)tag {
+  if (tag < 0) {
+    // Invalid proto tag
+    return;
+  }
+
+  Class klassForTag = FIRMessagingGetClassForTag((FIRMessagingProtoTag)tag);
+  if ([klassForTag isSubclassOfClass:[NSNull class]]) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeConnection003, @"Invalid tag %d for proto",
+                            tag);
+    return;
+  }
+
+  GPBMessage *proto = [klassForTag parseFromData:data error:NULL];
+  if (tag == kFIRMessagingProtoTagLoginResponse && self.state != kFIRMessagingConnectionConnected) {
+    FIRMessagingLoggerDebug(
+        kFIRMessagingMessageCodeConnection004,
+        @"Should not receive generated message when the connection is not connected.");
+    return;
+  } else if (tag != kFIRMessagingProtoTagLoginResponse && self.state != kFIRMessagingConnectionSignedIn) {
+    FIRMessagingLoggerDebug(
+        kFIRMessagingMessageCodeConnection005,
+        @"Should not receive generated message when the connection is not signed in.");
+    return;
+  }
+
+  // If traffic is received after a heartbeat it is safe to assume the connection is healthy.
+  [self cancelConnectionTimeoutTask];
+  [self performSelector:@selector(sendHeartbeatPing)
+             withObject:nil
+             afterDelay:kHeartbeatInterval];
+
+  [self willProcessProto:proto];
+  switch (tag) {
+    case kFIRMessagingProtoTagLoginResponse:
+      [self didReceiveLoginResponse:(GtalkLoginResponse *)proto];
+      break;
+    case kFIRMessagingProtoTagDataMessageStanza:
+      [self didReceiveDataMessageStanza:(GtalkDataMessageStanza *)proto];
+      break;
+    case kFIRMessagingProtoTagHeartbeatPing:
+      [self didReceiveHeartbeatPing:(GtalkHeartbeatPing *)proto];
+      break;
+    case kFIRMessagingProtoTagHeartbeatAck:
+      [self didReceiveHeartbeatAck:(GtalkHeartbeatAck *)proto];
+      break;
+    case kFIRMessagingProtoTagClose:
+      [self didReceiveClose:(GtalkClose *)proto];
+      break;
+    case kFIRMessagingProtoTagIqStanza:
+      [self handleIqStanza:(GtalkIqStanza *)proto];
+      break;
+    default:
+      [self didReceiveUnhandledProto:proto];
+      break;
+  }
+}
+
+// Called from secure socket once we have send the proto with given rmqId over the wire
+// since we are mostly concerned with user facing messages which certainly have a rmqId
+// we can retrieve them from the Rmq if necessary to look at stuff but for now we just
+// log it.
+- (void)secureSocket:(FIRMessagingSecureSocket *)socket
+ didSendProtoWithTag:(int8_t)tag
+               rmqId:(NSString *)rmqId {
+  // log the message
+  [self logMessage:rmqId messageType:tag isOut:YES];
+}
+
+#pragma mark - FIRMessagingTestConnection
+
+- (void)sendProto:(GPBMessage *)proto {
+  FIRMessagingProtoTag tag = FIRMessagingGetTagForProto(proto);
+  if (tag == kFIRMessagingProtoTagLoginRequest && self.state != kFIRMessagingConnectionConnected) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection006,
+                            @"Cannot send generated message when the connection is not connected.");
+    return;
+  } else if (tag != kFIRMessagingProtoTagLoginRequest && self.state != kFIRMessagingConnectionSignedIn) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection007,
+                            @"Cannot send generated message when the connection is not signed in.");
+    return;
+  }
+
+  _FIRMessagingDevAssert(self.socket != nil, @"Socket shouldn't be nil");
+  if (self.socket == nil) {
+    return;
+  }
+
+  [self willSendProto:proto];
+
+  [self.socket sendData:proto.data withTag:tag rmqId:FIRMessagingGetRmq2Id(proto)];
+}
+
+- (void)sendOnConnectOrDrop:(GPBMessage *)message {
+  if (self.state == kFIRMessagingConnectionSignedIn) {
+    // If a connection has already been established, send normally
+    [self sendProto:message];
+  } else {
+    // Otherwise add them to the list of messages to send after login
+    [self.sendOnConnectMessages addObject:message];
+  }
+}
+
++ (GtalkLoginRequest *)loginRequestWithToken:(NSString *)token authID:(NSString *)authID {
+  GtalkLoginRequest *login = [[GtalkLoginRequest alloc] init];
+  login.accountId = 1000000;
+  login.authService = GtalkLoginRequest_AuthService_AndroidId;
+  login.authToken = token;
+  login.id_p = [NSString stringWithFormat:@"%@-%@", @"ios", FIRMessagingCurrentLibraryVersion()];
+  login.domain = @"mcs.android.com";
+  login.deviceId = [NSString stringWithFormat:@"android-%llx", authID.longLongValue];
+  login.networkType = [self currentNetworkType];
+  login.resource = authID;
+  login.user = authID;
+  login.useRmq2 = YES;
+  login.lastRmqId = 1; // Sending not enabled yet so this stays as 1.
+  return login;
+}
+
++ (int32_t)currentNetworkType {
+  // http://developer.android.com/reference/android/net/ConnectivityManager.html
+  int32_t fcmNetworkType;
+  FIRMessagingNetworkStatus type = [[FIRMessaging messaging] networkType];
+  switch (type) {
+    case kFIRMessagingReachabilityReachableViaWiFi:
+      fcmNetworkType = 1;
+      break;
+
+    case kFIRMessagingReachabilityReachableViaWWAN:
+      fcmNetworkType = 0;
+      break;
+
+    default:
+      fcmNetworkType = -1;
+      break;
+  }
+  return fcmNetworkType;
+}
+
+- (void)sendLoginRequest:(NSString *)authId
+                   token:(NSString *)token {
+  GtalkLoginRequest *login = [[self class] loginRequestWithToken:token authID:authId];
+
+  // clear the messages sent during last connection
+  if ([self.d2sInfos count]) {
+    [self.d2sInfos removeAllObjects];
+  }
+
+  if (self.unackedS2dIds.count > 0) {
+    FIRMessagingLoggerDebug(
+        kFIRMessagingMessageCodeConnection008,
+        @"There are unacked persistent Ids in the login request: %@",
+        [self.unackedS2dIds.description stringByReplacingOccurrencesOfString:@"%"
+                                                                  withString:@"%%"]);
+  }
+  // Send out acks.
+  for (NSString *unackedPersistentS2dId in self.unackedS2dIds) {
+    [login.receivedPersistentIdArray addObject:unackedPersistentS2dId];
+  }
+
+  GtalkSetting *setting = [[GtalkSetting alloc] init];
+  setting.name = @"new_vc";
+  setting.value = @"1";
+  [login.settingArray addObject:setting];
+
+  [self sendProto:login];
+}
+
+- (void)sendHeartbeatAck {
+  [self sendProto:[[GtalkHeartbeatAck alloc] init]];
+}
+
+- (void)sendHeartbeatPing {
+  // cancel the previous heartbeat request.
+  [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                           selector:@selector(sendHeartbeatPing)
+                                             object:nil];
+  [self scheduleConnectionTimeoutTask];
+  [self sendProto:[[GtalkHeartbeatPing alloc] init]];
+}
+
++ (GtalkIqStanza *)createStreamAck {
+  GtalkIqStanza *iq = [[GtalkIqStanza alloc] init];
+  iq.type = GtalkIqStanza_IqType_Set;
+  iq.id_p = @"";
+  GtalkExtension *ext = [[GtalkExtension alloc] init];
+  ext.id_p = kIqStreamAck;
+  ext.data_p = @"";
+  iq.extension = ext;
+  return iq;
+}
+
+- (void)sendStreamAck {
+  GtalkIqStanza *iq = [[self class] createStreamAck];
+  [self sendProto:iq];
+}
+
+- (void)sendClose {
+  [self sendProto:[[GtalkClose alloc] init]];
+}
+
+- (void)handleIqStanza:(GtalkIqStanza *)iq {
+  if (iq.hasExtension) {
+    if (iq.extension.id_p == kIqStreamAck) {
+      [self didReceiveStreamAck:iq];
+      return;
+    }
+    if (iq.extension.id_p == kIqSelectiveAck) {
+      [self didReceiveSelectiveAck:iq];
+      return;
+    }
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection009, @"Unknown ack extension id %d.",
+                            iq.extension.id_p);
+  } else {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection010, @"Ip stanza without extension.");
+  }
+  [self didReceiveUnhandledProto:iq];
+}
+
+- (void)didReceiveLoginResponse:(GtalkLoginResponse *)loginResponse {
+  if (loginResponse.hasError) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection011,
+                            @"Login error with type: %@, message: %@.", loginResponse.error.type,
+                            loginResponse.error.message);
+    return;
+  }
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection012, @"Logged onto MCS service.");
+  // We sent the persisted list of unack'd messages with login so we can assume they have been ack'd
+  // by the server.
+  _FIRMessagingDevAssert(self.unackedS2dIds.count == 0, @"No ids present");
+  _FIRMessagingDevAssert(self.outStreamId == 1, @"Login should be the first stream id");
+
+  self.state = kFIRMessagingConnectionSignedIn;
+  self.lastLoginServerTimestamp = loginResponse.serverTimestamp;
+  [self.delegate didLoginWithConnection:self];
+  [self sendHeartbeatPing];
+
+  // Add all the TTL=0 messages on connect
+  for (GPBMessage *message in self.sendOnConnectMessages) {
+    [self sendProto:message];
+  }
+  [self.sendOnConnectMessages removeAllObjects];
+}
+
+- (void)didReceiveHeartbeatPing:(GtalkHeartbeatPing *)heartbeatPing {
+  [self sendHeartbeatAck];
+}
+
+- (void)didReceiveHeartbeatAck:(GtalkHeartbeatAck *)heartbeatAck {
+}
+
+- (void)didReceiveDataMessageStanza:(GtalkDataMessageStanza *)dataMessageStanza {
+  // TODO: Maybe add support raw data later
+  [self.delegate connectionDidRecieveMessage:dataMessageStanza];
+}
+
+- (void)didReceiveUnhandledProto:(GPBMessage *)proto {
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection013, @"Received unhandled proto");
+}
+
+- (void)didReceiveStreamAck:(GtalkIqStanza *)iq {
+  // Server received some stuff from us we don't really need to do anything special
+}
+
+- (void)didReceiveSelectiveAck:(GtalkIqStanza *)iq {
+  GtalkExtension *extension = iq.extension;
+  if (extension) {
+    int extensionId = extension.id_p;
+    if (extensionId == kIqSelectiveAck) {
+
+      NSString *dataString = extension.data_p;
+      GtalkSelectiveAck *selectiveAck = [[GtalkSelectiveAck alloc] init];
+      [selectiveAck mergeFromData:[dataString dataUsingEncoding:NSUTF8StringEncoding]
+                extensionRegistry:nil];
+
+      NSArray <NSString *>*acks = [selectiveAck idArray];
+
+      // we've received ACK's
+      [self.delegate connectionDidReceiveAckForRmqIds:acks];
+
+      // resend unacked messages
+      [self.dataMessageManager resendMessagesWithConnection:self];
+    }
+  }
+}
+
+- (void)didReceiveClose:(GtalkClose *)close {
+  [self disconnect];
+}
+
+- (void)willProcessProto:(GPBMessage *)proto {
+  self.inStreamId++;
+
+  if ([proto isKindOfClass:GtalkDataMessageStanza.class]) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection014,
+                            @"RMQ: Receiving %@ with rmq_id: %@ incoming stream Id: %d",
+                            proto.class, FIRMessagingGetRmq2Id(proto), self.inStreamId);
+  } else {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection015,
+                            @"RMQ: Receiving %@ with incoming stream Id: %d.", proto.class,
+                            self.inStreamId);
+  }
+  int streamId = FIRMessagingGetLastStreamId(proto);
+  if (streamId != kInvalidStreamId) {
+    // confirm the D2S messages that were sent by us
+    [self confirmAckedD2sIdsWithStreamId:streamId];
+
+    // We can now confirm that our ack was received by the server and start our unack'd list fresh
+    // with the proto we just received.
+    [self confirmAckedS2dIdsWithStreamId:streamId];
+  }
+  NSString *rmq2Id = FIRMessagingGetRmq2Id(proto);
+  if (rmq2Id != nil) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection016,
+                            @"RMQ: Add unacked persistent Id: %@.",
+                            [rmq2Id stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]);
+    [self.unackedS2dIds addObject:rmq2Id];
+    [self.rmq2Manager saveS2dMessageWithRmqId:rmq2Id]; // RMQ save
+  }
+  BOOL explicitAck = ([proto isKindOfClass:[GtalkDataMessageStanza class]] &&
+                      [(GtalkDataMessageStanza *)proto immediateAck]);
+  // If we have not sent anything and the ack threshold has been reached then explicitly send one
+  // to notify the server that we have received messages.
+  if (self.inStreamId - self.lastStreamIdAcked >= kAckingInterval || explicitAck) {
+    [self sendStreamAck];
+  }
+}
+
+- (void)willSendProto:(GPBMessage *)proto {
+  self.outStreamId++;
+
+  NSString *rmq2Id = FIRMessagingGetRmq2Id(proto);
+  if ([rmq2Id length]) {
+    FIRMessagingD2SInfo *d2sInfo = [[FIRMessagingD2SInfo alloc] initWithStreamId:self.outStreamId d2sId:rmq2Id];
+    [self.d2sInfos addObject:d2sInfo];
+  }
+
+  // each time we send a d2s message, it acks previously received
+  // s2d messages via the last (s2d) stream id received.
+
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection017,
+                          @"RMQ: Sending %@ with outgoing stream Id: %d.", proto.class,
+                          self.outStreamId);
+  // We have received messages since last time we sent something - send ack info to server.
+  if (self.inStreamId > self.lastStreamIdAcked) {
+    FIRMessagingSetLastStreamId(proto, self.inStreamId);
+    self.lastStreamIdAcked = self.inStreamId;
+  }
+
+  if (self.unackedS2dIds.count > 0) {
+    // Move all 'unack'd' messages to the ack'd map so they can be removed once the
+    // ack is confirmed.
+    NSArray *ackedS2dIds = [NSArray arrayWithArray:self.unackedS2dIds];
+    FIRMessagingLoggerDebug(
+        kFIRMessagingMessageCodeConnection018, @"RMQ: Mark persistent Ids as acked: %@.",
+        [ackedS2dIds.description stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]);
+    [self.unackedS2dIds removeAllObjects];
+    self.ackedS2dMap[[@(self.outStreamId) stringValue]] = ackedS2dIds;
+  }
+}
+
+#pragma mark - Private
+
+/**
+ * This processes the s2d message received in reference to the d2s messages
+ * that we have sent before.
+ */
+- (void)confirmAckedD2sIdsWithStreamId:(int)lastReceivedStreamId {
+  NSMutableArray *d2sIdsAcked = [NSMutableArray array];
+  for (FIRMessagingD2SInfo *d2sInfo in self.d2sInfos) {
+    if (lastReceivedStreamId < d2sInfo.streamId) {
+      break;
+    }
+    [d2sIdsAcked addObject:d2sInfo];
+  }
+
+  NSMutableArray *rmqIds = [NSMutableArray arrayWithCapacity:[d2sIdsAcked count]];
+  // remove ACK'ed messages
+  for (FIRMessagingD2SInfo *d2sInfo in d2sIdsAcked) {
+    if ([d2sInfo.d2sID length]) {
+      [rmqIds addObject:d2sInfo.d2sID];
+    }
+    [self.d2sInfos removeObject:d2sInfo];
+  }
+  [self.delegate connectionDidReceiveAckForRmqIds:rmqIds];
+  int count = [self.delegate connectionDidReceiveAckForRmqIds:rmqIds];
+  if (kMessageRemoveAckThresholdCount > 0 && count >= kMessageRemoveAckThresholdCount) {
+    // For short lived connections, if a large number of messages are removed, send an
+    // ack straight away so the server knows that this message was received.
+    [self sendStreamAck];
+  }
+}
+
+/**
+ * Called when a stream ACK or a selective ACK are received - this indicates the message has
+ * been received by MCS.
+ */
+- (void)didReceiveAckForRmqIds:(NSArray *)rmqIds {
+  // TODO: let the user know that the following messages were received by the server
+}
+
+- (void)confirmAckedS2dIdsWithStreamId:(int)lastReceivedStreamId {
+  // If the server hasn't received the streamId yet.
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection019,
+                          @"RMQ: Server last received stream Id: %d.", lastReceivedStreamId);
+  if (lastReceivedStreamId < self.outStreamId) {
+    // TODO: This could be a good indicator that we need to re-send something (acks)?
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection020,
+                            @"RMQ: There are unsent messages that should be send...\n"
+                             "server received: %d\nlast stream id sent: %d",
+                            lastReceivedStreamId, self.outStreamId);
+  }
+
+  NSSet *ackedStreamIds =
+    [self.ackedS2dMap keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
+      NSString *streamId = key;
+      return streamId.intValue <= lastReceivedStreamId;
+    }];
+  NSMutableArray *s2dIdsToDelete = [NSMutableArray array];
+
+  for (NSString *streamId in ackedStreamIds) {
+    NSArray *ackedS2dIds = self.ackedS2dMap[streamId];
+    if (ackedS2dIds.count > 0) {
+      FIRMessagingLoggerDebug(
+          kFIRMessagingMessageCodeConnection021,
+          @"RMQ: Mark persistent Ids as confirmed by stream id %@: %@.", streamId,
+          [ackedS2dIds.description stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]);
+      [self.ackedS2dMap removeObjectForKey:streamId];
+    }
+
+    [s2dIdsToDelete addObjectsFromArray:ackedS2dIds];
+  }
+
+  // clean up s2d ids that the server knows we've received.
+  // we let the server know via a s2d last stream id received in a
+  // d2s message. the server lets us know it has received our d2s
+  // message via a d2s last stream id received in a s2d message.
+  [self.rmq2Manager removeS2dIds:s2dIdsToDelete];
+}
+
+- (void)resetUnconfirmedAcks {
+  [self.ackedS2dMap enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
+    [self.unackedS2dIds addObjectsFromArray:obj];
+  }];
+  [self.ackedS2dMap removeAllObjects];
+}
+
+- (void)disconnect {
+  _FIRMessagingDevAssert(self.state != kFIRMessagingConnectionNotConnected, @"Connection already not connected");
+  // cancel pending timeout tasks.
+  [self cancelConnectionTimeoutTask];
+  // cancel pending heartbeat.
+  [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                           selector:@selector(sendHeartbeatPing)
+                                             object:nil];
+  // Unset the delegate. FIRMessagingConnection will not receive further events from the socket from now on.
+  self.socket.delegate = nil;
+  [self.socket disconnect];
+  self.state = kFIRMessagingConnectionNotConnected;
+}
+
+- (void)connectionTimedOut {
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection022,
+                          @"Connection to FIRMessaging service timed out.");
+  [self disconnect];
+  [self.delegate connection:self didCloseForReason:kFIRMessagingConnectionCloseReasonTimeout];
+}
+
+- (void)scheduleConnectionTimeoutTask {
+  // cancel the previous heartbeat timeout event and schedule a new one.
+  [self cancelConnectionTimeoutTask];
+  [self performSelector:@selector(connectionTimedOut)
+             withObject:nil
+             afterDelay:[self connectionTimeoutInterval]];
+}
+
+- (void)cancelConnectionTimeoutTask {
+  // cancel pending timeout tasks.
+  [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                           selector:@selector(connectionTimedOut)
+                                             object:nil];
+}
+
+- (void)logMessage:(NSString *)description messageType:(int)messageType isOut:(BOOL)isOut {
+  messageType = isOut ? -messageType : messageType;
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeConnection023,
+                          @"Send msg: %@ type: %d inStreamId: %d outStreamId: %d", description,
+                          messageType, self.inStreamId, self.outStreamId);
+}
+
+- (NSTimeInterval)connectionTimeoutInterval {
+  return kConnectionTimeout;
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConstants.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConstants.h
new file mode 100644 (file)
index 0000000..ad0d6c9
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ *  Global constants to be put here.
+ *
+ */
+#import <Foundation/Foundation.h>
+
+#ifndef _FIRMessaging_CONSTANTS_H
+#define _FIRMessaging_CONSTANTS_H
+
+FOUNDATION_EXPORT NSString *const kFIRMessagingRawDataKey;
+FOUNDATION_EXPORT NSString *const kFIRMessagingCollapseKey;
+FOUNDATION_EXPORT NSString *const kFIRMessagingFromKey;
+
+FOUNDATION_EXPORT NSString *const kFIRMessagingSendTo;
+FOUNDATION_EXPORT NSString *const kFIRMessagingSendTTL;
+FOUNDATION_EXPORT NSString *const kFIRMessagingSendDelay;
+FOUNDATION_EXPORT NSString *const kFIRMessagingSendMessageID;
+FOUNDATION_EXPORT NSString *const KFIRMessagingSendMessageAppData;
+
+FOUNDATION_EXPORT NSString *const kFIRMessagingMessageInternalReservedKeyword;
+FOUNDATION_EXPORT NSString *const kFIRMessagingMessagePersistentIDKey;
+
+FOUNDATION_EXPORT NSString *const kFIRMessagingMessageIDKey;
+FOUNDATION_EXPORT NSString *const kFIRMessagingMessageAPNSContentAvailableKey;
+FOUNDATION_EXPORT NSString *const kFIRMessagingMessageSyncViaMCSKey;
+FOUNDATION_EXPORT NSString *const kFIRMessagingMessageSyncMessageTTLKey;
+FOUNDATION_EXPORT NSString *const kFIRMessagingMessageLinkKey;
+
+FOUNDATION_EXPORT NSString *const kFIRMessagingRemoteNotificationsProxyEnabledInfoPlistKey;
+
+FOUNDATION_EXPORT NSString *const kFIRMessagingApplicationSupportSubDirectory;
+
+// Notifications
+FOUNDATION_EXPORT NSString *const kFIRMessagingCheckinFetchedNotification;
+FOUNDATION_EXPORT NSString *const kFIRMessagingAPNSTokenNotification;
+FOUNDATION_EXPORT NSString *const kFIRMessagingFCMTokenNotification;
+FOUNDATION_EXPORT NSString *const kFIRMessagingInstanceIDTokenRefreshNotification __deprecated_msg("Use kFIRMessagingRegistrationTokenRefreshNotification instead");
+FOUNDATION_EXPORT NSString *const kFIRMessagingRegistrationTokenRefreshNotification;
+
+FOUNDATION_EXPORT const int kFIRMessagingSendTtlDefault; // 24 hours
+
+#endif
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConstants.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingConstants.m
new file mode 100644 (file)
index 0000000..8904cc5
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingConstants.h"
+
+NSString *const kFIRMessagingRawDataKey = @"rawData";
+NSString *const kFIRMessagingCollapseKey = @"collapse_key";
+NSString *const kFIRMessagingFromKey = @"from";
+
+NSString *const kFIRMessagingSendTo = @"google." @"to";
+NSString *const kFIRMessagingSendTTL = @"google." @"ttl";
+NSString *const kFIRMessagingSendDelay = @"google." @"delay";
+NSString *const kFIRMessagingSendMessageID = @"google." @"msg_id";
+NSString *const KFIRMessagingSendMessageAppData = @"google." @"data";
+
+NSString *const kFIRMessagingMessageInternalReservedKeyword = @"gcm.";
+NSString *const kFIRMessagingMessagePersistentIDKey = @"persistent_id";
+
+NSString *const kFIRMessagingMessageIDKey = @"gcm." @"message_id";
+NSString *const kFIRMessagingMessageAPNSContentAvailableKey = @"content-available";
+NSString *const kFIRMessagingMessageSyncViaMCSKey = @"gcm." @"duplex";
+NSString *const kFIRMessagingMessageSyncMessageTTLKey = @"gcm." @"ttl";
+NSString *const kFIRMessagingMessageLinkKey = @"gcm." @"app_link";
+
+NSString *const kFIRMessagingRemoteNotificationsProxyEnabledInfoPlistKey =
+    @"FirebaseAppDelegateProxyEnabled";
+
+NSString *const kFIRMessagingApplicationSupportSubDirectory = @"Google/FirebaseMessaging";
+
+// Notifications
+NSString *const kFIRMessagingCheckinFetchedNotification = @"com.google.gcm.notif-checkin-fetched";
+NSString *const kFIRMessagingAPNSTokenNotification = @"com.firebase.iid.notif.apns-token";
+NSString *const kFIRMessagingFCMTokenNotification = @"com.firebase.iid.notif.fcm-token";
+NSString *const kFIRMessagingInstanceIDTokenRefreshNotification =
+    @"com.firebase.iid.notif.refresh-token";
+NSString *const kFIRMessagingRegistrationTokenRefreshNotification =
+    @"com.firebase.iid.notif.refresh-token";
+
+const int kFIRMessagingSendTtlDefault = 24 * 60 * 60; // 24 hours
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingContextManagerService.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingContextManagerService.h
new file mode 100644 (file)
index 0000000..83e6444
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+FOUNDATION_EXPORT NSString *const kFIRMessagingContextManagerCategory;
+FOUNDATION_EXPORT NSString *const kFIRMessagingContextManagerLocalTimeStart;
+FOUNDATION_EXPORT NSString *const kFIRMessagingContextManagerLocalTimeEnd;
+FOUNDATION_EXPORT NSString *const kFIRMessagingContextManagerBodyKey;
+
+@interface FIRMessagingContextManagerService : NSObject
+
+/**
+ *  Check if the message is a context manager message or not.
+ *
+ *  @param message The message to verify.
+ *
+ *  @return YES if the message is a context manager message else NO.
+ */
++ (BOOL)isContextManagerMessage:(NSDictionary *)message;
+
+/**
+ *  Handle context manager message.
+ *
+ *  @param message The message to handle.
+ *
+ *  @return YES if the message was handled successfully else NO.
+ */
++ (BOOL)handleContextManagerMessage:(NSDictionary *)message;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingContextManagerService.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingContextManagerService.m
new file mode 100644 (file)
index 0000000..b4aac8b
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingContextManagerService.h"
+
+#import <UIKit/UIKit.h>
+
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingUtilities.h"
+
+#define kFIRMessagingContextManagerPrefixKey @"google.c.cm."
+#define kFIRMessagingContextManagerNotificationKeyPrefix @"gcm.notification."
+
+static NSString *const kLogTag = @"FIRMessagingAnalytics";
+
+static NSString *const kLocalTimeFormatString = @"yyyy-MM-dd HH:mm:ss";
+
+static NSString *const kContextManagerPrefixKey = kFIRMessagingContextManagerPrefixKey;
+
+// Local timed messages (format yyyy-mm-dd HH:mm:ss)
+NSString *const kFIRMessagingContextManagerLocalTimeStart = kFIRMessagingContextManagerPrefixKey @"lt_start";
+NSString *const kFIRMessagingContextManagerLocalTimeEnd = kFIRMessagingContextManagerPrefixKey @"lt_end";
+
+// Local Notification Params
+NSString *const kFIRMessagingContextManagerBodyKey = kFIRMessagingContextManagerNotificationKeyPrefix @"body";
+NSString *const kFIRMessagingContextManagerTitleKey = kFIRMessagingContextManagerNotificationKeyPrefix @"title";
+NSString *const kFIRMessagingContextManagerBadgeKey = kFIRMessagingContextManagerNotificationKeyPrefix @"badge";
+NSString *const kFIRMessagingContextManagerCategoryKey =
+    kFIRMessagingContextManagerNotificationKeyPrefix @"click_action";
+NSString *const kFIRMessagingContextManagerSoundKey = kFIRMessagingContextManagerNotificationKeyPrefix @"sound";
+NSString *const kFIRMessagingContextManagerContentAvailableKey =
+    kFIRMessagingContextManagerNotificationKeyPrefix @"content-available";
+
+typedef NS_ENUM(NSUInteger, FIRMessagingContextManagerMessageType) {
+  FIRMessagingContextManagerMessageTypeNone,
+  FIRMessagingContextManagerMessageTypeLocalTime,
+};
+
+@implementation FIRMessagingContextManagerService
+
++ (BOOL)isContextManagerMessage:(NSDictionary *)message {
+  // For now we only support local time in ContextManager.
+  if (![message[kFIRMessagingContextManagerLocalTimeStart] length]) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeContextManagerService000,
+                            @"Received message missing local start time, dropped.");
+    return NO;
+  }
+
+  return YES;
+}
+
++ (BOOL)handleContextManagerMessage:(NSDictionary *)message {
+  NSString *startTimeString = message[kFIRMessagingContextManagerLocalTimeStart];
+  if (startTimeString.length) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeContextManagerService001,
+                            @"%@ Received context manager message with local time %@", kLogTag,
+                            startTimeString);
+    return [self handleContextManagerLocalTimeMessage:message];
+  }
+
+  return NO;
+}
+
++ (BOOL)handleContextManagerLocalTimeMessage:(NSDictionary *)message {
+  NSString *startTimeString = message[kFIRMessagingContextManagerLocalTimeStart];
+  NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+  [dateFormatter setDateFormat:kLocalTimeFormatString];
+  NSDate *startDate = [dateFormatter dateFromString:startTimeString];
+
+  _FIRMessagingDevAssert(startDate, @"Invalid local start date format %@", startTimeString);
+  if (!startTimeString) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeContextManagerService002,
+                            @"Invalid local start date format %@. Message dropped",
+                            startTimeString);
+    return NO;
+  }
+
+  NSDate *currentDate = [NSDate date];
+
+  if ([currentDate compare:startDate] == NSOrderedAscending) {
+    [self scheduleLocalNotificationForMessage:message
+                                       atDate:startDate];
+  } else {
+    // check end time has not passed
+    NSString *endTimeString = message[kFIRMessagingContextManagerLocalTimeEnd];
+    if (!endTimeString) {
+      FIRMessagingLoggerInfo(
+          kFIRMessagingMessageCodeContextManagerService003,
+          @"No end date specified for message, start date elapsed. Message dropped.");
+      return YES;
+    }
+
+    NSDate *endDate = [dateFormatter dateFromString:endTimeString];
+
+    _FIRMessagingDevAssert(endDate, @"Invalid local end date format %@", endTimeString);
+    if (!endTimeString) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeContextManagerService004,
+                              @"Invalid local end date format %@. Message dropped", endTimeString);
+      return NO;
+    }
+
+    if ([endDate compare:currentDate] == NSOrderedAscending) {
+      // end date has already passed drop the message
+      FIRMessagingLoggerInfo(kFIRMessagingMessageCodeContextManagerService005,
+                             @"End date %@ has already passed. Message dropped.", endTimeString);
+      return YES;
+    }
+
+    // schedule message right now (buffer 10s)
+    [self scheduleLocalNotificationForMessage:message
+                                       atDate:[currentDate dateByAddingTimeInterval:10]];
+  }
+  return YES;
+}
+
++ (void)scheduleLocalNotificationForMessage:(NSDictionary *)message
+                                     atDate:(NSDate *)date {
+  NSDictionary *apsDictionary = message;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+  UILocalNotification *notification = [[UILocalNotification alloc] init];
+#pragma clang diagnostic pop
+
+  // A great way to understand timezones and UILocalNotifications
+  // http://stackoverflow.com/questions/18424569/understanding-uilocalnotification-timezone
+  notification.timeZone = [NSTimeZone defaultTimeZone];
+  notification.fireDate = date;
+
+  // In the current solution all of the display stuff goes into a special "aps" dictionary
+  // being sent in the message.
+  if ([apsDictionary[kFIRMessagingContextManagerBodyKey] length]) {
+    notification.alertBody = apsDictionary[kFIRMessagingContextManagerBodyKey];
+  }
+  if ([apsDictionary[kFIRMessagingContextManagerTitleKey] length]) {
+    // |alertTitle| is iOS 8.2+, so check if we can set it
+      if ([notification respondsToSelector:@selector(setAlertTitle:)]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+      notification.alertTitle = apsDictionary[kFIRMessagingContextManagerTitleKey];
+#pragma pop
+    }
+  }
+
+  if (apsDictionary[kFIRMessagingContextManagerSoundKey]) {
+    notification.soundName = apsDictionary[kFIRMessagingContextManagerSoundKey];
+  }
+  if (apsDictionary[kFIRMessagingContextManagerBadgeKey]) {
+    notification.applicationIconBadgeNumber =
+        [apsDictionary[kFIRMessagingContextManagerBadgeKey] integerValue];
+  }
+  if (apsDictionary[kFIRMessagingContextManagerCategoryKey]) {
+    // |category| is iOS 8.0+, so check if we can set it
+    if ([notification respondsToSelector:@selector(setCategory:)]) {
+      notification.category = apsDictionary[kFIRMessagingContextManagerCategoryKey];
+    }
+  }
+
+  NSDictionary *userInfo = [self parseDataFromMessage:message];
+  if (userInfo.count) {
+    notification.userInfo = userInfo;
+  }
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+  UIApplication *application = FIRMessagingUIApplication();
+  if (!application) {
+    return;
+  }
+  [application scheduleLocalNotification:notification];
+#pragma clang diagnostic pop
+}
+
++ (NSDictionary *)parseDataFromMessage:(NSDictionary *)message {
+  NSMutableDictionary *data = [NSMutableDictionary dictionary];
+  for (NSObject<NSCopying> *key in message) {
+    if ([key isKindOfClass:[NSString class]]) {
+      NSString *keyString = (NSString *)key;
+      if ([keyString isEqualToString:kFIRMessagingContextManagerContentAvailableKey]) {
+        continue;
+      } else if ([keyString hasPrefix:kContextManagerPrefixKey]) {
+        continue;
+      }
+    }
+    data[[key copy]] = message[key];
+  }
+  return [data copy];
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDataMessageManager.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDataMessageManager.h
new file mode 100644 (file)
index 0000000..cf8df74
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@class GtalkDataMessageStanza;
+
+@class FIRMessagingClient;
+@class FIRMessagingConnection;
+@class FIRMessagingReceiver;
+@class FIRMessagingRmqManager;
+@class FIRMessagingSyncMessageManager;
+
+@protocol FIRMessagingDataMessageManagerDelegate <NSObject>
+
+#pragma mark - Downstream Callbacks
+
+/**
+ *  Invoked when FIRMessaging receives a downstream message via the MCS connection.
+ *  Let's the user know that they have received a new message by invoking the
+ *  App's remoteNotification callback.
+ *
+ *  @param message The downstream message received by the MCS connection.
+ */
+- (void)didReceiveMessage:(nonnull NSDictionary *)message
+           withIdentifier:(nullable NSString *)messageID;
+
+#pragma mark - Upstream Callbacks
+
+/**
+ *  Notify the app that FIRMessaging will soon be sending the upstream message requested by the app.
+ *
+ *  @param messageID The messageId passed in by the app to track this particular message.
+ *  @param error     The error in case FIRMessaging cannot send the message upstream.
+ */
+- (void)willSendDataMessageWithID:(nullable NSString *)messageID error:(nullable NSError *)error;
+
+/**
+ *  Notify the app that FIRMessaging did successfully send it's message via the MCS
+ *  connection and the message was successfully delivered.
+ *
+ *  @param messageId The messageId passed in by the app to track this particular
+ *                   message.
+ */
+- (void)didSendDataMessageWithID:(nonnull NSString *)messageId;
+
+#pragma mark - Server Callbacks
+
+/**
+ *  Notify the app that FIRMessaging server deleted some messages which exceeded storage limits. This
+ *  indicates the "deleted_messages" message type we received from the server.
+ */
+- (void)didDeleteMessagesOnServer;
+
+@end
+
+/**
+ * This manages all of the data messages being sent by the client and also the messages that
+ * were received from the server.
+ */
+@interface FIRMessagingDataMessageManager : NSObject
+
+NS_ASSUME_NONNULL_BEGIN
+
+- (instancetype)initWithDelegate:(id<FIRMessagingDataMessageManagerDelegate>)delegate
+                          client:(FIRMessagingClient *)client
+                     rmq2Manager:(FIRMessagingRmqManager *)rmq2Manager
+              syncMessageManager:(FIRMessagingSyncMessageManager *)syncMessageManager;
+
+- (void)setDeviceAuthID:(NSString *)deviceAuthID secretToken:(NSString *)secretToken;
+
+- (void)refreshDelayedMessages;
+
+#pragma mark - Receive
+
+- (nullable NSDictionary *)processPacket:(GtalkDataMessageStanza *)packet;
+- (void)didReceiveParsedMessage:(NSDictionary *)message;
+
+#pragma mark - Send
+
+- (void)sendDataMessageStanza:(NSMutableDictionary *)dataMessage;
+- (void)didSendDataMessageStanza:(GtalkDataMessageStanza *)message;
+
+- (void)resendMessagesWithConnection:(FIRMessagingConnection *)connection;
+
+NS_ASSUME_NONNULL_END
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDataMessageManager.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDataMessageManager.m
new file mode 100644 (file)
index 0000000..b62763a
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingDataMessageManager.h"
+
+#import "Protos/GtalkCore.pbobjc.h"
+
+#import "FIRMessagingClient.h"
+#import "FIRMessagingConnection.h"
+#import "FIRMessagingConstants.h"
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingDelayedMessageQueue.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingReceiver.h"
+#import "FIRMessagingRmqManager.h"
+#import "FIRMessaging_Private.h"
+#import "FIRMessagingSyncMessageManager.h"
+#import "FIRMessagingUtilities.h"
+#import "NSError+FIRMessaging.h"
+
+static const int kMaxAppDataSizeDefault = 4 * 1024; // 4k
+static const int kMinDelaySeconds = 1; // 1 second
+static const int kMaxDelaySeconds = 60 * 60; // 1 hour
+
+static NSString *const kFromForFIRMessagingMessages = @"mcs.android.com";
+static NSString *const kGSFMessageCategory = @"com.google.android.gsf.gtalkservice";
+// TODO: Update Gcm to FIRMessaging in the constants below
+static NSString *const kFCMMessageCategory = @"com.google.gcm";
+static NSString *const kMessageReservedPrefix = @"google.";
+
+static NSString *const kFCMMessageSpecialMessage = @"message_type";
+
+// special messages sent by the server
+static NSString *const kFCMMessageTypeDeletedMessages = @"deleted_messages";
+
+static NSString *const kMCSNotificationPrefix = @"gcm.notification.";
+static NSString *const kDataMessageNotificationKey = @"notification";
+
+
+typedef NS_ENUM(int8_t, UpstreamForceReconnect) {
+  // Never force reconnect on upstream messages
+  kUpstreamForceReconnectOff = 0,
+  // Force reconnect for TTL=0 upstream messages
+  kUpstreamForceReconnectTTL0 = 1,
+  // Force reconnect for all upstream messages
+  kUpstreamForceReconnectAll = 2,
+};
+
+@interface FIRMessagingDataMessageManager ()
+
+@property(nonatomic, readwrite, weak) FIRMessagingClient *client;
+@property(nonatomic, readwrite, weak) FIRMessagingRmqManager *rmq2Manager;
+@property(nonatomic, readwrite, weak) FIRMessagingSyncMessageManager *syncMessageManager;
+@property(nonatomic, readwrite, weak) id<FIRMessagingDataMessageManagerDelegate> delegate;
+@property(nonatomic, readwrite, strong) FIRMessagingDelayedMessageQueue *delayedMessagesQueue;
+
+@property(nonatomic, readwrite, assign) int ttl;
+@property(nonatomic, readwrite, copy) NSString *deviceAuthID;
+@property(nonatomic, readwrite, copy) NSString *secretToken;
+@property(nonatomic, readwrite, assign) int maxAppDataSize;
+@property(nonatomic, readwrite, assign) UpstreamForceReconnect upstreamForceReconnect;
+
+@end
+
+@implementation FIRMessagingDataMessageManager
+
+- (instancetype)initWithDelegate:(id<FIRMessagingDataMessageManagerDelegate>)delegate
+                          client:(FIRMessagingClient *)client
+                     rmq2Manager:(FIRMessagingRmqManager *)rmq2Manager
+              syncMessageManager:(FIRMessagingSyncMessageManager *)syncMessageManager {
+  self = [super init];
+  if (self) {
+    _delegate = delegate;
+    _client = client;
+    _rmq2Manager = rmq2Manager;
+    _syncMessageManager = syncMessageManager;
+    _ttl = kFIRMessagingSendTtlDefault;
+    _maxAppDataSize = kMaxAppDataSizeDefault;
+    // on by default
+    _upstreamForceReconnect = kUpstreamForceReconnectAll;
+  }
+  return self;
+}
+
+- (void)setDeviceAuthID:(NSString *)deviceAuthID secretToken:(NSString *)secretToken {
+  _FIRMessagingDevAssert([deviceAuthID length] && [secretToken length],
+                @"Invalid credentials for FIRMessaging");
+  self.deviceAuthID = deviceAuthID;
+  self.secretToken = secretToken;
+}
+
+- (void)refreshDelayedMessages {
+  FIRMessaging_WEAKIFY(self);
+  self.delayedMessagesQueue =
+      [[FIRMessagingDelayedMessageQueue alloc] initWithRmqScanner:self.rmq2Manager
+                              sendDelayedMessagesHandler:^(NSArray *messages) {
+                                FIRMessaging_STRONGIFY(self);
+                                [self sendDelayedMessages:messages];
+                              }];
+}
+
+- (nullable NSDictionary *)processPacket:(GtalkDataMessageStanza *)dataMessage {
+  NSString *category = dataMessage.category;
+  NSString *from = dataMessage.from;
+  if ([kFCMMessageCategory isEqualToString:category] ||
+      [kGSFMessageCategory isEqualToString:category]) {
+    [self handleMCSDataMessage:dataMessage];
+    return nil;
+  } else if ([kFromForFIRMessagingMessages isEqualToString:from]) {
+    [self handleMCSDataMessage:dataMessage];
+    return nil;
+  }
+
+  return [self parseDataMessage:dataMessage];
+}
+
+- (void)handleMCSDataMessage:(GtalkDataMessageStanza *)dataMessage {
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeDataMessageManager000,
+                          @"Received message for FIRMessaging from downstream %@", dataMessage);
+}
+
+- (NSDictionary *)parseDataMessage:(GtalkDataMessageStanza *)dataMessage {
+  NSMutableDictionary *message = [NSMutableDictionary dictionary];
+  NSString *from = [dataMessage from];
+  if ([from length]) {
+    message[kFIRMessagingFromKey] = from;
+  }
+
+  // raw data
+  NSData *rawData = [dataMessage rawData];
+  if ([rawData length]) {
+    message[kFIRMessagingRawDataKey] = rawData;
+  }
+
+  NSString *token = [dataMessage token];
+  if ([token length]) {
+    message[kFIRMessagingCollapseKey] = token;
+  }
+
+  // Add the persistent_id. This would be removed later before sending the message to the device.
+  NSString *persistentID = [dataMessage persistentId];
+  _FIRMessagingDevAssert([persistentID length], @"Invalid MCS message without persistentID");
+  if ([persistentID length]) {
+    message[kFIRMessagingMessageIDKey] = persistentID;
+  }
+
+  // third-party data
+  for (GtalkAppData *item in dataMessage.appDataArray) {
+    _FIRMessagingDevAssert(item.hasKey && item.hasValue, @"Invalid AppData");
+
+    // do not process the "from" key -- is not useful
+    if ([kFIRMessagingFromKey isEqualToString:item.key]) {
+      continue;
+    }
+
+    // Filter the "gcm.notification." keys in the message
+    if ([item.key hasPrefix:kMCSNotificationPrefix]) {
+      NSString *key = [item.key substringFromIndex:[kMCSNotificationPrefix length]];
+      if ([key length]) {
+        if (!message[kDataMessageNotificationKey]) {
+          message[kDataMessageNotificationKey] = [NSMutableDictionary dictionary];
+        }
+        message[kDataMessageNotificationKey][key] = item.value;
+      } else {
+        _FIRMessagingDevAssert([key length], @"Invalid key in MCS message: %@", key);
+        FIRMessagingLoggerError(kFIRMessagingMessageCodeDataMessageManager001,
+                                @"Invalid key in MCS message: %@", key);
+      }
+      continue;
+    }
+
+    // Filter the "gcm.duplex" key
+    if ([item.key isEqualToString:kFIRMessagingMessageSyncViaMCSKey]) {
+      BOOL value = [item.value boolValue];
+      message[kFIRMessagingMessageSyncViaMCSKey] = @(value);
+      continue;
+    }
+
+    // do not allow keys with "reserved" keyword
+    if ([[item.key lowercaseString] hasPrefix:kMessageReservedPrefix]) {
+      continue;
+    }
+
+    [message setObject:item.value forKey:item.key];
+  }
+  // TODO: Add support for encrypting raw data later
+  return [NSDictionary dictionaryWithDictionary:message];
+}
+
+- (void)didReceiveParsedMessage:(NSDictionary *)message {
+  if ([message[kFCMMessageSpecialMessage] length]) {
+    NSString *messageType = message[kFCMMessageSpecialMessage];
+    if ([kFCMMessageTypeDeletedMessages isEqualToString:messageType]) {
+      // TODO: Maybe trim down message to remove some unnecessary fields.
+      // tell the FCM receiver of deleted messages
+      [self.delegate didDeleteMessagesOnServer];
+      return;
+    }
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeDataMessageManager002,
+                            @"Invalid message type received: %@", messageType);
+  } else if (message[kFIRMessagingMessageSyncViaMCSKey]) {
+    // Update SYNC_RMQ with the message
+    BOOL isDuplicate = [self.syncMessageManager didReceiveMCSSyncMessage:message];
+    if (isDuplicate) {
+      return;
+    }
+  }
+  NSString *messageId = message[kFIRMessagingMessageIDKey];
+  NSDictionary *filteredMessage = [self filterInternalFIRMessagingKeysFromMessage:message];
+  [self.delegate didReceiveMessage:filteredMessage withIdentifier:messageId];
+}
+
+- (NSDictionary *)filterInternalFIRMessagingKeysFromMessage:(NSDictionary *)message {
+  NSMutableDictionary *newMessage = [NSMutableDictionary dictionaryWithDictionary:message];
+  for (NSString *key in message) {
+    if ([key hasPrefix:kFIRMessagingMessageInternalReservedKeyword]) {
+      [newMessage removeObjectForKey:key];
+    }
+  }
+  return [newMessage copy];
+}
+
+- (void)sendDataMessageStanza:(NSMutableDictionary *)dataMessage {
+  NSNumber *ttlNumber = dataMessage[kFIRMessagingSendTTL];
+  NSString *to = dataMessage[kFIRMessagingSendTo];
+  NSString *msgId = dataMessage[kFIRMessagingSendMessageID];
+  NSString *appPackage = [self categoryForUpstreamMessages];
+  GtalkDataMessageStanza *stanza = [[GtalkDataMessageStanza alloc] init];
+
+  // TODO: enforce TTL (right now only ttl=0 is special, means no storage)
+  int ttl = [ttlNumber intValue];
+  if (ttl < 0 || ttl > self.ttl) {
+    ttl = self.ttl;
+  }
+  [stanza setTtl:ttl];
+  [stanza setSent:FIRMessagingCurrentTimestampInSeconds()];
+
+  int delay = [self delayForMessage:dataMessage];
+  if (delay > 0) {
+    [stanza setMaxDelay:delay];
+  }
+
+  if (msgId) {
+    [stanza setId_p:msgId];
+  }
+
+  // collapse key as given by the sender
+  NSString *token = dataMessage[KFIRMessagingSendMessageAppData][kFIRMessagingCollapseKey];
+  if ([token length]) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeDataMessageManager003,
+                            @"FIRMessaging using %@ as collapse key", token);
+    [stanza setToken:token];
+  }
+
+  if (!self.secretToken) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeDataMessageManager004,
+                            @"Trying to send data message without a secret token. "
+                            @"Authentication failed.");
+    [self willSendDataMessageFail:stanza
+                    withMessageId:msgId
+                            error:kFIRMessagingErrorCodeMissingDeviceID];
+    return;
+  }
+
+  if (![to length]) {
+    [self willSendDataMessageFail:stanza withMessageId:msgId error:kFIRMessagingErrorMissingTo];
+    return;
+  }
+  [stanza setTo:to];
+  [stanza setCategory:appPackage];
+  // required field in the proto this is set by the server
+  // set it to a sentinel so the runtime doesn't throw an exception
+  [stanza setFrom:@""];
+
+  // MCS itself would set the registration ID
+  // [stanza setRegId:nil];
+
+  int size = [self addData:dataMessage[KFIRMessagingSendMessageAppData] toStanza:stanza];
+  if (size > kMaxAppDataSizeDefault) {
+    [self willSendDataMessageFail:stanza withMessageId:msgId error:kFIRMessagingErrorSizeExceeded];
+    return;
+  }
+
+  BOOL useRmq = (ttl != 0) && (msgId != nil);
+  if (useRmq) {
+    if (!self.client.isConnected) {
+      // do nothing assuming rmq save is enabled
+    }
+
+    NSError *error;
+    if (![self.rmq2Manager saveRmqMessage:stanza error:&error]) {
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeDataMessageManager005, @"%@", error);
+      [self willSendDataMessageFail:stanza withMessageId:msgId error:kFIRMessagingErrorSave];
+      return;
+    }
+
+    [self willSendDataMessageSuccess:stanza withMessageId:msgId];
+  }
+
+  // if delay > 0 we don't really care about sending the message right now
+  // so we piggy-back on any other urgent(delay = 0) message that we are sending
+  if (delay > 0 && [self delayMessage:stanza]) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeDataMessageManager006, @"Delaying Message %@",
+                            dataMessage);
+    return;
+  }
+  // send delayed messages
+  [self sendDelayedMessages:[self.delayedMessagesQueue removeDelayedMessages]];
+
+  BOOL sending = [self tryToSendDataMessageStanza:stanza];
+  if (!sending) {
+    if (useRmq) {
+      NSString *event __unused = [NSString stringWithFormat:@"Queued message: %@", [stanza id_p]];
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeDataMessageManager007, @"%@", event);
+    } else {
+      [self willSendDataMessageFail:stanza
+                      withMessageId:msgId
+                              error:kFIRMessagingErrorCodeNetwork];
+      return;
+    }
+  }
+}
+
+- (void)sendDelayedMessages:(NSArray *)delayedMessages {
+  for (GtalkDataMessageStanza *message in delayedMessages) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeDataMessageManager008,
+                            @"%@ Sending delayed message %@", @"DMM", message);
+    [message setActualDelay:(int)(FIRMessagingCurrentTimestampInSeconds() - message.sent)];
+    [self tryToSendDataMessageStanza:message];
+  }
+}
+
+- (void)didSendDataMessageStanza:(GtalkDataMessageStanza *)message {
+  NSString *msgId = [message id_p] ?: @"";
+  [self.delegate didSendDataMessageWithID:msgId];
+}
+
+- (void)addParamWithKey:(NSString *)key
+                  value:(NSString *)val
+               toStanza:(GtalkDataMessageStanza *)stanza {
+  if (!key || !val) {
+    return;
+  }
+  GtalkAppData *appData = [[GtalkAppData alloc] init];
+  [appData setKey:key];
+  [appData setValue:val];
+  [[stanza appDataArray] addObject:appData];
+}
+
+/**
+ @return The size of the data being added to stanza.
+ */
+- (int)addData:(NSDictionary *)data toStanza:(GtalkDataMessageStanza *)stanza {
+  int size = 0;
+  for (NSString *key in data) {
+    NSObject *val = data[key];
+    if ([val isKindOfClass:[NSString class]]) {
+      NSString *strVal = (NSString *)val;
+      [self addParamWithKey:key value:strVal toStanza:stanza];
+      size += [key length] + [strVal length];
+    } else if ([val isKindOfClass:[NSNumber class]]) {
+      NSString *strVal = [(NSNumber *)val stringValue];
+      [self addParamWithKey:key value:strVal toStanza:stanza];
+      size += [key length] + [strVal length];
+    } else if ([kFIRMessagingRawDataKey isEqualToString:key] &&
+               [val isKindOfClass:[NSData class]]) {
+      NSData *rawData = (NSData *)val;
+      [stanza setRawData:[rawData copy]];
+      size += [rawData length];
+    } else {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeDataMessageManager009, @"Ignoring key: %@",
+                              key);
+    }
+  }
+  return size;
+}
+
+/**
+ * Notify the messenger that send data message completed with success. This is called for
+ * TTL=0, after the message has been sent, or when message is saved, to unlock the send()
+ * method.
+ */
+- (void)willSendDataMessageSuccess:(GtalkDataMessageStanza *)stanza
+                     withMessageId:(NSString *)messageId {
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeDataMessageManager010,
+                          @"send message success: %@", messageId);
+  [self.delegate willSendDataMessageWithID:messageId error:nil];
+}
+
+/**
+ * We send 'send failures' from server as normal FIRMessaging messages, with a 'message_type'
+ * extra - same as 'message deleted'.
+ *
+ * For TTL=0 or errors that can be detected during send ( too many messages, invalid, etc)
+ * we throw IOExceptions
+ */
+- (void)willSendDataMessageFail:(GtalkDataMessageStanza *)stanza
+                  withMessageId:(NSString *)messageId
+                          error:(FIRMessagingInternalErrorCode)errorCode {
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeDataMessageManager011,
+                          @"Send message fail: %@ error: %lu", messageId, (unsigned long)errorCode);
+
+  NSError *error = [NSError errorWithFCMErrorCode:errorCode];
+  if ([self.delegate respondsToSelector:@selector(willSendDataMessageWithID:error:)]) {
+    [self.delegate willSendDataMessageWithID:messageId error:error];
+  }
+}
+
+- (void)resendMessagesWithConnection:(FIRMessagingConnection *)connection {
+  NSMutableString *rmqIdsResent = [NSMutableString string];
+  NSMutableArray *toRemoveRmqIds = [NSMutableArray array];
+  FIRMessaging_WEAKIFY(self);
+  FIRMessaging_WEAKIFY(connection);
+  FIRMessagingRmqMessageHandler messageHandler = ^(int64_t rmqId, int8_t tag, NSData *data) {
+    FIRMessaging_STRONGIFY(self);
+    FIRMessaging_STRONGIFY(connection);
+    GPBMessage *proto =
+        [FIRMessagingGetClassForTag((FIRMessagingProtoTag)tag) parseFromData:data error:NULL];
+    if ([proto isKindOfClass:GtalkDataMessageStanza.class]) {
+      GtalkDataMessageStanza *stanza = (GtalkDataMessageStanza *)proto;
+
+      if (![self handleExpirationForDataMessage:stanza]) {
+        // time expired let's delete from RMQ
+        [toRemoveRmqIds addObject:stanza.persistentId];
+        return;
+      }
+      [rmqIdsResent appendString:[NSString stringWithFormat:@"%@,", stanza.id_p]];
+    }
+
+    [connection sendProto:proto];
+  };
+  [self.rmq2Manager scanWithRmqMessageHandler:messageHandler
+                           dataMessageHandler:nil];
+
+  if ([rmqIdsResent length]) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeDataMessageManager012, @"Resent: %@",
+                            rmqIdsResent);
+  }
+
+  if ([toRemoveRmqIds count]) {
+    [self.rmq2Manager removeRmqMessagesWithRmqIds:toRemoveRmqIds];
+  }
+}
+
+/**
+ *  Check the TTL and generate an error if needed.
+ *
+ *  @return false if the message needs to be deleted
+ */
+- (BOOL)handleExpirationForDataMessage:(GtalkDataMessageStanza *)message {
+  if (message.ttl == 0) {
+    return NO;
+  }
+
+  int64_t now = FIRMessagingCurrentTimestampInSeconds();
+  if (now > message.sent + message.ttl) {
+    [self willSendDataMessageFail:message
+                    withMessageId:message.id_p
+                            error:kFIRMessagingErrorServiceNotAvailable];
+    return NO;
+  }
+  return YES;
+}
+
+#pragma mark - Private
+
+- (int)delayForMessage:(NSMutableDictionary *)message {
+  int delay = 0; // default
+  if (message[kFIRMessagingSendDelay]) {
+    delay = [message[kFIRMessagingSendDelay] intValue];
+    [message removeObjectForKey:kFIRMessagingSendDelay];
+    if (delay < kMinDelaySeconds) {
+      delay = 0;
+    } else if (delay > kMaxDelaySeconds) {
+      delay = kMaxDelaySeconds;
+    }
+  }
+  return delay;
+}
+
+// return True if successfully delayed else False
+- (BOOL)delayMessage:(GtalkDataMessageStanza *)message {
+  return [self.delayedMessagesQueue queueMessage:message];
+}
+
+- (BOOL)tryToSendDataMessageStanza:(GtalkDataMessageStanza *)stanza {
+  if (self.client.isConnectionActive) {
+    [self.client sendMessage:stanza];
+    return YES;
+  }
+
+  // if we only reconnect for TTL = 0 messages check if we ttl = 0 or
+  // if we reconnect for all messages try to reconnect
+  if ((self.upstreamForceReconnect == kUpstreamForceReconnectTTL0 && stanza.ttl == 0) ||
+      self.upstreamForceReconnect == kUpstreamForceReconnectAll) {
+    BOOL isNetworkAvailable = [[FIRMessaging messaging] isNetworkAvailable];
+    if (isNetworkAvailable) {
+      if (stanza.ttl == 0) {
+        // Add TTL = 0 messages to be sent on next connect. TTL != 0 messages are
+        // persisted, and will be sent from the RMQ.
+        [self.client sendOnConnectOrDrop:stanza];
+      }
+
+      [self.client retryConnectionImmediately:YES];
+      return YES;
+    }
+  }
+  return NO;
+}
+
+- (NSString *)categoryForUpstreamMessages {
+  return FIRMessagingAppIdentifier();
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDefines.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDefines.h
new file mode 100644 (file)
index 0000000..62399b3
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 FIRMessaging_xcodeproj_FIRMessagingDefines_h
+#define FIRMessaging_xcodeproj_FIRMessagingDefines_h
+
+#define _FIRMessaging_VERBOSE_LOGGING 1
+
+// Verbose Logging
+#if (_FIRMessaging_VERBOSE_LOGGING)
+#define FIRMessaging_DEV_VERBOSE_LOG(...) NSLog(__VA_ARGS__)
+#else
+#define FIRMessaging_DEV_VERBOSE_LOG(...) do { } while (0)
+#endif // FIRMessaging_VERBOSE_LOGGING
+
+
+// WEAKIFY & STRONGIFY
+// Helper macro.
+#define _FIRMessaging_WEAKNAME(VAR) VAR ## _weak_
+
+#define FIRMessaging_WEAKIFY(VAR) __weak __typeof__(VAR) _FIRMessaging_WEAKNAME(VAR) = (VAR);
+
+#define FIRMessaging_STRONGIFY(VAR) \
+_Pragma("clang diagnostic push") \
+_Pragma("clang diagnostic ignored \"-Wshadow\"") \
+__strong __typeof__(VAR) VAR = _FIRMessaging_WEAKNAME(VAR); \
+_Pragma("clang diagnostic pop")
+
+
+// Type Conversions (used for NSInteger etc)
+#ifndef _FIRMessaging_L
+#define _FIRMessaging_L(v) (long)(v)
+#endif
+
+#ifndef _FIRMessaging_UL
+#define _FIRMessaging_UL(v) (unsigned long)(v)
+#endif
+
+#endif
+
+// Debug Assert
+#ifndef _FIRMessagingDevAssert
+// we directly invoke the NSAssert handler so we can pass on the varargs
+// (NSAssert doesn't have a macro we can use that takes varargs)
+#if !defined(NS_BLOCK_ASSERTIONS)
+#define _FIRMessagingDevAssert(condition, ...)                                       \
+  do {                                                                      \
+    if (!(condition)) {                                                     \
+      [[NSAssertionHandler currentHandler]                                  \
+          handleFailureInFunction:(NSString *)                              \
+                                      [NSString stringWithUTF8String:__PRETTY_FUNCTION__] \
+                             file:(NSString *)[NSString stringWithUTF8String:__FILE__]  \
+                       lineNumber:__LINE__                                  \
+                      description:__VA_ARGS__];                             \
+    }                                                                       \
+  } while(0)
+#else // !defined(NS_BLOCK_ASSERTIONS)
+#define _FIRMessagingDevAssert(condition, ...) do { } while (0)
+#endif // !defined(NS_BLOCK_ASSERTIONS)
+
+#endif // _FIRMessagingDevAssert
+
+// Invalidates the initializer from which it's called.
+#ifndef FIRMessagingInvalidateInitializer
+#define FIRMessagingInvalidateInitializer() \
+  do { \
+    [self class]; /* Avoid warning of dead store to |self|. */ \
+    _FIRMessagingDevAssert(NO, @"Invalid initializer."); \
+    return nil; \
+  } while (0)
+#endif
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDelayedMessageQueue.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDelayedMessageQueue.h
new file mode 100644 (file)
index 0000000..d20ec91
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@class GtalkDataMessageStanza;
+@class FIRMessagingRmqManager;
+
+@protocol FIRMessagingRmqScanner;
+
+typedef void(^FIRMessagingSendDelayedMessagesHandler)(NSArray *messages);
+
+@interface FIRMessagingDelayedMessageQueue : NSObject
+
+- (instancetype)initWithRmqScanner:(id<FIRMessagingRmqScanner>)rmqScanner
+        sendDelayedMessagesHandler:(FIRMessagingSendDelayedMessagesHandler)sendDelayedMessagesHandler;
+
+- (BOOL)queueMessage:(GtalkDataMessageStanza *)message;
+
+- (NSArray *)removeDelayedMessages;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDelayedMessageQueue.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingDelayedMessageQueue.m
new file mode 100644 (file)
index 0000000..0371c02
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingDelayedMessageQueue.h"
+
+#import "Protos/GtalkCore.pbobjc.h"
+
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingRmqManager.h"
+#import "FIRMessagingUtilities.h"
+
+static const int kMaxQueuedMessageCount = 10;
+
+@interface FIRMessagingDelayedMessageQueue ()
+
+@property(nonatomic, readonly, weak) id<FIRMessagingRmqScanner> rmqScanner;
+@property(nonatomic, readonly, copy) FIRMessagingSendDelayedMessagesHandler sendDelayedMessagesHandler;
+
+@property(nonatomic, readwrite, assign) int persistedMessageCount;
+// the scheduled timeout or -1 if not set
+@property(nonatomic, readwrite, assign) int64_t scheduledTimeoutMilliseconds;
+// The time  of the last scan of the message DB,
+// used to avoid retrieving messages more than once.
+@property(nonatomic, readwrite, assign) int64_t lastDBScanTimestampSeconds;
+
+@property(nonatomic, readwrite, strong) NSMutableArray *messages;
+@property(nonatomic, readwrite, strong) NSTimer *sendTimer;
+
+@end
+
+@implementation FIRMessagingDelayedMessageQueue
+
+- (instancetype)init {
+  FIRMessagingInvalidateInitializer();
+}
+
+- (instancetype)initWithRmqScanner:(id<FIRMessagingRmqScanner>)rmqScanner
+        sendDelayedMessagesHandler:(FIRMessagingSendDelayedMessagesHandler)sendDelayedMessagesHandler {
+  _FIRMessagingDevAssert(sendDelayedMessagesHandler, @"Invalid nil callback for delayed messages");
+  self = [super init];
+  if (self) {
+    _rmqScanner = rmqScanner;
+    _sendDelayedMessagesHandler = sendDelayedMessagesHandler;
+    _messages = [NSMutableArray arrayWithCapacity:10];
+    _scheduledTimeoutMilliseconds = -1;
+  }
+  return self;
+}
+
+- (BOOL)queueMessage:(GtalkDataMessageStanza *)message {
+  if (self.messages.count >= kMaxQueuedMessageCount) {
+    return NO;
+  }
+  if (message.ttl == 0) {
+    // ttl=0 messages aren't persisted, add it to memory
+    [self.messages addObject:message];
+  } else {
+    self.persistedMessageCount++;
+  }
+  int64_t timeoutMillis = [self calculateTimeoutInMillisWithDelayInSeconds:message.maxDelay];
+  if (![self isTimeoutScheduled] || timeoutMillis < self.scheduledTimeoutMilliseconds) {
+    [self scheduleTimeoutInMillis:timeoutMillis];
+  }
+  return YES;
+}
+
+- (NSArray *)removeDelayedMessages {
+  [self cancelTimeout];
+  if ([self messageCount] == 0) {
+    return @[];
+  }
+
+  NSMutableArray *delayedMessages = [NSMutableArray array];
+  // add the ttl=0 messages
+  if (self.messages.count) {
+    [delayedMessages addObjectsFromArray:delayedMessages];
+    [self.messages removeAllObjects];
+  }
+
+  // add persistent messages
+  if (self.persistedMessageCount > 0) {
+    FIRMessaging_WEAKIFY(self);
+    [self.rmqScanner scanWithRmqMessageHandler:nil
+                            dataMessageHandler:^(int64_t rmqId, GtalkDataMessageStanza *stanza) {
+                              FIRMessaging_STRONGIFY(self);
+                              if ([stanza hasMaxDelay] &&
+                                  [stanza sent] >= self.lastDBScanTimestampSeconds) {
+                                [delayedMessages addObject:stanza];
+                              }
+                            }];
+    self.lastDBScanTimestampSeconds = FIRMessagingCurrentTimestampInSeconds();
+    self.persistedMessageCount = 0;
+  }
+  return delayedMessages;
+}
+
+- (void)sendMessages {
+  if (self.sendDelayedMessagesHandler) {
+    self.sendDelayedMessagesHandler([self removeDelayedMessages]);
+  }
+}
+
+#pragma mark - Private
+
+- (NSInteger)messageCount {
+  return self.messages.count + self.persistedMessageCount;
+}
+
+- (BOOL)isTimeoutScheduled {
+  return self.scheduledTimeoutMilliseconds > 0;
+}
+
+- (int64_t)calculateTimeoutInMillisWithDelayInSeconds:(int)delay {
+  return FIRMessagingCurrentTimestampInMilliseconds() + delay * 1000.0;
+}
+
+- (void)scheduleTimeoutInMillis:(int64_t)time {
+  [self cancelTimeout];
+  self.scheduledTimeoutMilliseconds = time;
+  double delay = (time - FIRMessagingCurrentTimestampInMilliseconds()) / 1000.0;
+  [self performSelector:@selector(sendMessages) withObject:self afterDelay:delay];
+}
+
+- (void)cancelTimeout {
+  if ([self isTimeoutScheduled]) {
+    [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                             selector:@selector(sendMessages)
+                                               object:nil];
+    self.scheduledTimeoutMilliseconds = -1;
+  }
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingLogger.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingLogger.h
new file mode 100644 (file)
index 0000000..223ac9c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMMessageCode.h"
+
+// The convenience macros are only defined if they haven't already been defined.
+#ifndef FIRMessagingLoggerInfo
+
+// Convenience macros that log to the shared FIRMessagingLogger instance. These macros
+// are how users should typically log to FIRMessagingLogger.
+#define FIRMessagingLoggerDebug(code, ...)  \
+    [FIRMessagingSharedLogger() logFuncDebug:__func__ messageCode:code msg:__VA_ARGS__]
+#define FIRMessagingLoggerInfo(code, ...)   \
+    [FIRMessagingSharedLogger() logFuncInfo:__func__ messageCode:code msg:__VA_ARGS__]
+#define FIRMessagingLoggerNotice(code, ...)   \
+    [FIRMessagingSharedLogger() logFuncNotice:__func__ messageCode:code msg:__VA_ARGS__]
+#define FIRMessagingLoggerWarn(code, ...)   \
+    [FIRMessagingSharedLogger() logFuncWarning:__func__ messageCode:code msg:__VA_ARGS__]
+#define FIRMessagingLoggerError(code, ...)  \
+    [FIRMessagingSharedLogger() logFuncError:__func__ messageCode:code msg:__VA_ARGS__]
+
+#endif  // !defined(FIRMessagingLoggerInfo)
+
+@interface FIRMessagingLogger : NSObject
+
+- (void)logFuncDebug:(const char *)func
+         messageCode:(FIRMessagingMessageCode)messageCode
+                 msg:(NSString *)fmt, ... NS_FORMAT_FUNCTION(3, 4);
+
+- (void)logFuncInfo:(const char *)func
+        messageCode:(FIRMessagingMessageCode)messageCode
+                msg:(NSString *)fmt, ... NS_FORMAT_FUNCTION(3, 4);
+
+- (void)logFuncNotice:(const char *)func
+          messageCode:(FIRMessagingMessageCode)messageCode
+                  msg:(NSString *)fmt, ... NS_FORMAT_FUNCTION(3, 4);
+
+- (void)logFuncWarning:(const char *)func
+           messageCode:(FIRMessagingMessageCode)messageCode
+                   msg:(NSString *)fmt, ... NS_FORMAT_FUNCTION(3, 4);
+
+- (void)logFuncError:(const char *)func
+         messageCode:(FIRMessagingMessageCode)messageCode
+                 msg:(NSString *)fmt, ... NS_FORMAT_FUNCTION(3, 4);
+
+@end
+
+/**
+ * Instantiates and/or returns a shared FIRMessagingLogger used exclusively
+ * for FIRMessaging log messages.
+ *
+ * @return the shared FIRMessagingLogger instance
+ */
+FIRMessagingLogger *FIRMessagingSharedLogger(void);
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingLogger.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingLogger.m
new file mode 100644 (file)
index 0000000..62eb8da
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingLogger.h"
+
+#import <FirebaseCore/FIRLogger.h>
+
+@implementation FIRMessagingLogger
+
++ (instancetype)standardLogger {
+  return [[FIRMessagingLogger alloc] init];
+}
+
+#pragma mark - Log Helpers
+
++ (NSString *)formatMessageCode:(FIRMessagingMessageCode)messageCode {
+  return [NSString stringWithFormat:@"I-FCM%06ld", (long)messageCode];
+}
+
+- (void)logFuncDebug:(const char *)func
+         messageCode:(FIRMessagingMessageCode)messageCode
+                 msg:(NSString *)fmt, ... {
+  va_list args;
+  va_start(args, fmt);
+  FIRLogBasic(FIRLoggerLevelDebug, kFIRLoggerMessaging,
+              [FIRMessagingLogger formatMessageCode:messageCode], fmt, args);
+  va_end(args);
+}
+
+- (void)logFuncInfo:(const char *)func
+        messageCode:(FIRMessagingMessageCode)messageCode
+                msg:(NSString *)fmt, ... {
+  va_list args;
+  va_start(args, fmt);
+  FIRLogBasic(FIRLoggerLevelInfo, kFIRLoggerMessaging,
+              [FIRMessagingLogger formatMessageCode:messageCode], fmt, args);
+  va_end(args);
+}
+
+- (void)logFuncNotice:(const char *)func
+          messageCode:(FIRMessagingMessageCode)messageCode
+                  msg:(NSString *)fmt, ... {
+  va_list args;
+  va_start(args, fmt);
+  FIRLogBasic(FIRLoggerLevelNotice, kFIRLoggerMessaging,
+              [FIRMessagingLogger formatMessageCode:messageCode], fmt, args);
+  va_end(args);
+}
+
+- (void)logFuncWarning:(const char *)func
+           messageCode:(FIRMessagingMessageCode)messageCode
+                   msg:(NSString *)fmt, ... {
+  va_list args;
+  va_start(args, fmt);
+  FIRLogBasic(FIRLoggerLevelWarning, kFIRLoggerMessaging,
+              [FIRMessagingLogger formatMessageCode:messageCode], fmt, args);
+  va_end(args);
+}
+
+- (void)logFuncError:(const char *)func
+         messageCode:(FIRMessagingMessageCode)messageCode
+                 msg:(NSString *)fmt, ... {
+  va_list args;
+  va_start(args, fmt);
+  FIRLogBasic(FIRLoggerLevelError, kFIRLoggerMessaging,
+              [FIRMessagingLogger formatMessageCode:messageCode], fmt, args);
+  va_end(args);
+}
+
+@end
+
+FIRMessagingLogger *FIRMessagingSharedLogger(void) {
+  static dispatch_once_t onceToken;
+  static FIRMessagingLogger *logger;
+  dispatch_once(&onceToken, ^{
+    logger = [FIRMessagingLogger standardLogger];
+  });
+
+  return logger;
+}
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPacketQueue.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPacketQueue.h
new file mode 100644 (file)
index 0000000..1f528ab
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@interface FIRMessagingPacket : NSObject
+
++ (FIRMessagingPacket *)packetWithTag:(int8_t)tag rmqId:(NSString *)rmqId data:(NSData *)data;
+
+@property(nonatomic, readonly, strong) NSData *data;
+@property(nonatomic, readonly, assign) int8_t tag;
+// not sent over the wire required for bookkeeping
+@property(nonatomic, readonly, assign) NSString *rmqId;
+
+@end
+
+
+/**
+ * A queue of the packets(protos) that need to be send over the wire.
+ */
+@interface FIRMessagingPacketQueue : NSObject
+
+@property(nonatomic, readonly, assign) NSUInteger count;
+@property(nonatomic, readonly, assign) BOOL isEmpty;
+
+- (void)push:(FIRMessagingPacket *)packet;
+- (void)pushHead:(FIRMessagingPacket *)packet;
+- (FIRMessagingPacket *)pop;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPacketQueue.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPacketQueue.m
new file mode 100644 (file)
index 0000000..2b3410a
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingPacketQueue.h"
+
+#import "FIRMessagingDefines.h"
+
+@interface FIRMessagingPacket ()
+
+@property(nonatomic, readwrite, strong) NSData *data;
+@property(nonatomic, readwrite, assign) int8_t tag;
+@property(nonatomic, readwrite, assign) NSString *rmqId;
+
+@end
+
+@implementation FIRMessagingPacket
+
++ (FIRMessagingPacket *)packetWithTag:(int8_t)tag rmqId:(NSString *)rmqId data:(NSData *)data {
+  return [[self alloc] initWithTag:tag rmqId:rmqId data:data];
+}
+
+- (instancetype)init {
+  FIRMessagingInvalidateInitializer();
+}
+
+- (instancetype)initWithTag:(int8_t)tag rmqId:(NSString *)rmqId data:(NSData *)data {
+  self = [super init];
+  if (self != nil) {
+    _data = data;
+    _tag = tag;
+    _rmqId = rmqId;
+  }
+  return self;
+}
+
+- (NSString *)description {
+  if ([self.rmqId length]) {
+    return [NSString stringWithFormat:@"<Packet: Tag - %d, Length - %lu>, RmqId - %@",
+            self.tag, _FIRMessaging_UL(self.data.length), self.rmqId];
+  } else {
+    return [NSString stringWithFormat:@"<Packet: Tag - %d, Length - %lu>",
+            self.tag, _FIRMessaging_UL(self.data.length)];
+  }
+}
+
+@end
+
+@interface FIRMessagingPacketQueue ()
+
+@property(nonatomic, readwrite, strong) NSMutableArray *packetsContainer;
+
+@end
+
+
+@implementation FIRMessagingPacketQueue;
+
+- (id)init {
+  self = [super init];
+  if (self) {
+    _packetsContainer = [[NSMutableArray alloc] init];
+  }
+  return self;
+}
+
+- (BOOL)isEmpty {
+  return self.packetsContainer.count == 0;
+}
+
+- (NSUInteger)count {
+  return self.packetsContainer.count;
+}
+
+- (void)push:(FIRMessagingPacket *)packet {
+  [self.packetsContainer addObject:packet];
+}
+
+- (void)pushHead:(FIRMessagingPacket *)packet {
+  [self.packetsContainer insertObject:packet atIndex:0];
+}
+
+- (FIRMessagingPacket *)pop {
+  if (!self.isEmpty) {
+    FIRMessagingPacket *packet = self.packetsContainer[0];
+    [self.packetsContainer removeObjectAtIndex:0];
+    return packet;
+  }
+  return nil;
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPendingTopicsList.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPendingTopicsList.h
new file mode 100644 (file)
index 0000000..a8108bf
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#import "FIRMessaging.h"
+#import "FIRMessagingTopicsCommon.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ *  Represents a single batch of topics, with the same action.
+ *
+ *  Topic operations which have the same action (subscribe or unsubscribe) can be executed
+ *  simultaneously, as the order of operations do not matter with the same action. The set of
+ *  topics is unique, as it doesn't make sense to apply the same action to the same topic
+ *  repeatedly; the result would be the same as the first time.
+ */
+@interface FIRMessagingTopicBatch : NSObject <NSCoding>
+
+@property(nonatomic, readonly, assign) FIRMessagingTopicAction action;
+@property(nonatomic, readonly, copy) NSMutableSet <NSString *> *topics;
+
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithAction:(FIRMessagingTopicAction)action NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@class FIRMessagingPendingTopicsList;
+/**
+ *  This delegate must be supplied to the instance of FIRMessagingPendingTopicsList, via the
+ *  @cdelegate property. It lets the
+ *  pending topics list know whether or not it can begin making requests via
+ *  @c-pendingTopicsListCanRequestTopicUpdates:, and handles the request to actually
+ *  perform the topic operation. The delegate also handles when the pending topics list is updated,
+ *  so that it can be archived or persisted.
+ *
+ *  @see FIRMessagingPendingTopicsList
+ */
+@protocol FIRMessagingPendingTopicsListDelegate <NSObject>
+
+- (void)pendingTopicsList:(FIRMessagingPendingTopicsList *)list
+  requestedUpdateForTopic:(NSString *)topic
+                   action:(FIRMessagingTopicAction)action
+               completion:(FIRMessagingTopicOperationCompletion)completion;
+- (void)pendingTopicsListDidUpdate:(FIRMessagingPendingTopicsList *)list;
+- (BOOL)pendingTopicsListCanRequestTopicUpdates:(FIRMessagingPendingTopicsList *)list;
+
+@end
+
+/**
+ *  FIRMessagingPendingTopicsList manages a list of topic subscription updates, batched by the same
+ *  action (subscribe or unsubscribe). The list roughly maintains the order of the topic operations,
+ *  batched together whenever the topic action (subscribe or unsubscribe) changes.
+ *
+ *  Topics operations are batched by action because it is safe to perform the same topic action
+ *  (subscribe or unsubscribe) on many topics simultaneously. After each batch is successfully
+ *  completed, the next batch operations can begin.
+ *
+ *  When asked to resume its operations, FIRMessagingPendingTopicsList will begin performing updates
+ *  of its current batch of topics. For example, it may begin subscription operations for topics
+ *  [A, B, C] simultaneously.
+ *
+ *  When the current batch is completed, the next batch of operations will be started. For example
+ *  the list may begin unsubscribe operations for [D, A, E]. Note that because A is in both batches,
+ *  A will be correctly subscribed in the first batch, then unsubscribed as part of the second batch
+ *  of operations. Without batching, it would be ambiguous whether A's subscription operation or the
+ *  unsubscription operation would be completed first.
+ *
+ *  An app can subscribe and unsubscribe from many topics, and this class helps persist the pending
+ *  topics and perform the operation safely and correctly.
+ *
+ *  When a topic fails to subscribe or unsubscribe due to a network error, it is considered a
+ *  recoverable error, and so it remains in the current batch until it is succesfully completed.
+ *  Topic updates are completed when they either (a) succeed, (b) are cancelled, or (c) result in an
+ *  unrecoverable error. Any error outside of `NSURLErrorDomain` is considered an unrecoverable
+ *  error.
+ *
+ *  In addition to maintaining the list of pending topic updates, FIRMessagingPendingTopicsList also
+ *  can track completion handlers for topic operations.
+ *
+ *  @discussion Completion handlers for topic updates are not maintained if it was restored from a
+ *  keyed archive. They are only called if the topic operation finished within the same app session.
+ *
+ *  You must supply an object conforming to FIRMessagingPendingTopicsListDelegate in order for the
+ *  topic operations to execute.
+ *
+ *  @see FIRMessagingPendingTopicsListDelegate
+ */
+@interface FIRMessagingPendingTopicsList : NSObject <NSCoding>
+
+@property(nonatomic, weak) NSObject <FIRMessagingPendingTopicsListDelegate> *delegate;
+
+@property(nonatomic, readonly, strong, nullable) NSDate *archiveDate;
+@property(nonatomic, readonly) NSUInteger numberOfBatches;
+
+
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+- (void)addOperationForTopic:(NSString *)topic
+                  withAction:(FIRMessagingTopicAction)action
+                  completion:(nullable FIRMessagingTopicOperationCompletion)completion;
+- (void)resumeOperationsIfNeeded;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPendingTopicsList.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPendingTopicsList.m
new file mode 100644 (file)
index 0000000..b10b552
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingPendingTopicsList.h"
+
+#import "FIRMessaging_Private.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingPubSub.h"
+
+#import "FIRMessagingDefines.h"
+
+NSString *const kPendingTopicBatchActionKey = @"action";
+NSString *const kPendingTopicBatchTopicsKey = @"topics";
+
+NSString *const kPendingBatchesEncodingKey = @"batches";
+NSString *const kPendingTopicsTimestampEncodingKey = @"ts";
+
+#pragma mark - FIRMessagingTopicBatch
+
+@interface FIRMessagingTopicBatch ()
+
+@property(nonatomic, strong, nonnull) NSMutableDictionary
+    <NSString *, NSMutableArray <FIRMessagingTopicOperationCompletion> *> *topicHandlers;
+
+@end
+
+@implementation FIRMessagingTopicBatch
+
+- (instancetype)initWithAction:(FIRMessagingTopicAction)action {
+  if (self = [super init]) {
+    _action = action;
+    _topics = [NSMutableSet set];
+    _topicHandlers = [NSMutableDictionary dictionary];
+  }
+  return self;
+}
+
+#pragma mark NSCoding
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+  [aCoder encodeInteger:self.action forKey:kPendingTopicBatchActionKey];
+  [aCoder encodeObject:self.topics forKey:kPendingTopicBatchTopicsKey];
+}
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+
+  // Ensure that our integer -> enum casting is safe
+  NSInteger actionRawValue = [aDecoder decodeIntegerForKey:kPendingTopicBatchActionKey];
+  FIRMessagingTopicAction action = FIRMessagingTopicActionSubscribe;
+  if (actionRawValue == FIRMessagingTopicActionUnsubscribe) {
+    action = FIRMessagingTopicActionUnsubscribe;
+  }
+
+  if (self = [self initWithAction:action]) {
+    NSSet *topics = [aDecoder decodeObjectForKey:kPendingTopicBatchTopicsKey];
+    if ([topics isKindOfClass:[NSSet class]]) {
+      _topics = [topics mutableCopy];
+    }
+    _topicHandlers = [NSMutableDictionary dictionary];
+  }
+  return self;
+}
+
+@end
+
+#pragma mark - FIRMessagingPendingTopicsList
+
+@interface FIRMessagingPendingTopicsList ()
+
+@property(nonatomic, readwrite, strong) NSDate *archiveDate;
+@property(nonatomic, strong) NSMutableArray <FIRMessagingTopicBatch *> *topicBatches;
+
+@property(nonatomic, strong) FIRMessagingTopicBatch *currentBatch;
+@property(nonatomic, strong) NSMutableSet <NSString *> *topicsInFlight;
+
+@end
+
+@implementation FIRMessagingPendingTopicsList
+
+- (instancetype)init {
+  if (self = [super init]) {
+    _topicBatches = [NSMutableArray array];
+    _topicsInFlight = [NSMutableSet set];
+  }
+  return self;
+}
+
++ (void)pruneTopicBatches:(NSMutableArray <FIRMessagingTopicBatch *> *)topicBatches {
+  // For now, just remove empty batches. In the future we can use this to make the subscriptions
+  // more efficient, by actually pruning topic actions that cancel each other out, for example.
+  for (NSInteger i = topicBatches.count-1; i >= 0; i--) {
+    FIRMessagingTopicBatch *batch = topicBatches[i];
+    if (batch.topics.count == 0) {
+      [topicBatches removeObjectAtIndex:i];
+    }
+  }
+}
+
+#pragma mark NSCoding
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+  [aCoder encodeObject:[NSDate date] forKey:kPendingTopicsTimestampEncodingKey];
+  [aCoder encodeObject:self.topicBatches forKey:kPendingBatchesEncodingKey];
+}
+
+- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
+
+  if (self = [self init]) {
+    _archiveDate = [aDecoder decodeObjectForKey:kPendingTopicsTimestampEncodingKey];
+    NSArray *archivedBatches = [aDecoder decodeObjectForKey:kPendingBatchesEncodingKey];
+    if (archivedBatches) {
+      _topicBatches = [archivedBatches mutableCopy];
+      [FIRMessagingPendingTopicsList pruneTopicBatches:_topicBatches];
+    }
+    _topicsInFlight = [NSMutableSet set];
+  }
+  return self;
+}
+
+#pragma mark Getters
+
+- (NSUInteger)numberOfBatches {
+  return self.topicBatches.count;
+}
+
+#pragma mark Adding/Removing topics
+
+- (void)addOperationForTopic:(NSString *)topic
+                  withAction:(FIRMessagingTopicAction)action
+                  completion:(nullable FIRMessagingTopicOperationCompletion)completion {
+
+  FIRMessagingTopicBatch *lastBatch = nil;
+  @synchronized (self) {
+    lastBatch = self.topicBatches.lastObject;
+    if (!lastBatch || lastBatch.action != action) {
+      // There either was no last batch, or our last batch's action was not the same, so we have to
+      // create a new batch
+      lastBatch = [[FIRMessagingTopicBatch alloc] initWithAction:action];
+      [self.topicBatches addObject:lastBatch];
+    }
+    BOOL topicExistedBefore = ([lastBatch.topics member:topic] != nil);
+    if (!topicExistedBefore) {
+      [lastBatch.topics addObject:topic];
+      [self.delegate pendingTopicsListDidUpdate:self];
+    }
+    // Add the completion handler to the batch
+    if (completion) {
+      NSMutableArray *handlers = lastBatch.topicHandlers[topic];
+      if (!handlers) {
+        handlers = [[NSMutableArray alloc] init];
+      }
+      [handlers addObject:completion];
+      lastBatch.topicHandlers[topic] = handlers;
+    }
+    if (!self.currentBatch) {
+      self.currentBatch = lastBatch;
+    }
+    // This may have been the first topic added, or was added to an ongoing batch
+    if (self.currentBatch == lastBatch && !topicExistedBefore) {
+      // Add this topic to our ongoing operations
+      FIRMessaging_WEAKIFY(self);
+      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+        FIRMessaging_STRONGIFY(self);
+        [self resumeOperationsIfNeeded];
+      });
+    }
+  }
+}
+
+- (void)resumeOperationsIfNeeded {
+  @synchronized (self) {
+    // If current batch is not set, set it now
+    if (!self.currentBatch) {
+      self.currentBatch = self.topicBatches.firstObject;
+    }
+    if (self.currentBatch.topics.count == 0) {
+      return;
+    }
+    if (!self.delegate) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodePendingTopicsList000,
+                              @"Attempted to update pending topics without a delegate");
+      return;
+    }
+    if (![self.delegate pendingTopicsListCanRequestTopicUpdates:self]) {
+      return;
+    }
+    for (NSString *topic in self.currentBatch.topics) {
+      if ([self.topicsInFlight member:topic]) {
+        // This topic is already active, so skip
+        continue;
+      }
+      [self beginUpdateForCurrentBatchTopic:topic];
+    }
+  }
+}
+
+- (BOOL)subscriptionErrorIsRecoverable:(NSError *)error {
+  return [error.domain isEqualToString:NSURLErrorDomain];
+}
+
+- (void)beginUpdateForCurrentBatchTopic:(NSString *)topic {
+
+  @synchronized (self) {
+    [self.topicsInFlight addObject:topic];
+  }
+  FIRMessaging_WEAKIFY(self);
+  [self.delegate
+            pendingTopicsList:self
+      requestedUpdateForTopic:topic
+                       action:self.currentBatch.action
+                   completion:^(NSError *error) {
+                     dispatch_async(
+                         dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+                           FIRMessaging_STRONGIFY(self);
+                           @synchronized(self) {
+                             [self.topicsInFlight removeObject:topic];
+
+                             BOOL recoverableError = [self subscriptionErrorIsRecoverable:error];
+                             if (!error || !recoverableError) {
+                               // Notify our handlers and remove the topic from our batch
+                               NSMutableArray *handlers = self.currentBatch.topicHandlers[topic];
+                               if (handlers.count) {
+                                 dispatch_async(dispatch_get_main_queue(), ^{
+                                   for (FIRMessagingTopicOperationCompletion handler in handlers) {
+                                     handler(error);
+                                   }
+                                   [handlers removeAllObjects];
+                                 });
+                               }
+                               [self.currentBatch.topics removeObject:topic];
+                               [self.currentBatch.topicHandlers removeObjectForKey:topic];
+                               if (self.currentBatch.topics.count == 0) {
+                                 // All topic updates successfully finished in this batch, move on
+                                 // to the next batch
+                                 [self.topicBatches removeObject:self.currentBatch];
+                                 self.currentBatch = nil;
+                               }
+                               [self.delegate pendingTopicsListDidUpdate:self];
+                               FIRMessaging_WEAKIFY(self);
+                               dispatch_async(
+                                   dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
+                                   ^{
+                                     FIRMessaging_STRONGIFY(self);
+                                     [self resumeOperationsIfNeeded];
+                                   });
+                             }
+                           }
+                         });
+                   }];
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPersistentSyncMessage.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPersistentSyncMessage.h
new file mode 100644 (file)
index 0000000..5a48e99
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@interface FIRMessagingPersistentSyncMessage : NSObject
+
+@property(nonatomic, readonly, strong) NSString *rmqID;
+@property(nonatomic, readwrite, assign) BOOL apnsReceived;
+@property(nonatomic, readwrite, assign) BOOL mcsReceived;
+@property(nonatomic, readonly, assign) int64_t expirationTime;
+
+- (instancetype)initWithRMQID:(NSString *)rmqID expirationTime:(int64_t)expirationTime;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPersistentSyncMessage.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPersistentSyncMessage.m
new file mode 100644 (file)
index 0000000..bf7d05b
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingPersistentSyncMessage.h"
+
+#import "FIRMessagingDefines.h"
+
+@interface FIRMessagingPersistentSyncMessage ()
+
+@property(nonatomic, readwrite, strong) NSString *rmqID;
+@property(nonatomic, readwrite, assign) int64_t expirationTime;
+
+@end
+
+@implementation FIRMessagingPersistentSyncMessage
+
+- (instancetype)init {
+  FIRMessagingInvalidateInitializer();
+}
+
+- (instancetype)initWithRMQID:(NSString *)rmqID expirationTime:(int64_t)expirationTime {
+  self = [super init];
+  if (self) {
+    _rmqID = [rmqID copy];
+    _expirationTime = expirationTime;
+  }
+  return self;
+}
+
+- (NSString *)description {
+  NSString *classDescription = NSStringFromClass([self class]);
+  NSDate *date = [NSDate dateWithTimeIntervalSince1970:self.expirationTime];
+  return [NSString stringWithFormat:@"%@: (rmqID: %@, apns: %d, mcs: %d, expiry: %@",
+          classDescription, self.rmqID, self.mcsReceived, self.apnsReceived, date];
+}
+
+- (NSString *)debugDescription {
+  return [self description];
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSub.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSub.h
new file mode 100644 (file)
index 0000000..ebb4ca8
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessaging.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class FIRMessagingClient;
+@class FIRMessagingPubSubCache;
+
+/**
+ *  FIRMessagingPubSub provides a publish-subscribe model for sending FIRMessaging topic messages.
+ *
+ *  An app can subscribe to different topics defined by the
+ *  developer. The app server can then send messages to the subscribed devices
+ *  without having to maintain topic-subscribers mapping. Topics do not
+ *  need to be explicitly created before subscribing or publishing&mdash;they
+ *  are automatically created when publishing or subscribing.
+ *
+ *  Messages published to the topic will be received as regular FIRMessaging messages
+ *  with `"from"` set to `"/topics/myTopic"`.
+ *
+ *  Only topic names that match the pattern `"/topics/[a-zA-Z0-9-_.~%]{1,900}"`
+ *  are allowed for subscribing and publishing.
+ */
+@interface FIRMessagingPubSub : NSObject
+
+@property(nonatomic, readonly, strong) FIRMessagingPubSubCache *cache;
+@property(nonatomic, readonly, strong) FIRMessagingClient *client;
+
+/**
+ *  Initializes an instance of FIRMessagingPubSub.
+ *
+ *  @return An instance of FIRMessagingPubSub.
+ */
+- (instancetype)initWithClient:(FIRMessagingClient *)client NS_DESIGNATED_INITIALIZER;
+
+/**
+ *  Subscribes an app instance to a topic, enabling it to receive messages
+ *  sent to that topic.
+ *
+ *  This is an asynchronous call. If subscription fails, FIRMessaging
+ *  invokes the completion callback with the appropriate error.
+ *
+ *  @see FIRMessagingPubSub unsubscribeWithToken:topic:handler:
+ *
+ *  @param token    The registration token as received from the InstanceID
+ *                   library for a given `authorizedEntity` and "gcm" scope.
+ *  @param topic    The topic to subscribe to. Should be of the form
+ *                  `"/topics/<topic-name>"`.
+ *  @param options  Unused parameter, please pass nil or empty dictionary.
+ *  @param handler  The callback handler invoked when the subscribe call
+ *                  ends. In case of success, a nil error is returned. Otherwise,
+ *                  an appropriate error object is returned.
+ *  @discussion     This method is thread-safe. However, it is not guaranteed to
+ *                  return on the main thread.
+ */
+- (void)subscribeWithToken:(NSString *)token
+                     topic:(NSString *)topic
+                   options:(nullable NSDictionary *)options
+                   handler:(FIRMessagingTopicOperationCompletion)handler;
+
+/**
+ *  Unsubscribes an app instance from a topic, stopping it from receiving
+ *  any further messages sent to that topic.
+ *
+ *  This is an asynchronous call. If the attempt to unsubscribe fails,
+ *  we invoke the `completion` callback passed in with an appropriate error.
+ *
+ *  @param token   The token used to subscribe to this topic.
+ *  @param topic   The topic to unsubscribe from. Should be of the form
+ *                 `"/topics/<topic-name>"`.
+ *  @param options Unused parameter, please pass nil or empty dictionary.
+ *  @param handler The handler that is invoked once the unsubscribe call ends.
+ *                 In case of success, nil error is returned. Otherwise, an
+ *                  appropriate error object is returned.
+ *  @discussion     This method is thread-safe. However, it is not guaranteed to
+ *                  return on the main thread.
+ */
+- (void)unsubscribeWithToken:(NSString *)token
+                       topic:(NSString *)topic
+                     options:(nullable NSDictionary *)options
+                     handler:(FIRMessagingTopicOperationCompletion)handler;
+
+/**
+ *  Asynchronously subscribe to the topic. Adds to the pending list of topic operations.
+ *  Retry in case of failures. This makes a repeated attempt to subscribe to the topic
+ *  as compared to the `subscribe` method above which tries once.
+ *
+ *  @param topic The topic name to subscribe to. Should be of the form `"/topics/<topic-name>"`.
+ *  @param handler The handler that is invoked once the unsubscribe call ends.
+ *                 In case of success, nil error is returned. Otherwise, an
+ *                  appropriate error object is returned.
+ */
+- (void)subscribeToTopic:(NSString *)topic
+                 handler:(nullable FIRMessagingTopicOperationCompletion)handler;
+
+/**
+ *  Asynchronously unsubscribe from the topic. Adds to the pending list of topic operations.
+ *  Retry in case of failures. This makes a repeated attempt to unsubscribe from the topic
+ *  as compared to the `unsubscribe` method above which tries once.
+ *
+ *  @param topic The topic name to unsubscribe from. Should be of the form `"/topics/<topic-name>"`.
+ *  @param handler The handler that is invoked once the unsubscribe call ends.
+ *                 In case of success, nil error is returned. Otherwise, an
+ *                  appropriate error object is returned.
+ */
+- (void)unsubscribeFromTopic:(NSString *)topic
+                     handler:(nullable FIRMessagingTopicOperationCompletion)handler;
+
+/**
+ *  Schedule subscriptions sync.
+ *
+ *  @param immediately YES if the sync should be scheduled immediately else NO if we can delay
+ *                     the sync.
+ */
+- (void)scheduleSync:(BOOL)immediately;
+
+/**
+ *  Adds the "/topics/" prefix to the topic.
+ *
+ *  @param topic The topic to add the prefix to.
+ *
+ *  @return The new topic name with the "/topics/" prefix added.
+ */
++ (NSString *)addPrefixToTopic:(NSString *)topic;
+
+/**
+ *  Removes the "/topics/" prefix from the topic.
+ *
+ *  @param topic The topic to remove the prefix from.
+ *
+ *  @return The new topic name with the "/topics/" prefix removed.
+ */
+
++ (NSString *)removePrefixFromTopic:(NSString *)topic;
+/**
+ *  Check if the topic name has "/topics/" prefix.
+ *
+ *  @param topic The topic name to verify.
+ *
+ *  @return YES if the topic name has "/topics/" prefix else NO.
+ */
++ (BOOL)hasTopicsPrefix:(NSString *)topic;
+
+/**
+ *  Check if it's a valid topic name. This includes "/topics/" prefix in the topic name.
+ *
+ *  @param topic The topic name to verify.
+ *
+ *  @return YES if the topic name satisfies the regex "/topics/[a-zA-Z0-9-_.~%]{1,900}".
+ */
++ (BOOL)isValidTopicWithPrefix:(NSString *)topic;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSub.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSub.m
new file mode 100644 (file)
index 0000000..3f954e8
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingPubSub.h"
+
+#import "FIRMessaging.h"
+#import "FIRMessagingClient.h"
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingPendingTopicsList.h"
+#import "FIRMessagingUtilities.h"
+#import "FIRMessaging_Private.h"
+#import "NSDictionary+FIRMessaging.h"
+#import "NSError+FIRMessaging.h"
+
+static NSString *const kPendingSubscriptionsListKey =
+    @"com.firebase.messaging.pending-subscriptions";
+
+@interface FIRMessagingPubSub () <FIRMessagingPendingTopicsListDelegate>
+
+@property(nonatomic, readwrite, strong) FIRMessagingPendingTopicsList *pendingTopicUpdates;
+@property(nonatomic, readwrite, strong) FIRMessagingClient *client;
+
+@end
+
+@implementation FIRMessagingPubSub
+
+- (instancetype)init {
+  FIRMessagingInvalidateInitializer();
+  // Need this to disable an Xcode warning.
+  return [self initWithClient:nil];
+}
+
+- (instancetype)initWithClient:(FIRMessagingClient *)client {
+  self = [super init];
+  if (self) {
+    _client = client;
+    [self restorePendingTopicsList];
+  }
+  return self;
+}
+
+- (void)subscribeWithToken:(NSString *)token
+                     topic:(NSString *)topic
+                   options:(NSDictionary *)options
+                   handler:(FIRMessagingTopicOperationCompletion)handler {
+  _FIRMessagingDevAssert([token length], @"FIRMessaging error no token specified");
+  _FIRMessagingDevAssert([topic length], @"FIRMessaging error Invalid empty topic specified");
+  if (!self.client) {
+    handler([NSError errorWithFCMErrorCode:kFIRMessagingErrorCodePubSubFIRMessagingNotSetup]);
+    return;
+  }
+
+  token = [token copy];
+  topic = [topic copy];
+
+  if (![options count]) {
+    options = @{};
+  }
+
+  if (![[self class] isValidTopicWithPrefix:topic]) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodePubSub000,
+                            @"Invalid FIRMessaging Pubsub topic %@", topic);
+    handler([NSError errorWithFCMErrorCode:kFIRMessagingErrorCodePubSubInvalidTopic]);
+    return;
+  }
+
+  if (![self verifyPubSubOptions:options]) {
+    // we do not want to quit even if options have some invalid values.
+    FIRMessagingLoggerError(kFIRMessagingMessageCodePubSub001,
+                            @"Invalid options passed to FIRMessagingPubSub with non-string keys or "
+                             "values.");
+  }
+  // copy the dictionary would trim non-string keys or values if any.
+  options = [options fcm_trimNonStringValues];
+
+  [self.client updateSubscriptionWithToken:token
+                                     topic:topic
+                                   options:options
+                              shouldDelete:NO
+                                   handler:^void(NSError *error) {
+                                     handler(error);
+                                   }];
+}
+
+- (void)unsubscribeWithToken:(NSString *)token
+                       topic:(NSString *)topic
+                     options:(NSDictionary *)options
+                     handler:(FIRMessagingTopicOperationCompletion)handler {
+  _FIRMessagingDevAssert([token length], @"FIRMessaging error no token specified");
+  _FIRMessagingDevAssert([topic length], @"FIRMessaging error Invalid empty topic specified");
+
+  if (!self.client) {
+    handler([NSError errorWithFCMErrorCode:kFIRMessagingErrorCodePubSubFIRMessagingNotSetup]);
+    return;
+  }
+
+  token = [token copy];
+  topic = [topic copy];
+  if (![options count]) {
+    options = @{};
+  }
+
+  if (![[self class] isValidTopicWithPrefix:topic]) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodePubSub002,
+                            @"Invalid FIRMessaging Pubsub topic %@", topic);
+    handler([NSError errorWithFCMErrorCode:kFIRMessagingErrorCodePubSubInvalidTopic]);
+    return;
+  }
+  if (![self verifyPubSubOptions:options]) {
+    // we do not want to quit even if options have some invalid values.
+    FIRMessagingLoggerError(
+        kFIRMessagingMessageCodePubSub003,
+        @"Invalid options passed to FIRMessagingPubSub with non-string keys or values.");
+  }
+  // copy the dictionary would trim non-string keys or values if any.
+  options = [options fcm_trimNonStringValues];
+
+  [self.client updateSubscriptionWithToken:token
+                                     topic:topic
+                                   options:options
+                              shouldDelete:YES
+                                   handler:^void(NSError *error) {
+                                     handler(error);
+                                   }];
+}
+
+- (void)subscribeToTopic:(NSString *)topic
+                 handler:(nullable FIRMessagingTopicOperationCompletion)handler {
+  [self.pendingTopicUpdates addOperationForTopic:topic
+                                      withAction:FIRMessagingTopicActionSubscribe
+                                      completion:handler];
+}
+
+- (void)unsubscribeFromTopic:(NSString *)topic
+                     handler:(nullable FIRMessagingTopicOperationCompletion)handler {
+  [self.pendingTopicUpdates addOperationForTopic:topic
+                                      withAction:FIRMessagingTopicActionUnsubscribe
+                                      completion:handler];
+}
+
+- (void)scheduleSync:(BOOL)immediately {
+  NSString *fcmToken = [[FIRMessaging messaging] defaultFcmToken];
+  if (fcmToken.length) {
+    [self.pendingTopicUpdates resumeOperationsIfNeeded];
+  }
+}
+
+#pragma mark - FIRMessagingPendingTopicsListDelegate
+
+- (void)pendingTopicsList:(FIRMessagingPendingTopicsList *)list
+  requestedUpdateForTopic:(NSString *)topic
+                   action:(FIRMessagingTopicAction)action
+               completion:(FIRMessagingTopicOperationCompletion)completion {
+
+  NSString *fcmToken = [[FIRMessaging messaging] defaultFcmToken];
+  if (action == FIRMessagingTopicActionSubscribe) {
+    [self subscribeWithToken:fcmToken topic:topic options:nil handler:completion];
+  } else {
+    [self unsubscribeWithToken:fcmToken topic:topic options:nil handler:completion];
+  }
+}
+
+- (void)pendingTopicsListDidUpdate:(FIRMessagingPendingTopicsList *)list {
+  [self archivePendingTopicsList:list];
+}
+
+- (BOOL)pendingTopicsListCanRequestTopicUpdates:(FIRMessagingPendingTopicsList *)list {
+  NSString *fcmToken = [[FIRMessaging messaging] defaultFcmToken];
+  return (fcmToken.length > 0);
+}
+
+#pragma mark - Storing Pending Topics
+
+- (void)archivePendingTopicsList:(FIRMessagingPendingTopicsList *)topicsList {
+  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+  NSData *pendingData = [NSKeyedArchiver archivedDataWithRootObject:topicsList];
+  [defaults setObject:pendingData forKey:kPendingSubscriptionsListKey];
+  [defaults synchronize];
+}
+
+- (void)restorePendingTopicsList {
+  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+  NSData *pendingData = [defaults objectForKey:kPendingSubscriptionsListKey];
+  FIRMessagingPendingTopicsList *subscriptions;
+  @try {
+    if (pendingData) {
+      subscriptions = [NSKeyedUnarchiver unarchiveObjectWithData:pendingData];
+    }
+  } @catch (NSException *exception) {
+    // Nothing we can do, just continue as if we don't have pending subscriptions
+  } @finally {
+    if (subscriptions) {
+      self.pendingTopicUpdates = subscriptions;
+    } else {
+      self.pendingTopicUpdates = [[FIRMessagingPendingTopicsList alloc] init];
+    }
+    self.pendingTopicUpdates.delegate = self;
+  }
+}
+
+#pragma mark - Private Helpers
+
+- (BOOL)verifyPubSubOptions:(NSDictionary *)options {
+  return ![options fcm_hasNonStringKeysOrValues];
+}
+
+#pragma mark - Topic Name Helpers
+
+static NSString *const kTopicsPrefix = @"/topics/";
+static NSString *const kTopicRegexPattern = @"/topics/([a-zA-Z0-9-_.~%]+)";
+
++ (NSString *)addPrefixToTopic:(NSString *)topic {
+  if (![self hasTopicsPrefix:topic]) {
+    return [NSString stringWithFormat:@"%@%@", kTopicsPrefix, topic];
+  } else {
+    return [topic copy];
+  }
+}
+
++ (NSString *)removePrefixFromTopic:(NSString *)topic {
+  if ([self hasTopicsPrefix:topic]) {
+    return [topic substringFromIndex:kTopicsPrefix.length];
+  } else {
+    return [topic copy];
+  }
+}
+
++ (BOOL)hasTopicsPrefix:(NSString *)topic {
+  return [topic hasPrefix:kTopicsPrefix];
+}
+
+/**
+ *  Returns a regular expression for matching a topic sender.
+ *
+ *  @return The topic matching regular expression
+ */
++ (NSRegularExpression *)topicRegex {
+  // Since this is a static regex pattern, we only only need to declare it once.
+  static NSRegularExpression *topicRegex;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    NSError *error;
+    topicRegex =
+        [NSRegularExpression regularExpressionWithPattern:kTopicRegexPattern
+                                                  options:NSRegularExpressionAnchorsMatchLines
+                                                    error:&error];
+  });
+  return topicRegex;
+}
+
+/**
+ *  Gets the class describing occurences of topic names and sender IDs in the sender.
+ *
+ *  @param expression The topic expression used to generate a pubsub topic
+ *
+ *  @return Representation of captured subexpressions in topic regular expression
+ */
++ (BOOL)isValidTopicWithPrefix:(NSString *)topic {
+  NSRange topicRange = NSMakeRange(0, topic.length);
+  NSRange regexMatchRange = [[self topicRegex] rangeOfFirstMatchInString:topic
+                                                                 options:NSMatchingAnchored
+                                                                   range:topicRange];
+  return NSEqualRanges(topicRange, regexMatchRange);
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSubRegistrar.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSubRegistrar.h
new file mode 100644 (file)
index 0000000..b51813f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingTopicOperation.h"
+
+@class FIRMessagingCheckinService;
+
+@interface FIRMessagingPubSubRegistrar : NSObject
+
+/**
+ *  Designated Initializer.
+ *
+ *  @param checkinService The checkin service used to register with Checkin
+ *                        server.
+ *
+ *  @return A new FIRMessagingPubSubRegistrar instance used to subscribe/unsubscribe.
+ */
+- (instancetype)initWithCheckinService:(FIRMessagingCheckinService *)checkinService;
+
+/**
+ *  Stops all the subscription requests going on in parallel. This would
+ *  invalidate all the handlers associated with the subscription requests.
+ */
+- (void)stopAllSubscriptionRequests;
+
+/**
+ *  Update subscription status for a given topic with FIRMessaging's backend.
+ *
+ *  @param topic        The topic to subscribe to.
+ *  @param token        The registration token to be used.
+ *  @param options      The options to be passed in during subscription request.
+ *  @param shouldDelete NO if the subscription is being added else YES if being
+ *                      removed.
+ *  @param handler      The handler invoked once the update subscription request
+ *                      finishes.
+ */
+- (void)updateSubscriptionToTopic:(NSString *)topic
+                        withToken:(NSString *)token
+                          options:(NSDictionary *)options
+                     shouldDelete:(BOOL)shouldDelete
+                          handler:(FIRMessagingTopicOperationCompletion)handler;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSubRegistrar.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingPubSubRegistrar.m
new file mode 100644 (file)
index 0000000..6268302
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingPubSubRegistrar.h"
+
+#import "FIRMessagingCheckinService.h"
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingPubSubRegistrar.h"
+#import "FIRMessagingTopicsCommon.h"
+#import "NSError+FIRMessaging.h"
+
+@interface FIRMessagingPubSubRegistrar ()
+
+@property(nonatomic, readwrite, strong) FIRMessagingCheckinService *checkinService;
+
+@property(nonatomic, readonly, strong) NSOperationQueue *topicOperations;
+// Common errors, instantiated, to avoid generating multiple copies
+@property(nonatomic, readwrite, strong) NSError *operationInProgressError;
+
+@end
+
+@implementation FIRMessagingPubSubRegistrar
+
+- (instancetype)init {
+  FIRMessagingInvalidateInitializer();
+}
+
+- (instancetype)initWithCheckinService:(FIRMessagingCheckinService *)checkinService {
+  self = [super init];
+  if (self) {
+    _checkinService = checkinService;
+    _topicOperations = [[NSOperationQueue alloc] init];
+    // Do 10 topic operations at a time; it's enough to keep the TCP connection to the host alive,
+    // saving hundreds of milliseconds on each request (compared to a serial queue).
+    _topicOperations.maxConcurrentOperationCount = 10;
+  }
+  return self;
+}
+
+- (void)stopAllSubscriptionRequests {
+  [self.topicOperations cancelAllOperations];
+}
+
+- (void)updateSubscriptionToTopic:(NSString *)topic
+                        withToken:(NSString *)token
+                          options:(NSDictionary *)options
+                     shouldDelete:(BOOL)shouldDelete
+                          handler:(FIRMessagingTopicOperationCompletion)handler {
+
+  FIRMessagingTopicAction action = FIRMessagingTopicActionSubscribe;
+  if (shouldDelete) {
+    action = FIRMessagingTopicActionUnsubscribe;
+  }
+  FIRMessagingTopicOperation *operation =
+      [[FIRMessagingTopicOperation alloc] initWithTopic:topic
+                                                 action:action
+                                                  token:token
+                                                options:options
+                                         checkinService:self.checkinService
+                                             completion:handler];
+  [self.topicOperations addOperation:operation];
+
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingReceiver.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingReceiver.h
new file mode 100644 (file)
index 0000000..6e4a693
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingDataMessageManager.h"
+#import "FIRMessaging.h"
+
+@class FIRMessagingReceiver;
+@protocol FIRMessagingReceiverDelegate <NSObject>
+
+- (void)receiver:(nonnull FIRMessagingReceiver *)receiver
+      receivedRemoteMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage;
+
+@end
+
+
+@interface FIRMessagingReceiver : NSObject <FIRMessagingDataMessageManagerDelegate>
+@property(nonatomic, weak, nullable) id<FIRMessagingReceiverDelegate> delegate;
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingReceiver.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingReceiver.m
new file mode 100644 (file)
index 0000000..7567eda
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingReceiver.h"
+
+#import <UIKit/UIKit.h>
+
+#import "FIRMessaging.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingUtilities.h"
+#import "FIRMessaging_Private.h"
+
+static NSString *const kUpstreamMessageIDUserInfoKey = @"messageID";
+static NSString *const kUpstreamErrorUserInfoKey = @"error";
+
+// Copied from Apple's header in case it is missing in some cases.
+#ifndef NSFoundationVersionNumber_iOS_9_x_Max
+#define NSFoundationVersionNumber_iOS_9_x_Max 1299
+#endif
+
+static int downstreamMessageID = 0;
+
+@implementation FIRMessagingReceiver
+
+#pragma mark - FIRMessagingDataMessageManager protocol
+
+- (void)didReceiveMessage:(NSDictionary *)message withIdentifier:(nullable NSString *)messageID {
+  if (![messageID length]) {
+    messageID = [[self class] nextMessageID];
+  }
+
+  if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) {
+    // Use delegate method for iOS 10
+    [self scheduleIos10NotificationForMessage:message withIdentifier:messageID];
+  } else {
+    // Post notification directly to AppDelegate handlers. This is valid pre-iOS 10.
+    [self scheduleNotificationForMessage:message];
+  }
+}
+
+- (void)willSendDataMessageWithID:(NSString *)messageID error:(NSError *)error {
+  NSNotification *notification;
+  if (error) {
+    NSDictionary *userInfo = @{
+      kUpstreamMessageIDUserInfoKey : [messageID copy],
+      kUpstreamErrorUserInfoKey : error
+    };
+    notification = [NSNotification notificationWithName:FIRMessagingSendErrorNotification
+                                                 object:nil
+                                               userInfo:userInfo];
+    [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostASAP];
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeReceiver000,
+                            @"Fail to send upstream message: %@ error: %@", messageID, error);
+  } else {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeReceiver001, @"Will send upstream message: %@",
+                            messageID);
+  }
+}
+
+- (void)didSendDataMessageWithID:(NSString *)messageID {
+  // invoke the callbacks asynchronously
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeReceiver002, @"Did send upstream message: %@",
+                          messageID);
+  NSNotification * notification =
+      [NSNotification notificationWithName:FIRMessagingSendSuccessNotification
+                                    object:nil
+                                  userInfo:@{ kUpstreamMessageIDUserInfoKey : [messageID copy] }];
+
+  [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostASAP];
+}
+
+- (void)didDeleteMessagesOnServer {
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeReceiver003,
+                          @"Will send deleted messages notification");
+  NSNotification * notification =
+      [NSNotification notificationWithName:FIRMessagingMessagesDeletedNotification
+                                    object:nil];
+
+  [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostASAP];
+}
+
+#pragma mark - Private Helpers
+// As the new UserNotifications framework in iOS 10 doesn't support constructor/mutation for
+// UNNotification object, FCM can't inject the message to the app with UserNotifications framework.
+// Define our own protocol, which means app developers need to implement two interfaces to receive
+// display notifications and data messages respectively for devices running iOS 10 or above. Devices
+// running iOS 9 or below are not affected.
+- (void)scheduleIos10NotificationForMessage:(NSDictionary *)message
+                             withIdentifier:(NSString *)messageID {
+  FIRMessagingRemoteMessage *wrappedMessage = [[FIRMessagingRemoteMessage alloc] init];
+  // TODO: wrap title, body, badge and other fields
+  wrappedMessage.appData = [message copy];
+  [self.delegate receiver:self receivedRemoteMessage:wrappedMessage];
+}
+
+- (void)scheduleNotificationForMessage:(NSDictionary *)message {
+  SEL newNotificationSelector =
+      @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:);
+  SEL oldNotificationSelector = @selector(application:didReceiveRemoteNotification:);
+
+  dispatch_async(dispatch_get_main_queue(), ^{
+    UIApplication *application = FIRMessagingUIApplication();
+    if (!application) {
+      return;
+    }
+    id<UIApplicationDelegate> appDelegate = [application delegate];
+    if ([appDelegate respondsToSelector:newNotificationSelector]) {
+      // Try the new remote notification callback
+      [appDelegate application:application
+          didReceiveRemoteNotification:message
+                fetchCompletionHandler:^(UIBackgroundFetchResult result) {
+                }];
+
+    } else if ([appDelegate respondsToSelector:oldNotificationSelector]) {
+      // Try the old remote notification callback
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+      [appDelegate application:application didReceiveRemoteNotification:message];
+#pragma clang diagnostic pop
+    } else {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeReceiver005,
+                              @"None of the remote notification callbacks implemented by "
+                              @"UIApplicationDelegate");
+    }
+  });
+}
+
++ (NSString *)nextMessageID {
+  @synchronized (self) {
+    ++downstreamMessageID;
+    return [NSString stringWithFormat:@"gcm-%d", downstreamMessageID];
+  }
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRegistrar.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRegistrar.h
new file mode 100644 (file)
index 0000000..065479c
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessaging.h"
+#import "FIRMessagingCheckinService.h"
+
+@class FIRMessagingCheckinStore;
+@class FIRMessagingPubSubRegistrar;
+
+/**
+ *  Handle the registration process for the client. Fetch checkin information from the Checkin
+ *  service if not cached on the device and then try to register the client with FIRMessaging backend.
+ */
+@interface FIRMessagingRegistrar : NSObject
+
+@property(nonatomic, readonly, strong) FIRMessagingPubSubRegistrar *pubsubRegistrar;
+@property(nonatomic, readonly, strong) NSString *deviceAuthID;
+@property(nonatomic, readonly, strong) NSString *secretToken;
+
+/**
+ *  Initialize a FIRMessaging Registrar.
+ *
+ *  @return A FIRMessaging Registrar object.
+ */
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+#pragma mark - Checkin
+
+/**
+ *  Try to load checkin info from the disk if not currently loaded into memory.
+ *
+ *  @return YES if successfully loaded valid checkin info to memory else NO.
+ */
+- (BOOL)tryToLoadValidCheckinInfo;
+
+#pragma mark - Subscribe/Unsubscribe
+
+/**
+ *  Update the subscription for a given topic for the client.
+ *
+ *  @param topic        The topic for which the subscription should be updated.
+ *  @param token        The registration token to be used by the client.
+ *  @param options      The extra options if any being passed as part of
+ *                      subscription request.
+ *  @param shouldDelete YES if we want to delete an existing subscription else NO
+ *                      if we want to create a new subscription.
+ *  @param handler      The handler to invoke once the subscription request is
+ *                      complete.
+ */
+- (void)updateSubscriptionToTopic:(NSString *)topic
+                        withToken:(NSString *)token
+                          options:(NSDictionary *)options
+                     shouldDelete:(BOOL)shouldDelete
+                          handler:(FIRMessagingTopicOperationCompletion)handler;
+
+/**
+ *  Cancel all subscription requests as well as any requests to checkin. Note if
+ *  there are subscription requests waiting on checkin to complete those requests
+ *  would be marked as stale and be NO-OP's if they happen in the future.
+ *
+ *  Also note this is a one time operation, you should only call this if you want
+ *  to immediately stop all requests and deallocate the registrar. After calling
+ *  this once you would no longer be able to use this registrar object.
+ */
+- (void)cancelAllRequests;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRegistrar.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRegistrar.m
new file mode 100644 (file)
index 0000000..47a22ec
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingRegistrar.h"
+
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingPubSubRegistrar.h"
+#import "FIRMessagingUtilities.h"
+#import "NSError+FIRMessaging.h"
+
+@interface FIRMessagingRegistrar ()
+
+@property(nonatomic, readwrite, assign) BOOL stopAllSubscriptions;
+
+@property(nonatomic, readwrite, strong) FIRMessagingCheckinService *checkinService;
+@property(nonatomic, readwrite, strong) FIRMessagingPubSubRegistrar *pubsubRegistrar;
+
+@end
+
+@implementation FIRMessagingRegistrar
+
+- (NSString *)deviceAuthID {
+  return self.checkinService.deviceAuthID;
+}
+
+- (NSString *)secretToken {
+  return self.checkinService.secretToken;
+}
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _checkinService = [[FIRMessagingCheckinService alloc] init];
+    // TODO(chliangGoogle): Merge pubsubRegistrar with Registrar as it is hard to track how many
+    // checkinService instances by separating classes too often.
+    _pubsubRegistrar = [[FIRMessagingPubSubRegistrar alloc] initWithCheckinService:_checkinService];
+  }
+  return self;
+}
+
+#pragma mark - Checkin
+
+- (BOOL)tryToLoadValidCheckinInfo {
+  [self.checkinService tryToLoadPrefetchedCheckinPreferences];
+  return [self.checkinService hasValidCheckinInfo];
+}
+
+#pragma mark - Subscribe/Unsubscribe
+
+- (void)updateSubscriptionToTopic:(NSString *)topic
+                        withToken:(NSString *)token
+                          options:(NSDictionary *)options
+                     shouldDelete:(BOOL)shouldDelete
+                          handler:(FIRMessagingTopicOperationCompletion)handler {
+  _FIRMessagingDevAssert(handler, @"Invalid nil handler");
+
+  if ([self tryToLoadValidCheckinInfo]) {
+    [self doUpdateSubscriptionForTopic:topic
+                                 token:token
+                               options:options
+                          shouldDelete:shouldDelete
+                            completion:handler];
+
+  } else {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeRegistrar000,
+                            @"Device check in error, no auth credentials found");
+    NSError *error = [NSError errorWithFCMErrorCode:kFIRMessagingErrorCodeMissingDeviceID];
+    handler(error);
+  }
+}
+
+- (void)cancelAllRequests {
+  self.stopAllSubscriptions = YES;
+  [self.pubsubRegistrar stopAllSubscriptionRequests];
+}
+
+#pragma mark - Private
+
+- (void)doUpdateSubscriptionForTopic:(NSString *)topic
+                               token:(NSString *)token
+                             options:(NSDictionary *)options
+                        shouldDelete:(BOOL)shouldDelete
+                          completion:(FIRMessagingTopicOperationCompletion)completion {
+  _FIRMessagingDevAssert([self.checkinService hasValidCheckinInfo],
+                @"No valid checkin info found before subscribe");
+
+  [self.pubsubRegistrar updateSubscriptionToTopic:topic
+                                        withToken:token
+                                          options:options
+                                     shouldDelete:shouldDelete
+                                          handler:completion];
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.h
new file mode 100644 (file)
index 0000000..59c3c15
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+/**
+ *  Swizzle remote-notification callbacks to invoke FIRMessaging methods
+ *  before calling original implementations.
+ */
+@interface FIRMessagingRemoteNotificationsProxy : NSObject
+
+/**
+ *  Checks the `FirebaseAppDelegateProxyEnabled` key in the App's Info.plist. If the key is
+ *  missing or incorrectly formatted, returns `YES`.
+ *
+ *  @return YES if the Application Delegate and User Notification Center methods can be swizzled.
+ *  Otherwise, returns NO.
+ */
++ (BOOL)canSwizzleMethods;
+
+/**
+ *  Swizzles Application Delegate's remote-notification callbacks and User Notification Center
+ *  delegate callback, and invokes the original selectors once done.
+ */
++ (void)swizzleMethods;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m
new file mode 100644 (file)
index 0000000..7cea178
--- /dev/null
@@ -0,0 +1,723 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingRemoteNotificationsProxy.h"
+
+#import <objc/runtime.h>
+#import <UIKit/UIKit.h>
+
+#import "FIRMessagingConstants.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingUtilities.h"
+#import "FIRMessaging_Private.h"
+
+static const BOOL kDefaultAutoRegisterEnabledValue = YES;
+static void * UserNotificationObserverContext = &UserNotificationObserverContext;
+
+static NSString *kUserNotificationWillPresentSelectorString =
+    @"userNotificationCenter:willPresentNotification:withCompletionHandler:";
+static NSString *kUserNotificationDidReceiveResponseSelectorString =
+    @"userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:";
+static NSString *kReceiveDataMessageSelectorString = @"messaging:didReceiveMessage:";
+
+@interface FIRMessagingRemoteNotificationsProxy ()
+
+@property(strong, nonatomic) NSMutableDictionary<NSString *, NSValue *> *originalAppDelegateImps;
+@property(strong, nonatomic) NSMutableDictionary<NSString *, NSArray *> *swizzledSelectorsByClass;
+
+@property(nonatomic) BOOL didSwizzleMethods;
+@property(nonatomic) BOOL didSwizzleAppDelegateMethods;
+
+@property(nonatomic) BOOL hasSwizzledUserNotificationDelegate;
+@property(nonatomic) BOOL isObservingUserNotificationDelegateChanges;
+
+@property(strong, nonatomic) id userNotificationCenter;
+@property(strong, nonatomic) id currentUserNotificationCenterDelegate;
+
+@end
+
+@implementation FIRMessagingRemoteNotificationsProxy
+
++ (BOOL)canSwizzleMethods {
+  id canSwizzleValue =
+      [[NSBundle mainBundle]
+          objectForInfoDictionaryKey: kFIRMessagingRemoteNotificationsProxyEnabledInfoPlistKey];
+  if (canSwizzleValue && [canSwizzleValue isKindOfClass:[NSNumber class]]) {
+    NSNumber *canSwizzleNumberValue = (NSNumber *)canSwizzleValue;
+    return canSwizzleNumberValue.boolValue;
+  } else {
+    return kDefaultAutoRegisterEnabledValue;
+  }
+}
+
++ (void)swizzleMethods {
+  [[FIRMessagingRemoteNotificationsProxy sharedProxy] swizzleMethodsIfPossible];
+}
+
++ (instancetype)sharedProxy {
+  static FIRMessagingRemoteNotificationsProxy *proxy;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    proxy = [[FIRMessagingRemoteNotificationsProxy alloc] init];
+  });
+  return proxy;
+}
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _originalAppDelegateImps = [[NSMutableDictionary alloc] init];
+    _swizzledSelectorsByClass = [[NSMutableDictionary alloc] init];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [self unswizzleAllMethods];
+  self.swizzledSelectorsByClass = nil;
+  [self.originalAppDelegateImps removeAllObjects];
+  self.originalAppDelegateImps = nil;
+  [self removeUserNotificationCenterDelegateObserver];
+}
+
+- (void)swizzleMethodsIfPossible {
+  // Already swizzled.
+  if (self.didSwizzleMethods) {
+    return;
+  }
+
+  UIApplication *application = FIRMessagingUIApplication();
+  if (!application) {
+    return;
+  }
+  NSObject<UIApplicationDelegate> *appDelegate = [application delegate];
+  [self swizzleAppDelegateMethods:appDelegate];
+
+  // Add KVO listener on [UNUserNotificationCenter currentNotificationCenter]'s delegate property
+  Class notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter");
+  if (notificationCenterClass) {
+    // We are linked against iOS 10 SDK or above
+    id notificationCenter = getNamedPropertyFromObject(notificationCenterClass,
+                                                       @"currentNotificationCenter",
+                                                       notificationCenterClass);
+    if (notificationCenter) {
+      [self listenForDelegateChangesInUserNotificationCenter:notificationCenter];
+    }
+  }
+
+  self.didSwizzleMethods = YES;
+}
+
+- (void)unswizzleAllMethods {
+  for (NSString *className in self.swizzledSelectorsByClass) {
+    Class klass = NSClassFromString(className);
+    NSArray *selectorStrings = self.swizzledSelectorsByClass[className];
+    for (NSString *selectorString in selectorStrings) {
+      SEL selector = NSSelectorFromString(selectorString);
+      [self unswizzleSelector:selector inClass:klass];
+    }
+  }
+  [self.swizzledSelectorsByClass removeAllObjects];
+}
+
+- (void)swizzleAppDelegateMethods:(id<UIApplicationDelegate>)appDelegate {
+  if (![appDelegate conformsToProtocol:@protocol(UIApplicationDelegate)]) {
+    return;
+  }
+  Class appDelegateClass = [appDelegate class];
+
+  BOOL didSwizzleAppDelegate = NO;
+  // Message receiving handler for iOS 9, 8, 7 devices (both display notification and data message).
+  SEL remoteNotificationSelector =
+      @selector(application:didReceiveRemoteNotification:);
+
+  SEL remoteNotificationWithFetchHandlerSelector =
+      @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:);
+
+  // For recording when APNS tokens are registered (or fail to register)
+  SEL registerForAPNSFailSelector =
+      @selector(application:didFailToRegisterForRemoteNotificationsWithError:);
+
+  SEL registerForAPNSSuccessSelector =
+      @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:);
+
+
+  // Receive Remote Notifications.
+  BOOL selectorWithFetchHandlerImplemented = NO;
+  if ([appDelegate respondsToSelector:remoteNotificationWithFetchHandlerSelector]) {
+    selectorWithFetchHandlerImplemented = YES;
+    [self swizzleSelector:remoteNotificationWithFetchHandlerSelector
+                  inClass:appDelegateClass
+       withImplementation:(IMP)FCM_swizzle_appDidReceiveRemoteNotificationWithHandler
+               inProtocol:@protocol(UIApplicationDelegate)];
+    didSwizzleAppDelegate = YES;
+  }
+
+  if ([appDelegate respondsToSelector:remoteNotificationSelector] ||
+      !selectorWithFetchHandlerImplemented) {
+    [self swizzleSelector:remoteNotificationSelector
+                  inClass:appDelegateClass
+       withImplementation:(IMP)FCM_swizzle_appDidReceiveRemoteNotification
+               inProtocol:@protocol(UIApplicationDelegate)];
+    didSwizzleAppDelegate = YES;
+  }
+
+  // For data message from MCS.
+  SEL receiveDataMessageSelector = NSSelectorFromString(kReceiveDataMessageSelectorString);
+  if ([appDelegate respondsToSelector:receiveDataMessageSelector]) {
+    [self swizzleSelector:receiveDataMessageSelector
+                   inClass:appDelegateClass
+        withImplementation:(IMP)FCM_swizzle_messagingDidReceiveMessage
+                inProtocol:@protocol(UIApplicationDelegate)];
+    didSwizzleAppDelegate = YES;
+  }
+
+  // Receive APNS token
+  [self swizzleSelector:registerForAPNSSuccessSelector
+                inClass:appDelegateClass
+     withImplementation:(IMP)FCM_swizzle_appDidRegisterForRemoteNotifications
+             inProtocol:@protocol(UIApplicationDelegate)];
+
+  [self swizzleSelector:registerForAPNSFailSelector
+                inClass:appDelegateClass
+     withImplementation:(IMP)FCM_swizzle_appDidFailToRegisterForRemoteNotifications
+             inProtocol:@protocol(UIApplicationDelegate)];
+
+  self.didSwizzleAppDelegateMethods = didSwizzleAppDelegate;
+}
+
+- (void)listenForDelegateChangesInUserNotificationCenter:(id)notificationCenter {
+  Class notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter");
+  if (![notificationCenter isKindOfClass:notificationCenterClass]) {
+    return;
+  }
+  id delegate = getNamedPropertyFromObject(notificationCenter, @"delegate", nil);
+  Protocol *delegateProtocol = NSProtocolFromString(@"UNUserNotificationCenterDelegate");
+  if ([delegate conformsToProtocol:delegateProtocol]) {
+    // Swizzle this object now, if available
+    [self swizzleUserNotificationCenterDelegate:delegate];
+  }
+  // Add KVO observer for "delegate" keyPath for future changes
+  [self addDelegateObserverToUserNotificationCenter:notificationCenter];
+}
+
+#pragma mark - UNNotificationCenter Swizzling
+
+- (void)swizzleUserNotificationCenterDelegate:(id _Nonnull)delegate {
+  if (self.currentUserNotificationCenterDelegate == delegate) {
+    // Via pointer-check, compare if we have already swizzled this item.
+    return;
+  }
+  Protocol *userNotificationCenterProtocol =
+      NSProtocolFromString(@"UNUserNotificationCenterDelegate");
+  if ([delegate conformsToProtocol:userNotificationCenterProtocol]) {
+    SEL willPresentNotificationSelector =
+        NSSelectorFromString(kUserNotificationWillPresentSelectorString);
+    // Swizzle the optional method
+    // "userNotificationCenter:willPresentNotification:withCompletionHandler:", if it is
+    // implemented. Do not swizzle otherwise, as an implementation *will* be created, which will
+    // fool iOS into thinking that this method is implemented, and therefore not send notifications
+    // to the fallback method in the app delegate
+    // "application:didReceiveRemoteNotification:fetchCompletionHandler:".
+    if ([delegate respondsToSelector:willPresentNotificationSelector]) {
+      [self swizzleSelector:willPresentNotificationSelector
+                    inClass:[delegate class]
+         withImplementation:(IMP)FCM_swizzle_willPresentNotificationWithHandler
+                 inProtocol:userNotificationCenterProtocol];
+    }
+    SEL didReceiveNotificationResponseSelector =
+        NSSelectorFromString(kUserNotificationDidReceiveResponseSelectorString);
+    if ([delegate respondsToSelector:didReceiveNotificationResponseSelector]) {
+      [self swizzleSelector:didReceiveNotificationResponseSelector
+                    inClass:[delegate class]
+         withImplementation:(IMP)FCM_swizzle_didReceiveNotificationResponseWithHandler
+                 inProtocol:userNotificationCenterProtocol];
+    }
+    self.currentUserNotificationCenterDelegate = delegate;
+    self.hasSwizzledUserNotificationDelegate = YES;
+  }
+}
+
+- (void)unswizzleUserNotificationCenterDelegate:(id _Nonnull)delegate {
+  if (self.currentUserNotificationCenterDelegate != delegate) {
+    // We aren't swizzling this delegate, so don't do anything.
+    return;
+  }
+  SEL willPresentNotificationSelector =
+      NSSelectorFromString(kUserNotificationWillPresentSelectorString);
+  // Call unswizzle methods, even if the method was not implemented (it will fail gracefully).
+  [self unswizzleSelector:willPresentNotificationSelector
+                  inClass:[self.currentUserNotificationCenterDelegate class]];
+  SEL didReceiveNotificationResponseSelector =
+      NSSelectorFromString(kUserNotificationDidReceiveResponseSelectorString);
+  [self unswizzleSelector:didReceiveNotificationResponseSelector
+                  inClass:[self.currentUserNotificationCenterDelegate class]];
+  self.currentUserNotificationCenterDelegate = nil;
+  self.hasSwizzledUserNotificationDelegate = NO;
+}
+
+#pragma mark - KVO for UNUserNotificationCenter
+
+- (void)addDelegateObserverToUserNotificationCenter:(id)userNotificationCenter {
+  [self removeUserNotificationCenterDelegateObserver];
+  @try {
+    [userNotificationCenter addObserver:self
+                             forKeyPath:NSStringFromSelector(@selector(delegate))
+                                options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
+                                context:UserNotificationObserverContext];
+    self.userNotificationCenter = userNotificationCenter;
+    self.isObservingUserNotificationDelegateChanges = YES;
+  } @catch (NSException *exception) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeRemoteNotificationsProxy000,
+                            @"Encountered exception trying to add a KVO observer for "
+                            @"UNUserNotificationCenter's 'delegate' property: %@",
+                            exception);
+  } @finally {
+
+  }
+}
+
+- (void)removeUserNotificationCenterDelegateObserver {
+  if (!self.userNotificationCenter) {
+    return;
+  }
+  @try {
+    [self.userNotificationCenter removeObserver:self
+                                 forKeyPath:NSStringFromSelector(@selector(delegate))
+                                    context:UserNotificationObserverContext];
+    self.userNotificationCenter = nil;
+    self.isObservingUserNotificationDelegateChanges = NO;
+  } @catch (NSException *exception) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeRemoteNotificationsProxy001,
+                            @"Encountered exception trying to remove a KVO observer for "
+                            @"UNUserNotificationCenter's 'delegate' property: %@",
+                            exception);
+  } @finally {
+
+  }
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath
+                      ofObject:(id)object
+                        change:(NSDictionary<NSKeyValueChangeKey, id> *)change
+                       context:(void *)context {
+  if (context == UserNotificationObserverContext) {
+    if ([keyPath isEqualToString:NSStringFromSelector(@selector(delegate))]) {
+      id oldDelegate = change[NSKeyValueChangeOldKey];
+      if (oldDelegate && oldDelegate != [NSNull null]) {
+        [self unswizzleUserNotificationCenterDelegate:oldDelegate];
+      }
+      id newDelegate = change[NSKeyValueChangeNewKey];
+      if (newDelegate && newDelegate != [NSNull null]) {
+        [self swizzleUserNotificationCenterDelegate:newDelegate];
+      }
+    }
+  } else {
+    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+  }
+}
+
+#pragma mark - NSProxy methods
+
+- (void)saveOriginalImplementation:(IMP)imp forSelector:(SEL)selector {
+  if (imp && selector) {
+    NSValue *IMPValue = [NSValue valueWithPointer:imp];
+    NSString *selectorString = NSStringFromSelector(selector);
+    self.originalAppDelegateImps[selectorString] = IMPValue;
+  }
+}
+
+- (IMP)originalImplementationForSelector:(SEL)selector {
+  NSString *selectorString = NSStringFromSelector(selector);
+  NSValue *implementation_value = self.originalAppDelegateImps[selectorString];
+  if (!implementation_value) {
+    return nil;
+  }
+
+  IMP imp;
+  [implementation_value getValue:&imp];
+  return imp;
+}
+
+- (void)trackSwizzledSelector:(SEL)selector ofClass:(Class)klass {
+  NSString *className = NSStringFromClass(klass);
+  NSString *selectorString = NSStringFromSelector(selector);
+  NSArray *selectors = self.swizzledSelectorsByClass[selectorString];
+  if (selectors) {
+    selectors = [selectors arrayByAddingObject:selectorString];
+  } else {
+    selectors = @[selectorString];
+  }
+  self.swizzledSelectorsByClass[className] = selectors;
+}
+
+- (void)removeImplementationForSelector:(SEL)selector {
+  NSString *selectorString = NSStringFromSelector(selector);
+  [self.originalAppDelegateImps removeObjectForKey:selectorString];
+}
+
+- (void)swizzleSelector:(SEL)originalSelector
+                inClass:(Class)klass
+     withImplementation:(IMP)swizzledImplementation
+             inProtocol:(Protocol *)protocol {
+  Method originalMethod = class_getInstanceMethod(klass, originalSelector);
+
+  if (originalMethod) {
+    // This class implements this method, so replace the original implementation
+    // with our new implementation and save the old implementation.
+
+    IMP __original_method_implementation =
+        method_setImplementation(originalMethod, swizzledImplementation);
+
+    IMP __nonexistant_method_implementation = [self nonExistantMethodImplementationForClass:klass];
+
+    if (__original_method_implementation &&
+        __original_method_implementation != __nonexistant_method_implementation &&
+        __original_method_implementation != swizzledImplementation) {
+      [self saveOriginalImplementation:__original_method_implementation
+                           forSelector:originalSelector];
+    }
+  } else {
+    // The class doesn't have this method, so add our swizzled implementation as the
+    // original implementation of the original method.
+    struct objc_method_description method_description =
+        protocol_getMethodDescription(protocol, originalSelector, NO, YES);
+
+    BOOL methodAdded = class_addMethod(klass,
+                                       originalSelector,
+                                       swizzledImplementation,
+                                       method_description.types);
+    if (!methodAdded) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeRemoteNotificationsProxyMethodNotAdded,
+                              @"Could not add method for %@ to class %@",
+                              NSStringFromSelector(originalSelector),
+                              NSStringFromClass(klass));
+    }
+  }
+  [self trackSwizzledSelector:originalSelector ofClass:klass];
+}
+
+- (void)unswizzleSelector:(SEL)selector inClass:(Class)klass {
+
+  Method swizzledMethod = class_getInstanceMethod(klass, selector);
+  if (!swizzledMethod) {
+    // This class doesn't seem to have this selector as an instance method? Bail out.
+    return;
+  }
+
+  IMP original_imp = [self originalImplementationForSelector:selector];
+  if (original_imp) {
+    // Restore the original implementation as the current implementation
+    method_setImplementation(swizzledMethod, original_imp);
+    [self removeImplementationForSelector:selector];
+  } else {
+    // This class originally did not have an implementation for this selector.
+
+    // We can't actually remove methods in Objective C 2.0, but we could set
+    // its method to something non-existent. This should give us the same
+    // behavior as if the method was not implemented.
+    // See: http://stackoverflow.com/a/8276527/9849
+
+    IMP nonExistantMethodImplementation = [self nonExistantMethodImplementationForClass:klass];
+    method_setImplementation(swizzledMethod, nonExistantMethodImplementation);
+  }
+}
+
+#pragma mark - Reflection Helpers
+
+// This is useful to generate from a stable, "known missing" selector, as the IMP can be compared
+// in case we are setting an implementation for a class that was previously "unswizzled" into a
+// non-existant implementation.
+- (IMP)nonExistantMethodImplementationForClass:(Class)klass {
+  SEL nonExistantSelector = NSSelectorFromString(@"aNonExistantMethod");
+  IMP nonExistantMethodImplementation = class_getMethodImplementation(klass, nonExistantSelector);
+  return nonExistantMethodImplementation;
+}
+
+// A safe, non-leaky way return a property object by its name
+id getNamedPropertyFromObject(id object, NSString *propertyName, Class klass) {
+  SEL selector = NSSelectorFromString(propertyName);
+  if (![object respondsToSelector:selector]) {
+    return nil;
+  }
+  if (!klass) {
+    klass = [NSObject class];
+  }
+  // Suppress clang warning about leaks in performSelector
+  // The alternative way to perform this is to invoke
+  // the method as a block (see http://stackoverflow.com/a/20058585),
+  // but this approach sometimes returns incomplete objects.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+  id property = [object performSelector:selector];
+#pragma clang diagnostic pop
+  if (![property isKindOfClass:klass]) {
+    return nil;
+  }
+  return property;
+}
+
+#pragma mark - Swizzled Methods
+
+void FCM_swizzle_appDidReceiveRemoteNotification(id self,
+                                                 SEL _cmd,
+                                                 UIApplication *app,
+                                                 NSDictionary *userInfo) {
+  [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
+
+  IMP original_imp =
+      [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd];
+  if (original_imp) {
+    ((void (*)(id, SEL, UIApplication *, NSDictionary *))original_imp)(self,
+                                                                       _cmd,
+                                                                       app,
+                                                                       userInfo);
+  }
+}
+
+void FCM_swizzle_appDidReceiveRemoteNotificationWithHandler(
+    id self, SEL _cmd, UIApplication *app, NSDictionary *userInfo,
+    void (^handler)(UIBackgroundFetchResult)) {
+
+  [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
+
+  IMP original_imp =
+      [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd];
+  if (original_imp) {
+    ((void (*)(id, SEL, UIApplication *, NSDictionary *,
+               void (^)(UIBackgroundFetchResult)))original_imp)(
+        self, _cmd, app, userInfo, handler);
+  }
+}
+
+/**
+ * Swizzle the notification handler for iOS 10+ devices.
+ * Signature of original handler is as below:
+ * - (void)userNotificationCenter:(UNUserNotificationCenter *)center
+ *        willPresentNotification:(UNNotification *)notification
+ *          withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
+ * In order to make FCM SDK compile and compatible with iOS SDKs before iOS 10, hide the
+ * parameter types from the swizzling implementation.
+ */
+void FCM_swizzle_willPresentNotificationWithHandler(
+    id self, SEL _cmd, id center, id notification, void (^handler)(NSUInteger)) {
+
+  FIRMessagingRemoteNotificationsProxy *proxy = [FIRMessagingRemoteNotificationsProxy sharedProxy];
+  IMP original_imp = [proxy originalImplementationForSelector:_cmd];
+
+  void (^callOriginalMethodIfAvailable)(void) = ^{
+    if (original_imp) {
+      ((void (*)(id, SEL, id, id, void (^)(NSUInteger)))original_imp)(
+          self, _cmd, center, notification, handler);
+    }
+    return;
+  };
+
+  Class notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter");
+  Class notificationClass = NSClassFromString(@"UNNotification");
+  if (!notificationCenterClass || !notificationClass) {
+    // Can't find UserNotifications framework. Do not swizzle, just execute the original method.
+    callOriginalMethodIfAvailable();
+  }
+
+  if (!center || ![center isKindOfClass:[notificationCenterClass class]]) {
+    // Invalid parameter type from the original method.
+    // Do not swizzle, just execute the original method.
+    callOriginalMethodIfAvailable();
+    return;
+  }
+
+  if (!notification || ![notification isKindOfClass:[notificationClass class]]) {
+    // Invalid parameter type from the original method.
+    // Do not swizzle, just execute the original method.
+    callOriginalMethodIfAvailable();
+    return;
+  }
+
+  if (!handler) {
+    // Invalid parameter type from the original method.
+    // Do not swizzle, just execute the original method.
+    callOriginalMethodIfAvailable();
+    return;
+  }
+
+  // Attempt to access the user info
+  id notificationUserInfo = userInfoFromNotification(notification);
+
+  if (!notificationUserInfo) {
+    // Could not access notification.request.content.userInfo.
+    callOriginalMethodIfAvailable();
+    return;
+  }
+
+  [[FIRMessaging messaging] appDidReceiveMessage:notificationUserInfo];
+  // Execute the original implementation.
+  callOriginalMethodIfAvailable();
+}
+
+/**
+ * Swizzle the notification handler for iOS 10+ devices.
+ * Signature of original handler is as below:
+ * - (void)userNotificationCenter:(UNUserNotificationCenter *)center
+ *     didReceiveNotificationResponse:(UNNotificationResponse *)response
+ *     withCompletionHandler:(void (^)(void))completionHandler
+ * In order to make FCM SDK compile and compatible with iOS SDKs before iOS 10, hide the
+ * parameter types from the swizzling implementation.
+ */
+void FCM_swizzle_didReceiveNotificationResponseWithHandler(
+    id self, SEL _cmd, id center, id response, void (^handler)(void)) {
+
+  FIRMessagingRemoteNotificationsProxy *proxy = [FIRMessagingRemoteNotificationsProxy sharedProxy];
+  IMP original_imp = [proxy originalImplementationForSelector:_cmd];
+
+  void (^callOriginalMethodIfAvailable)(void) = ^{
+    if (original_imp) {
+      ((void (*)(id, SEL, id, id, void (^)(void)))original_imp)(
+          self, _cmd, center, response, handler);
+    }
+    return;
+  };
+
+  Class notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter");
+  Class responseClass = NSClassFromString(@"UNNotificationResponse");
+  if (!center || ![center isKindOfClass:[notificationCenterClass class]]) {
+    // Invalid parameter type from the original method.
+    // Do not swizzle, just execute the original method.
+    callOriginalMethodIfAvailable();
+    return;
+  }
+
+  if (!response || ![response isKindOfClass:[responseClass class]]) {
+    // Invalid parameter type from the original method.
+    // Do not swizzle, just execute the original method.
+    callOriginalMethodIfAvailable();
+    return;
+  }
+
+  if (!handler) {
+    // Invalid parameter type from the original method.
+    // Do not swizzle, just execute the original method.
+    callOriginalMethodIfAvailable();
+    return;
+  }
+
+  // Try to access the response.notification property
+  SEL notificationSelector = NSSelectorFromString(@"notification");
+  if (![response respondsToSelector:notificationSelector]) {
+    // Cannot access the .notification property.
+    callOriginalMethodIfAvailable();
+    return;
+  }
+  id notificationClass = NSClassFromString(@"UNNotification");
+  id notification = getNamedPropertyFromObject(response, @"notification", notificationClass);
+
+  // With a notification object, use the common code to reach deep into notification
+  // (notification.request.content.userInfo)
+  id notificationUserInfo = userInfoFromNotification(notification);
+  if (!notificationUserInfo) {
+    // Could not access notification.request.content.userInfo.
+    callOriginalMethodIfAvailable();
+    return;
+  }
+
+  [[FIRMessaging messaging] appDidReceiveMessage:notificationUserInfo];
+  // Execute the original implementation.
+  callOriginalMethodIfAvailable();
+}
+
+id userInfoFromNotification(id notification) {
+
+  // Select the userInfo field from UNNotification.request.content.userInfo.
+  SEL requestSelector = NSSelectorFromString(@"request");
+  if (![notification respondsToSelector:requestSelector]) {
+    // Cannot access the request property.
+    return nil;
+  }
+  Class requestClass = NSClassFromString(@"UNNotificationRequest");
+  id notificationRequest = getNamedPropertyFromObject(notification, @"request", requestClass);
+
+  SEL notificationContentSelector = NSSelectorFromString(@"content");
+  if (!notificationRequest
+      || ![notificationRequest respondsToSelector:notificationContentSelector]) {
+    // Cannot access the content property.
+    return nil;
+  }
+  Class contentClass = NSClassFromString(@"UNNotificationContent");
+  id notificationContent = getNamedPropertyFromObject(notificationRequest,
+                                                      @"content",
+                                                      contentClass);
+
+  SEL notificationUserInfoSelector = NSSelectorFromString(@"userInfo");
+  if (!notificationContent
+      || ![notificationContent respondsToSelector:notificationUserInfoSelector]) {
+    // Cannot access the userInfo property.
+    return nil;
+  }
+  id notificationUserInfo = getNamedPropertyFromObject(notificationContent,
+                                                       @"userInfo",
+                                                       [NSDictionary class]);
+
+  if (!notificationUserInfo) {
+    // This is not the expected notification handler.
+    return nil;
+  }
+
+  return notificationUserInfo;
+}
+
+void FCM_swizzle_messagingDidReceiveMessage(id self, SEL _cmd, FIRMessaging *message,
+                                            FIRMessagingRemoteMessage *remoteMessage) {
+  [[FIRMessaging messaging] appDidReceiveMessage:remoteMessage.appData];
+
+  IMP original_imp =
+      [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd];
+  if (original_imp) {
+    ((void (*)(id, SEL, FIRMessaging *, FIRMessagingRemoteMessage *))original_imp)(
+        self, _cmd, message, remoteMessage);
+  }
+}
+
+void FCM_swizzle_appDidFailToRegisterForRemoteNotifications(id self,
+                                                            SEL _cmd,
+                                                            UIApplication *app,
+                                                            NSError *error) {
+  // Log the fact that we failed to register for remote notifications
+  FIRMessagingLoggerError(kFIRMessagingMessageCodeRemoteNotificationsProxyAPNSFailed,
+                          @"Error in "
+                          @"application:didFailToRegisterForRemoteNotificationsWithError: %@",
+                          error.localizedDescription);
+  IMP original_imp =
+      [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd];
+  if (original_imp) {
+    ((void (*)(id, SEL, UIApplication *, NSError *))original_imp)(self, _cmd, app, error);
+  }
+}
+
+void FCM_swizzle_appDidRegisterForRemoteNotifications(id self,
+                                                      SEL _cmd,
+                                                      UIApplication *app,
+                                                      NSData *deviceToken) {
+  // Pass the APNSToken along to FIRMessaging (and auto-detect the token type)
+  [FIRMessaging messaging].APNSToken = deviceToken;
+
+  IMP original_imp =
+      [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd];
+  if (original_imp) {
+    ((void (*)(id, SEL, UIApplication *, NSData *))original_imp)(self, _cmd, app, deviceToken);
+  }
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmq2PersistentStore.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmq2PersistentStore.h
new file mode 100644 (file)
index 0000000..09f1d44
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@class FIRMessagingPersistentSyncMessage;
+
+// table data handlers
+/**
+ *  Handle message stored in the outgoing RMQ messages table.
+ *
+ *  @param rmqId The rmqID of the message.
+ *  @param tag   The message tag.
+ *  @param data  The data stored in the message.
+ */
+typedef void(^FCMOutgoingRmqMessagesTableHandler)(int64_t rmqId, int8_t tag, NSData *data);
+
+/// Outgoing messages RMQ table
+extern NSString *const kTableOutgoingRmqMessages;
+/// Server to device RMQ table
+extern NSString *const kTableS2DRmqIds;
+
+@interface FIRMessagingRmq2PersistentStore : NSObject
+
+/**
+ *  Initialize and open the RMQ database on the client.
+ *
+ *  @param databaseName The name for RMQ database.
+ *
+ *  @return A store used to persist messages on the client.
+ */
+- (instancetype)initWithDatabaseName:(NSString *)databaseName;
+
+/**
+ *  Save outgoing message in RMQ.
+ *
+ *  @param rmqId The rmqID for the message.
+ *  @param tag   The tag of the message proto.
+ *  @param data  The data being sent in the message.
+ *  @param error The error if any while saving the message to the persistent store.
+ *
+ *  @return YES if the message was successfully saved to the persistent store else NO.
+ */
+- (BOOL)saveMessageWithRmqId:(int64_t)rmqId
+                         tag:(int8_t)tag
+                        data:(NSData *)data
+                       error:(NSError **)error;
+
+/**
+ *  Add unacked server to device message with a given rmqID to the persistent store.
+ *
+ *  @param rmqId The rmqID of the message that was not acked by the cient.
+ *
+ *  @return YES if the save was successful else NO.
+ */
+- (BOOL)saveUnackedS2dMessageWithRmqId:(NSString *)rmqId;
+
+/**
+ *  Update the last RMQ ID that was sent by the client.
+ *
+ *  @param rmqID The latest rmqID sent by the device.
+ *
+ *  @return YES if the last rmqID was successfully saved else NO.
+ */
+- (BOOL)updateLastOutgoingRmqId:(int64_t)rmqID;
+
+#pragma mark - Query
+
+/**
+ *  Query the highest rmqID saved in the Outgoing messages table.
+ *
+ *  @return The highest rmqID amongst all the messages in the Outgoing RMQ table. If no message
+ *          was ever persisted return 0.
+ */
+- (int64_t)queryHighestRmqId;
+
+/**
+ *  The last rmqID that was saved on the client.
+ *
+ *  @return The last rmqID that was saved. If no rmqID was ever persisted return 0.
+ */
+- (int64_t)queryLastRmqId;
+
+/**
+ *  Get a list of all unacked server to device messages stored on the client.
+ *
+ *  @return List of all unacked s2d messages in the persistent store.
+ */
+- (NSArray *)unackedS2dRmqIds;
+
+/**
+ *  Iterate over all outgoing messages in the RMQ table.
+ *
+ *  @param handler The handler invoked with each message in the outgoing RMQ table.
+ */
+- (void)scanOutgoingRmqMessagesWithHandler:(FCMOutgoingRmqMessagesTableHandler)handler;
+
+#pragma mark - Delete
+
+/**
+ *  Delete messages with given rmqID's from a table.
+ *
+ *  @param tableName The table name from which to delete the rmq messages.
+ *  @param rmqIds    The rmqID's of the messages to be deleted.
+ *
+ *  @return The number of messages that were successfully deleted.
+ */
+- (int)deleteMessagesFromTable:(NSString *)tableName
+                    withRmqIds:(NSArray *)rmqIds;
+
+/**
+ *  Remove database from the device.
+ *
+ *  @param dbName The database name to be deleted.
+ */
++ (void)removeDatabase:(NSString *)dbName;
+
+#pragma mark - Sync Messages
+
+/**
+ *  Save sync message to persistent store to check for duplicates.
+ *
+ *  @param rmqID          The rmqID of the message to save.
+ *  @param expirationTime The expiration time of the message to save.
+ *  @param apnsReceived   YES if the message was received via APNS else NO.
+ *  @param mcsReceived    YES if the message was received via MCS else NO.
+ *  @param error          The error if any while saving the message to store.
+ *
+ *  @return YES if the message was saved successfully else NO.
+ */
+- (BOOL)saveSyncMessageWithRmqID:(NSString *)rmqID
+                  expirationTime:(int64_t)expirationTime
+                    apnsReceived:(BOOL)apnsReceived
+                     mcsReceived:(BOOL)mcsReceived
+                           error:(NSError **)error;
+
+/**
+ *  Update sync message received via APNS.
+ *
+ *  @param rmqID The rmqID of the sync message.
+ *  @param error The error if any while updating the sync message in persistence.
+ *
+ *  @return YES if the update was successful else NO.
+ */
+- (BOOL)updateSyncMessageViaAPNSWithRmqID:(NSString *)rmqID
+                                    error:(NSError **)error;
+
+/**
+ *  Update sync message received via MCS.
+ *
+ *  @param rmqID The rmqID of the sync message.
+ *  @param error The error if any while updating the sync message in persistence.
+ *
+ *  @return YES if the update was successful else NO.
+ */
+- (BOOL)updateSyncMessageViaMCSWithRmqID:(NSString *)rmqID
+                                   error:(NSError **)error;
+
+/**
+ *  Query sync message table for a given rmqID.
+ *
+ *  @param rmqID The rmqID to search for in SYNC_RMQ.
+ *
+ *  @return The sync message that was persisted with `rmqID`. If no such message was persisted
+ *          return nil.
+ */
+- (FIRMessagingPersistentSyncMessage *)querySyncMessageWithRmqID:(NSString *)rmqID;
+
+/**
+ *  Delete sync message with rmqID.
+ *
+ *  @param rmqID The rmqID of the message to delete.
+ *
+ *  @return YES if a sync message with rmqID was found and deleted successfully else NO.
+ */
+- (BOOL)deleteSyncMessageWithRmqID:(NSString *)rmqID;
+
+/**
+ *  Delete the expired sync messages from persisten store. Also deletes messages that have been
+ *  delivered both via APNS and MCS.
+ *
+ *  @param error The error if any while deleting the messages.
+ *
+ *  @return The total number of messages that were deleted from the persistent store.
+ */
+- (int)deleteExpiredOrFinishedSyncMessages:(NSError **)error;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m
new file mode 100644 (file)
index 0000000..dc6e6c9
--- /dev/null
@@ -0,0 +1,807 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingRmq2PersistentStore.h"
+
+#import <sqlite3.h>
+
+#import "FIRMessagingConstants.h"
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingPersistentSyncMessage.h"
+#import "FIRMessagingUtilities.h"
+#import "NSError+FIRMessaging.h"
+#import "Protos/GtalkCore.pbobjc.h"
+
+#ifndef _FIRMessagingRmqLogAndExit
+#define _FIRMessagingRmqLogAndExit(stmt, return_value)   \
+do {                              \
+[self logErrorAndFinalizeStatement:stmt];  \
+return return_value; \
+} while(0)
+#endif
+
+typedef enum : NSUInteger {
+  FIRMessagingRmqDirectoryUnknown,
+  FIRMessagingRmqDirectoryDocuments,
+  FIRMessagingRmqDirectoryApplicationSupport,
+} FIRMessagingRmqDirectory;
+
+static NSString *const kFCMRmqStoreTag = @"FIRMessagingRmqStore:";
+
+// table names
+NSString *const kTableOutgoingRmqMessages = @"outgoingRmqMessages";
+NSString *const kTableLastRmqId = @"lastrmqid";
+NSString *const kOldTableS2DRmqIds = @"s2dRmqIds";
+NSString *const kTableS2DRmqIds = @"s2dRmqIds_1";
+
+// Used to prevent de-duping of sync messages received both via APNS and MCS.
+NSString *const kTableSyncMessages = @"incomingSyncMessages";
+
+static NSString *const kTablePrefix = @"";
+
+// create tables
+static NSString *const kCreateTableOutgoingRmqMessages =
+    @"create TABLE IF NOT EXISTS %@%@ "
+    @"(_id INTEGER PRIMARY KEY, "
+    @"rmq_id INTEGER, "
+    @"type INTEGER, "
+    @"ts INTEGER, "
+    @"data BLOB)";
+
+static NSString *const kCreateTableLastRmqId =
+    @"create TABLE IF NOT EXISTS %@%@ "
+    @"(_id INTEGER PRIMARY KEY, "
+    @"rmq_id INTEGER)";
+
+static NSString *const kCreateTableS2DRmqIds =
+    @"create TABLE IF NOT EXISTS %@%@ "
+    @"(_id INTEGER PRIMARY KEY, "
+    @"rmq_id TEXT)";
+
+static NSString *const kCreateTableSyncMessages =
+    @"create TABLE IF NOT EXISTS %@%@ "
+    @"(_id INTEGER PRIMARY KEY, "
+    @"rmq_id TEXT, "
+    @"expiration_ts INTEGER, "
+    @"apns_recv INTEGER, "
+    @"mcs_recv INTEGER)";
+
+static NSString *const kDropTableCommand =
+    @"drop TABLE if exists %@%@";
+
+// table infos
+static NSString *const kRmqIdColumn = @"rmq_id";
+static NSString *const kDataColumn = @"data";
+static NSString *const kProtobufTagColumn = @"type";
+static NSString *const kIdColumn = @"_id";
+
+static NSString *const kOutgoingRmqMessagesColumns = @"rmq_id, type, data";
+
+// Sync message columns
+static NSString *const kSyncMessagesColumns = @"rmq_id, expiration_ts, apns_recv, mcs_recv";
+// Message time expiration in seconds since 1970
+static NSString *const kSyncMessageExpirationTimestampColumn = @"expiration_ts";
+static NSString *const kSyncMessageAPNSReceivedColumn = @"apns_recv";
+static NSString *const kSyncMessageMCSReceivedColumn = @"mcs_recv";
+
+// table data handlers
+typedef void(^FCMOutgoingRmqMessagesTableHandler)(int64_t rmqId, int8_t tag, NSData *data);
+
+// Utility to create an NSString from a sqlite3 result code
+NSString * _Nonnull FIRMessagingStringFromSQLiteResult(int result) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+  const char *errorStr = sqlite3_errstr(result);
+#pragma pop
+  NSString *errorString = [NSString stringWithFormat:@"%d - %s", result, errorStr];
+  return errorString;
+}
+
+@interface FIRMessagingRmq2PersistentStore () {
+  sqlite3 *_database;
+}
+
+@property(nonatomic, readwrite, strong) NSString *databaseName;
+@property(nonatomic, readwrite, assign) FIRMessagingRmqDirectory currentDirectory;
+
+@end
+
+@implementation FIRMessagingRmq2PersistentStore
+
+- (instancetype)initWithDatabaseName:(NSString *)databaseName {
+  self = [super init];
+  if (self) {
+    _databaseName = [databaseName copy];
+    BOOL didMoveToApplicationSupport =
+        [self moveToApplicationSupportSubDirectory:kFIRMessagingApplicationSupportSubDirectory];
+
+    _currentDirectory = didMoveToApplicationSupport
+                            ? FIRMessagingRmqDirectoryApplicationSupport
+                            : FIRMessagingRmqDirectoryDocuments;
+
+    [self openDatabase:_databaseName];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  sqlite3_close(_database);
+}
+
+- (BOOL)moveToApplicationSupportSubDirectory:(NSString *)subDirectoryName {
+  NSArray *directoryPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,
+                                                                NSUserDomainMask, YES);
+  NSString *applicationSupportDirPath = directoryPaths.lastObject;
+  NSArray *components = @[applicationSupportDirPath, subDirectoryName];
+  NSString *subDirectoryPath = [NSString pathWithComponents:components];
+  BOOL hasSubDirectory;
+
+  if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
+                                            isDirectory:&hasSubDirectory]) {
+    // Cannot move to non-existent directory
+    return NO;
+  }
+
+  if ([self doesFileExistInDirectory:FIRMessagingRmqDirectoryDocuments]) {
+    NSString *oldPlistPath = [[self class] pathForDatabase:self.databaseName
+                                               inDirectory:FIRMessagingRmqDirectoryDocuments];
+    NSString *newPlistPath = [[self class]
+        pathForDatabase:self.databaseName
+            inDirectory:FIRMessagingRmqDirectoryApplicationSupport];
+
+    if ([self doesFileExistInDirectory:FIRMessagingRmqDirectoryApplicationSupport]) {
+      // File exists in both Documents and ApplicationSupport, delete the one in Documents
+      NSError *deleteError;
+      if (![[NSFileManager defaultManager] removeItemAtPath:oldPlistPath error:&deleteError]) {
+        FIRMessagingLoggerError(kFIRMessagingMessageCodeRmq2PersistentStore000,
+                                @"Failed to delete old copy of %@.sqlite in Documents %@",
+                                self.databaseName, deleteError);
+      }
+      return NO;
+    }
+    NSError *moveError;
+    if (![[NSFileManager defaultManager] moveItemAtPath:oldPlistPath
+                                                 toPath:newPlistPath
+                                                  error:&moveError]) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeRmq2PersistentStore001,
+                              @"Failed to move file %@ from %@ to %@. Error: %@", self.databaseName,
+                              oldPlistPath, newPlistPath, moveError);
+      return NO;
+    }
+  }
+  // We moved the file if it existed, otherwise we didn't need to do anything
+  return YES;
+}
+
+- (BOOL)doesFileExistInDirectory:(FIRMessagingRmqDirectory)directory {
+  NSString *path = [[self class] pathForDatabase:self.databaseName inDirectory:directory];
+  return [[NSFileManager defaultManager] fileExistsAtPath:path];
+}
+
++ (NSString *)pathForDatabase:(NSString *)dbName inDirectory:(FIRMessagingRmqDirectory)directory {
+  NSArray *paths;
+  NSArray *components;
+  NSString *dbNameWithExtension = [NSString stringWithFormat:@"%@.sqlite", dbName];
+  NSString *errorMessage;
+
+  switch (directory) {
+    case FIRMessagingRmqDirectoryDocuments:
+      paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+      components = @[paths.lastObject, dbNameWithExtension];
+      break;
+
+    case FIRMessagingRmqDirectoryApplicationSupport:
+      paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,
+                                                  NSUserDomainMask,
+                                                  YES);
+      components = @[
+                     paths.lastObject,
+                     kFIRMessagingApplicationSupportSubDirectory,
+                     dbNameWithExtension
+                     ];
+      break;
+
+    default:
+      errorMessage = [NSString stringWithFormat:@"Invalid directory type %lu",
+                      (unsigned long)directory];
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeRmq2PersistentStoreInvalidRmqDirectory,
+                              @"%@",
+                              errorMessage);
+      NSAssert(NO, errorMessage);
+      break;
+  }
+
+  return [NSString pathWithComponents:components];
+}
+
+- (void)createTableWithName:(NSString *)tableName command:(NSString *)command {
+  char *error;
+  NSString *createDatabase = [NSString stringWithFormat:command, kTablePrefix, tableName];
+  if (sqlite3_exec(_database, [createDatabase UTF8String], NULL, NULL, &error) != SQLITE_OK) {
+    // remove db before failing
+    [self removeDatabase];
+    NSString *errorMessage = [NSString stringWithFormat:@"Couldn't create table: %@ %@",
+                              kCreateTableOutgoingRmqMessages,
+                              [NSString stringWithCString:error encoding:NSUTF8StringEncoding]];
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeRmq2PersistentStoreErrorCreatingTable,
+                            @"%@",
+                            errorMessage);
+    NSAssert(NO, errorMessage);
+  }
+}
+
+- (void)dropTableWithName:(NSString *)tableName {
+  char *error;
+  NSString *dropTableSQL = [NSString stringWithFormat:kDropTableCommand, kTablePrefix, tableName];
+  if (sqlite3_exec(_database, [dropTableSQL UTF8String], NULL, NULL, &error) != SQLITE_OK) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeRmq2PersistentStore002,
+                            @"Failed to remove table %@", tableName);
+  }
+}
+
+- (void)removeDatabase {
+  NSString *path = [[self class] pathForDatabase:self.databaseName
+                                     inDirectory:self.currentDirectory];
+  [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
+}
+
++ (void)removeDatabase:(NSString *)dbName {
+  NSString *documentsDirPath = [self pathForDatabase:dbName
+                                         inDirectory:FIRMessagingRmqDirectoryDocuments];
+  NSString *applicationSupportDirPath =
+      [self pathForDatabase:dbName inDirectory:FIRMessagingRmqDirectoryApplicationSupport];
+  [[NSFileManager defaultManager] removeItemAtPath:documentsDirPath error:nil];
+  [[NSFileManager defaultManager] removeItemAtPath:applicationSupportDirPath error:nil];
+}
+
+- (void)openDatabase:(NSString *)dbName {
+  NSFileManager *fileManager = [NSFileManager defaultManager];
+  NSString *path = [[self class] pathForDatabase:dbName inDirectory:self.currentDirectory];
+
+  BOOL didOpenDatabase = YES;
+  if (![fileManager fileExistsAtPath:path]) {
+    // We've to separate between different versions here because of backwards compatbility issues.
+    int result = sqlite3_open([path UTF8String], &_database);
+    if (result != SQLITE_OK) {
+      NSString *errorString = FIRMessagingStringFromSQLiteResult(result);
+      NSString *errorMessage =
+          [NSString stringWithFormat:@"Could not open existing RMQ database at path %@, error: %@",
+                                     path,
+                                     errorString];
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeRmq2PersistentStoreErrorOpeningDatabase,
+                              @"%@",
+                              errorMessage);
+      NSAssert(NO, errorMessage);
+      return;
+    }
+    [self createTableWithName:kTableOutgoingRmqMessages
+                      command:kCreateTableOutgoingRmqMessages];
+
+    [self createTableWithName:kTableLastRmqId command:kCreateTableLastRmqId];
+    [self createTableWithName:kTableS2DRmqIds command:kCreateTableS2DRmqIds];
+  } else {
+    // Calling sqlite3_open should create the database, since the file doesn't exist.
+    int result = sqlite3_open([path UTF8String], &_database);
+    if (result != SQLITE_OK) {
+      NSString *errorString = FIRMessagingStringFromSQLiteResult(result);
+      NSString *errorMessage =
+          [NSString stringWithFormat:@"Could not create RMQ database at path %@, error: %@",
+                                     path,
+                                     errorString];
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeRmq2PersistentStoreErrorCreatingDatabase,
+                              @"%@",
+                              errorMessage);
+      NSAssert(NO, errorMessage);
+      didOpenDatabase = NO;
+    } else {
+      [self updateDbWithStringRmqID];
+    }
+  }
+
+  if (didOpenDatabase) {
+    [self createTableWithName:kTableSyncMessages command:kCreateTableSyncMessages];
+  }
+}
+
+- (void)updateDbWithStringRmqID {
+  [self createTableWithName:kTableS2DRmqIds command:kCreateTableS2DRmqIds];
+  [self dropTableWithName:kOldTableS2DRmqIds];
+}
+
+#pragma mark - Insert
+
+- (BOOL)saveUnackedS2dMessageWithRmqId:(NSString *)rmqId {
+  NSString *insertFormat = @"INSERT INTO %@ (%@) VALUES (?)";
+  NSString *insertSQL = [NSString stringWithFormat:insertFormat,
+                         kTableS2DRmqIds,
+                         kRmqIdColumn];
+  sqlite3_stmt *insert_statement;
+  if (sqlite3_prepare_v2(_database, [insertSQL UTF8String], -1, &insert_statement, NULL)
+      != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(insert_statement, NO);
+  }
+  if (sqlite3_bind_text(insert_statement,
+                        1,
+                        [rmqId UTF8String],
+                        (int)[rmqId length],
+                        SQLITE_STATIC) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(insert_statement, NO);
+  }
+  if (sqlite3_step(insert_statement) != SQLITE_DONE) {
+    _FIRMessagingRmqLogAndExit(insert_statement, NO);
+  }
+  sqlite3_finalize(insert_statement);
+  return YES;
+}
+
+- (BOOL)saveMessageWithRmqId:(int64_t)rmqId
+                         tag:(int8_t)tag
+                        data:(NSData *)data
+                       error:(NSError **)error {
+  NSString *insertFormat = @"INSERT INTO %@ (%@, %@, %@) VALUES (?, ?, ?)";
+  NSString *insertSQL = [NSString stringWithFormat:insertFormat,
+                         kTableOutgoingRmqMessages, // table
+                         kRmqIdColumn, kProtobufTagColumn, kDataColumn /* columns */];
+  sqlite3_stmt *insert_statement;
+  if (sqlite3_prepare_v2(_database, [insertSQL UTF8String], -1, &insert_statement, NULL)
+      != SQLITE_OK) {
+    if (error) {
+      *error = [NSError errorWithDomain:[NSString stringWithFormat:@"%s", sqlite3_errmsg(_database)]
+                                   code:sqlite3_errcode(_database)
+                               userInfo:nil];
+    }
+    _FIRMessagingRmqLogAndExit(insert_statement, NO);
+  }
+  if (sqlite3_bind_int64(insert_statement, 1, rmqId) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(insert_statement, NO);
+  }
+  if (sqlite3_bind_int(insert_statement, 2, tag) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(insert_statement, NO);
+  }
+  if (sqlite3_bind_blob(insert_statement, 3, [data bytes], (int)[data length], NULL) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(insert_statement, NO);
+  }
+  if (sqlite3_step(insert_statement) != SQLITE_DONE) {
+    _FIRMessagingRmqLogAndExit(insert_statement, NO);
+  }
+
+  sqlite3_finalize(insert_statement);
+  return YES;
+}
+
+- (int)deleteMessagesFromTable:(NSString *)tableName
+                    withRmqIds:(NSArray *)rmqIds {
+  _FIRMessagingDevAssert([tableName isEqualToString:kTableOutgoingRmqMessages] ||
+                [tableName isEqualToString:kTableLastRmqId] ||
+                [tableName isEqualToString:kTableS2DRmqIds] ||
+                [tableName isEqualToString:kTableSyncMessages],
+                @"%@: Invalid Table Name %@", kFCMRmqStoreTag, tableName);
+
+  BOOL isRmqIDString = NO;
+  // RmqID is a string only for outgoing messages
+  if ([tableName isEqualToString:kTableS2DRmqIds] ||
+      [tableName isEqualToString:kTableSyncMessages]) {
+    isRmqIDString = YES;
+  }
+
+  NSMutableString *delete = [NSMutableString stringWithFormat:@"DELETE FROM %@ WHERE ", tableName];
+
+  NSString *toDeleteArgument = [NSString stringWithFormat:@"%@ = ? OR ", kRmqIdColumn];
+
+  int toDelete = (int)[rmqIds count];
+  if (toDelete == 0) {
+    return 0;
+  }
+  int maxBatchSize = 100;
+  int start = 0;
+  int deleteCount = 0;
+  while (start < toDelete) {
+
+    // construct the WHERE argument
+    int end = MIN(start + maxBatchSize, toDelete);
+    NSMutableString *whereArgument = [NSMutableString string];
+    for (int i = start; i < end; i++) {
+      [whereArgument appendString:toDeleteArgument];
+    }
+    // remove the last * OR * from argument
+    NSRange range = NSMakeRange([whereArgument length] -4, 4);
+    [whereArgument deleteCharactersInRange:range];
+    NSString *deleteQuery = [NSString stringWithFormat:@"%@ %@", delete, whereArgument];
+
+
+    // sqlite update
+    sqlite3_stmt *delete_statement;
+    if (sqlite3_prepare_v2(_database, [deleteQuery UTF8String],
+                           -1, &delete_statement, NULL) != SQLITE_OK) {
+      _FIRMessagingRmqLogAndExit(delete_statement, 0);
+    }
+
+    // bind values
+    int rmqIndex = 0;
+    int placeholderIndex = 1; // placeholders in sqlite3 start with 1
+    for (NSString *rmqId in rmqIds) { // objectAtIndex: is O(n) -- would make it slow
+      if (rmqIndex < start) {
+        rmqIndex++;
+        continue;
+      } else if (rmqIndex >= end) {
+        break;
+      } else {
+        if (isRmqIDString) {
+          if (sqlite3_bind_text(delete_statement,
+                                placeholderIndex,
+                                [rmqId UTF8String],
+                                (int)[rmqId length],
+                                SQLITE_STATIC) != SQLITE_OK) {
+            FIRMessagingLoggerDebug(kFIRMessagingMessageCodeRmq2PersistentStore003,
+                                    @"Failed to bind rmqID %@", rmqId);
+            continue;
+          }
+        } else {
+          int64_t rmqIdValue = [rmqId longLongValue];
+          sqlite3_bind_int64(delete_statement, placeholderIndex, rmqIdValue);
+        }
+        placeholderIndex++;
+      }
+      rmqIndex++;
+    }
+    if (sqlite3_step(delete_statement) != SQLITE_DONE) {
+      _FIRMessagingRmqLogAndExit(delete_statement, deleteCount);
+    }
+    sqlite3_finalize(delete_statement);
+    deleteCount += sqlite3_changes(_database);
+    start = end;
+  }
+
+  // if we are here all of our sqlite queries should have succeeded
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeRmq2PersistentStore004,
+                          @"%@ Trying to delete %d s2D ID's, successfully deleted %d",
+                          kFCMRmqStoreTag, toDelete, deleteCount);
+  return deleteCount;
+}
+
+#pragma mark - Query
+
+- (int64_t)queryHighestRmqId {
+  NSString *queryFormat = @"SELECT %@ FROM %@ ORDER BY %@ DESC LIMIT %d";
+  NSString *query = [NSString stringWithFormat:queryFormat,
+                     kRmqIdColumn, // column
+                     kTableOutgoingRmqMessages, // table
+                     kRmqIdColumn, // order by column
+                     1]; // limit
+
+  sqlite3_stmt *statement;
+  int64_t highestRmqId = 0;
+  if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, NULL) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(statement, highestRmqId);
+  }
+  if (sqlite3_step(statement) == SQLITE_ROW) {
+    highestRmqId = sqlite3_column_int64(statement, 0);
+  }
+  sqlite3_finalize(statement);
+  return highestRmqId;
+}
+
+- (int64_t)queryLastRmqId {
+  NSString *queryFormat = @"SELECT %@ FROM %@ ORDER BY %@ DESC LIMIT %d";
+  NSString *query = [NSString stringWithFormat:queryFormat,
+                     kRmqIdColumn, // column
+                     kTableLastRmqId, // table
+                     kRmqIdColumn, // order by column
+                     1]; // limit
+
+  sqlite3_stmt *statement;
+  int64_t lastRmqId = 0;
+  if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, NULL) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(statement, lastRmqId);
+  }
+  if (sqlite3_step(statement) == SQLITE_ROW) {
+    lastRmqId = sqlite3_column_int64(statement, 0);
+  }
+  sqlite3_finalize(statement);
+  return lastRmqId;
+}
+
+- (BOOL)updateLastOutgoingRmqId:(int64_t)rmqID {
+  NSString *queryFormat = @"INSERT OR REPLACE INTO %@ (%@, %@) VALUES (?, ?)";
+  NSString *query = [NSString stringWithFormat:queryFormat,
+                     kTableLastRmqId, // table
+                     kIdColumn, kRmqIdColumn]; // columns
+  sqlite3_stmt *statement;
+  if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, NULL) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(statement, NO);
+  }
+  if (sqlite3_bind_int(statement, 1, 1) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(statement, NO);
+  }
+  if (sqlite3_bind_int64(statement, 2, rmqID) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(statement, NO);
+  }
+  if (sqlite3_step(statement) != SQLITE_DONE) {
+    _FIRMessagingRmqLogAndExit(statement, NO);
+  }
+  sqlite3_finalize(statement);
+  return YES;
+}
+
+- (NSArray *)unackedS2dRmqIds {
+  NSString *queryFormat = @"SELECT %@ FROM %@ ORDER BY %@ ASC";
+  NSString *query = [NSString stringWithFormat:queryFormat,
+                     kRmqIdColumn,
+                     kTableS2DRmqIds,
+                     kRmqIdColumn];
+  sqlite3_stmt *statement;
+  if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, NULL) != SQLITE_OK) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeRmq2PersistentStore005,
+                            @"%@: Could not find s2d ids", kFCMRmqStoreTag);
+    _FIRMessagingRmqLogAndExit(statement, @[]);
+  }
+  NSMutableArray *rmqIDArray = [NSMutableArray array];
+  while (sqlite3_step(statement) == SQLITE_ROW) {
+    const char *rmqID = (char *)sqlite3_column_text(statement, 0);
+    [rmqIDArray addObject:[NSString stringWithUTF8String:rmqID]];
+  }
+  sqlite3_finalize(statement);
+  return rmqIDArray;
+}
+
+#pragma mark - Scan
+
+- (void)scanOutgoingRmqMessagesWithHandler:(FCMOutgoingRmqMessagesTableHandler)handler {
+  static NSString *queryFormat = @"SELECT %@ FROM %@ WHERE %@ != 0 ORDER BY %@ ASC";
+  NSString *query = [NSString stringWithFormat:queryFormat,
+                     kOutgoingRmqMessagesColumns, // select (rmq_id, type, data)
+                     kTableOutgoingRmqMessages, // from table
+                     kRmqIdColumn, // where
+                     kRmqIdColumn]; // order by
+  sqlite3_stmt *statement;
+  if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, NULL) != SQLITE_OK) {
+    [self logError];
+    sqlite3_finalize(statement);
+    return;
+  }
+  // can query sqlite3 for this but this is fine
+  const int rmqIdColumnNumber = 0;
+  const int typeColumnNumber = 1;
+  const int dataColumnNumber = 2;
+  while (sqlite3_step(statement) == SQLITE_ROW) {
+    int64_t rmqId = sqlite3_column_int64(statement, rmqIdColumnNumber);
+    int8_t type = sqlite3_column_int(statement, typeColumnNumber);
+    const void *bytes = sqlite3_column_blob(statement, dataColumnNumber);
+    int length = sqlite3_column_bytes(statement, dataColumnNumber);
+    _FIRMessagingDevAssert(bytes != NULL,
+                           @"%@ Message with no data being stored in Rmq",
+                           kFCMRmqStoreTag);
+    NSData *data = [NSData dataWithBytes:bytes length:length];
+    handler(rmqId, type, data);
+  }
+  sqlite3_finalize(statement);
+}
+
+#pragma mark - Sync Messages
+
+- (FIRMessagingPersistentSyncMessage *)querySyncMessageWithRmqID:(NSString *)rmqID {
+  _FIRMessagingDevAssert([rmqID length], @"Invalid rmqID key %@ to search in SYNC_RMQ", rmqID);
+
+  NSString *queryFormat = @"SELECT %@ FROM %@ WHERE %@ = '%@'";
+  NSString *query = [NSString stringWithFormat:queryFormat,
+                     kSyncMessagesColumns, // SELECT (rmq_id, expiration_ts, apns_recv, mcs_recv)
+                     kTableSyncMessages,   // FROM sync_rmq
+                     kRmqIdColumn,         // WHERE rmq_id
+                     rmqID];
+
+  sqlite3_stmt *stmt;
+  if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL) != SQLITE_OK) {
+    [self logError];
+    sqlite3_finalize(stmt);
+    return nil;
+  }
+
+  const int rmqIDColumn = 0;
+  const int expirationTimestampColumn = 1;
+  const int apnsReceivedColumn = 2;
+  const int mcsReceivedColumn = 3;
+
+  int count = 0;
+  FIRMessagingPersistentSyncMessage *persistentMessage;
+
+  while (sqlite3_step(stmt) == SQLITE_ROW) {
+    NSString *rmqID =
+        [NSString stringWithUTF8String:(char *)sqlite3_column_text(stmt, rmqIDColumn)];
+    int64_t expirationTimestamp = sqlite3_column_int64(stmt, expirationTimestampColumn);
+    BOOL apnsReceived = sqlite3_column_int(stmt, apnsReceivedColumn);
+    BOOL mcsReceived = sqlite3_column_int(stmt, mcsReceivedColumn);
+
+    // create a new persistent message
+    persistentMessage =
+        [[FIRMessagingPersistentSyncMessage alloc] initWithRMQID:rmqID expirationTime:expirationTimestamp];
+    persistentMessage.apnsReceived = apnsReceived;
+    persistentMessage.mcsReceived = mcsReceived;
+
+    count++;
+  }
+  sqlite3_finalize(stmt);
+
+  _FIRMessagingDevAssert(count <= 1, @"Found multiple messages in %@ with same RMQ ID", kTableSyncMessages);
+  return persistentMessage;
+}
+
+- (BOOL)deleteSyncMessageWithRmqID:(NSString *)rmqID {
+  _FIRMessagingDevAssert([rmqID length], @"Invalid rmqID key %@ to delete in SYNC_RMQ", rmqID);
+  return [self deleteMessagesFromTable:kTableSyncMessages withRmqIds:@[rmqID]] > 0;
+}
+
+- (int)deleteExpiredOrFinishedSyncMessages:(NSError *__autoreleasing *)error {
+  int64_t now = FIRMessagingCurrentTimestampInSeconds();
+  NSString *deleteSQL = @"DELETE FROM %@ "
+                        @"WHERE %@ < %lld OR "  // expirationTime < now
+                        @"(%@ = 1 AND %@ = 1)";  // apns_received = 1 AND mcs_received = 1
+  NSString *query = [NSString stringWithFormat:deleteSQL,
+                     kTableSyncMessages,
+                     kSyncMessageExpirationTimestampColumn,
+                     now,
+                     kSyncMessageAPNSReceivedColumn,
+                     kSyncMessageMCSReceivedColumn];
+
+  NSString *errorReason = @"Failed to save delete expired sync messages from store.";
+
+  sqlite3_stmt *stmt;
+  if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL) != SQLITE_OK) {
+    if (error) {
+      *error = [NSError fcm_errorWithCode:sqlite3_errcode(_database)
+                                 userInfo:@{ @"error" : errorReason }];
+    }
+    _FIRMessagingRmqLogAndExit(stmt, 0);
+  }
+
+  if (sqlite3_step(stmt) != SQLITE_DONE) {
+    if (error) {
+      *error = [NSError fcm_errorWithCode:sqlite3_errcode(_database)
+                                 userInfo:@{ @"error" : errorReason }];
+    }
+    _FIRMessagingRmqLogAndExit(stmt, 0);
+  }
+
+  sqlite3_finalize(stmt);
+  int deleteCount = sqlite3_changes(_database);
+  return deleteCount;
+}
+
+- (BOOL)saveSyncMessageWithRmqID:(NSString *)rmqID
+                  expirationTime:(int64_t)expirationTime
+                    apnsReceived:(BOOL)apnsReceived
+                     mcsReceived:(BOOL)mcsReceived
+                           error:(NSError **)error {
+  _FIRMessagingDevAssert([rmqID length], @"Invalid nil message to persist to SYNC_RMQ");
+
+  NSString *insertFormat = @"INSERT INTO %@ (%@, %@, %@, %@) VALUES (?, ?, ?, ?)";
+  NSString *insertSQL = [NSString stringWithFormat:insertFormat,
+                         kTableSyncMessages, // Table name
+                         kRmqIdColumn, // rmq_id
+                         kSyncMessageExpirationTimestampColumn, // expiration_ts
+                         kSyncMessageAPNSReceivedColumn, // apns_recv
+                         kSyncMessageMCSReceivedColumn /* mcs_recv */];
+
+  sqlite3_stmt *stmt;
+
+  if (sqlite3_prepare_v2(_database, [insertSQL UTF8String], -1, &stmt, NULL) != SQLITE_OK) {
+    if (error) {
+      *error = [NSError fcm_errorWithCode:sqlite3_errcode(_database)
+                                 userInfo:@{ @"error" : @"Failed to save sync message to store." }];
+    }
+    _FIRMessagingRmqLogAndExit(stmt, NO);
+  }
+
+  if (sqlite3_bind_text(stmt, 1, [rmqID UTF8String], (int)[rmqID length], NULL) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(stmt, NO);
+  }
+
+  if (sqlite3_bind_int64(stmt, 2, expirationTime) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(stmt, NO);
+  }
+
+  if (sqlite3_bind_int(stmt, 3, apnsReceived ? 1 : 0) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(stmt, NO);
+  }
+
+  if (sqlite3_bind_int(stmt, 4, mcsReceived ? 1 : 0) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(stmt, NO);
+  }
+
+  if (sqlite3_step(stmt) != SQLITE_DONE) {
+    _FIRMessagingRmqLogAndExit(stmt, NO);
+  }
+
+  sqlite3_finalize(stmt);
+  return YES;
+}
+
+- (BOOL)updateSyncMessageViaAPNSWithRmqID:(NSString *)rmqID
+                                    error:(NSError **)error {
+  return [self updateSyncMessageWithRmqID:rmqID
+                                   column:kSyncMessageAPNSReceivedColumn
+                                    value:YES
+                                    error:error];
+}
+
+- (BOOL)updateSyncMessageViaMCSWithRmqID:(NSString *)rmqID
+                                   error:(NSError *__autoreleasing *)error {
+  return [self updateSyncMessageWithRmqID:rmqID
+                                   column:kSyncMessageMCSReceivedColumn
+                                    value:YES
+                                    error:error];
+}
+
+- (BOOL)updateSyncMessageWithRmqID:(NSString *)rmqID
+                            column:(NSString *)column
+                             value:(BOOL)value
+                             error:(NSError **)error {
+  _FIRMessagingDevAssert([column isEqualToString:kSyncMessageAPNSReceivedColumn] ||
+                [column isEqualToString:kSyncMessageMCSReceivedColumn],
+                @"Invalid column name %@ for SYNC_RMQ", column);
+  NSString *queryFormat = @"UPDATE %@ "  // Table name
+                          @"SET %@ = %d "  // column=value
+                          @"WHERE %@ = ?";  // condition
+  NSString *query = [NSString stringWithFormat:queryFormat,
+                     kTableSyncMessages,
+                     column,
+                     value ? 1 : 0,
+                     kRmqIdColumn];
+  sqlite3_stmt *stmt;
+
+  if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL) != SQLITE_OK) {
+    if (error) {
+      *error = [NSError fcm_errorWithCode:sqlite3_errcode(_database)
+                                 userInfo:@{ @"error" : @"Failed to update sync message"}];
+    }
+    _FIRMessagingRmqLogAndExit(stmt, NO);
+  }
+
+  if (sqlite3_bind_text(stmt, 1, [rmqID UTF8String], (int)[rmqID length], NULL) != SQLITE_OK) {
+    _FIRMessagingRmqLogAndExit(stmt, NO);
+  }
+
+  if (sqlite3_step(stmt) != SQLITE_DONE) {
+    _FIRMessagingRmqLogAndExit(stmt, NO);
+  }
+
+  sqlite3_finalize(stmt);
+  return YES;
+
+}
+
+#pragma mark - Private
+
+- (NSString *)lastErrorMessage {
+  return [NSString stringWithFormat:@"%s", sqlite3_errmsg(_database)];
+}
+
+- (int)lastErrorCode {
+  return sqlite3_errcode(_database);
+}
+
+- (void)logError {
+  FIRMessagingLoggerError(kFIRMessagingMessageCodeRmq2PersistentStore006,
+                          @"%@ error: code (%d) message: %@", kFCMRmqStoreTag, [self lastErrorCode],
+                          [self lastErrorMessage]);
+}
+
+- (void)logErrorAndFinalizeStatement:(sqlite3_stmt *)stmt {
+  [self logError];
+  sqlite3_finalize(stmt);
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmqManager.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmqManager.h
new file mode 100644 (file)
index 0000000..ba48b98
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@class GtalkDataMessageStanza;
+@class GPBMessage;
+
+@class FIRMessagingPersistentSyncMessage;
+
+/**
+ * Called on each raw message.
+ */
+typedef void(^FIRMessagingRmqMessageHandler)(int64_t rmqId, int8_t tag, NSData *data);
+
+/**
+ * Called on each DataMessageStanza.
+ */
+typedef void(^FIRMessagingDataMessageHandler)(int64_t rmqId, GtalkDataMessageStanza *stanza);
+
+/**
+ *  Used to scan through the rmq and perform actions on messages as required.
+ */
+@protocol FIRMessagingRmqScanner <NSObject>
+
+/**
+ *  Scan the RMQ for outgoing messages and process them as required.
+ */
+- (void)scanWithRmqMessageHandler:(FIRMessagingRmqMessageHandler)rmqMessageHandler
+               dataMessageHandler:(FIRMessagingDataMessageHandler)dataMessageHandler;
+
+@end
+
+/**
+ * This manages the RMQ persistent store.
+ *
+ * The store is used to store all the S2D id's that were received by the client and were ACK'ed
+ * by us but the server hasn't confirmed the ACK. We don't delete these id's until the server
+ * ACK's us that they have received them.
+ *
+ * We also store the upstream messages(d2s) that were sent by the client.
+ *
+ * Also store the lastRMQId that was sent by us so that for a new connection being setup we don't
+ * duplicate RMQ Id's for the new messages.
+ */
+@interface FIRMessagingRmqManager : NSObject <FIRMessagingRmqScanner>
+
+// designated initializer
+- (instancetype)initWithDatabaseName:(NSString *)databaseName;
+
+- (void)loadRmqId;
+
+/**
+ *  Save an upstream message to RMQ. If the message send fails for some reason we would not
+ *  lose the message since it would be saved in the RMQ.
+ *
+ *  @param message The upstream message to be saved.
+ *  @param error   The error if any while saving the message else nil.
+ *
+ *  @return YES if the message was successfully saved to RMQ else NO.
+ */
+- (BOOL)saveRmqMessage:(GPBMessage *)message error:(NSError **)error;
+
+/**
+ *  Save Server to device message with the given RMQ-ID.
+ *
+ *  @param rmqID The rmqID of the s2d message to save.
+ *
+ *  @return YES if the save was successfull else NO.
+ */
+- (BOOL)saveS2dMessageWithRmqId:(NSString *)rmqID;
+
+/**
+ *  A list of all unacked Server to device RMQ IDs.
+ *
+ *  @return A list of unacked Server to Device RMQ ID's. All values are Strings.
+ */
+- (NSArray *)unackedS2dRmqIds;
+
+/**
+ *  Removes the outgoing message from RMQ store.
+ *
+ *  @param rmqId The rmqID to remove from the store.
+ *
+ *  @return The number of messages deleted successfully.
+ */
+- (int)removeRmqMessagesWithRmqId:(NSString *)rmqId;
+
+/**
+ *  Removes the messages with the given rmqIDs from RMQ store.
+ *
+ *  @param rmqIds The lsit of rmqID's to remove from the store.
+ *
+ *  @return The number of messages deleted successfully.
+ */
+- (int)removeRmqMessagesWithRmqIds:(NSArray *)rmqIds;
+
+/**
+ *  Removes a list of downstream messages from the RMQ.
+ *
+ *  @param s2dIds The list of messages ACK'ed by the server that we should remove
+ *                from the RMQ store.
+ */
+- (void)removeS2dIds:(NSArray *)s2dIds;
+
+#pragma mark - Sync Messages
+
+/**
+ *  Get persisted sync message with rmqID.
+ *
+ *  @param rmqID The rmqID of the persisted sync message.
+ *
+ *  @return A valid persistent sync message with the given rmqID if found in the RMQ else nil.
+ */
+- (FIRMessagingPersistentSyncMessage *)querySyncMessageWithRmqID:(NSString *)rmqID;
+
+/**
+ *  Delete sync message with rmqID.
+ *
+ *  @param rmqID The rmqID of the persisted sync message.
+ *
+ *  @return YES if the message was successfully deleted else NO.
+ */
+- (BOOL)deleteSyncMessageWithRmqID:(NSString *)rmqID;
+
+/**
+ *  Delete the expired sync messages from persisten store. Also deletes messages that have been
+ *  delivered both via APNS and MCS.
+ *
+ *  @param error The error if any while deleting the messages.
+ *
+ *  @return The total number of messages that were deleted from the persistent store.
+ */
+- (int)deleteExpiredOrFinishedSyncMessages:(NSError **)error;
+
+/**
+ *  Save sync message received by the device.
+ *
+ *  @param rmqID          The rmqID of the message received.
+ *  @param expirationTime The expiration time of the sync message received.
+ *  @param apnsReceived   YES if the message was received via APNS else NO.
+ *  @param mcsReceived    YES if the message was received via MCS else NO.
+ *  @param error          The error if any while saving the sync message to persistent store.
+ *
+ *  @return YES if the message save was successful else NO.
+ */
+- (BOOL)saveSyncMessageWithRmqID:(NSString *)rmqID
+                  expirationTime:(int64_t)expirationTime
+                    apnsReceived:(BOOL)apnsReceived
+                     mcsReceived:(BOOL)mcsReceived
+                           error:(NSError **)error;
+
+/**
+ *  Update sync message received via APNS.
+ *
+ *  @param rmqID The rmqID of the received message.
+ *  @param error The error if any while updating the sync message.
+ *
+ *  @return YES if the persistent sync message was successfully updated else NO.
+ */
+- (BOOL)updateSyncMessageViaAPNSWithRmqID:(NSString *)rmqID error:(NSError **)error;
+
+/**
+ *  Update sync message received via MCS.
+ *
+ *  @param rmqID The rmqID of the received message.
+ *  @param error The error if any while updating the sync message.
+ *
+ *  @return YES if the persistent sync message was successfully updated else NO.
+ */
+- (BOOL)updateSyncMessageViaMCSWithRmqID:(NSString *)rmqID error:(NSError **)error;
+
+#pragma mark - Testing
+
++ (void)removeDatabaseWithName:(NSString *)dbName;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmqManager.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingRmqManager.m
new file mode 100644 (file)
index 0000000..449e3d6
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingRmqManager.h"
+
+#import <sqlite3.h>
+
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingRmq2PersistentStore.h"
+#import "FIRMessagingUtilities.h"
+#import "Protos/GtalkCore.pbobjc.h"
+
+#ifndef _FIRMessagingRmqLogAndExit
+#define _FIRMessagingRmqLogAndExit(stmt, return_value)   \
+do {                              \
+  [self logErrorAndFinalizeStatement:stmt];  \
+  return return_value; \
+} while(0)
+#endif
+
+static NSString *const kFCMRmqTag = @"FIRMessagingRmq:";
+
+@interface FIRMessagingRmqManager ()
+
+@property(nonatomic, readwrite, strong) FIRMessagingRmq2PersistentStore *rmq2Store;
+// map the category of an outgoing message with the number of messages for that category
+// should always have two keys -- the app, gcm
+@property(nonatomic, readwrite, strong) NSMutableDictionary *outstandingMessages;
+
+// Outgoing RMQ persistent id
+@property(nonatomic, readwrite, assign) int64_t rmqId;
+
+@end
+
+@implementation FIRMessagingRmqManager
+
+- (instancetype)initWithDatabaseName:(NSString *)databaseName {
+  self = [super init];
+  if (self) {
+    _FIRMessagingDevAssert([databaseName length] > 0, @"RMQ: Invalid rmq db name");
+    _rmq2Store = [[FIRMessagingRmq2PersistentStore alloc] initWithDatabaseName:databaseName];
+    _outstandingMessages = [NSMutableDictionary dictionaryWithCapacity:2];
+    _rmqId = -1;
+  }
+  return self;
+}
+
+- (void)loadRmqId {
+  if (self.rmqId >= 0) {
+    return; // already done
+  }
+
+  [self loadInitialOutgoingPersistentId];
+  if (self.outstandingMessages.count) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeRmqManager000,
+                            @"%@: outstanding categories %ld", kFCMRmqTag,
+                            _FIRMessaging_UL(self.outstandingMessages.count));
+  }
+}
+
+/**
+ * Initialize the 'initial RMQ':
+ * - max ID of any message in the queue
+ * - if the queue is empty, stored value in separate DB.
+ *
+ * Stream acks will remove from RMQ, when we remove the highest message we keep track
+ * of its ID.
+ */
+- (void)loadInitialOutgoingPersistentId {
+
+  // we shouldn't always trust the lastRmqId stored in the LastRmqId table, because
+  // we only save to the LastRmqId table once in a while (after getting the lastRmqId sent
+  // by the server after reconnect, and after getting a rmq ack from the server). The
+  // rmq message with the highest rmq id tells the real story, so check against that first.
+
+  int64_t rmqId = [self queryHighestRmqId];
+  if (rmqId == 0) {
+    rmqId = [self querylastRmqId];
+  }
+  self.rmqId = rmqId + 1;
+}
+
+#pragma mark - Save
+
+/**
+ * Save a message to RMQ2. Will populate the rmq2 persistent ID.
+ */
+- (BOOL)saveRmqMessage:(GPBMessage *)message
+                 error:(NSError **)error {
+  // send using rmq2manager
+  // the wire format of rmq2 id is a string. However, we keep it as a long internally
+  // in the database. So only convert the id to string when preparing for sending over
+  // the wire.
+  NSString *rmq2Id = FIRMessagingGetRmq2Id(message);
+  if (![rmq2Id length]) {
+    int64_t rmqId = [self nextRmqId];
+    rmq2Id = [NSString stringWithFormat:@"%lld", rmqId];
+    FIRMessagingSetRmq2Id(message, rmq2Id);
+  }
+  FIRMessagingProtoTag tag = FIRMessagingGetTagForProto(message);
+  return [self saveMessage:message withRmqId:[rmq2Id integerValue] tag:tag error:error];
+}
+
+- (BOOL)saveMessage:(GPBMessage *)message
+          withRmqId:(int64_t)rmqId
+                tag:(int8_t)tag
+              error:(NSError **)error {
+  NSData *data = [message data];
+  return [self.rmq2Store saveMessageWithRmqId:rmqId tag:tag data:data error:error];
+}
+
+/**
+ * This is called when we delete the largest outgoing message from queue.
+ */
+- (void)saveLastOutgoingRmqId:(int64_t)rmqID {
+  [self.rmq2Store updateLastOutgoingRmqId:rmqID];
+}
+
+- (BOOL)saveS2dMessageWithRmqId:(NSString *)rmqID {
+  return [self.rmq2Store saveUnackedS2dMessageWithRmqId:rmqID];
+}
+
+#pragma mark - Query
+
+- (int64_t)queryHighestRmqId {
+  return [self.rmq2Store queryHighestRmqId];
+}
+
+- (int64_t)querylastRmqId {
+  return [self.rmq2Store queryLastRmqId];
+}
+
+- (NSArray *)unackedS2dRmqIds {
+  return [self.rmq2Store unackedS2dRmqIds];
+}
+
+#pragma mark - FIRMessagingRMQScanner protocol
+
+/**
+ * We don't have a 'getMessages' method - it would require loading in memory
+ * the entire content body of all messages.
+ *
+ * Instead we iterate and call 'resend' for each message.
+ *
+ * This is called:
+ *  - on connect MCS, to resend any outstanding messages
+ *  - init
+ */
+- (void)scanWithRmqMessageHandler:(FIRMessagingRmqMessageHandler)rmqMessageHandler
+               dataMessageHandler:(FIRMessagingDataMessageHandler)dataMessageHandler {
+  // no need to scan database with no callbacks
+  if (rmqMessageHandler || dataMessageHandler) {
+    [self.rmq2Store scanOutgoingRmqMessagesWithHandler:^(int64_t rmqId, int8_t tag, NSData *data) {
+      if (rmqMessageHandler != nil) {
+        rmqMessageHandler(rmqId, tag, data);
+      }
+      if (dataMessageHandler != nil && kFIRMessagingProtoTagDataMessageStanza == tag) {
+        GPBMessage *proto =
+            [FIRMessagingGetClassForTag((FIRMessagingProtoTag)tag) parseFromData:data error:NULL];
+        GtalkDataMessageStanza *stanza = (GtalkDataMessageStanza *)proto;
+        dataMessageHandler(rmqId, stanza);
+      }
+    }];
+  }
+}
+
+#pragma mark - Remove
+
+- (void)ackReceivedForRmqId:(NSString *)rmqId {
+  // TODO: Optional book-keeping
+}
+
+- (int)removeRmqMessagesWithRmqId:(NSString *)rmqId {
+  return [self removeRmqMessagesWithRmqIds:@[rmqId]];
+}
+
+- (int)removeRmqMessagesWithRmqIds:(NSArray *)rmqIds {
+  if (![rmqIds count]) {
+    return 0;
+  }
+  for (NSString *rmqId in rmqIds) {
+    [self ackReceivedForRmqId:rmqId];
+  }
+  int64_t maxRmqId = -1;
+  for (NSString *rmqId in rmqIds) {
+    int64_t rmqIdValue = [rmqId longLongValue];
+    if (rmqIdValue > maxRmqId) {
+      maxRmqId = rmqIdValue;
+    }
+  }
+  maxRmqId++;
+  if (maxRmqId >= self.rmqId) {
+    [self saveLastOutgoingRmqId:maxRmqId];
+  }
+  return [self.rmq2Store deleteMessagesFromTable:kTableOutgoingRmqMessages withRmqIds:rmqIds];
+}
+
+- (void)removeS2dIds:(NSArray *)s2dIds {
+  [self.rmq2Store deleteMessagesFromTable:kTableS2DRmqIds withRmqIds:s2dIds];
+}
+
+#pragma mark - Sync Messages
+
+// TODO: RMQManager should also have a cache for all the sync messages
+// so we don't hit the DB each time.
+- (FIRMessagingPersistentSyncMessage *)querySyncMessageWithRmqID:(NSString *)rmqID {
+  return [self.rmq2Store querySyncMessageWithRmqID:rmqID];
+}
+
+- (BOOL)deleteSyncMessageWithRmqID:(NSString *)rmqID {
+  return [self.rmq2Store deleteSyncMessageWithRmqID:rmqID];
+}
+
+- (int)deleteExpiredOrFinishedSyncMessages:(NSError **)error {
+  return [self.rmq2Store deleteExpiredOrFinishedSyncMessages:error];
+}
+
+- (BOOL)saveSyncMessageWithRmqID:(NSString *)rmqID
+                  expirationTime:(int64_t)expirationTime
+                    apnsReceived:(BOOL)apnsReceived
+                     mcsReceived:(BOOL)mcsReceived
+                           error:(NSError *__autoreleasing *)error {
+  return [self.rmq2Store saveSyncMessageWithRmqID:rmqID
+                                   expirationTime:expirationTime
+                                     apnsReceived:apnsReceived
+                                      mcsReceived:mcsReceived
+                                            error:error];
+}
+
+- (BOOL)updateSyncMessageViaAPNSWithRmqID:(NSString *)rmqID error:(NSError **)error {
+  return [self.rmq2Store updateSyncMessageViaAPNSWithRmqID:rmqID error:error];
+}
+
+- (BOOL)updateSyncMessageViaMCSWithRmqID:(NSString *)rmqID error:(NSError **)error {
+  return [self.rmq2Store updateSyncMessageViaMCSWithRmqID:rmqID error:error];
+}
+
+#pragma mark - Testing
+
++ (void)removeDatabaseWithName:(NSString *)dbName {
+  [FIRMessagingRmq2PersistentStore removeDatabase:dbName];
+}
+
+#pragma mark - Private
+
+- (int64_t)nextRmqId {
+  return ++self.rmqId;
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSecureSocket.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSecureSocket.h
new file mode 100644 (file)
index 0000000..169f60e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+typedef NS_ENUM(NSUInteger, FIRMessagingSecureSocketState){
+  kFIRMessagingSecureSocketNotOpen = 0,
+  kFIRMessagingSecureSocketOpening,
+  kFIRMessagingSecureSocketOpen,
+  kFIRMessagingSecureSocketClosing,
+  kFIRMessagingSecureSocketClosed,
+  kFIRMessagingSecureSocketError
+};
+
+@class FIRMessagingSecureSocket;
+
+@protocol FIRMessagingSecureSocketDelegate<NSObject>
+
+- (void)secureSocket:(FIRMessagingSecureSocket *)socket
+      didReceiveData:(NSData *)data
+             withTag:(int8_t)tag;
+- (void)secureSocket:(FIRMessagingSecureSocket *)socket
+ didSendProtoWithTag:(int8_t)tag
+               rmqId:(NSString *)rmqId;
+- (void)secureSocketDidConnect:(FIRMessagingSecureSocket *)socket;
+- (void)didDisconnectWithSecureSocket:(FIRMessagingSecureSocket *)socket;
+
+@end
+
+/**
+ * This manages the input/output streams connected to the MCS server. Used to receive data from
+ * the server and send to it over the wire.
+ */
+@interface FIRMessagingSecureSocket : NSObject
+
+@property(nonatomic, readwrite, weak) id<FIRMessagingSecureSocketDelegate> delegate;
+@property(nonatomic, readonly, assign) FIRMessagingSecureSocketState state;
+
+- (void)connectToHost:(NSString *)host port:(NSUInteger)port onRunLoop:(NSRunLoop *)runLoop;
+- (void)disconnect;
+- (void)sendData:(NSData *)data withTag:(int8_t)tag rmqId:(NSString *)rmqId;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSecureSocket.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSecureSocket.m
new file mode 100644 (file)
index 0000000..3a03ee3
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingSecureSocket.h"
+
+#import "GPBMessage.h"
+#import "GPBCodedOutputStream.h"
+#import "GPBUtilities.h"
+
+#import "FIRMessagingCodedInputStream.h"
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingPacketQueue.h"
+
+static const NSUInteger kMaxBufferLength = 1024 * 1024;  // 1M
+static const NSUInteger kBufferLengthIncrement = 16 * 1024;  // 16k
+static const uint8_t kVersion = 40;
+static const uint8_t kInvalidTag = -1;
+
+typedef NS_ENUM(NSUInteger, FIRMessagingSecureSocketReadResult) {
+  kFIRMessagingSecureSocketReadResultNone,
+  kFIRMessagingSecureSocketReadResultIncomplete,
+  kFIRMessagingSecureSocketReadResultCorrupt,
+  kFIRMessagingSecureSocketReadResultSuccess
+};
+
+static int32_t LogicalRightShift32(int32_t value, int32_t spaces) {
+  return (int32_t)((uint32_t)(value) >> spaces);
+}
+
+static NSUInteger SerializedSize(int32_t value) {
+  NSUInteger bytes = 0;
+  while (YES) {
+    if ((value & ~0x7F) == 0) {
+      bytes += sizeof(uint8_t);
+      return bytes;
+    } else {
+      bytes += sizeof(uint8_t);
+      value = LogicalRightShift32(value, 7);
+    }
+  }
+}
+
+@interface FIRMessagingSecureSocket() <NSStreamDelegate>
+
+@property(nonatomic, readwrite, assign) FIRMessagingSecureSocketState state;
+@property(nonatomic, readwrite, strong) NSInputStream *inStream;
+@property(nonatomic, readwrite, strong) NSOutputStream *outStream;
+
+@property(nonatomic, readwrite, strong) NSMutableData *inputBuffer;
+@property(nonatomic, readwrite, assign) NSUInteger inputBufferLength;
+@property(nonatomic, readwrite, strong) NSMutableData *outputBuffer;
+@property(nonatomic, readwrite, assign) NSUInteger outputBufferLength;
+
+@property(nonatomic, readwrite, strong) FIRMessagingPacketQueue *packetQueue;
+@property(nonatomic, readwrite, assign) BOOL isVersionSent;
+@property(nonatomic, readwrite, assign) BOOL isVersionReceived;
+@property(nonatomic, readwrite, assign) BOOL isInStreamOpen;
+@property(nonatomic, readwrite, assign) BOOL isOutStreamOpen;
+
+@property(nonatomic, readwrite, strong) NSRunLoop *runLoop;
+@property(nonatomic, readwrite, strong) NSString *currentRmqIdBeingSent;
+@property(nonatomic, readwrite, assign) int8_t currentProtoTypeBeingSent;
+
+@end
+
+@implementation FIRMessagingSecureSocket
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _state = kFIRMessagingSecureSocketNotOpen;
+    _inputBuffer = [NSMutableData dataWithLength:kBufferLengthIncrement];
+    _packetQueue = [[FIRMessagingPacketQueue alloc] init];
+    _currentProtoTypeBeingSent = kInvalidTag;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [self disconnect];
+}
+
+- (void)connectToHost:(NSString *)host
+                 port:(NSUInteger)port
+            onRunLoop:(NSRunLoop *)runLoop {
+  _FIRMessagingDevAssert(host != nil, @"Invalid host");
+  _FIRMessagingDevAssert(runLoop != nil, @"Invalid runloop");
+  _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketNotOpen, @"Socket is already connected");
+
+  if (!host || self.state != kFIRMessagingSecureSocketNotOpen) {
+    return;
+  }
+
+  FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket000,
+                          @"Opening secure socket to FIRMessaging service");
+  self.state = kFIRMessagingSecureSocketOpening;
+  self.runLoop = runLoop;
+  CFReadStreamRef inputStreamRef;
+  CFWriteStreamRef outputStreamRef;
+  CFStreamCreatePairWithSocketToHost(NULL,
+                                     (__bridge CFStringRef)host,
+                                     (int)port,
+                                     &inputStreamRef,
+                                     &outputStreamRef);
+  self.inStream = CFBridgingRelease(inputStreamRef);
+  self.outStream = CFBridgingRelease(outputStreamRef);
+  if (!self.inStream || !self.outStream) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket001,
+                            @"Failed to initialize socket.");
+    return;
+  }
+
+  self.isInStreamOpen = NO;
+  self.isOutStreamOpen = NO;
+
+  BOOL isVOIPSocket = NO;
+
+  [self openStream:self.outStream isVOIPStream:isVOIPSocket];
+  [self openStream:self.inStream isVOIPStream:isVOIPSocket];
+}
+
+- (void)disconnect {
+  if (self.state == kFIRMessagingSecureSocketClosing) {
+    return;
+  }
+  if (!self.inStream && !self.outStream) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket002,
+                            @"The socket is not open or already closed.");
+    _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketClosed || self.state == kFIRMessagingSecureSocketNotOpen,
+                  @"Socket is already disconnected.");
+    return;
+  }
+
+  self.state = kFIRMessagingSecureSocketClosing;
+  if (self.inStream) {
+    [self closeStream:self.inStream];
+    self.inStream = nil;
+  }
+  if (self.outStream) {
+    [self closeStream:self.outStream];
+    self.outStream = nil;
+  }
+  self.state = kFIRMessagingSecureSocketClosed;
+  [self.delegate didDisconnectWithSecureSocket:self];
+}
+
+- (void)sendData:(NSData *)data withTag:(int8_t)tag rmqId:(NSString *)rmqId {
+  [self.packetQueue push:[FIRMessagingPacket packetWithTag:tag rmqId:rmqId data:data]];
+  if ([self.outStream hasSpaceAvailable]) {
+    [self performWrite];
+  }
+}
+
+#pragma mark - NSStreamDelegate
+
+- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
+  switch (eventCode) {
+    case NSStreamEventHasBytesAvailable:
+      if (self.state != kFIRMessagingSecureSocketOpen) {
+        FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket003,
+                                @"Try to read from socket that is not opened");
+        return;
+      }
+      _FIRMessagingDevAssert(stream == self.inStream, @"Incorrect stream");
+      if (![self performRead]) {
+        FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket004,
+                                @"Error occured when reading incoming stream");
+        [self disconnect];
+      }
+      break;
+    case NSStreamEventEndEncountered:
+      FIRMessagingLoggerDebug(
+          kFIRMessagingMessageCodeSecureSocket005, @"%@ end encountered",
+          stream == self.inStream
+              ? @"Input stream"
+              : (stream == self.outStream ? @"Output stream" : @"Unknown stream"));
+      [self disconnect];
+      break;
+    case NSStreamEventOpenCompleted:
+      if (stream == self.inStream) {
+        self.isInStreamOpen = YES;
+      } else if (stream == self.outStream) {
+        self.isOutStreamOpen = YES;
+      }
+      if (self.isInStreamOpen && self.isOutStreamOpen) {
+        FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket006,
+                                @"Secure socket to FIRMessaging service opened");
+        self.state = kFIRMessagingSecureSocketOpen;
+        [self.delegate secureSocketDidConnect:self];
+      }
+      break;
+    case NSStreamEventErrorOccurred: {
+      FIRMessagingLoggerDebug(
+          kFIRMessagingMessageCodeSecureSocket007, @"%@ error occurred",
+          stream == self.inStream
+              ? @"Input stream"
+              : (stream == self.outStream ? @"Output stream" : @"Unknown stream"));
+      [self disconnect];
+      break;
+    }
+    case NSStreamEventHasSpaceAvailable:
+      if (self.state != kFIRMessagingSecureSocketOpen) {
+        FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket008,
+                                @"Try to write to socket that is not opened");
+        return;
+      }
+      _FIRMessagingDevAssert(stream == self.outStream, @"Incorrect stream");
+      [self performWrite];
+      break;
+    default:
+      break;
+  }
+}
+
+#pragma mark - Private
+
+- (void)openStream:(NSStream *)stream isVOIPStream:(BOOL)isVOIPStream {
+  _FIRMessagingDevAssert(stream != nil, @"Invalid stream");
+  _FIRMessagingDevAssert(self.runLoop != nil, @"Invalid runloop");
+
+  if (stream) {
+    _FIRMessagingDevAssert([stream streamStatus] == NSStreamStatusNotOpen, @"Stream already open");
+    if ([stream streamStatus] != NSStreamStatusNotOpen) {
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket009,
+                              @"stream should not be open.");
+      return;
+    }
+    [stream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
+                 forKey:NSStreamSocketSecurityLevelKey];
+    if (isVOIPStream) {
+      [stream setProperty:NSStreamNetworkServiceTypeVoIP
+                   forKey:NSStreamNetworkServiceType];
+    }
+    stream.delegate = self;
+    [stream scheduleInRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
+    [stream open];
+  }
+}
+
+- (void)closeStream:(NSStream *)stream {
+  _FIRMessagingDevAssert(stream != nil, @"Invalid stream");
+  _FIRMessagingDevAssert(self.runLoop != nil, @"Invalid runloop");
+
+  if (stream) {
+    [stream close];
+    [stream removeFromRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
+    stream.delegate = nil;
+  }
+}
+
+- (BOOL)performRead {
+  _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketOpen, @"Socket should be open");
+
+  if (!self.isVersionReceived) {
+    self.isVersionReceived = YES;
+    uint8_t versionByte = 0;
+    NSInteger bytesRead = [self.inStream read:&versionByte maxLength:sizeof(uint8_t)];
+    if (bytesRead != sizeof(uint8_t) || kVersion != versionByte) {
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket010,
+                              @"Version do not match. Received %d, Expecting %d", versionByte,
+                              kVersion);
+      return NO;
+    }
+  }
+
+  while (YES) {
+    BOOL isInputBufferValid = [self.inputBuffer length] > 0;
+    _FIRMessagingDevAssert(isInputBufferValid,
+                  @"Invalid input buffer size %lu. Used bytes length %lu, buffer content: %@",
+                  _FIRMessaging_UL([self.inputBuffer length]),
+                  _FIRMessaging_UL(self.inputBufferLength),
+                  self.inputBuffer);
+    if (!isInputBufferValid) {
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket011,
+                              @"Input buffer is not valid.");
+      return NO;
+    }
+
+    if (![self.inStream hasBytesAvailable]) {
+      break;
+    }
+
+    // try to read more data
+    uint8_t *unusedBufferPtr = (uint8_t *)self.inputBuffer.mutableBytes + self.inputBufferLength;
+    NSUInteger unusedBufferLength = [self.inputBuffer length] - self.inputBufferLength;
+    NSInteger bytesRead = [self.inStream read:unusedBufferPtr maxLength:unusedBufferLength];
+    if (bytesRead <= 0) {
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket012,
+                              @"Failed to read input stream. Bytes read %ld, Used buffer size %lu, "
+                              @"Unused buffer size %lu",
+                              _FIRMessaging_UL(bytesRead), _FIRMessaging_UL(self.inputBufferLength),
+                              _FIRMessaging_UL(unusedBufferLength));
+      break;
+    }
+    // did successfully read some more data
+    self.inputBufferLength += (NSUInteger)bytesRead;
+
+    if ([self.inputBuffer length] <= self.inputBufferLength) {
+      // shouldn't be reading more than 1MB of data in one go
+      if ([self.inputBuffer length] + kBufferLengthIncrement > kMaxBufferLength) {
+        FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket013,
+                                @"Input buffer exceed 1M, disconnect socket");
+        return NO;
+      }
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket014,
+                              @"Input buffer limit exceeded. Used input buffer size %lu, "
+                              @"Total input buffer size %lu. No unused buffer left. "
+                              @"Increase buffer size.",
+                              _FIRMessaging_UL(self.inputBufferLength),
+                              _FIRMessaging_UL([self.inputBuffer length]));
+      [self.inputBuffer increaseLengthBy:kBufferLengthIncrement];
+      _FIRMessagingDevAssert([self.inputBuffer length] > self.inputBufferLength, @"Invalid buffer size");
+    }
+
+    while (self.inputBufferLength > 0 && [self.inputBuffer length] > 0) {
+      _FIRMessagingDevAssert([self.inputBuffer length] >= self.inputBufferLength,
+                             @"Buffer longer than length");
+      NSRange inputRange = NSMakeRange(0, self.inputBufferLength);
+      size_t protoBytes = 0;
+      // read the actual proto data coming in
+      FIRMessagingSecureSocketReadResult readResult =
+          [self processCurrentInputBuffer:[self.inputBuffer subdataWithRange:inputRange]
+                                outOffset:&protoBytes];
+      // Corrupt data encountered, stop processing.
+      if (readResult == kFIRMessagingSecureSocketReadResultCorrupt) {
+        return NO;
+        // Incomplete data, keep trying to read by loading more from the stream.
+      } else if (readResult == kFIRMessagingSecureSocketReadResultIncomplete) {
+        break;
+      }
+      _FIRMessagingDevAssert(self.inputBufferLength >= protoBytes, @"More bytes than buffer can handle");
+      // we have read (0, protoBytes) of data in the inputBuffer
+      if (protoBytes == self.inputBufferLength) {
+        // did completely read the buffer data can be reset for further processing
+        self.inputBufferLength = 0;
+      } else {
+        // delete processed bytes while maintaining the buffer size.
+        NSUInteger prevLength __unused = [self.inputBuffer length];
+        // delete the processed bytes
+        [self.inputBuffer replaceBytesInRange:NSMakeRange(0, protoBytes) withBytes:NULL length:0];
+        // reallocate more data
+        [self.inputBuffer increaseLengthBy:protoBytes];
+        _FIRMessagingDevAssert([self.inputBuffer length] == prevLength,
+                               @"Invalid input buffer size %lu. Used bytes length %lu, "
+                               @"buffer content: %@",
+                               _FIRMessaging_UL([self.inputBuffer length]),
+                               _FIRMessaging_UL(self.inputBufferLength),
+                               self.inputBuffer);
+        self.inputBufferLength -= protoBytes;
+      }
+    }
+  }
+  return YES;
+}
+
+- (FIRMessagingSecureSocketReadResult)processCurrentInputBuffer:(NSData *)readData
+                                             outOffset:(size_t *)outOffset {
+  *outOffset = 0;
+
+  FIRMessagingCodedInputStream *input = [[FIRMessagingCodedInputStream alloc] initWithData:readData];
+  int8_t rawTag;
+  if (![input readTag:&rawTag]) {
+    return kFIRMessagingSecureSocketReadResultIncomplete;
+  }
+  int32_t length;
+  if (![input readLength:&length]) {
+    return kFIRMessagingSecureSocketReadResultIncomplete;
+  }
+  // NOTE tag can be zero for |HeartbeatPing|, and length can be zero for |Close| proto
+  _FIRMessagingDevAssert(rawTag >= 0 && length >= 0, @"Invalid tag or length");
+  if (rawTag < 0 || length < 0) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket015, @"Buffer data corrupted.");
+    return kFIRMessagingSecureSocketReadResultCorrupt;
+  }
+  NSData *data = [input readDataWithLength:(uint32_t)length];
+  if (data == nil) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket016,
+                            @"Incomplete data, buffered data length %ld, expected length %d",
+                            _FIRMessaging_UL(self.inputBufferLength), length);
+    return kFIRMessagingSecureSocketReadResultIncomplete;
+  }
+  [self.delegate secureSocket:self didReceiveData:data withTag:rawTag];
+  *outOffset = input.offset;
+  return kFIRMessagingSecureSocketReadResultSuccess;
+}
+
+- (void)performWrite {
+  _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketOpen, @"Invalid socket state");
+
+  if (!self.isVersionSent) {
+    self.isVersionSent = YES;
+    uint8_t versionByte = kVersion;
+    [self.outStream write:&versionByte maxLength:sizeof(uint8_t)];
+  }
+
+  while (!self.packetQueue.isEmpty && self.outStream.hasSpaceAvailable) {
+    if (self.outputBuffer.length == 0) {
+      // serialize new packets only when the output buffer is flushed.
+      FIRMessagingPacket *packet = [self.packetQueue pop];
+      self.currentRmqIdBeingSent = packet.rmqId;
+      self.currentProtoTypeBeingSent = packet.tag;
+      NSUInteger length = SerializedSize(packet.tag) +
+          SerializedSize((int)packet.data.length) + packet.data.length;
+      self.outputBuffer = [NSMutableData dataWithLength:length];
+      GPBCodedOutputStream *output = [GPBCodedOutputStream streamWithData:self.outputBuffer];
+      [output writeRawVarint32:packet.tag];
+      [output writeBytesNoTag:packet.data];
+      self.outputBufferLength = 0;
+    }
+
+    // flush the output buffer.
+    NSInteger written = [self.outStream write:self.outputBuffer.bytes + self.outputBufferLength
+                                    maxLength:self.outputBuffer.length - self.outputBufferLength];
+    if (written <= 0) {
+      continue;
+    }
+    self.outputBufferLength += (NSUInteger)written;
+    if (self.outputBufferLength >= self.outputBuffer.length) {
+      self.outputBufferLength = 0;
+      self.outputBuffer = nil;
+      [self.delegate secureSocket:self
+              didSendProtoWithTag:self.currentProtoTypeBeingSent
+                            rmqId:self.currentRmqIdBeingSent];
+      self.currentRmqIdBeingSent = nil;
+      self.currentProtoTypeBeingSent = kInvalidTag;
+    }
+  }
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSyncMessageManager.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSyncMessageManager.h
new file mode 100644 (file)
index 0000000..3d30bdb
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@class FIRMessagingRmqManager;
+
+/**
+ *  Handle sync messages being received both via MCS and APNS.
+ */
+@interface FIRMessagingSyncMessageManager : NSObject
+
+/**
+ *  Initialize sync message manager.
+ *
+ *  @param rmqManager The RMQ manager on the client.
+ *
+ *  @return Sync message manager.
+ */
+- (instancetype)initWithRmqManager:(FIRMessagingRmqManager *)rmqManager;
+
+/**
+ *  Remove expired sync message from persistent store. Also removes messages that have
+ *  been received both via APNS and MCS.
+ */
+- (void)removeExpiredSyncMessages;
+
+/**
+ *  App did recive a sync message via APNS.
+ *
+ *  @param message The sync message received.
+ *
+ *  @return YES if the message is a duplicate of an already received sync message else NO.
+ */
+- (BOOL)didReceiveAPNSSyncMessage:(NSDictionary *)message;
+
+/**
+ *  App did receive a sync message via MCS.
+ *
+ *  @param message The sync message received.
+ *
+ *  @return YES if the message is a duplicate of an already received sync message else NO.
+ */
+- (BOOL)didReceiveMCSSyncMessage:(NSDictionary *)message;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSyncMessageManager.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingSyncMessageManager.m
new file mode 100644 (file)
index 0000000..1257b02
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingSyncMessageManager.h"
+
+#import "FIRMessagingConstants.h"
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingPersistentSyncMessage.h"
+#import "FIRMessagingRmqManager.h"
+#import "FIRMessagingUtilities.h"
+
+static const int64_t kDefaultSyncMessageTTL = 4 * 7 * 24 * 60 * 60;  // 4 weeks
+// 4 MB of free space is required to persist Sync messages
+static const uint64_t kMinFreeDiskSpaceInMB = 1;
+
+@interface FIRMessagingSyncMessageManager()
+
+@property(nonatomic, readwrite, strong) FIRMessagingRmqManager *rmqManager;
+
+@end
+
+@implementation FIRMessagingSyncMessageManager
+
+- (instancetype)init {
+  FIRMessagingInvalidateInitializer();
+}
+
+- (instancetype)initWithRmqManager:(FIRMessagingRmqManager *)rmqManager {
+  _FIRMessagingDevAssert(rmqManager, @"Invalid nil rmq manager while initalizing sync message manager");
+  self = [super init];
+  if (self) {
+    _rmqManager = rmqManager;
+  }
+  return self;
+}
+
+- (void)removeExpiredSyncMessages {
+  NSError *error;
+  int deleteCount = [self.rmqManager deleteExpiredOrFinishedSyncMessages:&error];
+  if (error) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeSyncMessageManager000,
+                            @"Error while deleting expired sync messages %@", error);
+  } else if (deleteCount > 0) {
+    FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSyncMessageManager001,
+                            @"Successfully deleted %d sync messages from store", deleteCount);
+  }
+}
+
+- (BOOL)didReceiveAPNSSyncMessage:(NSDictionary *)message {
+  return [self didReceiveSyncMessage:message viaAPNS:YES viaMCS:NO];
+}
+
+- (BOOL)didReceiveMCSSyncMessage:(NSDictionary *)message {
+  return [self didReceiveSyncMessage:message viaAPNS:NO viaMCS:YES];
+}
+
+- (BOOL)didReceiveSyncMessage:(NSDictionary *)message
+                      viaAPNS:(BOOL)viaAPNS
+                       viaMCS:(BOOL)viaMCS {
+  NSString *rmqID = message[kFIRMessagingMessageIDKey];
+  _FIRMessagingDevAssert([rmqID length], @"Invalid nil rmqID for message");
+  if (![rmqID length]) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeSyncMessageManager002,
+                            @"Invalid nil rmqID for sync message.");
+    return NO;
+  }
+
+  FIRMessagingPersistentSyncMessage *persistentMessage =
+      [self.rmqManager querySyncMessageWithRmqID:rmqID];
+
+  NSError *error;
+  if (!persistentMessage) {
+
+    // Do not persist the new message if we don't have enough disk space
+    uint64_t freeDiskSpace = FIRMessagingGetFreeDiskSpaceInMB();
+    if (freeDiskSpace < kMinFreeDiskSpaceInMB) {
+      return NO;
+    }
+
+    int64_t expirationTime = [[self class] expirationTimeForSyncMessage:message];
+    if (![self.rmqManager saveSyncMessageWithRmqID:rmqID
+                                    expirationTime:expirationTime
+                                      apnsReceived:viaAPNS
+                                       mcsReceived:viaMCS
+                                             error:&error]) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeSyncMessageManager003,
+                              @"Failed to save sync message with rmqID %@", rmqID);
+    } else {
+      FIRMessagingLoggerInfo(kFIRMessagingMessageCodeSyncMessageManager004,
+                             @"Added sync message to cache: %@", rmqID);
+    }
+    return NO;
+  }
+
+  if (viaAPNS && !persistentMessage.apnsReceived) {
+    persistentMessage.apnsReceived = YES;
+    if (![self.rmqManager updateSyncMessageViaAPNSWithRmqID:rmqID error:&error]) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeSyncMessageManager005,
+                              @"Failed to update APNS state for sync message %@", rmqID);
+    }
+  } else if (viaMCS && !persistentMessage.mcsReceived) {
+    persistentMessage.mcsReceived = YES;
+    if (![self.rmqManager updateSyncMessageViaMCSWithRmqID:rmqID error:&error]) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeSyncMessageManager006,
+                              @"Failed to update MCS state for sync message %@", rmqID);
+    }
+  }
+
+  // Received message via both ways we can safely delete it.
+  if (persistentMessage.apnsReceived && persistentMessage.mcsReceived) {
+    if (![self.rmqManager deleteSyncMessageWithRmqID:rmqID]) {
+      FIRMessagingLoggerError(kFIRMessagingMessageCodeSyncMessageManager007,
+                              @"Failed to delete sync message %@", rmqID);
+    } else {
+      FIRMessagingLoggerInfo(kFIRMessagingMessageCodeSyncMessageManager008,
+                             @"Successfully deleted sync message from cache %@", rmqID);
+    }
+  }
+
+  // Already received this message either via MCS or APNS.
+  return YES;
+}
+
++ (int64_t)expirationTimeForSyncMessage:(NSDictionary *)message {
+  int64_t ttl = kDefaultSyncMessageTTL;
+  if (message[kFIRMessagingMessageSyncMessageTTLKey]) {
+    ttl = [message[kFIRMessagingMessageSyncMessageTTLKey] longLongValue];
+  }
+  int64_t currentTime = FIRMessagingCurrentTimestampInSeconds();
+  return currentTime + ttl;
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingTopicOperation.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingTopicOperation.h
new file mode 100644 (file)
index 0000000..ea98e6d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#import "FIRMessaging.h"
+#import "FIRMessagingCheckinService.h"
+#import "FIRMessagingTopicsCommon.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ *  An asynchronous NSOperation subclass which performs a single network request for a topic
+ *  subscription operation. Once completed, it calls its provided completion handler.
+ */
+@interface FIRMessagingTopicOperation : NSOperation
+
+@property(nonatomic, readonly, copy) NSString *topic;
+@property(nonatomic, readonly, assign) FIRMessagingTopicAction action;
+@property(nonatomic, readonly, copy) NSString *token;
+@property(nonatomic, readonly, copy, nullable) NSDictionary *options;
+@property(nonatomic, readonly, strong) FIRMessagingCheckinService *checkinService;
+
+- (instancetype)initWithTopic:(NSString *)topic
+                       action:(FIRMessagingTopicAction)action
+                        token:(NSString *)token
+                      options:(nullable NSDictionary *)options
+               checkinService:(FIRMessagingCheckinService *)checkinService
+                   completion:(FIRMessagingTopicOperationCompletion)completion;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingTopicOperation.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingTopicOperation.m
new file mode 100644 (file)
index 0000000..6703178
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingTopicOperation.h"
+
+#import "FIRMessagingCheckinService.h"
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingLogger.h"
+#import "FIRMessagingUtilities.h"
+#import "NSError+FIRMessaging.h"
+
+#define DEBUG_LOG_SUBSCRIPTION_OPERATION_DURATIONS 0
+
+static NSString *const kFIRMessagingSubscribeServerHost =
+    @"https://iid.googleapis.com/iid/register";
+
+NSString *FIRMessagingSubscriptionsServer() {
+  static NSString *serverHost = nil;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    NSDictionary *environment = [[NSProcessInfo processInfo] environment];
+    NSString *customServerHost = environment[@"FCM_SERVER_ENDPOINT"];
+    if (customServerHost.length) {
+      serverHost = customServerHost;
+    } else {
+      serverHost = kFIRMessagingSubscribeServerHost;
+    }
+  });
+  return serverHost;
+}
+
+@interface FIRMessagingTopicOperation () {
+  BOOL _isFinished;
+  BOOL _isExecuting;
+}
+
+@property(nonatomic, readwrite, copy) NSString *topic;
+@property(nonatomic, readwrite, assign) FIRMessagingTopicAction action;
+@property(nonatomic, readwrite, copy) NSString *token;
+@property(nonatomic, readwrite, copy) NSDictionary *options;
+@property(nonatomic, readwrite, strong) FIRMessagingCheckinService *checkinService;
+@property(nonatomic, readwrite, copy) FIRMessagingTopicOperationCompletion completion;
+
+@property(atomic, strong) NSURLSessionDataTask *dataTask;
+
+@end
+
+@implementation FIRMessagingTopicOperation
+
++ (NSURLSession *)sharedSession {
+  static NSURLSession *subscriptionOperationSharedSession;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
+    config.timeoutIntervalForResource = 60.0f;  // 1 minute
+    subscriptionOperationSharedSession = [NSURLSession sessionWithConfiguration:config];
+    subscriptionOperationSharedSession.sessionDescription = @"com.google.fcm.topics.session";
+  });
+  return subscriptionOperationSharedSession;
+}
+
+- (instancetype)initWithTopic:(NSString *)topic
+                       action:(FIRMessagingTopicAction)action
+                        token:(NSString *)token
+                      options:(NSDictionary *)options
+               checkinService:(FIRMessagingCheckinService *)checkinService
+                   completion:(FIRMessagingTopicOperationCompletion)completion {
+  if (self = [super init]) {
+    _topic = topic;
+    _action = action;
+    _token = token;
+    _options = options;
+    _checkinService = checkinService;
+    _completion = completion;
+
+    _isExecuting = NO;
+    _isFinished = NO;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  _topic = nil;
+  _token = nil;
+  _checkinService = nil;
+  _completion = nil;
+}
+
+- (BOOL)isAsynchronous {
+  return YES;
+}
+
+- (BOOL)isExecuting {
+  return _isExecuting;
+}
+
+- (void)setExecuting:(BOOL)executing {
+  [self willChangeValueForKey:@"isExecuting"];
+  _isExecuting = executing;
+  [self didChangeValueForKey:@"isExecuting"];
+}
+
+- (BOOL)isFinished {
+  return _isFinished;
+}
+
+- (void)setFinished:(BOOL)finished {
+  [self willChangeValueForKey:@"isFinished"];
+  _isFinished = finished;
+  [self didChangeValueForKey:@"isFinished"];
+}
+
+- (void)start {
+  if (self.isCancelled) {
+    NSError *error =
+        [NSError errorWithFCMErrorCode:kFIRMessagingErrorCodePubSubOperationIsCancelled];
+    [self finishWithError:error];
+    return;
+  }
+
+  [self setExecuting:YES];
+
+  [self performSubscriptionChange];
+}
+
+- (void)finishWithError:(NSError *)error {
+  // Add a check to prevent this finish from being called more than once.
+  if (self.isFinished) {
+    return;
+  }
+  self.dataTask = nil;
+  if (self.completion) {
+    self.completion(error);
+  }
+
+  [self setExecuting:NO];
+  [self setFinished:YES];
+}
+
+- (void)cancel {
+  [super cancel];
+  [self.dataTask cancel];
+  NSError *error = [NSError errorWithFCMErrorCode:kFIRMessagingErrorCodePubSubOperationIsCancelled];
+  [self finishWithError:error];
+}
+
+- (void)performSubscriptionChange {
+
+  NSURL *url = [NSURL URLWithString:FIRMessagingSubscriptionsServer()];
+  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
+  NSString *appIdentifier = FIRMessagingAppIdentifier();
+  NSString *deviceAuthID = self.checkinService.deviceAuthID;
+  NSString *secretToken = self.checkinService.secretToken;
+  NSString *authString = [NSString stringWithFormat:@"AidLogin %@:%@", deviceAuthID, secretToken];
+  [request setValue:authString forHTTPHeaderField:@"Authorization"];
+  [request setValue:appIdentifier forHTTPHeaderField:@"app"];
+  [request setValue:self.checkinService.versionInfo forHTTPHeaderField:@"info"];
+
+  // Topic can contain special characters (like `%`) so encode the value.
+  NSCharacterSet *characterSet = [NSCharacterSet URLQueryAllowedCharacterSet];
+  NSString *encodedTopic =
+      [self.topic stringByAddingPercentEncodingWithAllowedCharacters:characterSet];
+  if (encodedTopic == nil) {
+    // The transformation was somehow not possible, so use the original topic.
+    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicOptionTopicEncodingFailed,
+                           @"Unable to encode the topic '%@' during topic subscription change. "
+                           @"Please ensure that the topic name contains only valid characters.",
+                           self.topic);
+    encodedTopic = self.topic;
+  }
+
+  NSMutableString *content = [NSMutableString stringWithFormat:
+                              @"sender=%@&app=%@&device=%@&"
+                              @"app_ver=%@&X-gcm.topic=%@&X-scope=%@",
+                              self.token,
+                              appIdentifier,
+                              deviceAuthID,
+                              FIRMessagingCurrentAppVersion(),
+                              encodedTopic,
+                              encodedTopic];
+
+  if (self.action == FIRMessagingTopicActionUnsubscribe) {
+    [content appendString:@"&delete=true"];
+  }
+
+  FIRMessagingLoggerInfo(kFIRMessagingMessageCodeTopicOption000, @"Topic subscription request: %@",
+                         content);
+
+  request.HTTPBody = [content dataUsingEncoding:NSUTF8StringEncoding];
+  [request setHTTPMethod:@"POST"];
+
+#if DEBUG_LOG_SUBSCRIPTION_OPERATION_DURATIONS
+  NSDate *start = [NSDate date];
+#endif
+
+  FIRMessaging_WEAKIFY(self)
+  void(^requestHandler)(NSData *, NSURLResponse *, NSError *) =
+      ^(NSData *data, NSURLResponse *URLResponse, NSError *error) {
+        FIRMessaging_STRONGIFY(self)
+    if (error) {
+      // Our operation could have been cancelled, which would result in our data task's error being
+      // NSURLErrorCancelled
+      if (error.code == NSURLErrorCancelled) {
+        // We would only have been cancelled in the -cancel method, which will call finish for us
+        // so just return and do nothing.
+        return;
+      }
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTopicOption001,
+                              @"Device registration HTTP fetch error. Error Code: %ld",
+                              _FIRMessaging_L(error.code));
+      [self finishWithError:error];
+      return;
+    }
+    NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+    if (response.length == 0) {
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTopicOperationEmptyResponse,
+                              @"Invalid registration response - zero length.");
+      [self finishWithError:[NSError errorWithFCMErrorCode:kFIRMessagingErrorCodeUnknown]];
+      return;
+    }
+    NSArray *parts = [response componentsSeparatedByString:@"="];
+    _FIRMessagingDevAssert(parts.count, @"Invalid registration response");
+    if (![parts[0] isEqualToString:@"token"] || parts.count <= 1) {
+      FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTopicOption002,
+                              @"Invalid registration response %@", response);
+      [self finishWithError:[NSError errorWithFCMErrorCode:kFIRMessagingErrorCodeUnknown]];
+      return;
+    }
+#if DEBUG_LOG_SUBSCRIPTION_OPERATION_DURATIONS
+    NSTimeInterval duration = -[start timeIntervalSinceNow];
+    FIRMessagingLoggerDebug(@"%@ change took %.2fs", self.topic, duration);
+#endif
+    [self finishWithError:nil];
+
+  };
+
+  NSURLSession *urlSession = [FIRMessagingTopicOperation sharedSession];
+
+  self.dataTask = [urlSession dataTaskWithRequest:request completionHandler:requestHandler];
+  NSString *description;
+  if (_action == FIRMessagingTopicActionSubscribe) {
+    description = [NSString stringWithFormat:@"com.google.fcm.topics.subscribe: %@", _topic];
+  } else {
+    description = [NSString stringWithFormat:@"com.google.fcm.topics.unsubscribe: %@", _topic];
+  }
+  self.dataTask.taskDescription = description;
+  [self.dataTask resume];
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingTopicsCommon.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingTopicsCommon.h
new file mode 100644 (file)
index 0000000..030b3ff
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ *  Represents the action taken on a subscription topic.
+ */
+typedef NS_ENUM(NSInteger, FIRMessagingTopicAction) {
+  FIRMessagingTopicActionSubscribe,
+  FIRMessagingTopicActionUnsubscribe
+};
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingUtilities.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingUtilities.h
new file mode 100644 (file)
index 0000000..206ff07
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <UIKit/UIKit.h>
+
+typedef NS_ENUM(int8_t, FIRMessagingProtoTag) {
+  kFIRMessagingProtoTagInvalid = -1,
+  kFIRMessagingProtoTagHeartbeatPing = 0,
+  kFIRMessagingProtoTagHeartbeatAck = 1,
+  kFIRMessagingProtoTagLoginRequest = 2,
+  kFIRMessagingProtoTagLoginResponse = 3,
+  kFIRMessagingProtoTagClose = 4,
+  kFIRMessagingProtoTagIqStanza = 7,
+  kFIRMessagingProtoTagDataMessageStanza = 8,
+};
+
+@class GPBMessage;
+
+#pragma mark - Protocol Buffers
+
+FOUNDATION_EXPORT FIRMessagingProtoTag FIRMessagingGetTagForProto(GPBMessage *protoClass);
+FOUNDATION_EXPORT Class FIRMessagingGetClassForTag(FIRMessagingProtoTag tag);
+
+#pragma mark - MCS
+
+FOUNDATION_EXPORT NSString *FIRMessagingGetRmq2Id(GPBMessage *proto);
+FOUNDATION_EXPORT void FIRMessagingSetRmq2Id(GPBMessage *proto, NSString *pID);
+FOUNDATION_EXPORT int FIRMessagingGetLastStreamId(GPBMessage *proto);
+FOUNDATION_EXPORT void FIRMessagingSetLastStreamId(GPBMessage *proto, int sid);
+
+#pragma mark - Time
+
+FOUNDATION_EXPORT int64_t FIRMessagingCurrentTimestampInSeconds(void);
+FOUNDATION_EXPORT int64_t FIRMessagingCurrentTimestampInMilliseconds(void);
+
+#pragma mark - App Info
+
+FOUNDATION_EXPORT NSString *FIRMessagingCurrentAppVersion(void);
+FOUNDATION_EXPORT NSString *FIRMessagingAppIdentifier(void);
+
+#pragma mark - Others
+
+FOUNDATION_EXPORT uint64_t FIRMessagingGetFreeDiskSpaceInMB(void);
+FOUNDATION_EXPORT UIApplication *FIRMessagingUIApplication(void);
+
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingUtilities.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingUtilities.m
new file mode 100644 (file)
index 0000000..fa3a233
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingUtilities.h"
+
+#import "Protos/GtalkCore.pbobjc.h"
+
+#import "FIRMessagingLogger.h"
+
+#import <GoogleUtilities/GULAppEnvironmentUtil.h>
+
+// Convert the macro to a string
+#define STR_EXPAND(x) #x
+#define STR(x) STR_EXPAND(x)
+
+static const uint64_t kBytesToMegabytesDivisor = 1024 * 1024LL;
+
+#pragma mark - Protocol Buffers
+
+FIRMessagingProtoTag FIRMessagingGetTagForProto(GPBMessage *proto) {
+  if ([proto isKindOfClass:[GtalkHeartbeatPing class]]) {
+    return kFIRMessagingProtoTagHeartbeatPing;
+  } else if ([proto isKindOfClass:[GtalkHeartbeatAck class]]) {
+    return kFIRMessagingProtoTagHeartbeatAck;
+  } else if ([proto isKindOfClass:[GtalkLoginRequest class]]) {
+    return kFIRMessagingProtoTagLoginRequest;
+  } else if ([proto isKindOfClass:[GtalkLoginResponse class]]) {
+    return kFIRMessagingProtoTagLoginResponse;
+  } else if ([proto isKindOfClass:[GtalkClose class]]) {
+    return kFIRMessagingProtoTagClose;
+  } else if ([proto isKindOfClass:[GtalkIqStanza class]]) {
+    return kFIRMessagingProtoTagIqStanza;
+  } else if ([proto isKindOfClass:[GtalkDataMessageStanza class]]) {
+    return kFIRMessagingProtoTagDataMessageStanza;
+  }
+  return kFIRMessagingProtoTagInvalid;
+}
+
+Class FIRMessagingGetClassForTag(FIRMessagingProtoTag tag) {
+  switch (tag) {
+    case kFIRMessagingProtoTagHeartbeatPing:
+      return GtalkHeartbeatPing.class;
+    case kFIRMessagingProtoTagHeartbeatAck:
+      return GtalkHeartbeatAck.class;
+    case kFIRMessagingProtoTagLoginRequest:
+      return GtalkLoginRequest.class;
+    case kFIRMessagingProtoTagLoginResponse:
+      return GtalkLoginResponse.class;
+    case kFIRMessagingProtoTagClose:
+      return GtalkClose.class;
+    case kFIRMessagingProtoTagIqStanza:
+      return GtalkIqStanza.class;
+    case kFIRMessagingProtoTagDataMessageStanza:
+      return GtalkDataMessageStanza.class;
+    case kFIRMessagingProtoTagInvalid:
+      return NSNull.class;
+  }
+  return NSNull.class;
+}
+
+#pragma mark - MCS
+
+NSString *FIRMessagingGetRmq2Id(GPBMessage *proto) {
+  if ([proto isKindOfClass:[GtalkIqStanza class]]) {
+    if (((GtalkIqStanza *)proto).hasPersistentId) {
+      return ((GtalkIqStanza *)proto).persistentId;
+    }
+  } else if ([proto isKindOfClass:[GtalkDataMessageStanza class]]) {
+    if (((GtalkDataMessageStanza *)proto).hasPersistentId) {
+      return ((GtalkDataMessageStanza *)proto).persistentId;
+    }
+  }
+  return nil;
+}
+
+void FIRMessagingSetRmq2Id(GPBMessage *proto, NSString *pID) {
+  if ([proto isKindOfClass:[GtalkIqStanza class]]) {
+    ((GtalkIqStanza *)proto).persistentId = pID;
+  } else if ([proto isKindOfClass:[GtalkDataMessageStanza class]]) {
+    ((GtalkDataMessageStanza *)proto).persistentId = pID;
+  }
+}
+
+int FIRMessagingGetLastStreamId(GPBMessage *proto) {
+  if ([proto isKindOfClass:[GtalkIqStanza class]]) {
+    if (((GtalkIqStanza *)proto).hasLastStreamIdReceived) {
+      return ((GtalkIqStanza *)proto).lastStreamIdReceived;
+    }
+  } else if ([proto isKindOfClass:[GtalkDataMessageStanza class]]) {
+    if (((GtalkDataMessageStanza *)proto).hasLastStreamIdReceived) {
+      return ((GtalkDataMessageStanza *)proto).lastStreamIdReceived;
+    }
+  } else if ([proto isKindOfClass:[GtalkHeartbeatPing class]]) {
+    if (((GtalkHeartbeatPing *)proto).hasLastStreamIdReceived) {
+      return ((GtalkHeartbeatPing *)proto).lastStreamIdReceived;
+    }
+  } else if ([proto isKindOfClass:[GtalkHeartbeatAck class]]) {
+    if (((GtalkHeartbeatAck *)proto).hasLastStreamIdReceived) {
+      return ((GtalkHeartbeatAck *)proto).lastStreamIdReceived;
+    }
+  }
+  return -1;
+}
+
+void FIRMessagingSetLastStreamId(GPBMessage *proto, int sid) {
+  if ([proto isKindOfClass:[GtalkIqStanza class]]) {
+    ((GtalkIqStanza *)proto).lastStreamIdReceived = sid;
+  } else if ([proto isKindOfClass:[GtalkDataMessageStanza class]]) {
+    ((GtalkDataMessageStanza *)proto).lastStreamIdReceived = sid;
+  } else if ([proto isKindOfClass:[GtalkHeartbeatPing class]]) {
+    ((GtalkHeartbeatPing *)proto).lastStreamIdReceived = sid;
+  } else if ([proto isKindOfClass:[GtalkHeartbeatAck class]]) {
+    ((GtalkHeartbeatAck *)proto).lastStreamIdReceived = sid;
+  }
+}
+
+#pragma mark - Time
+
+int64_t FIRMessagingCurrentTimestampInSeconds(void) {
+  return (int64_t)[[NSDate date] timeIntervalSince1970];
+}
+
+int64_t FIRMessagingCurrentTimestampInMilliseconds(void) {
+  return (int64_t)(FIRMessagingCurrentTimestampInSeconds() * 1000.0);
+}
+
+#pragma mark - App Info
+
+NSString *FIRMessagingCurrentAppVersion(void) {
+  NSString *version = [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"];
+  if (![version length]) {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeUtilities000,
+                            @"Could not find current app version");
+    return @"";
+  }
+  return version;
+}
+
+NSString *FIRMessagingAppIdentifier(void) {
+  return [[NSBundle mainBundle] bundleIdentifier];
+}
+
+uint64_t FIRMessagingGetFreeDiskSpaceInMB(void) {
+  NSError *error;
+  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+
+  NSDictionary *attributesMap =
+      [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject]
+                                                              error:&error];
+  if (attributesMap) {
+    uint64_t totalSizeInBytes __unused = [attributesMap[NSFileSystemSize] longLongValue];
+    uint64_t freeSizeInBytes = [attributesMap[NSFileSystemFreeSize] longLongValue];
+    FIRMessagingLoggerDebug(
+        kFIRMessagingMessageCodeUtilities001, @"Device has capacity %llu MB with %llu MB free.",
+        totalSizeInBytes / kBytesToMegabytesDivisor, freeSizeInBytes / kBytesToMegabytesDivisor);
+    return ((double)freeSizeInBytes) / kBytesToMegabytesDivisor;
+  } else {
+    FIRMessagingLoggerError(kFIRMessagingMessageCodeUtilities002,
+                            @"Error in retreiving device's free memory %@", error);
+    return 0;
+  }
+}
+
+UIApplication *FIRMessagingUIApplication(void) {
+  static Class applicationClass = nil;
+  // iOS App extensions should not call [UIApplication sharedApplication], even if UIApplication
+  // responds to it.
+  if (![GULAppEnvironmentUtil isAppExtension]) {
+    Class cls = NSClassFromString(@"UIApplication");
+    if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) {
+      applicationClass = cls;
+    }
+  }
+  return [applicationClass sharedApplication];
+}
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingVersionUtilities.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingVersionUtilities.h
new file mode 100644 (file)
index 0000000..cd292af
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+/**
+ *  Parsing utility for FIRMessaging Library versions. FIRMessaging Library follows semantic versioning.
+ *  This provides utilities to parse the library versions to enable features and do
+ *  updates based on appropriate library versions.
+ *
+ *  Some example semantic versions are 1.0.1, 2.1.0, 2.1.1, 2.2.0-alpha1, 2.2.1-beta1
+ */
+
+FOUNDATION_EXPORT NSString *FIRMessagingCurrentLibraryVersion(void);
+/// Returns the current Major version of FIRMessaging library.
+FOUNDATION_EXPORT int FIRMessagingCurrentLibraryVersionMajor(void);
+/// Returns the current Minor version of FIRMessaging library.
+FOUNDATION_EXPORT int FIRMessagingCurrentLibraryVersionMinor(void);
+/// Returns the current Patch version of FIRMessaging library.
+FOUNDATION_EXPORT int FIRMessagingCurrentLibraryVersionPatch(void);
+/// Returns YES if current library version is `beta` else NO.
+FOUNDATION_EXPORT BOOL FIRMessagingCurrentLibraryVersionIsBeta(void);
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingVersionUtilities.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessagingVersionUtilities.m
new file mode 100644 (file)
index 0000000..1a3333c
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessagingVersionUtilities.h"
+
+#import "FIRMessagingDefines.h"
+
+// Convert the macro to a string
+#define STR_EXPAND(x) #x
+#define STR(x) STR_EXPAND(x)
+
+static NSString *const kSemanticVersioningSeparator = @".";
+static NSString *const kBetaVersionPrefix = @"-beta";
+
+static NSString *libraryVersion;
+static int majorVersion;
+static int minorVersion;
+static int patchVersion;
+static int betaVersion;
+
+void FIRMessagingParseCurrentLibraryVersion(void) {
+  static NSArray *allVersions;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    NSMutableString *daylightVersion = [NSMutableString stringWithUTF8String:STR(FIRMessaging_LIB_VERSION)];
+    // Parse versions
+    // major, minor, patch[-beta#]
+    allVersions = [daylightVersion componentsSeparatedByString:kSemanticVersioningSeparator];
+    _FIRMessagingDevAssert(allVersions.count == 3, @"Invalid versioning of FIRMessaging library");
+    if (allVersions.count == 3) {
+      majorVersion = [allVersions[0] intValue];
+      minorVersion = [allVersions[1] intValue];
+
+      // Parse patch and beta versions
+      NSArray *patchAndBetaVersion =
+          [allVersions[2] componentsSeparatedByString:kBetaVersionPrefix];
+      _FIRMessagingDevAssert(patchAndBetaVersion.count <= 2, @"Invalid versioning of FIRMessaging library");
+      if (patchAndBetaVersion.count == 2) {
+        patchVersion = [patchAndBetaVersion[0] intValue];
+        betaVersion = [patchAndBetaVersion[1] intValue];
+      } else if (patchAndBetaVersion.count == 1) {
+        patchVersion = [patchAndBetaVersion[0] intValue];
+      }
+    }
+
+    // Copy library version
+    libraryVersion = [daylightVersion copy];
+  });
+}
+
+NSString *FIRMessagingCurrentLibraryVersion(void) {
+  FIRMessagingParseCurrentLibraryVersion();
+  return libraryVersion;
+}
+
+int FIRMessagingCurrentLibraryVersionMajor(void) {
+  FIRMessagingParseCurrentLibraryVersion();
+  return majorVersion;
+}
+
+int FIRMessagingCurrentLibraryVersionMinor(void) {
+  FIRMessagingParseCurrentLibraryVersion();
+  return minorVersion;
+}
+
+int FIRMessagingCurrentLibraryVersionPatch(void) {
+  FIRMessagingParseCurrentLibraryVersion();
+  return patchVersion;
+}
+
+BOOL FIRMessagingCurrentLibraryVersionIsBeta(void) {
+  FIRMessagingParseCurrentLibraryVersion();
+  return betaVersion > 0;
+}
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessaging_Private.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FIRMessaging_Private.h
new file mode 100644 (file)
index 0000000..143cc9f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessaging.h"
+
+@class FIRMessagingClient;
+@class FIRMessagingPubSub;
+
+typedef NS_ENUM(int8_t, FIRMessagingNetworkStatus) {
+  kFIRMessagingReachabilityNotReachable = 0,
+  kFIRMessagingReachabilityReachableViaWiFi,
+  kFIRMessagingReachabilityReachableViaWWAN,
+};
+
+FOUNDATION_EXPORT NSString *const kFIRMessagingPlistAutoInitEnabled;
+FOUNDATION_EXPORT NSString *const kFIRMessagingUserDefaultsKeyAutoInitEnabled;
+
+@interface FIRMessagingRemoteMessage ()
+
+@property(nonatomic, strong) NSDictionary *appData;
+
+@end
+
+@interface FIRMessaging ()
+
+#pragma mark - Private API
+
+- (NSString *)defaultFcmToken;
+- (FIRMessagingClient *)client;
+- (FIRMessagingPubSub *)pubsub;
+
+// Create a sample message to be sent over the wire using FIRMessaging. Look at
+// FIRMessagingService.h to see what each param signifies.
++ (NSMutableDictionary *)createFIRMessagingMessageWithMessage:(NSDictionary *)message
+                                                  to:(NSString *)to
+                                              withID:(NSString *)msgID
+                                          timeToLive:(int64_t)ttl
+                                               delay:(int)delay;
+
+- (BOOL)isNetworkAvailable;
+- (FIRMessagingNetworkStatus)networkType;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FirebaseMessaging.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/FirebaseMessaging.h
new file mode 100644 (file)
index 0000000..ef081c9
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessaging.h"
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/InternalHeaders/FIRMessagingInternalUtilities.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/InternalHeaders/FIRMessagingInternalUtilities.h
new file mode 100644 (file)
index 0000000..d6a1639
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/// @file FIRMessagingInternalUtilities.h
+///
+/// Internal Class Names and Methods that other libs can query at runtime.
+
+/// FIRMessaging Class that responds to the FIRMessaging SDK version selector.
+/// Verify at runtime if the class exists and implements the
+/// required method.
+static NSString *const kFIRMessagingSDKClassString = @"FIRMessaging";
+
+/// FIRMessaging selector that returns the current FIRMessaging library version.
+static NSString *const kFIRMessagingSDKVersionSelectorString = @"FIRMessagingSDKVersion";
+
+/// FIRMessaging selector that returns the current device locale.
+static NSString *const kFIRMessagingSDKLocaleSelectorString = @"FIRMessagingSDKCurrentLocale";
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSDictionary+FIRMessaging.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSDictionary+FIRMessaging.h
new file mode 100644 (file)
index 0000000..fe14451
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@interface NSDictionary (FIRMessaging)
+
+/**
+ *  Returns a string representation for the given dictionary. Assumes that all
+ *  keys and values are strings.
+ *
+ *  @return A string representation of all keys and values in the dictionary.
+ *          The returned string is not pretty-printed.
+ */
+- (NSString *)fcm_string;
+
+/**
+ *  Check if the dictionary has any non-string keys or values.
+ *
+ *  @return YES if the dictionary has any non-string keys or values else NO.
+ */
+- (BOOL)fcm_hasNonStringKeysOrValues;
+
+/**
+ *  Trims all (key, value) pair in a dictionary that are not strings.
+ *
+ *  @return A new copied dictionary with all the non-string keys or values
+ *          removed from the original dictionary.
+ */
+- (NSDictionary *)fcm_trimNonStringValues;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSDictionary+FIRMessaging.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSDictionary+FIRMessaging.m
new file mode 100644 (file)
index 0000000..8df22ab
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "NSDictionary+FIRMessaging.h"
+
+@implementation NSDictionary (FIRMessaging)
+
+- (NSString *)fcm_string {
+  NSMutableString *dictAsString = [NSMutableString string];
+  NSString *separator = @"|";
+  for (id key in self) {
+    id value = self[key];
+    if ([key isKindOfClass:[NSString class]] && [value isKindOfClass:[NSString class]]) {
+      [dictAsString appendFormat:@"%@:%@%@", key, value, separator];
+    }
+  }
+  // remove the last separator
+  if ([dictAsString length]) {
+    [dictAsString deleteCharactersInRange:NSMakeRange(dictAsString.length - 1, 1)];
+  }
+  return [dictAsString copy];
+}
+
+- (BOOL)fcm_hasNonStringKeysOrValues {
+  for (id key in self) {
+    id value = self[key];
+    if (![key isKindOfClass:[NSString class]] || ![value isKindOfClass:[NSString class]]) {
+      return YES;
+    }
+  }
+  return NO;
+}
+
+- (NSDictionary *)fcm_trimNonStringValues {
+  NSMutableDictionary *trimDictionary =
+      [NSMutableDictionary dictionaryWithCapacity:self.count];
+  for (id key in self) {
+    id value = self[key];
+    if ([key isKindOfClass:[NSString class]] && [value isKindOfClass:[NSString class]]) {
+      trimDictionary[(NSString *)key] = value;
+    }
+  }
+  return trimDictionary;
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSError+FIRMessaging.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSError+FIRMessaging.h
new file mode 100644 (file)
index 0000000..ae25b5b
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+FOUNDATION_EXPORT NSString *const kFIRMessagingDomain;
+
+typedef NS_ENUM(NSUInteger, FIRMessagingInternalErrorCode) {
+  // Unknown error.
+  kFIRMessagingErrorCodeUnknown = 0,
+
+  // HTTP related errors.
+  kFIRMessagingErrorCodeAuthentication = 1,
+  kFIRMessagingErrorCodeNoAccess = 2,
+  kFIRMessagingErrorCodeTimeout = 3,
+  kFIRMessagingErrorCodeNetwork = 4,
+
+  // Another operation is in progress.
+  kFIRMessagingErrorCodeOperationInProgress = 5,
+
+  // Failed to perform device check in.
+  kFIRMessagingErrorCodeRegistrarFailedToCheckIn = 6,
+
+  kFIRMessagingErrorCodeInvalidRequest = 7,
+
+  // FIRMessaging generic errors
+  kFIRMessagingErrorCodeMissingDeviceID = 501,
+
+  // upstream send errors
+  kFIRMessagingErrorServiceNotAvailable = 1001,
+  kFIRMessagingErrorInvalidParameters = 1002,
+  kFIRMessagingErrorMissingTo = 1003,
+  kFIRMessagingErrorSave = 1004,
+  kFIRMessagingErrorSizeExceeded = 1005,
+  // Future Send Errors
+
+  // MCS errors
+  // Already connected with MCS
+  kFIRMessagingErrorCodeAlreadyConnected = 2001,
+
+  // PubSub errors
+  kFIRMessagingErrorCodePubSubAlreadySubscribed = 3001,
+  kFIRMessagingErrorCodePubSubAlreadyUnsubscribed = 3002,
+  kFIRMessagingErrorCodePubSubInvalidTopic = 3003,
+  kFIRMessagingErrorCodePubSubFIRMessagingNotSetup = 3004,
+  kFIRMessagingErrorCodePubSubOperationIsCancelled = 3005,
+};
+
+@interface NSError (FIRMessaging)
+
+@property(nonatomic, readonly) FIRMessagingInternalErrorCode fcmErrorCode;
+
++ (NSError *)errorWithFCMErrorCode:(FIRMessagingInternalErrorCode)fcmErrorCode;
++ (NSError *)fcm_errorWithCode:(NSInteger)code userInfo:(NSDictionary *)userInfo;
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSError+FIRMessaging.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/NSError+FIRMessaging.m
new file mode 100644 (file)
index 0000000..e4b8736
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "NSError+FIRMessaging.h"
+
+NSString *const kFIRMessagingDomain = @"com.google.fcm";
+
+@implementation NSError (FIRMessaging)
+
+- (FIRMessagingInternalErrorCode)fcmErrorCode {
+  return (FIRMessagingInternalErrorCode)self.code;
+}
+
++ (NSError *)errorWithFCMErrorCode:(FIRMessagingInternalErrorCode)fcmErrorCode {
+  return [NSError errorWithDomain:kFIRMessagingDomain code:fcmErrorCode userInfo:nil];
+}
+
++ (NSError *)fcm_errorWithCode:(NSInteger)code userInfo:(NSDictionary *)userInfo {
+  return [NSError errorWithDomain:kFIRMessagingDomain code:code userInfo:userInfo];
+}
+
+@end
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkCore.pbobjc.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkCore.pbobjc.h
new file mode 100644 (file)
index 0000000..46d2d9c
--- /dev/null
@@ -0,0 +1,1374 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: buzz/mobile/proto/gtalk_core.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers.h>
+#else
+ #import "GPBProtocolBuffers.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+@class GtalkAppData;
+@class GtalkCellTower;
+@class GtalkClientEvent;
+@class GtalkErrorInfo;
+@class GtalkExtension;
+@class GtalkHeartbeatConfig;
+@class GtalkHeartbeatStat;
+@class GtalkPresenceStanza;
+@class GtalkSetting;
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - Enum GtalkLoginRequest_AuthService
+
+typedef GPB_ENUM(GtalkLoginRequest_AuthService) {
+  GtalkLoginRequest_AuthService_Mail = 0,
+  GtalkLoginRequest_AuthService_AndroidCloudToDeviceMessage = 1,
+  GtalkLoginRequest_AuthService_AndroidId = 2,
+};
+
+GPBEnumDescriptor *GtalkLoginRequest_AuthService_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkLoginRequest_AuthService_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkMessageStanza_MessageType
+
+typedef GPB_ENUM(GtalkMessageStanza_MessageType) {
+  GtalkMessageStanza_MessageType_Normal = 0,
+  GtalkMessageStanza_MessageType_Chat = 1,
+  GtalkMessageStanza_MessageType_Groupchat = 2,
+  GtalkMessageStanza_MessageType_Headline = 3,
+  GtalkMessageStanza_MessageType_Error = 4,
+};
+
+GPBEnumDescriptor *GtalkMessageStanza_MessageType_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkMessageStanza_MessageType_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkPresenceStanza_PresenceType
+
+typedef GPB_ENUM(GtalkPresenceStanza_PresenceType) {
+  GtalkPresenceStanza_PresenceType_Unavailable = 0,
+  GtalkPresenceStanza_PresenceType_Subscribe = 1,
+  GtalkPresenceStanza_PresenceType_Subscribed = 2,
+  GtalkPresenceStanza_PresenceType_Unsubscribe = 3,
+  GtalkPresenceStanza_PresenceType_Unsubscribed = 4,
+  GtalkPresenceStanza_PresenceType_Probe = 5,
+  GtalkPresenceStanza_PresenceType_Error = 6,
+};
+
+GPBEnumDescriptor *GtalkPresenceStanza_PresenceType_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkPresenceStanza_PresenceType_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkPresenceStanza_ShowType
+
+typedef GPB_ENUM(GtalkPresenceStanza_ShowType) {
+  GtalkPresenceStanza_ShowType_Away = 0,
+  GtalkPresenceStanza_ShowType_Chat = 1,
+  GtalkPresenceStanza_ShowType_Dnd = 2,
+  GtalkPresenceStanza_ShowType_Xa = 3,
+};
+
+GPBEnumDescriptor *GtalkPresenceStanza_ShowType_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkPresenceStanza_ShowType_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkPresenceStanza_ClientType
+
+typedef GPB_ENUM(GtalkPresenceStanza_ClientType) {
+  GtalkPresenceStanza_ClientType_Mobile = 0,
+  GtalkPresenceStanza_ClientType_Android = 1,
+};
+
+GPBEnumDescriptor *GtalkPresenceStanza_ClientType_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkPresenceStanza_ClientType_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkPresenceStanza_CapabilitiesFlags
+
+typedef GPB_ENUM(GtalkPresenceStanza_CapabilitiesFlags) {
+  GtalkPresenceStanza_CapabilitiesFlags_HasVoiceV1 = 1,
+  GtalkPresenceStanza_CapabilitiesFlags_HasVideoV1 = 2,
+  GtalkPresenceStanza_CapabilitiesFlags_HasCameraV1 = 4,
+  GtalkPresenceStanza_CapabilitiesFlags_HasPmucV1 = 8,
+};
+
+GPBEnumDescriptor *GtalkPresenceStanza_CapabilitiesFlags_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkPresenceStanza_CapabilitiesFlags_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkBatchPresenceStanza_Type
+
+typedef GPB_ENUM(GtalkBatchPresenceStanza_Type) {
+  GtalkBatchPresenceStanza_Type_Get = 0,
+  GtalkBatchPresenceStanza_Type_Set = 1,
+};
+
+GPBEnumDescriptor *GtalkBatchPresenceStanza_Type_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkBatchPresenceStanza_Type_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkIqStanza_IqType
+
+typedef GPB_ENUM(GtalkIqStanza_IqType) {
+  GtalkIqStanza_IqType_Get = 0,
+  GtalkIqStanza_IqType_Set = 1,
+  GtalkIqStanza_IqType_Result = 2,
+  GtalkIqStanza_IqType_Error = 3,
+};
+
+GPBEnumDescriptor *GtalkIqStanza_IqType_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkIqStanza_IqType_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkClientEvent_Type
+
+typedef GPB_ENUM(GtalkClientEvent_Type) {
+  GtalkClientEvent_Type_Unknown = 0,
+  GtalkClientEvent_Type_DiscardedEvents = 1,
+  GtalkClientEvent_Type_FailedConnection = 2,
+  GtalkClientEvent_Type_SuccessfulConnection = 3,
+  GtalkClientEvent_Type_McsReconnectRequest = 4,
+  GtalkClientEvent_Type_FailedSocketCreationMcsReconnect = 5,
+  GtalkClientEvent_Type_McsReconnectLimited = 6,
+};
+
+GPBEnumDescriptor *GtalkClientEvent_Type_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkClientEvent_Type_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkClientEvent_McsReconnectAction
+
+typedef GPB_ENUM(GtalkClientEvent_McsReconnectAction) {
+  GtalkClientEvent_McsReconnectAction_None = 0,
+  GtalkClientEvent_McsReconnectAction_NotConnected = 1,
+  GtalkClientEvent_McsReconnectAction_TooSoon = 2,
+};
+
+GPBEnumDescriptor *GtalkClientEvent_McsReconnectAction_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkClientEvent_McsReconnectAction_IsValidValue(int32_t value);
+
+#pragma mark - GtalkGtalkCoreRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GtalkGtalkCoreRoot : GPBRootObject
+@end
+
+#pragma mark - GtalkHeartbeatPing
+
+typedef GPB_ENUM(GtalkHeartbeatPing_FieldNumber) {
+  GtalkHeartbeatPing_FieldNumber_StreamId = 1,
+  GtalkHeartbeatPing_FieldNumber_LastStreamIdReceived = 2,
+  GtalkHeartbeatPing_FieldNumber_Status = 3,
+  GtalkHeartbeatPing_FieldNumber_CellTower = 4,
+  GtalkHeartbeatPing_FieldNumber_IntervalMs = 5,
+};
+
+@interface GtalkHeartbeatPing : GPBMessage
+
+
+@property(nonatomic, readwrite) int32_t streamId;
+
+@property(nonatomic, readwrite) BOOL hasStreamId;
+
+@property(nonatomic, readwrite) int32_t lastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL hasLastStreamIdReceived;
+
+@property(nonatomic, readwrite) int64_t status;
+
+@property(nonatomic, readwrite) BOOL hasStatus;
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower DEPRECATED_ATTRIBUTE;
+/** Test to see if @c cellTower has been set. */
+@property(nonatomic, readwrite) BOOL hasCellTower DEPRECATED_ATTRIBUTE;
+
+
+@property(nonatomic, readwrite) int32_t intervalMs;
+
+@property(nonatomic, readwrite) BOOL hasIntervalMs;
+@end
+
+#pragma mark - GtalkHeartbeatAck
+
+typedef GPB_ENUM(GtalkHeartbeatAck_FieldNumber) {
+  GtalkHeartbeatAck_FieldNumber_StreamId = 1,
+  GtalkHeartbeatAck_FieldNumber_LastStreamIdReceived = 2,
+  GtalkHeartbeatAck_FieldNumber_Status = 3,
+  GtalkHeartbeatAck_FieldNumber_CellTower = 4,
+  GtalkHeartbeatAck_FieldNumber_IntervalMs = 5,
+};
+
+@interface GtalkHeartbeatAck : GPBMessage
+
+
+@property(nonatomic, readwrite) int32_t streamId;
+
+@property(nonatomic, readwrite) BOOL hasStreamId;
+
+@property(nonatomic, readwrite) int32_t lastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL hasLastStreamIdReceived;
+
+@property(nonatomic, readwrite) int64_t status;
+
+@property(nonatomic, readwrite) BOOL hasStatus;
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower DEPRECATED_ATTRIBUTE;
+/** Test to see if @c cellTower has been set. */
+@property(nonatomic, readwrite) BOOL hasCellTower DEPRECATED_ATTRIBUTE;
+
+
+@property(nonatomic, readwrite) int32_t intervalMs;
+
+@property(nonatomic, readwrite) BOOL hasIntervalMs;
+@end
+
+#pragma mark - GtalkErrorInfo
+
+typedef GPB_ENUM(GtalkErrorInfo_FieldNumber) {
+  GtalkErrorInfo_FieldNumber_Code = 1,
+  GtalkErrorInfo_FieldNumber_Message = 2,
+  GtalkErrorInfo_FieldNumber_Type = 3,
+  GtalkErrorInfo_FieldNumber_Extension = 4,
+};
+
+@interface GtalkErrorInfo : GPBMessage
+
+
+@property(nonatomic, readwrite) int32_t code;
+
+@property(nonatomic, readwrite) BOOL hasCode;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *message;
+/** Test to see if @c message has been set. */
+@property(nonatomic, readwrite) BOOL hasMessage;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *type;
+/** Test to see if @c type has been set. */
+@property(nonatomic, readwrite) BOOL hasType;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkExtension *extension;
+/** Test to see if @c extension has been set. */
+@property(nonatomic, readwrite) BOOL hasExtension;
+
+@end
+
+#pragma mark - GtalkSetting
+
+typedef GPB_ENUM(GtalkSetting_FieldNumber) {
+  GtalkSetting_FieldNumber_Name = 1,
+  GtalkSetting_FieldNumber_Value = 2,
+};
+
+@interface GtalkSetting : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
+/** Test to see if @c name has been set. */
+@property(nonatomic, readwrite) BOOL hasName;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *value;
+/** Test to see if @c value has been set. */
+@property(nonatomic, readwrite) BOOL hasValue;
+
+@end
+
+#pragma mark - GtalkHeartbeatStat
+
+typedef GPB_ENUM(GtalkHeartbeatStat_FieldNumber) {
+  GtalkHeartbeatStat_FieldNumber_Ip = 1,
+  GtalkHeartbeatStat_FieldNumber_Timeout = 2,
+  GtalkHeartbeatStat_FieldNumber_IntervalMs = 3,
+};
+
+@interface GtalkHeartbeatStat : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *ip;
+/** Test to see if @c ip has been set. */
+@property(nonatomic, readwrite) BOOL hasIp;
+
+
+@property(nonatomic, readwrite) BOOL timeout;
+
+@property(nonatomic, readwrite) BOOL hasTimeout;
+
+@property(nonatomic, readwrite) int32_t intervalMs;
+
+@property(nonatomic, readwrite) BOOL hasIntervalMs;
+@end
+
+#pragma mark - GtalkHeartbeatConfig
+
+typedef GPB_ENUM(GtalkHeartbeatConfig_FieldNumber) {
+  GtalkHeartbeatConfig_FieldNumber_UploadStat = 1,
+  GtalkHeartbeatConfig_FieldNumber_Ip = 2,
+  GtalkHeartbeatConfig_FieldNumber_IntervalMs = 3,
+};
+
+@interface GtalkHeartbeatConfig : GPBMessage
+
+
+@property(nonatomic, readwrite) BOOL uploadStat;
+
+@property(nonatomic, readwrite) BOOL hasUploadStat;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *ip;
+/** Test to see if @c ip has been set. */
+@property(nonatomic, readwrite) BOOL hasIp;
+
+
+@property(nonatomic, readwrite) int32_t intervalMs;
+
+@property(nonatomic, readwrite) BOOL hasIntervalMs;
+@end
+
+#pragma mark - GtalkLoginRequest
+
+typedef GPB_ENUM(GtalkLoginRequest_FieldNumber) {
+  GtalkLoginRequest_FieldNumber_Id_p = 1,
+  GtalkLoginRequest_FieldNumber_Domain = 2,
+  GtalkLoginRequest_FieldNumber_User = 3,
+  GtalkLoginRequest_FieldNumber_Resource = 4,
+  GtalkLoginRequest_FieldNumber_AuthToken = 5,
+  GtalkLoginRequest_FieldNumber_DeviceId = 6,
+  GtalkLoginRequest_FieldNumber_LastRmqId = 7,
+  GtalkLoginRequest_FieldNumber_SettingArray = 8,
+  GtalkLoginRequest_FieldNumber_ReceivedPersistentIdArray = 10,
+  GtalkLoginRequest_FieldNumber_IncludeStreamIds = 11,
+  GtalkLoginRequest_FieldNumber_HeartbeatStat = 13,
+  GtalkLoginRequest_FieldNumber_UseRmq2 = 14,
+  GtalkLoginRequest_FieldNumber_AccountId = 15,
+  GtalkLoginRequest_FieldNumber_AuthService = 16,
+  GtalkLoginRequest_FieldNumber_NetworkType = 17,
+  GtalkLoginRequest_FieldNumber_Status = 18,
+  GtalkLoginRequest_FieldNumber_TokenVersionInfo = 19,
+  GtalkLoginRequest_FieldNumber_CellTower = 20,
+  GtalkLoginRequest_FieldNumber_GcmStartTimeMs = 21,
+  GtalkLoginRequest_FieldNumber_ClientEventArray = 22,
+  GtalkLoginRequest_FieldNumber_OnFallback = 23,
+  GtalkLoginRequest_FieldNumber_NoPendingUpstream = 24,
+  GtalkLoginRequest_FieldNumber_ReconnectRequestId = 25,
+};
+
+@interface GtalkLoginRequest : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p;
+/** Test to see if @c id_p has been set. */
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *domain;
+/** Test to see if @c domain has been set. */
+@property(nonatomic, readwrite) BOOL hasDomain;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *user;
+/** Test to see if @c user has been set. */
+@property(nonatomic, readwrite) BOOL hasUser;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *resource;
+/** Test to see if @c resource has been set. */
+@property(nonatomic, readwrite) BOOL hasResource;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *authToken;
+/** Test to see if @c authToken has been set. */
+@property(nonatomic, readwrite) BOOL hasAuthToken;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *deviceId;
+/** Test to see if @c deviceId has been set. */
+@property(nonatomic, readwrite) BOOL hasDeviceId;
+
+
+@property(nonatomic, readwrite) int64_t lastRmqId;
+
+@property(nonatomic, readwrite) BOOL hasLastRmqId;
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GtalkSetting*> *settingArray;
+/** The number of items in @c settingArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger settingArray_Count;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *receivedPersistentIdArray;
+/** The number of items in @c receivedPersistentIdArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger receivedPersistentIdArray_Count;
+
+
+@property(nonatomic, readwrite) BOOL includeStreamIds;
+
+@property(nonatomic, readwrite) BOOL hasIncludeStreamIds;
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkHeartbeatStat *heartbeatStat;
+/** Test to see if @c heartbeatStat has been set. */
+@property(nonatomic, readwrite) BOOL hasHeartbeatStat;
+
+
+@property(nonatomic, readwrite) BOOL useRmq2;
+
+@property(nonatomic, readwrite) BOOL hasUseRmq2;
+
+@property(nonatomic, readwrite) int64_t accountId;
+
+@property(nonatomic, readwrite) BOOL hasAccountId;
+
+@property(nonatomic, readwrite) GtalkLoginRequest_AuthService authService;
+
+@property(nonatomic, readwrite) BOOL hasAuthService;
+
+@property(nonatomic, readwrite) int32_t networkType;
+
+@property(nonatomic, readwrite) BOOL hasNetworkType;
+
+@property(nonatomic, readwrite) int64_t status;
+
+@property(nonatomic, readwrite) BOOL hasStatus;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *tokenVersionInfo;
+/** Test to see if @c tokenVersionInfo has been set. */
+@property(nonatomic, readwrite) BOOL hasTokenVersionInfo;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower DEPRECATED_ATTRIBUTE;
+/** Test to see if @c cellTower has been set. */
+@property(nonatomic, readwrite) BOOL hasCellTower DEPRECATED_ATTRIBUTE;
+
+
+@property(nonatomic, readwrite) uint64_t gcmStartTimeMs;
+
+@property(nonatomic, readwrite) BOOL hasGcmStartTimeMs;
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GtalkClientEvent*> *clientEventArray;
+/** The number of items in @c clientEventArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger clientEventArray_Count;
+
+
+@property(nonatomic, readwrite) BOOL onFallback;
+
+@property(nonatomic, readwrite) BOOL hasOnFallback;
+
+@property(nonatomic, readwrite) BOOL noPendingUpstream;
+
+@property(nonatomic, readwrite) BOOL hasNoPendingUpstream;
+
+@property(nonatomic, readwrite) int32_t reconnectRequestId;
+
+@property(nonatomic, readwrite) BOOL hasReconnectRequestId;
+@end
+
+#pragma mark - GtalkLoginResponse
+
+typedef GPB_ENUM(GtalkLoginResponse_FieldNumber) {
+  GtalkLoginResponse_FieldNumber_Id_p = 1,
+  GtalkLoginResponse_FieldNumber_Jid = 2,
+  GtalkLoginResponse_FieldNumber_Error = 3,
+  GtalkLoginResponse_FieldNumber_SettingArray = 4,
+  GtalkLoginResponse_FieldNumber_StreamId = 5,
+  GtalkLoginResponse_FieldNumber_LastStreamIdReceived = 6,
+  GtalkLoginResponse_FieldNumber_HeartbeatConfig = 7,
+  GtalkLoginResponse_FieldNumber_ServerTimestamp = 8,
+};
+
+@interface GtalkLoginResponse : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p;
+/** Test to see if @c id_p has been set. */
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *jid;
+/** Test to see if @c jid has been set. */
+@property(nonatomic, readwrite) BOOL hasJid;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkErrorInfo *error;
+/** Test to see if @c error has been set. */
+@property(nonatomic, readwrite) BOOL hasError;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GtalkSetting*> *settingArray;
+/** The number of items in @c settingArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger settingArray_Count;
+
+
+@property(nonatomic, readwrite) int32_t streamId;
+
+@property(nonatomic, readwrite) BOOL hasStreamId;
+
+@property(nonatomic, readwrite) int32_t lastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL hasLastStreamIdReceived;
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkHeartbeatConfig *heartbeatConfig;
+/** Test to see if @c heartbeatConfig has been set. */
+@property(nonatomic, readwrite) BOOL hasHeartbeatConfig;
+
+
+@property(nonatomic, readwrite) int64_t serverTimestamp;
+
+@property(nonatomic, readwrite) BOOL hasServerTimestamp;
+@end
+
+#pragma mark - GtalkBindAccountRequest
+
+typedef GPB_ENUM(GtalkBindAccountRequest_FieldNumber) {
+  GtalkBindAccountRequest_FieldNumber_Id_p = 1,
+  GtalkBindAccountRequest_FieldNumber_Domain = 2,
+  GtalkBindAccountRequest_FieldNumber_User = 3,
+  GtalkBindAccountRequest_FieldNumber_Resource = 4,
+  GtalkBindAccountRequest_FieldNumber_AuthToken = 5,
+  GtalkBindAccountRequest_FieldNumber_PersistentId = 6,
+  GtalkBindAccountRequest_FieldNumber_StreamId = 7,
+  GtalkBindAccountRequest_FieldNumber_LastStreamIdReceived = 8,
+  GtalkBindAccountRequest_FieldNumber_AccountId = 9,
+};
+
+@interface GtalkBindAccountRequest : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p;
+/** Test to see if @c id_p has been set. */
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *domain;
+/** Test to see if @c domain has been set. */
+@property(nonatomic, readwrite) BOOL hasDomain;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *user;
+/** Test to see if @c user has been set. */
+@property(nonatomic, readwrite) BOOL hasUser;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *resource;
+/** Test to see if @c resource has been set. */
+@property(nonatomic, readwrite) BOOL hasResource;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *authToken;
+/** Test to see if @c authToken has been set. */
+@property(nonatomic, readwrite) BOOL hasAuthToken;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *persistentId;
+/** Test to see if @c persistentId has been set. */
+@property(nonatomic, readwrite) BOOL hasPersistentId;
+
+
+@property(nonatomic, readwrite) int32_t streamId;
+
+@property(nonatomic, readwrite) BOOL hasStreamId;
+
+@property(nonatomic, readwrite) int32_t lastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL hasLastStreamIdReceived;
+
+@property(nonatomic, readwrite) int64_t accountId;
+
+@property(nonatomic, readwrite) BOOL hasAccountId;
+@end
+
+#pragma mark - GtalkBindAccountResponse
+
+typedef GPB_ENUM(GtalkBindAccountResponse_FieldNumber) {
+  GtalkBindAccountResponse_FieldNumber_Id_p = 1,
+  GtalkBindAccountResponse_FieldNumber_Jid = 2,
+  GtalkBindAccountResponse_FieldNumber_Error = 3,
+  GtalkBindAccountResponse_FieldNumber_StreamId = 4,
+  GtalkBindAccountResponse_FieldNumber_LastStreamIdReceived = 5,
+};
+
+@interface GtalkBindAccountResponse : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p;
+/** Test to see if @c id_p has been set. */
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *jid;
+/** Test to see if @c jid has been set. */
+@property(nonatomic, readwrite) BOOL hasJid;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkErrorInfo *error;
+/** Test to see if @c error has been set. */
+@property(nonatomic, readwrite) BOOL hasError;
+
+
+@property(nonatomic, readwrite) int32_t streamId;
+
+@property(nonatomic, readwrite) BOOL hasStreamId;
+
+@property(nonatomic, readwrite) int32_t lastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL hasLastStreamIdReceived;
+@end
+
+#pragma mark - GtalkStreamErrorStanza
+
+typedef GPB_ENUM(GtalkStreamErrorStanza_FieldNumber) {
+  GtalkStreamErrorStanza_FieldNumber_Type = 1,
+  GtalkStreamErrorStanza_FieldNumber_Text = 2,
+};
+
+@interface GtalkStreamErrorStanza : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *type;
+/** Test to see if @c type has been set. */
+@property(nonatomic, readwrite) BOOL hasType;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *text;
+/** Test to see if @c text has been set. */
+@property(nonatomic, readwrite) BOOL hasText;
+
+@end
+
+#pragma mark - GtalkClose
+
+@interface GtalkClose : GPBMessage
+
+@end
+
+#pragma mark - GtalkExtension
+
+typedef GPB_ENUM(GtalkExtension_FieldNumber) {
+  GtalkExtension_FieldNumber_Id_p = 1,
+  GtalkExtension_FieldNumber_Data_p = 2,
+};
+
+@interface GtalkExtension : GPBMessage
+
+
+@property(nonatomic, readwrite) int32_t id_p;
+
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *data_p;
+/** Test to see if @c data_p has been set. */
+@property(nonatomic, readwrite) BOOL hasData_p;
+
+@end
+
+#pragma mark - GtalkMessageStanza
+
+typedef GPB_ENUM(GtalkMessageStanza_FieldNumber) {
+  GtalkMessageStanza_FieldNumber_RmqId = 1,
+  GtalkMessageStanza_FieldNumber_Type = 2,
+  GtalkMessageStanza_FieldNumber_Id_p = 3,
+  GtalkMessageStanza_FieldNumber_From = 4,
+  GtalkMessageStanza_FieldNumber_To = 5,
+  GtalkMessageStanza_FieldNumber_Subject = 6,
+  GtalkMessageStanza_FieldNumber_Body = 7,
+  GtalkMessageStanza_FieldNumber_Thread = 8,
+  GtalkMessageStanza_FieldNumber_Error = 9,
+  GtalkMessageStanza_FieldNumber_ExtensionArray = 10,
+  GtalkMessageStanza_FieldNumber_Nosave = 11,
+  GtalkMessageStanza_FieldNumber_Timestamp = 12,
+  GtalkMessageStanza_FieldNumber_PersistentId = 13,
+  GtalkMessageStanza_FieldNumber_StreamId = 14,
+  GtalkMessageStanza_FieldNumber_LastStreamIdReceived = 15,
+  GtalkMessageStanza_FieldNumber_Read = 16,
+  GtalkMessageStanza_FieldNumber_AccountId = 17,
+};
+
+@interface GtalkMessageStanza : GPBMessage
+
+
+@property(nonatomic, readwrite) int64_t rmqId;
+
+@property(nonatomic, readwrite) BOOL hasRmqId;
+
+@property(nonatomic, readwrite) GtalkMessageStanza_MessageType type;
+
+@property(nonatomic, readwrite) BOOL hasType;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p;
+/** Test to see if @c id_p has been set. */
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *from;
+/** Test to see if @c from has been set. */
+@property(nonatomic, readwrite) BOOL hasFrom;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *to;
+/** Test to see if @c to has been set. */
+@property(nonatomic, readwrite) BOOL hasTo;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *subject;
+/** Test to see if @c subject has been set. */
+@property(nonatomic, readwrite) BOOL hasSubject;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *body;
+/** Test to see if @c body has been set. */
+@property(nonatomic, readwrite) BOOL hasBody;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *thread;
+/** Test to see if @c thread has been set. */
+@property(nonatomic, readwrite) BOOL hasThread;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkErrorInfo *error;
+/** Test to see if @c error has been set. */
+@property(nonatomic, readwrite) BOOL hasError;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GtalkExtension*> *extensionArray;
+/** The number of items in @c extensionArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger extensionArray_Count;
+
+
+@property(nonatomic, readwrite) BOOL nosave;
+
+@property(nonatomic, readwrite) BOOL hasNosave;
+
+@property(nonatomic, readwrite) int64_t timestamp;
+
+@property(nonatomic, readwrite) BOOL hasTimestamp;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *persistentId;
+/** Test to see if @c persistentId has been set. */
+@property(nonatomic, readwrite) BOOL hasPersistentId;
+
+
+@property(nonatomic, readwrite) int32_t streamId;
+
+@property(nonatomic, readwrite) BOOL hasStreamId;
+
+@property(nonatomic, readwrite) int32_t lastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL hasLastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL read;
+
+@property(nonatomic, readwrite) BOOL hasRead;
+
+@property(nonatomic, readwrite) int64_t accountId;
+
+@property(nonatomic, readwrite) BOOL hasAccountId;
+@end
+
+#pragma mark - GtalkPresenceStanza
+
+typedef GPB_ENUM(GtalkPresenceStanza_FieldNumber) {
+  GtalkPresenceStanza_FieldNumber_RmqId = 1,
+  GtalkPresenceStanza_FieldNumber_Type = 2,
+  GtalkPresenceStanza_FieldNumber_Id_p = 3,
+  GtalkPresenceStanza_FieldNumber_From = 4,
+  GtalkPresenceStanza_FieldNumber_To = 5,
+  GtalkPresenceStanza_FieldNumber_Show = 6,
+  GtalkPresenceStanza_FieldNumber_Status = 7,
+  GtalkPresenceStanza_FieldNumber_Priority = 8,
+  GtalkPresenceStanza_FieldNumber_Error = 9,
+  GtalkPresenceStanza_FieldNumber_ExtensionArray = 10,
+  GtalkPresenceStanza_FieldNumber_Client = 11,
+  GtalkPresenceStanza_FieldNumber_AvatarHash = 12,
+  GtalkPresenceStanza_FieldNumber_PersistentId = 13,
+  GtalkPresenceStanza_FieldNumber_StreamId = 14,
+  GtalkPresenceStanza_FieldNumber_LastStreamIdReceived = 15,
+  GtalkPresenceStanza_FieldNumber_CapabilitiesFlags = 16,
+  GtalkPresenceStanza_FieldNumber_AccountId = 17,
+};
+
+@interface GtalkPresenceStanza : GPBMessage
+
+
+@property(nonatomic, readwrite) int64_t rmqId;
+
+@property(nonatomic, readwrite) BOOL hasRmqId;
+
+@property(nonatomic, readwrite) GtalkPresenceStanza_PresenceType type;
+
+@property(nonatomic, readwrite) BOOL hasType;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p;
+/** Test to see if @c id_p has been set. */
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *from;
+/** Test to see if @c from has been set. */
+@property(nonatomic, readwrite) BOOL hasFrom;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *to;
+/** Test to see if @c to has been set. */
+@property(nonatomic, readwrite) BOOL hasTo;
+
+
+@property(nonatomic, readwrite) GtalkPresenceStanza_ShowType show;
+
+@property(nonatomic, readwrite) BOOL hasShow;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *status;
+/** Test to see if @c status has been set. */
+@property(nonatomic, readwrite) BOOL hasStatus;
+
+
+@property(nonatomic, readwrite) int32_t priority;
+
+@property(nonatomic, readwrite) BOOL hasPriority;
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkErrorInfo *error;
+/** Test to see if @c error has been set. */
+@property(nonatomic, readwrite) BOOL hasError;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GtalkExtension*> *extensionArray;
+/** The number of items in @c extensionArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger extensionArray_Count;
+
+
+@property(nonatomic, readwrite) GtalkPresenceStanza_ClientType client;
+
+@property(nonatomic, readwrite) BOOL hasClient;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *avatarHash;
+/** Test to see if @c avatarHash has been set. */
+@property(nonatomic, readwrite) BOOL hasAvatarHash;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *persistentId;
+/** Test to see if @c persistentId has been set. */
+@property(nonatomic, readwrite) BOOL hasPersistentId;
+
+
+@property(nonatomic, readwrite) int32_t streamId;
+
+@property(nonatomic, readwrite) BOOL hasStreamId;
+
+@property(nonatomic, readwrite) int32_t lastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL hasLastStreamIdReceived;
+
+@property(nonatomic, readwrite) int32_t capabilitiesFlags;
+
+@property(nonatomic, readwrite) BOOL hasCapabilitiesFlags;
+
+@property(nonatomic, readwrite) int64_t accountId;
+
+@property(nonatomic, readwrite) BOOL hasAccountId;
+@end
+
+#pragma mark - GtalkBatchPresenceStanza
+
+typedef GPB_ENUM(GtalkBatchPresenceStanza_FieldNumber) {
+  GtalkBatchPresenceStanza_FieldNumber_Id_p = 1,
+  GtalkBatchPresenceStanza_FieldNumber_To = 2,
+  GtalkBatchPresenceStanza_FieldNumber_PresenceArray = 3,
+  GtalkBatchPresenceStanza_FieldNumber_PersistentId = 4,
+  GtalkBatchPresenceStanza_FieldNumber_StreamId = 5,
+  GtalkBatchPresenceStanza_FieldNumber_LastStreamIdReceived = 6,
+  GtalkBatchPresenceStanza_FieldNumber_AccountId = 7,
+  GtalkBatchPresenceStanza_FieldNumber_Type = 8,
+  GtalkBatchPresenceStanza_FieldNumber_Error = 9,
+};
+
+@interface GtalkBatchPresenceStanza : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p;
+/** Test to see if @c id_p has been set. */
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *to;
+/** Test to see if @c to has been set. */
+@property(nonatomic, readwrite) BOOL hasTo;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GtalkPresenceStanza*> *presenceArray;
+/** The number of items in @c presenceArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger presenceArray_Count;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *persistentId;
+/** Test to see if @c persistentId has been set. */
+@property(nonatomic, readwrite) BOOL hasPersistentId;
+
+
+@property(nonatomic, readwrite) int32_t streamId;
+
+@property(nonatomic, readwrite) BOOL hasStreamId;
+
+@property(nonatomic, readwrite) int32_t lastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL hasLastStreamIdReceived;
+
+@property(nonatomic, readwrite) int64_t accountId;
+
+@property(nonatomic, readwrite) BOOL hasAccountId;
+
+@property(nonatomic, readwrite) GtalkBatchPresenceStanza_Type type;
+
+@property(nonatomic, readwrite) BOOL hasType;
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkErrorInfo *error;
+/** Test to see if @c error has been set. */
+@property(nonatomic, readwrite) BOOL hasError;
+
+@end
+
+#pragma mark - GtalkIqStanza
+
+typedef GPB_ENUM(GtalkIqStanza_FieldNumber) {
+  GtalkIqStanza_FieldNumber_RmqId = 1,
+  GtalkIqStanza_FieldNumber_Type = 2,
+  GtalkIqStanza_FieldNumber_Id_p = 3,
+  GtalkIqStanza_FieldNumber_From = 4,
+  GtalkIqStanza_FieldNumber_To = 5,
+  GtalkIqStanza_FieldNumber_Error = 6,
+  GtalkIqStanza_FieldNumber_Extension = 7,
+  GtalkIqStanza_FieldNumber_PersistentId = 8,
+  GtalkIqStanza_FieldNumber_StreamId = 9,
+  GtalkIqStanza_FieldNumber_LastStreamIdReceived = 10,
+  GtalkIqStanza_FieldNumber_AccountId = 11,
+  GtalkIqStanza_FieldNumber_Status = 12,
+};
+
+@interface GtalkIqStanza : GPBMessage
+
+
+@property(nonatomic, readwrite) int64_t rmqId;
+
+@property(nonatomic, readwrite) BOOL hasRmqId;
+
+@property(nonatomic, readwrite) GtalkIqStanza_IqType type;
+
+@property(nonatomic, readwrite) BOOL hasType;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p;
+/** Test to see if @c id_p has been set. */
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *from;
+/** Test to see if @c from has been set. */
+@property(nonatomic, readwrite) BOOL hasFrom;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *to;
+/** Test to see if @c to has been set. */
+@property(nonatomic, readwrite) BOOL hasTo;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkErrorInfo *error;
+/** Test to see if @c error has been set. */
+@property(nonatomic, readwrite) BOOL hasError;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkExtension *extension;
+/** Test to see if @c extension has been set. */
+@property(nonatomic, readwrite) BOOL hasExtension;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *persistentId;
+/** Test to see if @c persistentId has been set. */
+@property(nonatomic, readwrite) BOOL hasPersistentId;
+
+
+@property(nonatomic, readwrite) int32_t streamId;
+
+@property(nonatomic, readwrite) BOOL hasStreamId;
+
+@property(nonatomic, readwrite) int32_t lastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL hasLastStreamIdReceived;
+
+@property(nonatomic, readwrite) int64_t accountId;
+
+@property(nonatomic, readwrite) BOOL hasAccountId;
+
+@property(nonatomic, readwrite) int64_t status;
+
+@property(nonatomic, readwrite) BOOL hasStatus;
+@end
+
+#pragma mark - GtalkAppData
+
+typedef GPB_ENUM(GtalkAppData_FieldNumber) {
+  GtalkAppData_FieldNumber_Key = 1,
+  GtalkAppData_FieldNumber_Value = 2,
+};
+
+@interface GtalkAppData : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *key;
+/** Test to see if @c key has been set. */
+@property(nonatomic, readwrite) BOOL hasKey;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *value;
+/** Test to see if @c value has been set. */
+@property(nonatomic, readwrite) BOOL hasValue;
+
+@end
+
+#pragma mark - GtalkDataMessageStanza
+
+typedef GPB_ENUM(GtalkDataMessageStanza_FieldNumber) {
+  GtalkDataMessageStanza_FieldNumber_RmqId = 1,
+  GtalkDataMessageStanza_FieldNumber_Id_p = 2,
+  GtalkDataMessageStanza_FieldNumber_From = 3,
+  GtalkDataMessageStanza_FieldNumber_To = 4,
+  GtalkDataMessageStanza_FieldNumber_Category = 5,
+  GtalkDataMessageStanza_FieldNumber_Token = 6,
+  GtalkDataMessageStanza_FieldNumber_AppDataArray = 7,
+  GtalkDataMessageStanza_FieldNumber_FromTrustedServer = 8,
+  GtalkDataMessageStanza_FieldNumber_PersistentId = 9,
+  GtalkDataMessageStanza_FieldNumber_StreamId = 10,
+  GtalkDataMessageStanza_FieldNumber_LastStreamIdReceived = 11,
+  GtalkDataMessageStanza_FieldNumber_Permission = 12,
+  GtalkDataMessageStanza_FieldNumber_RegId = 13,
+  GtalkDataMessageStanza_FieldNumber_PkgSignature = 14,
+  GtalkDataMessageStanza_FieldNumber_ClientId = 15,
+  GtalkDataMessageStanza_FieldNumber_DeviceUserId = 16,
+  GtalkDataMessageStanza_FieldNumber_Ttl = 17,
+  GtalkDataMessageStanza_FieldNumber_Sent = 18,
+  GtalkDataMessageStanza_FieldNumber_Queued = 19,
+  GtalkDataMessageStanza_FieldNumber_Status = 20,
+  GtalkDataMessageStanza_FieldNumber_RawData = 21,
+  GtalkDataMessageStanza_FieldNumber_MaxDelay = 22,
+  GtalkDataMessageStanza_FieldNumber_ActualDelay = 23,
+  GtalkDataMessageStanza_FieldNumber_ImmediateAck = 24,
+  GtalkDataMessageStanza_FieldNumber_DeliveryReceiptRequested = 25,
+  GtalkDataMessageStanza_FieldNumber_ExternalMessageId = 26,
+  GtalkDataMessageStanza_FieldNumber_Flags = 27,
+  GtalkDataMessageStanza_FieldNumber_CellTower = 28,
+  GtalkDataMessageStanza_FieldNumber_Priority = 29,
+};
+
+@interface GtalkDataMessageStanza : GPBMessage
+
+
+@property(nonatomic, readwrite) int64_t rmqId;
+
+@property(nonatomic, readwrite) BOOL hasRmqId;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p;
+/** Test to see if @c id_p has been set. */
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *from;
+/** Test to see if @c from has been set. */
+@property(nonatomic, readwrite) BOOL hasFrom;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *to;
+/** Test to see if @c to has been set. */
+@property(nonatomic, readwrite) BOOL hasTo;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *category;
+/** Test to see if @c category has been set. */
+@property(nonatomic, readwrite) BOOL hasCategory;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *token;
+/** Test to see if @c token has been set. */
+@property(nonatomic, readwrite) BOOL hasToken;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GtalkAppData*> *appDataArray;
+/** The number of items in @c appDataArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger appDataArray_Count;
+
+
+@property(nonatomic, readwrite) BOOL fromTrustedServer;
+
+@property(nonatomic, readwrite) BOOL hasFromTrustedServer;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *persistentId;
+/** Test to see if @c persistentId has been set. */
+@property(nonatomic, readwrite) BOOL hasPersistentId;
+
+
+@property(nonatomic, readwrite) int32_t streamId;
+
+@property(nonatomic, readwrite) BOOL hasStreamId;
+
+@property(nonatomic, readwrite) int32_t lastStreamIdReceived;
+
+@property(nonatomic, readwrite) BOOL hasLastStreamIdReceived;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *permission;
+/** Test to see if @c permission has been set. */
+@property(nonatomic, readwrite) BOOL hasPermission;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *regId;
+/** Test to see if @c regId has been set. */
+@property(nonatomic, readwrite) BOOL hasRegId;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *pkgSignature;
+/** Test to see if @c pkgSignature has been set. */
+@property(nonatomic, readwrite) BOOL hasPkgSignature;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *clientId;
+/** Test to see if @c clientId has been set. */
+@property(nonatomic, readwrite) BOOL hasClientId;
+
+
+@property(nonatomic, readwrite) int64_t deviceUserId;
+
+@property(nonatomic, readwrite) BOOL hasDeviceUserId;
+
+@property(nonatomic, readwrite) int32_t ttl;
+
+@property(nonatomic, readwrite) BOOL hasTtl;
+
+@property(nonatomic, readwrite) int64_t sent;
+
+@property(nonatomic, readwrite) BOOL hasSent;
+
+@property(nonatomic, readwrite) int32_t queued;
+
+@property(nonatomic, readwrite) BOOL hasQueued;
+
+@property(nonatomic, readwrite) int64_t status;
+
+@property(nonatomic, readwrite) BOOL hasStatus;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSData *rawData;
+/** Test to see if @c rawData has been set. */
+@property(nonatomic, readwrite) BOOL hasRawData;
+
+
+@property(nonatomic, readwrite) int32_t maxDelay;
+
+@property(nonatomic, readwrite) BOOL hasMaxDelay;
+
+@property(nonatomic, readwrite) int32_t actualDelay;
+
+@property(nonatomic, readwrite) BOOL hasActualDelay;
+
+@property(nonatomic, readwrite) BOOL immediateAck;
+
+@property(nonatomic, readwrite) BOOL hasImmediateAck;
+
+@property(nonatomic, readwrite) BOOL deliveryReceiptRequested;
+
+@property(nonatomic, readwrite) BOOL hasDeliveryReceiptRequested;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *externalMessageId;
+/** Test to see if @c externalMessageId has been set. */
+@property(nonatomic, readwrite) BOOL hasExternalMessageId;
+
+
+@property(nonatomic, readwrite) int64_t flags;
+
+@property(nonatomic, readwrite) BOOL hasFlags;
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower DEPRECATED_ATTRIBUTE;
+/** Test to see if @c cellTower has been set. */
+@property(nonatomic, readwrite) BOOL hasCellTower DEPRECATED_ATTRIBUTE;
+
+
+@property(nonatomic, readwrite) int32_t priority;
+
+@property(nonatomic, readwrite) BOOL hasPriority;
+@end
+
+#pragma mark - GtalkTalkMetadata
+
+typedef GPB_ENUM(GtalkTalkMetadata_FieldNumber) {
+  GtalkTalkMetadata_FieldNumber_Foreground = 1,
+};
+
+@interface GtalkTalkMetadata : GPBMessage
+
+
+@property(nonatomic, readwrite) BOOL foreground;
+
+@property(nonatomic, readwrite) BOOL hasForeground;
+@end
+
+#pragma mark - GtalkCellTower
+
+typedef GPB_ENUM(GtalkCellTower_FieldNumber) {
+  GtalkCellTower_FieldNumber_Id_p = 1,
+  GtalkCellTower_FieldNumber_KnownCongestionStatus = 2,
+};
+
+DEPRECATED_ATTRIBUTE
+@interface GtalkCellTower : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *id_p;
+/** Test to see if @c id_p has been set. */
+@property(nonatomic, readwrite) BOOL hasId_p;
+
+
+@property(nonatomic, readwrite) int32_t knownCongestionStatus;
+
+@property(nonatomic, readwrite) BOOL hasKnownCongestionStatus;
+@end
+
+#pragma mark - GtalkClientEvent
+
+typedef GPB_ENUM(GtalkClientEvent_FieldNumber) {
+  GtalkClientEvent_FieldNumber_Type = 1,
+  GtalkClientEvent_FieldNumber_NumberDiscardedEvents = 100,
+  GtalkClientEvent_FieldNumber_NetworkType = 200,
+  GtalkClientEvent_FieldNumber_NetworkPort = 201,
+  GtalkClientEvent_FieldNumber_TimeConnectionStartedMs = 202,
+  GtalkClientEvent_FieldNumber_TimeConnectionEndedMs = 203,
+  GtalkClientEvent_FieldNumber_ErrorCode = 204,
+  GtalkClientEvent_FieldNumber_TimeConnectionEstablishedMs = 300,
+  GtalkClientEvent_FieldNumber_McsReconnectAction = 400,
+};
+
+@interface GtalkClientEvent : GPBMessage
+
+
+@property(nonatomic, readwrite) GtalkClientEvent_Type type;
+
+@property(nonatomic, readwrite) BOOL hasType;
+
+@property(nonatomic, readwrite) uint32_t numberDiscardedEvents;
+
+@property(nonatomic, readwrite) BOOL hasNumberDiscardedEvents;
+
+@property(nonatomic, readwrite) int32_t networkType;
+
+@property(nonatomic, readwrite) BOOL hasNetworkType;
+
+@property(nonatomic, readwrite) int32_t networkPort;
+
+@property(nonatomic, readwrite) BOOL hasNetworkPort;
+
+@property(nonatomic, readwrite) uint64_t timeConnectionStartedMs;
+
+@property(nonatomic, readwrite) BOOL hasTimeConnectionStartedMs;
+
+@property(nonatomic, readwrite) uint64_t timeConnectionEndedMs;
+
+@property(nonatomic, readwrite) BOOL hasTimeConnectionEndedMs;
+
+@property(nonatomic, readwrite) int32_t errorCode;
+
+@property(nonatomic, readwrite) BOOL hasErrorCode;
+
+@property(nonatomic, readwrite) uint64_t timeConnectionEstablishedMs;
+
+@property(nonatomic, readwrite) BOOL hasTimeConnectionEstablishedMs;
+
+@property(nonatomic, readwrite) GtalkClientEvent_McsReconnectAction mcsReconnectAction;
+
+@property(nonatomic, readwrite) BOOL hasMcsReconnectAction;
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkCore.pbobjc.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkCore.pbobjc.m
new file mode 100644 (file)
index 0000000..06c9134
--- /dev/null
@@ -0,0 +1,3017 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: buzz/mobile/proto/gtalk_core.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+ #import "GtalkCore.pbobjc.h"
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GtalkGtalkCoreRoot
+
+@implementation GtalkGtalkCoreRoot
+
+// No extensions in the file and no imports, so no need to generate
+// +extensionRegistry.
+
+@end
+
+#pragma mark - GtalkGtalkCoreRoot_FileDescriptor
+
+static GPBFileDescriptor *GtalkGtalkCoreRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"mobilegtalk"
+                                                 objcPrefix:@"Gtalk"
+                                                     syntax:GPBFileSyntaxProto2];
+  }
+  return descriptor;
+}
+
+#pragma mark - GtalkHeartbeatPing
+
+@implementation GtalkHeartbeatPing
+
+@dynamic hasStreamId, streamId;
+@dynamic hasLastStreamIdReceived, lastStreamIdReceived;
+@dynamic hasStatus, status;
+@dynamic hasCellTower, cellTower;
+@dynamic hasIntervalMs, intervalMs;
+
+typedef struct GtalkHeartbeatPing__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t streamId;
+  int32_t lastStreamIdReceived;
+  int32_t intervalMs;
+  GtalkCellTower *cellTower;
+  int64_t status;
+} GtalkHeartbeatPing__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "streamId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatPing_FieldNumber_StreamId,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatPing__storage_, streamId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "lastStreamIdReceived",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatPing_FieldNumber_LastStreamIdReceived,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatPing__storage_, lastStreamIdReceived),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "status",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatPing_FieldNumber_Status,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatPing__storage_, status),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "cellTower",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkCellTower),
+        .number = GtalkHeartbeatPing_FieldNumber_CellTower,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatPing__storage_, cellTower),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "intervalMs",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatPing_FieldNumber_IntervalMs,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatPing__storage_, intervalMs),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkHeartbeatPing class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkHeartbeatPing__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkHeartbeatAck
+
+@implementation GtalkHeartbeatAck
+
+@dynamic hasStreamId, streamId;
+@dynamic hasLastStreamIdReceived, lastStreamIdReceived;
+@dynamic hasStatus, status;
+@dynamic hasCellTower, cellTower;
+@dynamic hasIntervalMs, intervalMs;
+
+typedef struct GtalkHeartbeatAck__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t streamId;
+  int32_t lastStreamIdReceived;
+  int32_t intervalMs;
+  GtalkCellTower *cellTower;
+  int64_t status;
+} GtalkHeartbeatAck__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "streamId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatAck_FieldNumber_StreamId,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatAck__storage_, streamId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "lastStreamIdReceived",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatAck_FieldNumber_LastStreamIdReceived,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatAck__storage_, lastStreamIdReceived),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "status",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatAck_FieldNumber_Status,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatAck__storage_, status),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "cellTower",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkCellTower),
+        .number = GtalkHeartbeatAck_FieldNumber_CellTower,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatAck__storage_, cellTower),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "intervalMs",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatAck_FieldNumber_IntervalMs,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatAck__storage_, intervalMs),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkHeartbeatAck class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkHeartbeatAck__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkErrorInfo
+
+@implementation GtalkErrorInfo
+
+@dynamic hasCode, code;
+@dynamic hasMessage, message;
+@dynamic hasType, type;
+@dynamic hasExtension, extension;
+
+typedef struct GtalkErrorInfo__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t code;
+  NSString *message;
+  NSString *type;
+  GtalkExtension *extension;
+} GtalkErrorInfo__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "code",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkErrorInfo_FieldNumber_Code,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkErrorInfo__storage_, code),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "message",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkErrorInfo_FieldNumber_Message,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkErrorInfo__storage_, message),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "type",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkErrorInfo_FieldNumber_Type,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkErrorInfo__storage_, type),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "extension",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkExtension),
+        .number = GtalkErrorInfo_FieldNumber_Extension,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkErrorInfo__storage_, extension),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkErrorInfo class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkErrorInfo__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkSetting
+
+@implementation GtalkSetting
+
+@dynamic hasName, name;
+@dynamic hasValue, value;
+
+typedef struct GtalkSetting__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *name;
+  NSString *value;
+} GtalkSetting__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "name",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkSetting_FieldNumber_Name,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkSetting__storage_, name),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkSetting_FieldNumber_Value,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkSetting__storage_, value),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkSetting class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkSetting__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkHeartbeatStat
+
+@implementation GtalkHeartbeatStat
+
+@dynamic hasIp, ip;
+@dynamic hasTimeout, timeout;
+@dynamic hasIntervalMs, intervalMs;
+
+typedef struct GtalkHeartbeatStat__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t intervalMs;
+  NSString *ip;
+} GtalkHeartbeatStat__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "ip",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatStat_FieldNumber_Ip,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatStat__storage_, ip),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "timeout",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatStat_FieldNumber_Timeout,
+        .hasIndex = 1,
+        .offset = 2,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "intervalMs",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatStat_FieldNumber_IntervalMs,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatStat__storage_, intervalMs),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkHeartbeatStat class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkHeartbeatStat__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkHeartbeatConfig
+
+@implementation GtalkHeartbeatConfig
+
+@dynamic hasUploadStat, uploadStat;
+@dynamic hasIp, ip;
+@dynamic hasIntervalMs, intervalMs;
+
+typedef struct GtalkHeartbeatConfig__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t intervalMs;
+  NSString *ip;
+} GtalkHeartbeatConfig__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "uploadStat",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatConfig_FieldNumber_UploadStat,
+        .hasIndex = 0,
+        .offset = 1,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "ip",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatConfig_FieldNumber_Ip,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatConfig__storage_, ip),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "intervalMs",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkHeartbeatConfig_FieldNumber_IntervalMs,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkHeartbeatConfig__storage_, intervalMs),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkHeartbeatConfig class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkHeartbeatConfig__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkLoginRequest
+
+@implementation GtalkLoginRequest
+
+@dynamic hasId_p, id_p;
+@dynamic hasDomain, domain;
+@dynamic hasUser, user;
+@dynamic hasResource, resource;
+@dynamic hasAuthToken, authToken;
+@dynamic hasDeviceId, deviceId;
+@dynamic hasLastRmqId, lastRmqId;
+@dynamic settingArray, settingArray_Count;
+@dynamic receivedPersistentIdArray, receivedPersistentIdArray_Count;
+@dynamic hasIncludeStreamIds, includeStreamIds;
+@dynamic hasHeartbeatStat, heartbeatStat;
+@dynamic hasUseRmq2, useRmq2;
+@dynamic hasAccountId, accountId;
+@dynamic hasAuthService, authService;
+@dynamic hasNetworkType, networkType;
+@dynamic hasStatus, status;
+@dynamic hasTokenVersionInfo, tokenVersionInfo;
+@dynamic hasCellTower, cellTower;
+@dynamic hasGcmStartTimeMs, gcmStartTimeMs;
+@dynamic clientEventArray, clientEventArray_Count;
+@dynamic hasOnFallback, onFallback;
+@dynamic hasNoPendingUpstream, noPendingUpstream;
+@dynamic hasReconnectRequestId, reconnectRequestId;
+
+typedef struct GtalkLoginRequest__storage_ {
+  uint32_t _has_storage_[1];
+  GtalkLoginRequest_AuthService authService;
+  int32_t networkType;
+  int32_t reconnectRequestId;
+  NSString *id_p;
+  NSString *domain;
+  NSString *user;
+  NSString *resource;
+  NSString *authToken;
+  NSString *deviceId;
+  NSMutableArray *settingArray;
+  NSMutableArray *receivedPersistentIdArray;
+  GtalkHeartbeatStat *heartbeatStat;
+  NSString *tokenVersionInfo;
+  GtalkCellTower *cellTower;
+  NSMutableArray *clientEventArray;
+  int64_t lastRmqId;
+  int64_t accountId;
+  int64_t status;
+  uint64_t gcmStartTimeMs;
+} GtalkLoginRequest__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_Id_p,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, id_p),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "domain",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_Domain,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, domain),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "user",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_User,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, user),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "resource",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_Resource,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, resource),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "authToken",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_AuthToken,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, authToken),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "deviceId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_DeviceId,
+        .hasIndex = 5,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, deviceId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "lastRmqId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_LastRmqId,
+        .hasIndex = 6,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, lastRmqId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "settingArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkSetting),
+        .number = GtalkLoginRequest_FieldNumber_SettingArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, settingArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "receivedPersistentIdArray",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_ReceivedPersistentIdArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, receivedPersistentIdArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "includeStreamIds",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_IncludeStreamIds,
+        .hasIndex = 7,
+        .offset = 8,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "heartbeatStat",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkHeartbeatStat),
+        .number = GtalkLoginRequest_FieldNumber_HeartbeatStat,
+        .hasIndex = 9,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, heartbeatStat),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "useRmq2",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_UseRmq2,
+        .hasIndex = 10,
+        .offset = 11,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "accountId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_AccountId,
+        .hasIndex = 12,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, accountId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "authService",
+        .dataTypeSpecific.enumDescFunc = GtalkLoginRequest_AuthService_EnumDescriptor,
+        .number = GtalkLoginRequest_FieldNumber_AuthService,
+        .hasIndex = 13,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, authService),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "networkType",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_NetworkType,
+        .hasIndex = 14,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, networkType),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "status",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_Status,
+        .hasIndex = 15,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, status),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "tokenVersionInfo",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_TokenVersionInfo,
+        .hasIndex = 16,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, tokenVersionInfo),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "cellTower",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkCellTower),
+        .number = GtalkLoginRequest_FieldNumber_CellTower,
+        .hasIndex = 17,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, cellTower),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "gcmStartTimeMs",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_GcmStartTimeMs,
+        .hasIndex = 18,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, gcmStartTimeMs),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeUInt64,
+      },
+      {
+        .name = "clientEventArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkClientEvent),
+        .number = GtalkLoginRequest_FieldNumber_ClientEventArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, clientEventArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "onFallback",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_OnFallback,
+        .hasIndex = 19,
+        .offset = 20,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "noPendingUpstream",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_NoPendingUpstream,
+        .hasIndex = 21,
+        .offset = 22,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "reconnectRequestId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginRequest_FieldNumber_ReconnectRequestId,
+        .hasIndex = 23,
+        .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, reconnectRequestId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkLoginRequest class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkLoginRequest__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - Enum GtalkLoginRequest_AuthService
+
+GPBEnumDescriptor *GtalkLoginRequest_AuthService_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Mail\000AndroidCloudToDeviceMessage\000Android"
+        "Id\000";
+    static const int32_t values[] = {
+        GtalkLoginRequest_AuthService_Mail,
+        GtalkLoginRequest_AuthService_AndroidCloudToDeviceMessage,
+        GtalkLoginRequest_AuthService_AndroidId,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkLoginRequest_AuthService)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkLoginRequest_AuthService_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkLoginRequest_AuthService_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkLoginRequest_AuthService_Mail:
+    case GtalkLoginRequest_AuthService_AndroidCloudToDeviceMessage:
+    case GtalkLoginRequest_AuthService_AndroidId:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GtalkLoginResponse
+
+@implementation GtalkLoginResponse
+
+@dynamic hasId_p, id_p;
+@dynamic hasJid, jid;
+@dynamic hasError, error;
+@dynamic settingArray, settingArray_Count;
+@dynamic hasStreamId, streamId;
+@dynamic hasLastStreamIdReceived, lastStreamIdReceived;
+@dynamic hasHeartbeatConfig, heartbeatConfig;
+@dynamic hasServerTimestamp, serverTimestamp;
+
+typedef struct GtalkLoginResponse__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t streamId;
+  int32_t lastStreamIdReceived;
+  NSString *id_p;
+  NSString *jid;
+  GtalkErrorInfo *error;
+  NSMutableArray *settingArray;
+  GtalkHeartbeatConfig *heartbeatConfig;
+  int64_t serverTimestamp;
+} GtalkLoginResponse__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginResponse_FieldNumber_Id_p,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkLoginResponse__storage_, id_p),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "jid",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginResponse_FieldNumber_Jid,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkLoginResponse__storage_, jid),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "error",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkErrorInfo),
+        .number = GtalkLoginResponse_FieldNumber_Error,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkLoginResponse__storage_, error),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "settingArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkSetting),
+        .number = GtalkLoginResponse_FieldNumber_SettingArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkLoginResponse__storage_, settingArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "streamId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginResponse_FieldNumber_StreamId,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkLoginResponse__storage_, streamId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "lastStreamIdReceived",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginResponse_FieldNumber_LastStreamIdReceived,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkLoginResponse__storage_, lastStreamIdReceived),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "heartbeatConfig",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkHeartbeatConfig),
+        .number = GtalkLoginResponse_FieldNumber_HeartbeatConfig,
+        .hasIndex = 5,
+        .offset = (uint32_t)offsetof(GtalkLoginResponse__storage_, heartbeatConfig),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "serverTimestamp",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkLoginResponse_FieldNumber_ServerTimestamp,
+        .hasIndex = 6,
+        .offset = (uint32_t)offsetof(GtalkLoginResponse__storage_, serverTimestamp),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkLoginResponse class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkLoginResponse__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkBindAccountRequest
+
+@implementation GtalkBindAccountRequest
+
+@dynamic hasId_p, id_p;
+@dynamic hasDomain, domain;
+@dynamic hasUser, user;
+@dynamic hasResource, resource;
+@dynamic hasAuthToken, authToken;
+@dynamic hasPersistentId, persistentId;
+@dynamic hasStreamId, streamId;
+@dynamic hasLastStreamIdReceived, lastStreamIdReceived;
+@dynamic hasAccountId, accountId;
+
+typedef struct GtalkBindAccountRequest__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t streamId;
+  int32_t lastStreamIdReceived;
+  NSString *id_p;
+  NSString *domain;
+  NSString *user;
+  NSString *resource;
+  NSString *authToken;
+  NSString *persistentId;
+  int64_t accountId;
+} GtalkBindAccountRequest__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountRequest_FieldNumber_Id_p,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkBindAccountRequest__storage_, id_p),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "domain",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountRequest_FieldNumber_Domain,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkBindAccountRequest__storage_, domain),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "user",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountRequest_FieldNumber_User,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkBindAccountRequest__storage_, user),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "resource",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountRequest_FieldNumber_Resource,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkBindAccountRequest__storage_, resource),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "authToken",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountRequest_FieldNumber_AuthToken,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkBindAccountRequest__storage_, authToken),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "persistentId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountRequest_FieldNumber_PersistentId,
+        .hasIndex = 5,
+        .offset = (uint32_t)offsetof(GtalkBindAccountRequest__storage_, persistentId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "streamId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountRequest_FieldNumber_StreamId,
+        .hasIndex = 6,
+        .offset = (uint32_t)offsetof(GtalkBindAccountRequest__storage_, streamId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "lastStreamIdReceived",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountRequest_FieldNumber_LastStreamIdReceived,
+        .hasIndex = 7,
+        .offset = (uint32_t)offsetof(GtalkBindAccountRequest__storage_, lastStreamIdReceived),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "accountId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountRequest_FieldNumber_AccountId,
+        .hasIndex = 8,
+        .offset = (uint32_t)offsetof(GtalkBindAccountRequest__storage_, accountId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkBindAccountRequest class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkBindAccountRequest__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkBindAccountResponse
+
+@implementation GtalkBindAccountResponse
+
+@dynamic hasId_p, id_p;
+@dynamic hasJid, jid;
+@dynamic hasError, error;
+@dynamic hasStreamId, streamId;
+@dynamic hasLastStreamIdReceived, lastStreamIdReceived;
+
+typedef struct GtalkBindAccountResponse__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t streamId;
+  int32_t lastStreamIdReceived;
+  NSString *id_p;
+  NSString *jid;
+  GtalkErrorInfo *error;
+} GtalkBindAccountResponse__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountResponse_FieldNumber_Id_p,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkBindAccountResponse__storage_, id_p),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "jid",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountResponse_FieldNumber_Jid,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkBindAccountResponse__storage_, jid),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "error",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkErrorInfo),
+        .number = GtalkBindAccountResponse_FieldNumber_Error,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkBindAccountResponse__storage_, error),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "streamId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountResponse_FieldNumber_StreamId,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkBindAccountResponse__storage_, streamId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "lastStreamIdReceived",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBindAccountResponse_FieldNumber_LastStreamIdReceived,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkBindAccountResponse__storage_, lastStreamIdReceived),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkBindAccountResponse class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkBindAccountResponse__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkStreamErrorStanza
+
+@implementation GtalkStreamErrorStanza
+
+@dynamic hasType, type;
+@dynamic hasText, text;
+
+typedef struct GtalkStreamErrorStanza__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *type;
+  NSString *text;
+} GtalkStreamErrorStanza__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "type",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkStreamErrorStanza_FieldNumber_Type,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkStreamErrorStanza__storage_, type),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "text",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkStreamErrorStanza_FieldNumber_Text,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkStreamErrorStanza__storage_, text),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkStreamErrorStanza class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkStreamErrorStanza__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkClose
+
+@implementation GtalkClose
+
+
+typedef struct GtalkClose__storage_ {
+  uint32_t _has_storage_[1];
+} GtalkClose__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkClose class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:NULL
+                                    fieldCount:0
+                                   storageSize:sizeof(GtalkClose__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkExtension
+
+@implementation GtalkExtension
+
+@dynamic hasId_p, id_p;
+@dynamic hasData_p, data_p;
+
+typedef struct GtalkExtension__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t id_p;
+  NSString *data_p;
+} GtalkExtension__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkExtension_FieldNumber_Id_p,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkExtension__storage_, id_p),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "data_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkExtension_FieldNumber_Data_p,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkExtension__storage_, data_p),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkExtension class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkExtension__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkMessageStanza
+
+@implementation GtalkMessageStanza
+
+@dynamic hasRmqId, rmqId;
+@dynamic hasType, type;
+@dynamic hasId_p, id_p;
+@dynamic hasFrom, from;
+@dynamic hasTo, to;
+@dynamic hasSubject, subject;
+@dynamic hasBody, body;
+@dynamic hasThread, thread;
+@dynamic hasError, error;
+@dynamic extensionArray, extensionArray_Count;
+@dynamic hasNosave, nosave;
+@dynamic hasTimestamp, timestamp;
+@dynamic hasPersistentId, persistentId;
+@dynamic hasStreamId, streamId;
+@dynamic hasLastStreamIdReceived, lastStreamIdReceived;
+@dynamic hasRead, read;
+@dynamic hasAccountId, accountId;
+
+typedef struct GtalkMessageStanza__storage_ {
+  uint32_t _has_storage_[1];
+  GtalkMessageStanza_MessageType type;
+  int32_t streamId;
+  int32_t lastStreamIdReceived;
+  NSString *id_p;
+  NSString *from;
+  NSString *to;
+  NSString *subject;
+  NSString *body;
+  NSString *thread;
+  GtalkErrorInfo *error;
+  NSMutableArray *extensionArray;
+  NSString *persistentId;
+  int64_t rmqId;
+  int64_t timestamp;
+  int64_t accountId;
+} GtalkMessageStanza__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "rmqId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_RmqId,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, rmqId),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "type",
+        .dataTypeSpecific.enumDescFunc = GtalkMessageStanza_MessageType_EnumDescriptor,
+        .number = GtalkMessageStanza_FieldNumber_Type,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, type),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_Id_p,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, id_p),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "from",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_From,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, from),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "to",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_To,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, to),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "subject",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_Subject,
+        .hasIndex = 5,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, subject),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "body",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_Body,
+        .hasIndex = 6,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, body),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "thread",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_Thread,
+        .hasIndex = 7,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, thread),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "error",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkErrorInfo),
+        .number = GtalkMessageStanza_FieldNumber_Error,
+        .hasIndex = 8,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, error),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "extensionArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkExtension),
+        .number = GtalkMessageStanza_FieldNumber_ExtensionArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, extensionArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "nosave",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_Nosave,
+        .hasIndex = 9,
+        .offset = 10,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "timestamp",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_Timestamp,
+        .hasIndex = 11,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, timestamp),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "persistentId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_PersistentId,
+        .hasIndex = 12,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, persistentId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "streamId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_StreamId,
+        .hasIndex = 13,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, streamId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "lastStreamIdReceived",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_LastStreamIdReceived,
+        .hasIndex = 14,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, lastStreamIdReceived),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "read",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_Read,
+        .hasIndex = 15,
+        .offset = 16,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "accountId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkMessageStanza_FieldNumber_AccountId,
+        .hasIndex = 17,
+        .offset = (uint32_t)offsetof(GtalkMessageStanza__storage_, accountId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkMessageStanza class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkMessageStanza__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+#if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    static const char *extraTextFormatInfo =
+        "\001\001\005\000";
+    [localDescriptor setupExtraTextInfo:extraTextFormatInfo];
+#endif  // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - Enum GtalkMessageStanza_MessageType
+
+GPBEnumDescriptor *GtalkMessageStanza_MessageType_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Normal\000Chat\000Groupchat\000Headline\000Error\000";
+    static const int32_t values[] = {
+        GtalkMessageStanza_MessageType_Normal,
+        GtalkMessageStanza_MessageType_Chat,
+        GtalkMessageStanza_MessageType_Groupchat,
+        GtalkMessageStanza_MessageType_Headline,
+        GtalkMessageStanza_MessageType_Error,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkMessageStanza_MessageType)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkMessageStanza_MessageType_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkMessageStanza_MessageType_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkMessageStanza_MessageType_Normal:
+    case GtalkMessageStanza_MessageType_Chat:
+    case GtalkMessageStanza_MessageType_Groupchat:
+    case GtalkMessageStanza_MessageType_Headline:
+    case GtalkMessageStanza_MessageType_Error:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GtalkPresenceStanza
+
+@implementation GtalkPresenceStanza
+
+@dynamic hasRmqId, rmqId;
+@dynamic hasType, type;
+@dynamic hasId_p, id_p;
+@dynamic hasFrom, from;
+@dynamic hasTo, to;
+@dynamic hasShow, show;
+@dynamic hasStatus, status;
+@dynamic hasPriority, priority;
+@dynamic hasError, error;
+@dynamic extensionArray, extensionArray_Count;
+@dynamic hasClient, client;
+@dynamic hasAvatarHash, avatarHash;
+@dynamic hasPersistentId, persistentId;
+@dynamic hasStreamId, streamId;
+@dynamic hasLastStreamIdReceived, lastStreamIdReceived;
+@dynamic hasCapabilitiesFlags, capabilitiesFlags;
+@dynamic hasAccountId, accountId;
+
+typedef struct GtalkPresenceStanza__storage_ {
+  uint32_t _has_storage_[1];
+  GtalkPresenceStanza_PresenceType type;
+  GtalkPresenceStanza_ShowType show;
+  int32_t priority;
+  GtalkPresenceStanza_ClientType client;
+  int32_t streamId;
+  int32_t lastStreamIdReceived;
+  int32_t capabilitiesFlags;
+  NSString *id_p;
+  NSString *from;
+  NSString *to;
+  NSString *status;
+  GtalkErrorInfo *error;
+  NSMutableArray *extensionArray;
+  NSString *avatarHash;
+  NSString *persistentId;
+  int64_t rmqId;
+  int64_t accountId;
+} GtalkPresenceStanza__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "rmqId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_RmqId,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, rmqId),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "type",
+        .dataTypeSpecific.enumDescFunc = GtalkPresenceStanza_PresenceType_EnumDescriptor,
+        .number = GtalkPresenceStanza_FieldNumber_Type,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, type),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_Id_p,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, id_p),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "from",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_From,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, from),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "to",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_To,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, to),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "show",
+        .dataTypeSpecific.enumDescFunc = GtalkPresenceStanza_ShowType_EnumDescriptor,
+        .number = GtalkPresenceStanza_FieldNumber_Show,
+        .hasIndex = 5,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, show),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "status",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_Status,
+        .hasIndex = 6,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, status),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "priority",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_Priority,
+        .hasIndex = 7,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, priority),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "error",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkErrorInfo),
+        .number = GtalkPresenceStanza_FieldNumber_Error,
+        .hasIndex = 8,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, error),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "extensionArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkExtension),
+        .number = GtalkPresenceStanza_FieldNumber_ExtensionArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, extensionArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "client",
+        .dataTypeSpecific.enumDescFunc = GtalkPresenceStanza_ClientType_EnumDescriptor,
+        .number = GtalkPresenceStanza_FieldNumber_Client,
+        .hasIndex = 9,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, client),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "avatarHash",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_AvatarHash,
+        .hasIndex = 10,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, avatarHash),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "persistentId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_PersistentId,
+        .hasIndex = 11,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, persistentId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "streamId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_StreamId,
+        .hasIndex = 12,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, streamId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "lastStreamIdReceived",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_LastStreamIdReceived,
+        .hasIndex = 13,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, lastStreamIdReceived),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "capabilitiesFlags",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_CapabilitiesFlags,
+        .hasIndex = 14,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, capabilitiesFlags),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "accountId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPresenceStanza_FieldNumber_AccountId,
+        .hasIndex = 15,
+        .offset = (uint32_t)offsetof(GtalkPresenceStanza__storage_, accountId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkPresenceStanza class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkPresenceStanza__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+#if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    static const char *extraTextFormatInfo =
+        "\001\001\005\000";
+    [localDescriptor setupExtraTextInfo:extraTextFormatInfo];
+#endif  // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - Enum GtalkPresenceStanza_PresenceType
+
+GPBEnumDescriptor *GtalkPresenceStanza_PresenceType_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Unavailable\000Subscribe\000Subscribed\000Unsubsc"
+        "ribe\000Unsubscribed\000Probe\000Error\000";
+    static const int32_t values[] = {
+        GtalkPresenceStanza_PresenceType_Unavailable,
+        GtalkPresenceStanza_PresenceType_Subscribe,
+        GtalkPresenceStanza_PresenceType_Subscribed,
+        GtalkPresenceStanza_PresenceType_Unsubscribe,
+        GtalkPresenceStanza_PresenceType_Unsubscribed,
+        GtalkPresenceStanza_PresenceType_Probe,
+        GtalkPresenceStanza_PresenceType_Error,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkPresenceStanza_PresenceType)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkPresenceStanza_PresenceType_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkPresenceStanza_PresenceType_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkPresenceStanza_PresenceType_Unavailable:
+    case GtalkPresenceStanza_PresenceType_Subscribe:
+    case GtalkPresenceStanza_PresenceType_Subscribed:
+    case GtalkPresenceStanza_PresenceType_Unsubscribe:
+    case GtalkPresenceStanza_PresenceType_Unsubscribed:
+    case GtalkPresenceStanza_PresenceType_Probe:
+    case GtalkPresenceStanza_PresenceType_Error:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - Enum GtalkPresenceStanza_ShowType
+
+GPBEnumDescriptor *GtalkPresenceStanza_ShowType_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Away\000Chat\000Dnd\000Xa\000";
+    static const int32_t values[] = {
+        GtalkPresenceStanza_ShowType_Away,
+        GtalkPresenceStanza_ShowType_Chat,
+        GtalkPresenceStanza_ShowType_Dnd,
+        GtalkPresenceStanza_ShowType_Xa,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkPresenceStanza_ShowType)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkPresenceStanza_ShowType_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkPresenceStanza_ShowType_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkPresenceStanza_ShowType_Away:
+    case GtalkPresenceStanza_ShowType_Chat:
+    case GtalkPresenceStanza_ShowType_Dnd:
+    case GtalkPresenceStanza_ShowType_Xa:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - Enum GtalkPresenceStanza_ClientType
+
+GPBEnumDescriptor *GtalkPresenceStanza_ClientType_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Mobile\000Android\000";
+    static const int32_t values[] = {
+        GtalkPresenceStanza_ClientType_Mobile,
+        GtalkPresenceStanza_ClientType_Android,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkPresenceStanza_ClientType)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkPresenceStanza_ClientType_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkPresenceStanza_ClientType_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkPresenceStanza_ClientType_Mobile:
+    case GtalkPresenceStanza_ClientType_Android:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - Enum GtalkPresenceStanza_CapabilitiesFlags
+
+GPBEnumDescriptor *GtalkPresenceStanza_CapabilitiesFlags_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "HasVoiceV1\000HasVideoV1\000HasCameraV1\000HasPmu"
+        "cV1\000";
+    static const int32_t values[] = {
+        GtalkPresenceStanza_CapabilitiesFlags_HasVoiceV1,
+        GtalkPresenceStanza_CapabilitiesFlags_HasVideoV1,
+        GtalkPresenceStanza_CapabilitiesFlags_HasCameraV1,
+        GtalkPresenceStanza_CapabilitiesFlags_HasPmucV1,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkPresenceStanza_CapabilitiesFlags)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkPresenceStanza_CapabilitiesFlags_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkPresenceStanza_CapabilitiesFlags_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkPresenceStanza_CapabilitiesFlags_HasVoiceV1:
+    case GtalkPresenceStanza_CapabilitiesFlags_HasVideoV1:
+    case GtalkPresenceStanza_CapabilitiesFlags_HasCameraV1:
+    case GtalkPresenceStanza_CapabilitiesFlags_HasPmucV1:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GtalkBatchPresenceStanza
+
+@implementation GtalkBatchPresenceStanza
+
+@dynamic hasId_p, id_p;
+@dynamic hasTo, to;
+@dynamic presenceArray, presenceArray_Count;
+@dynamic hasPersistentId, persistentId;
+@dynamic hasStreamId, streamId;
+@dynamic hasLastStreamIdReceived, lastStreamIdReceived;
+@dynamic hasAccountId, accountId;
+@dynamic hasType, type;
+@dynamic hasError, error;
+
+typedef struct GtalkBatchPresenceStanza__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t streamId;
+  int32_t lastStreamIdReceived;
+  GtalkBatchPresenceStanza_Type type;
+  NSString *id_p;
+  NSString *to;
+  NSMutableArray *presenceArray;
+  NSString *persistentId;
+  GtalkErrorInfo *error;
+  int64_t accountId;
+} GtalkBatchPresenceStanza__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBatchPresenceStanza_FieldNumber_Id_p,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkBatchPresenceStanza__storage_, id_p),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "to",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBatchPresenceStanza_FieldNumber_To,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkBatchPresenceStanza__storage_, to),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "presenceArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkPresenceStanza),
+        .number = GtalkBatchPresenceStanza_FieldNumber_PresenceArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkBatchPresenceStanza__storage_, presenceArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "persistentId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBatchPresenceStanza_FieldNumber_PersistentId,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkBatchPresenceStanza__storage_, persistentId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "streamId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBatchPresenceStanza_FieldNumber_StreamId,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkBatchPresenceStanza__storage_, streamId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "lastStreamIdReceived",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBatchPresenceStanza_FieldNumber_LastStreamIdReceived,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkBatchPresenceStanza__storage_, lastStreamIdReceived),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "accountId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkBatchPresenceStanza_FieldNumber_AccountId,
+        .hasIndex = 5,
+        .offset = (uint32_t)offsetof(GtalkBatchPresenceStanza__storage_, accountId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "type",
+        .dataTypeSpecific.enumDescFunc = GtalkBatchPresenceStanza_Type_EnumDescriptor,
+        .number = GtalkBatchPresenceStanza_FieldNumber_Type,
+        .hasIndex = 6,
+        .offset = (uint32_t)offsetof(GtalkBatchPresenceStanza__storage_, type),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "error",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkErrorInfo),
+        .number = GtalkBatchPresenceStanza_FieldNumber_Error,
+        .hasIndex = 7,
+        .offset = (uint32_t)offsetof(GtalkBatchPresenceStanza__storage_, error),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkBatchPresenceStanza class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkBatchPresenceStanza__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - Enum GtalkBatchPresenceStanza_Type
+
+GPBEnumDescriptor *GtalkBatchPresenceStanza_Type_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Get\000Set\000";
+    static const int32_t values[] = {
+        GtalkBatchPresenceStanza_Type_Get,
+        GtalkBatchPresenceStanza_Type_Set,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkBatchPresenceStanza_Type)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkBatchPresenceStanza_Type_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkBatchPresenceStanza_Type_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkBatchPresenceStanza_Type_Get:
+    case GtalkBatchPresenceStanza_Type_Set:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GtalkIqStanza
+
+@implementation GtalkIqStanza
+
+@dynamic hasRmqId, rmqId;
+@dynamic hasType, type;
+@dynamic hasId_p, id_p;
+@dynamic hasFrom, from;
+@dynamic hasTo, to;
+@dynamic hasError, error;
+@dynamic hasExtension, extension;
+@dynamic hasPersistentId, persistentId;
+@dynamic hasStreamId, streamId;
+@dynamic hasLastStreamIdReceived, lastStreamIdReceived;
+@dynamic hasAccountId, accountId;
+@dynamic hasStatus, status;
+
+typedef struct GtalkIqStanza__storage_ {
+  uint32_t _has_storage_[1];
+  GtalkIqStanza_IqType type;
+  int32_t streamId;
+  int32_t lastStreamIdReceived;
+  NSString *id_p;
+  NSString *from;
+  NSString *to;
+  GtalkErrorInfo *error;
+  GtalkExtension *extension;
+  NSString *persistentId;
+  int64_t rmqId;
+  int64_t accountId;
+  int64_t status;
+} GtalkIqStanza__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "rmqId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIqStanza_FieldNumber_RmqId,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, rmqId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "type",
+        .dataTypeSpecific.enumDescFunc = GtalkIqStanza_IqType_EnumDescriptor,
+        .number = GtalkIqStanza_FieldNumber_Type,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, type),
+        .flags = (GPBFieldFlags)(GPBFieldRequired | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIqStanza_FieldNumber_Id_p,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, id_p),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "from",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIqStanza_FieldNumber_From,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, from),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "to",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIqStanza_FieldNumber_To,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, to),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "error",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkErrorInfo),
+        .number = GtalkIqStanza_FieldNumber_Error,
+        .hasIndex = 5,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, error),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "extension",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkExtension),
+        .number = GtalkIqStanza_FieldNumber_Extension,
+        .hasIndex = 6,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, extension),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "persistentId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIqStanza_FieldNumber_PersistentId,
+        .hasIndex = 7,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, persistentId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "streamId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIqStanza_FieldNumber_StreamId,
+        .hasIndex = 8,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, streamId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "lastStreamIdReceived",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIqStanza_FieldNumber_LastStreamIdReceived,
+        .hasIndex = 9,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, lastStreamIdReceived),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "accountId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIqStanza_FieldNumber_AccountId,
+        .hasIndex = 10,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, accountId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "status",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIqStanza_FieldNumber_Status,
+        .hasIndex = 11,
+        .offset = (uint32_t)offsetof(GtalkIqStanza__storage_, status),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkIqStanza class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkIqStanza__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - Enum GtalkIqStanza_IqType
+
+GPBEnumDescriptor *GtalkIqStanza_IqType_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Get\000Set\000Result\000Error\000";
+    static const int32_t values[] = {
+        GtalkIqStanza_IqType_Get,
+        GtalkIqStanza_IqType_Set,
+        GtalkIqStanza_IqType_Result,
+        GtalkIqStanza_IqType_Error,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkIqStanza_IqType)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkIqStanza_IqType_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkIqStanza_IqType_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkIqStanza_IqType_Get:
+    case GtalkIqStanza_IqType_Set:
+    case GtalkIqStanza_IqType_Result:
+    case GtalkIqStanza_IqType_Error:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GtalkAppData
+
+@implementation GtalkAppData
+
+@dynamic hasKey, key;
+@dynamic hasValue, value;
+
+typedef struct GtalkAppData__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *key;
+  NSString *value;
+} GtalkAppData__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "key",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkAppData_FieldNumber_Key,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkAppData__storage_, key),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkAppData_FieldNumber_Value,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkAppData__storage_, value),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkAppData class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkAppData__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkDataMessageStanza
+
+@implementation GtalkDataMessageStanza
+
+@dynamic hasRmqId, rmqId;
+@dynamic hasId_p, id_p;
+@dynamic hasFrom, from;
+@dynamic hasTo, to;
+@dynamic hasCategory, category;
+@dynamic hasToken, token;
+@dynamic appDataArray, appDataArray_Count;
+@dynamic hasFromTrustedServer, fromTrustedServer;
+@dynamic hasPersistentId, persistentId;
+@dynamic hasStreamId, streamId;
+@dynamic hasLastStreamIdReceived, lastStreamIdReceived;
+@dynamic hasPermission, permission;
+@dynamic hasRegId, regId;
+@dynamic hasPkgSignature, pkgSignature;
+@dynamic hasClientId, clientId;
+@dynamic hasDeviceUserId, deviceUserId;
+@dynamic hasTtl, ttl;
+@dynamic hasSent, sent;
+@dynamic hasQueued, queued;
+@dynamic hasStatus, status;
+@dynamic hasRawData, rawData;
+@dynamic hasMaxDelay, maxDelay;
+@dynamic hasActualDelay, actualDelay;
+@dynamic hasImmediateAck, immediateAck;
+@dynamic hasDeliveryReceiptRequested, deliveryReceiptRequested;
+@dynamic hasExternalMessageId, externalMessageId;
+@dynamic hasFlags, flags;
+@dynamic hasCellTower, cellTower;
+@dynamic hasPriority, priority;
+
+typedef struct GtalkDataMessageStanza__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t streamId;
+  int32_t lastStreamIdReceived;
+  int32_t ttl;
+  int32_t queued;
+  int32_t maxDelay;
+  int32_t actualDelay;
+  int32_t priority;
+  NSString *id_p;
+  NSString *from;
+  NSString *to;
+  NSString *category;
+  NSString *token;
+  NSMutableArray *appDataArray;
+  NSString *persistentId;
+  NSString *permission;
+  NSString *regId;
+  NSString *pkgSignature;
+  NSString *clientId;
+  NSData *rawData;
+  NSString *externalMessageId;
+  GtalkCellTower *cellTower;
+  int64_t rmqId;
+  int64_t deviceUserId;
+  int64_t sent;
+  int64_t status;
+  int64_t flags;
+} GtalkDataMessageStanza__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "rmqId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_RmqId,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, rmqId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_Id_p,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, id_p),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "from",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_From,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, from),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "to",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_To,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, to),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "category",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_Category,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, category),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "token",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_Token,
+        .hasIndex = 5,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, token),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "appDataArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkAppData),
+        .number = GtalkDataMessageStanza_FieldNumber_AppDataArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, appDataArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "fromTrustedServer",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_FromTrustedServer,
+        .hasIndex = 6,
+        .offset = 7,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "persistentId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_PersistentId,
+        .hasIndex = 8,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, persistentId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "streamId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_StreamId,
+        .hasIndex = 9,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, streamId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "lastStreamIdReceived",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_LastStreamIdReceived,
+        .hasIndex = 10,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, lastStreamIdReceived),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "permission",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_Permission,
+        .hasIndex = 11,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, permission),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "regId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_RegId,
+        .hasIndex = 12,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, regId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "pkgSignature",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_PkgSignature,
+        .hasIndex = 13,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, pkgSignature),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "clientId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_ClientId,
+        .hasIndex = 14,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, clientId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "deviceUserId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_DeviceUserId,
+        .hasIndex = 15,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, deviceUserId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "ttl",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_Ttl,
+        .hasIndex = 16,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, ttl),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "sent",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_Sent,
+        .hasIndex = 17,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, sent),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "queued",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_Queued,
+        .hasIndex = 18,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, queued),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "status",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_Status,
+        .hasIndex = 19,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, status),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "rawData",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_RawData,
+        .hasIndex = 20,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, rawData),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBytes,
+      },
+      {
+        .name = "maxDelay",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_MaxDelay,
+        .hasIndex = 21,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, maxDelay),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "actualDelay",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_ActualDelay,
+        .hasIndex = 22,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, actualDelay),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "immediateAck",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_ImmediateAck,
+        .hasIndex = 23,
+        .offset = 24,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "deliveryReceiptRequested",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_DeliveryReceiptRequested,
+        .hasIndex = 25,
+        .offset = 26,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "externalMessageId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_ExternalMessageId,
+        .hasIndex = 27,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, externalMessageId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "flags",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_Flags,
+        .hasIndex = 28,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, flags),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "cellTower",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkCellTower),
+        .number = GtalkDataMessageStanza_FieldNumber_CellTower,
+        .hasIndex = 29,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, cellTower),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "priority",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkDataMessageStanza_FieldNumber_Priority,
+        .hasIndex = 30,
+        .offset = (uint32_t)offsetof(GtalkDataMessageStanza__storage_, priority),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkDataMessageStanza class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkDataMessageStanza__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkTalkMetadata
+
+@implementation GtalkTalkMetadata
+
+@dynamic hasForeground, foreground;
+
+typedef struct GtalkTalkMetadata__storage_ {
+  uint32_t _has_storage_[1];
+} GtalkTalkMetadata__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "foreground",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkTalkMetadata_FieldNumber_Foreground,
+        .hasIndex = 0,
+        .offset = 1,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkTalkMetadata class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkTalkMetadata__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkCellTower
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+
+@implementation GtalkCellTower
+
+@dynamic hasId_p, id_p;
+@dynamic hasKnownCongestionStatus, knownCongestionStatus;
+
+typedef struct GtalkCellTower__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t knownCongestionStatus;
+  NSString *id_p;
+} GtalkCellTower__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkCellTower_FieldNumber_Id_p,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkCellTower__storage_, id_p),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "knownCongestionStatus",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkCellTower_FieldNumber_KnownCongestionStatus,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkCellTower__storage_, knownCongestionStatus),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkCellTower class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkCellTower__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma clang diagnostic pop
+
+#pragma mark - GtalkClientEvent
+
+@implementation GtalkClientEvent
+
+@dynamic hasType, type;
+@dynamic hasNumberDiscardedEvents, numberDiscardedEvents;
+@dynamic hasNetworkType, networkType;
+@dynamic hasNetworkPort, networkPort;
+@dynamic hasTimeConnectionStartedMs, timeConnectionStartedMs;
+@dynamic hasTimeConnectionEndedMs, timeConnectionEndedMs;
+@dynamic hasErrorCode, errorCode;
+@dynamic hasTimeConnectionEstablishedMs, timeConnectionEstablishedMs;
+@dynamic hasMcsReconnectAction, mcsReconnectAction;
+
+typedef struct GtalkClientEvent__storage_ {
+  uint32_t _has_storage_[1];
+  GtalkClientEvent_Type type;
+  uint32_t numberDiscardedEvents;
+  int32_t networkType;
+  int32_t networkPort;
+  int32_t errorCode;
+  GtalkClientEvent_McsReconnectAction mcsReconnectAction;
+  uint64_t timeConnectionStartedMs;
+  uint64_t timeConnectionEndedMs;
+  uint64_t timeConnectionEstablishedMs;
+} GtalkClientEvent__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "type",
+        .dataTypeSpecific.enumDescFunc = GtalkClientEvent_Type_EnumDescriptor,
+        .number = GtalkClientEvent_FieldNumber_Type,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkClientEvent__storage_, type),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "numberDiscardedEvents",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkClientEvent_FieldNumber_NumberDiscardedEvents,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkClientEvent__storage_, numberDiscardedEvents),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeUInt32,
+      },
+      {
+        .name = "networkType",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkClientEvent_FieldNumber_NetworkType,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkClientEvent__storage_, networkType),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "networkPort",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkClientEvent_FieldNumber_NetworkPort,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkClientEvent__storage_, networkPort),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "timeConnectionStartedMs",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkClientEvent_FieldNumber_TimeConnectionStartedMs,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkClientEvent__storage_, timeConnectionStartedMs),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeUInt64,
+      },
+      {
+        .name = "timeConnectionEndedMs",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkClientEvent_FieldNumber_TimeConnectionEndedMs,
+        .hasIndex = 5,
+        .offset = (uint32_t)offsetof(GtalkClientEvent__storage_, timeConnectionEndedMs),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeUInt64,
+      },
+      {
+        .name = "errorCode",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkClientEvent_FieldNumber_ErrorCode,
+        .hasIndex = 6,
+        .offset = (uint32_t)offsetof(GtalkClientEvent__storage_, errorCode),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "timeConnectionEstablishedMs",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkClientEvent_FieldNumber_TimeConnectionEstablishedMs,
+        .hasIndex = 7,
+        .offset = (uint32_t)offsetof(GtalkClientEvent__storage_, timeConnectionEstablishedMs),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeUInt64,
+      },
+      {
+        .name = "mcsReconnectAction",
+        .dataTypeSpecific.enumDescFunc = GtalkClientEvent_McsReconnectAction_EnumDescriptor,
+        .number = GtalkClientEvent_FieldNumber_McsReconnectAction,
+        .hasIndex = 8,
+        .offset = (uint32_t)offsetof(GtalkClientEvent__storage_, mcsReconnectAction),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkClientEvent class]
+                                     rootClass:[GtalkGtalkCoreRoot class]
+                                          file:GtalkGtalkCoreRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkClientEvent__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - Enum GtalkClientEvent_Type
+
+GPBEnumDescriptor *GtalkClientEvent_Type_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Unknown\000DiscardedEvents\000FailedConnection"
+        "\000SuccessfulConnection\000McsReconnectReques"
+        "t\000FailedSocketCreationMcsReconnect\000McsRe"
+        "connectLimited\000";
+    static const int32_t values[] = {
+        GtalkClientEvent_Type_Unknown,
+        GtalkClientEvent_Type_DiscardedEvents,
+        GtalkClientEvent_Type_FailedConnection,
+        GtalkClientEvent_Type_SuccessfulConnection,
+        GtalkClientEvent_Type_McsReconnectRequest,
+        GtalkClientEvent_Type_FailedSocketCreationMcsReconnect,
+        GtalkClientEvent_Type_McsReconnectLimited,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkClientEvent_Type)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkClientEvent_Type_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkClientEvent_Type_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkClientEvent_Type_Unknown:
+    case GtalkClientEvent_Type_DiscardedEvents:
+    case GtalkClientEvent_Type_FailedConnection:
+    case GtalkClientEvent_Type_SuccessfulConnection:
+    case GtalkClientEvent_Type_McsReconnectRequest:
+    case GtalkClientEvent_Type_FailedSocketCreationMcsReconnect:
+    case GtalkClientEvent_Type_McsReconnectLimited:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - Enum GtalkClientEvent_McsReconnectAction
+
+GPBEnumDescriptor *GtalkClientEvent_McsReconnectAction_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "None\000NotConnected\000TooSoon\000";
+    static const int32_t values[] = {
+        GtalkClientEvent_McsReconnectAction_None,
+        GtalkClientEvent_McsReconnectAction_NotConnected,
+        GtalkClientEvent_McsReconnectAction_TooSoon,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkClientEvent_McsReconnectAction)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkClientEvent_McsReconnectAction_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkClientEvent_McsReconnectAction_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkClientEvent_McsReconnectAction_None:
+    case GtalkClientEvent_McsReconnectAction_NotConnected:
+    case GtalkClientEvent_McsReconnectAction_TooSoon:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkExtensions.pbobjc.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkExtensions.pbobjc.h
new file mode 100644 (file)
index 0000000..f461884
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: buzz/mobile/proto/gtalk_extensions.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers.h>
+#else
+ #import "GPBProtocolBuffers.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+@class GtalkOtrItem;
+@class GtalkPhoto;
+@class GtalkRosterItem;
+@class GtalkSharedStatus_StatusList;
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - Enum GtalkRosterItem_SubscriptionType
+
+typedef GPB_ENUM(GtalkRosterItem_SubscriptionType) {
+  GtalkRosterItem_SubscriptionType_None = 0,
+  GtalkRosterItem_SubscriptionType_To = 1,
+  GtalkRosterItem_SubscriptionType_From = 2,
+  GtalkRosterItem_SubscriptionType_Both = 3,
+  GtalkRosterItem_SubscriptionType_Remove = 4,
+};
+
+GPBEnumDescriptor *GtalkRosterItem_SubscriptionType_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkRosterItem_SubscriptionType_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkRosterItem_AskType
+
+typedef GPB_ENUM(GtalkRosterItem_AskType) {
+  GtalkRosterItem_AskType_Subscribe = 0,
+};
+
+GPBEnumDescriptor *GtalkRosterItem_AskType_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkRosterItem_AskType_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkRosterItem_DisplayType
+
+typedef GPB_ENUM(GtalkRosterItem_DisplayType) {
+  GtalkRosterItem_DisplayType_Blocked = 0,
+  GtalkRosterItem_DisplayType_Hidden = 1,
+  GtalkRosterItem_DisplayType_Pinned = 2,
+};
+
+GPBEnumDescriptor *GtalkRosterItem_DisplayType_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkRosterItem_DisplayType_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkSharedStatus_ShowType
+
+typedef GPB_ENUM(GtalkSharedStatus_ShowType) {
+  GtalkSharedStatus_ShowType_Default = 0,
+  GtalkSharedStatus_ShowType_Dnd = 1,
+};
+
+GPBEnumDescriptor *GtalkSharedStatus_ShowType_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkSharedStatus_ShowType_IsValidValue(int32_t value);
+
+#pragma mark - Enum GtalkPostAuthBatchQuery_CapabilitiesExtFlags
+
+typedef GPB_ENUM(GtalkPostAuthBatchQuery_CapabilitiesExtFlags) {
+  GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasVoiceV1 = 1,
+  GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasVideoV1 = 2,
+  GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasCameraV1 = 4,
+  GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasPmucV1 = 8,
+};
+
+GPBEnumDescriptor *GtalkPostAuthBatchQuery_CapabilitiesExtFlags_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GtalkPostAuthBatchQuery_CapabilitiesExtFlags_IsValidValue(int32_t value);
+
+#pragma mark - GtalkGtalkExtensionsRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GtalkGtalkExtensionsRoot : GPBRootObject
+@end
+
+#pragma mark - GtalkRosterQuery
+
+typedef GPB_ENUM(GtalkRosterQuery_FieldNumber) {
+  GtalkRosterQuery_FieldNumber_Etag = 1,
+  GtalkRosterQuery_FieldNumber_NotModified = 2,
+  GtalkRosterQuery_FieldNumber_ItemArray = 3,
+  GtalkRosterQuery_FieldNumber_AvatarWidth = 4,
+  GtalkRosterQuery_FieldNumber_AvatarHeight = 5,
+};
+
+@interface GtalkRosterQuery : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *etag;
+/** Test to see if @c etag has been set. */
+@property(nonatomic, readwrite) BOOL hasEtag;
+
+
+@property(nonatomic, readwrite) BOOL notModified;
+
+@property(nonatomic, readwrite) BOOL hasNotModified;
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GtalkRosterItem*> *itemArray;
+/** The number of items in @c itemArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger itemArray_Count;
+
+
+@property(nonatomic, readwrite) int32_t avatarWidth;
+
+@property(nonatomic, readwrite) BOOL hasAvatarWidth;
+
+@property(nonatomic, readwrite) int32_t avatarHeight;
+
+@property(nonatomic, readwrite) BOOL hasAvatarHeight;
+@end
+
+#pragma mark - GtalkRosterItem
+
+typedef GPB_ENUM(GtalkRosterItem_FieldNumber) {
+  GtalkRosterItem_FieldNumber_Jid = 1,
+  GtalkRosterItem_FieldNumber_Name = 2,
+  GtalkRosterItem_FieldNumber_Subscription = 3,
+  GtalkRosterItem_FieldNumber_Ask = 4,
+  GtalkRosterItem_FieldNumber_GroupArray = 5,
+  GtalkRosterItem_FieldNumber_QuickContact = 6,
+  GtalkRosterItem_FieldNumber_Display = 7,
+  GtalkRosterItem_FieldNumber_Rejected = 8,
+};
+
+@interface GtalkRosterItem : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *jid;
+/** Test to see if @c jid has been set. */
+@property(nonatomic, readwrite) BOOL hasJid;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
+/** Test to see if @c name has been set. */
+@property(nonatomic, readwrite) BOOL hasName;
+
+
+@property(nonatomic, readwrite) GtalkRosterItem_SubscriptionType subscription;
+
+@property(nonatomic, readwrite) BOOL hasSubscription;
+
+@property(nonatomic, readwrite) GtalkRosterItem_AskType ask;
+
+@property(nonatomic, readwrite) BOOL hasAsk;
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *groupArray;
+/** The number of items in @c groupArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger groupArray_Count;
+
+
+@property(nonatomic, readwrite) BOOL quickContact;
+
+@property(nonatomic, readwrite) BOOL hasQuickContact;
+
+@property(nonatomic, readwrite) GtalkRosterItem_DisplayType display;
+
+@property(nonatomic, readwrite) BOOL hasDisplay;
+
+@property(nonatomic, readwrite) BOOL rejected;
+
+@property(nonatomic, readwrite) BOOL hasRejected;
+@end
+
+#pragma mark - GtalkRmqLastId
+
+typedef GPB_ENUM(GtalkRmqLastId_FieldNumber) {
+  GtalkRmqLastId_FieldNumber_Id_p = 1,
+};
+
+@interface GtalkRmqLastId : GPBMessage
+
+
+@property(nonatomic, readwrite) int64_t id_p;
+
+@property(nonatomic, readwrite) BOOL hasId_p;
+@end
+
+#pragma mark - GtalkRmqAck
+
+typedef GPB_ENUM(GtalkRmqAck_FieldNumber) {
+  GtalkRmqAck_FieldNumber_Id_p = 1,
+};
+
+@interface GtalkRmqAck : GPBMessage
+
+
+@property(nonatomic, readwrite) int64_t id_p;
+
+@property(nonatomic, readwrite) BOOL hasId_p;
+@end
+
+#pragma mark - GtalkVCard
+
+typedef GPB_ENUM(GtalkVCard_FieldNumber) {
+  GtalkVCard_FieldNumber_Version = 1,
+  GtalkVCard_FieldNumber_FullName = 2,
+  GtalkVCard_FieldNumber_Photo = 3,
+  GtalkVCard_FieldNumber_AvatarHash = 4,
+  GtalkVCard_FieldNumber_Modified = 5,
+};
+
+@interface GtalkVCard : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *version;
+/** Test to see if @c version has been set. */
+@property(nonatomic, readwrite) BOOL hasVersion;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *fullName;
+/** Test to see if @c fullName has been set. */
+@property(nonatomic, readwrite) BOOL hasFullName;
+
+
+@property(nonatomic, readwrite, strong, null_resettable) GtalkPhoto *photo;
+/** Test to see if @c photo has been set. */
+@property(nonatomic, readwrite) BOOL hasPhoto;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *avatarHash;
+/** Test to see if @c avatarHash has been set. */
+@property(nonatomic, readwrite) BOOL hasAvatarHash;
+
+
+@property(nonatomic, readwrite) BOOL modified;
+
+@property(nonatomic, readwrite) BOOL hasModified;
+@end
+
+#pragma mark - GtalkPhoto
+
+typedef GPB_ENUM(GtalkPhoto_FieldNumber) {
+  GtalkPhoto_FieldNumber_Type = 1,
+  GtalkPhoto_FieldNumber_Data_p = 2,
+};
+
+@interface GtalkPhoto : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *type;
+/** Test to see if @c type has been set. */
+@property(nonatomic, readwrite) BOOL hasType;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *data_p;
+/** Test to see if @c data_p has been set. */
+@property(nonatomic, readwrite) BOOL hasData_p;
+
+@end
+
+#pragma mark - GtalkChatRead
+
+typedef GPB_ENUM(GtalkChatRead_FieldNumber) {
+  GtalkChatRead_FieldNumber_User = 1,
+};
+
+@interface GtalkChatRead : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *user;
+/** Test to see if @c user has been set. */
+@property(nonatomic, readwrite) BOOL hasUser;
+
+@end
+
+#pragma mark - GtalkChatClosed
+
+typedef GPB_ENUM(GtalkChatClosed_FieldNumber) {
+  GtalkChatClosed_FieldNumber_User = 1,
+};
+
+@interface GtalkChatClosed : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *user;
+/** Test to see if @c user has been set. */
+@property(nonatomic, readwrite) BOOL hasUser;
+
+@end
+
+#pragma mark - GtalkCapabilities
+
+typedef GPB_ENUM(GtalkCapabilities_FieldNumber) {
+  GtalkCapabilities_FieldNumber_Node = 1,
+  GtalkCapabilities_FieldNumber_Ver = 2,
+  GtalkCapabilities_FieldNumber_Ext = 3,
+  GtalkCapabilities_FieldNumber_Hash_p = 4,
+};
+
+@interface GtalkCapabilities : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *node;
+/** Test to see if @c node has been set. */
+@property(nonatomic, readwrite) BOOL hasNode;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *ver;
+/** Test to see if @c ver has been set. */
+@property(nonatomic, readwrite) BOOL hasVer;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *ext;
+/** Test to see if @c ext has been set. */
+@property(nonatomic, readwrite) BOOL hasExt;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *hash_p;
+/** Test to see if @c hash_p has been set. */
+@property(nonatomic, readwrite) BOOL hasHash_p;
+
+@end
+
+#pragma mark - GtalkSharedStatus
+
+typedef GPB_ENUM(GtalkSharedStatus_FieldNumber) {
+  GtalkSharedStatus_FieldNumber_StatusMax = 1,
+  GtalkSharedStatus_FieldNumber_StatusListMax = 2,
+  GtalkSharedStatus_FieldNumber_StatusListContentsMax = 3,
+  GtalkSharedStatus_FieldNumber_Status = 4,
+  GtalkSharedStatus_FieldNumber_Show = 5,
+  GtalkSharedStatus_FieldNumber_StatusListArray = 6,
+  GtalkSharedStatus_FieldNumber_Invisible = 9,
+  GtalkSharedStatus_FieldNumber_StatusMinVersion = 10,
+};
+
+@interface GtalkSharedStatus : GPBMessage
+
+
+@property(nonatomic, readwrite) int32_t statusMax;
+
+@property(nonatomic, readwrite) BOOL hasStatusMax;
+
+@property(nonatomic, readwrite) int32_t statusListMax;
+
+@property(nonatomic, readwrite) BOOL hasStatusListMax;
+
+@property(nonatomic, readwrite) int32_t statusListContentsMax;
+
+@property(nonatomic, readwrite) BOOL hasStatusListContentsMax;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *status;
+/** Test to see if @c status has been set. */
+@property(nonatomic, readwrite) BOOL hasStatus;
+
+
+@property(nonatomic, readwrite) GtalkSharedStatus_ShowType show;
+
+@property(nonatomic, readwrite) BOOL hasShow;
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GtalkSharedStatus_StatusList*> *statusListArray;
+/** The number of items in @c statusListArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger statusListArray_Count;
+
+
+@property(nonatomic, readwrite) BOOL invisible;
+
+@property(nonatomic, readwrite) BOOL hasInvisible;
+
+@property(nonatomic, readwrite) int32_t statusMinVersion;
+
+@property(nonatomic, readwrite) BOOL hasStatusMinVersion;
+@end
+
+#pragma mark - GtalkSharedStatus_StatusList
+
+typedef GPB_ENUM(GtalkSharedStatus_StatusList_FieldNumber) {
+  GtalkSharedStatus_StatusList_FieldNumber_Show = 7,
+  GtalkSharedStatus_StatusList_FieldNumber_StatusArray = 8,
+};
+
+@interface GtalkSharedStatus_StatusList : GPBMessage
+
+
+@property(nonatomic, readwrite) GtalkSharedStatus_ShowType show;
+
+@property(nonatomic, readwrite) BOOL hasShow;
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *statusArray;
+/** The number of items in @c statusArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger statusArray_Count;
+
+@end
+
+#pragma mark - GtalkOtrQuery
+
+typedef GPB_ENUM(GtalkOtrQuery_FieldNumber) {
+  GtalkOtrQuery_FieldNumber_NosaveDefault = 1,
+  GtalkOtrQuery_FieldNumber_ItemArray = 2,
+  GtalkOtrQuery_FieldNumber_Etag = 3,
+  GtalkOtrQuery_FieldNumber_NotModified = 4,
+};
+
+@interface GtalkOtrQuery : GPBMessage
+
+
+@property(nonatomic, readwrite) BOOL nosaveDefault;
+
+@property(nonatomic, readwrite) BOOL hasNosaveDefault;
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GtalkOtrItem*> *itemArray;
+/** The number of items in @c itemArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger itemArray_Count;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *etag;
+/** Test to see if @c etag has been set. */
+@property(nonatomic, readwrite) BOOL hasEtag;
+
+
+@property(nonatomic, readwrite) BOOL notModified;
+
+@property(nonatomic, readwrite) BOOL hasNotModified;
+@end
+
+#pragma mark - GtalkOtrItem
+
+typedef GPB_ENUM(GtalkOtrItem_FieldNumber) {
+  GtalkOtrItem_FieldNumber_Jid = 1,
+  GtalkOtrItem_FieldNumber_Nosave = 2,
+  GtalkOtrItem_FieldNumber_ChangedByBuddy = 3,
+};
+
+@interface GtalkOtrItem : GPBMessage
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *jid;
+/** Test to see if @c jid has been set. */
+@property(nonatomic, readwrite) BOOL hasJid;
+
+
+@property(nonatomic, readwrite) BOOL nosave;
+
+@property(nonatomic, readwrite) BOOL hasNosave;
+
+@property(nonatomic, readwrite) BOOL changedByBuddy;
+
+@property(nonatomic, readwrite) BOOL hasChangedByBuddy;
+@end
+
+#pragma mark - GtalkIdle
+
+typedef GPB_ENUM(GtalkIdle_FieldNumber) {
+  GtalkIdle_FieldNumber_Idle = 1,
+  GtalkIdle_FieldNumber_Away = 2,
+};
+
+@interface GtalkIdle : GPBMessage
+
+
+@property(nonatomic, readwrite) BOOL idle;
+
+@property(nonatomic, readwrite) BOOL hasIdle;
+
+@property(nonatomic, readwrite) BOOL away;
+
+@property(nonatomic, readwrite) BOOL hasAway;
+@end
+
+#pragma mark - GtalkPostAuthBatchQuery
+
+typedef GPB_ENUM(GtalkPostAuthBatchQuery_FieldNumber) {
+  GtalkPostAuthBatchQuery_FieldNumber_Available = 1,
+  GtalkPostAuthBatchQuery_FieldNumber_DeviceIdle = 2,
+  GtalkPostAuthBatchQuery_FieldNumber_MobileIndicator = 3,
+  GtalkPostAuthBatchQuery_FieldNumber_SharedStatusVersion = 4,
+  GtalkPostAuthBatchQuery_FieldNumber_RosterEtag = 5,
+  GtalkPostAuthBatchQuery_FieldNumber_OtrEtag = 6,
+  GtalkPostAuthBatchQuery_FieldNumber_AvatarHash = 7,
+  GtalkPostAuthBatchQuery_FieldNumber_VcardQueryStanzaId = 8,
+  GtalkPostAuthBatchQuery_FieldNumber_CapabilitiesExtFlags = 9,
+};
+
+@interface GtalkPostAuthBatchQuery : GPBMessage
+
+
+@property(nonatomic, readwrite) BOOL available;
+
+@property(nonatomic, readwrite) BOOL hasAvailable;
+
+@property(nonatomic, readwrite) BOOL deviceIdle;
+
+@property(nonatomic, readwrite) BOOL hasDeviceIdle;
+
+@property(nonatomic, readwrite) BOOL mobileIndicator;
+
+@property(nonatomic, readwrite) BOOL hasMobileIndicator;
+
+@property(nonatomic, readwrite) int32_t sharedStatusVersion;
+
+@property(nonatomic, readwrite) BOOL hasSharedStatusVersion;
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *rosterEtag;
+/** Test to see if @c rosterEtag has been set. */
+@property(nonatomic, readwrite) BOOL hasRosterEtag;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *otrEtag;
+/** Test to see if @c otrEtag has been set. */
+@property(nonatomic, readwrite) BOOL hasOtrEtag;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *avatarHash;
+/** Test to see if @c avatarHash has been set. */
+@property(nonatomic, readwrite) BOOL hasAvatarHash;
+
+
+@property(nonatomic, readwrite, copy, null_resettable) NSString *vcardQueryStanzaId;
+/** Test to see if @c vcardQueryStanzaId has been set. */
+@property(nonatomic, readwrite) BOOL hasVcardQueryStanzaId;
+
+
+@property(nonatomic, readwrite) int32_t capabilitiesExtFlags;
+
+@property(nonatomic, readwrite) BOOL hasCapabilitiesExtFlags;
+@end
+
+#pragma mark - GtalkStreamAck
+
+@interface GtalkStreamAck : GPBMessage
+
+@end
+
+#pragma mark - GtalkSelectiveAck
+
+typedef GPB_ENUM(GtalkSelectiveAck_FieldNumber) {
+  GtalkSelectiveAck_FieldNumber_IdArray = 1,
+};
+
+@interface GtalkSelectiveAck : GPBMessage
+
+
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *idArray;
+/** The number of items in @c idArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger idArray_Count;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkExtensions.pbobjc.m b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Protos/GtalkExtensions.pbobjc.m
new file mode 100644 (file)
index 0000000..e41d416
--- /dev/null
@@ -0,0 +1,1407 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: buzz/mobile/proto/gtalk_extensions.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+ #import "GtalkExtensions.pbobjc.h"
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GtalkGtalkExtensionsRoot
+
+@implementation GtalkGtalkExtensionsRoot
+
+// No extensions in the file and no imports, so no need to generate
+// +extensionRegistry.
+
+@end
+
+#pragma mark - GtalkGtalkExtensionsRoot_FileDescriptor
+
+static GPBFileDescriptor *GtalkGtalkExtensionsRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"mobilegtalk"
+                                                 objcPrefix:@"Gtalk"
+                                                     syntax:GPBFileSyntaxProto2];
+  }
+  return descriptor;
+}
+
+#pragma mark - GtalkRosterQuery
+
+@implementation GtalkRosterQuery
+
+@dynamic hasEtag, etag;
+@dynamic hasNotModified, notModified;
+@dynamic itemArray, itemArray_Count;
+@dynamic hasAvatarWidth, avatarWidth;
+@dynamic hasAvatarHeight, avatarHeight;
+
+typedef struct GtalkRosterQuery__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t avatarWidth;
+  int32_t avatarHeight;
+  NSString *etag;
+  NSMutableArray *itemArray;
+} GtalkRosterQuery__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "etag",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRosterQuery_FieldNumber_Etag,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkRosterQuery__storage_, etag),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "notModified",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRosterQuery_FieldNumber_NotModified,
+        .hasIndex = 1,
+        .offset = 2,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "itemArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkRosterItem),
+        .number = GtalkRosterQuery_FieldNumber_ItemArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkRosterQuery__storage_, itemArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "avatarWidth",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRosterQuery_FieldNumber_AvatarWidth,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkRosterQuery__storage_, avatarWidth),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "avatarHeight",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRosterQuery_FieldNumber_AvatarHeight,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkRosterQuery__storage_, avatarHeight),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkRosterQuery class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkRosterQuery__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+#if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    static const char *extraTextFormatInfo =
+        "\002\004\013\000\005\014\000";
+    [localDescriptor setupExtraTextInfo:extraTextFormatInfo];
+#endif  // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkRosterItem
+
+@implementation GtalkRosterItem
+
+@dynamic hasJid, jid;
+@dynamic hasName, name;
+@dynamic hasSubscription, subscription;
+@dynamic hasAsk, ask;
+@dynamic groupArray, groupArray_Count;
+@dynamic hasQuickContact, quickContact;
+@dynamic hasDisplay, display;
+@dynamic hasRejected, rejected;
+
+typedef struct GtalkRosterItem__storage_ {
+  uint32_t _has_storage_[1];
+  GtalkRosterItem_SubscriptionType subscription;
+  GtalkRosterItem_AskType ask;
+  GtalkRosterItem_DisplayType display;
+  NSString *jid;
+  NSString *name;
+  NSMutableArray *groupArray;
+} GtalkRosterItem__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "jid",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRosterItem_FieldNumber_Jid,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkRosterItem__storage_, jid),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "name",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRosterItem_FieldNumber_Name,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkRosterItem__storage_, name),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "subscription",
+        .dataTypeSpecific.enumDescFunc = GtalkRosterItem_SubscriptionType_EnumDescriptor,
+        .number = GtalkRosterItem_FieldNumber_Subscription,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkRosterItem__storage_, subscription),
+        .flags = (GPBFieldFlags)(GPBFieldRequired | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "ask",
+        .dataTypeSpecific.enumDescFunc = GtalkRosterItem_AskType_EnumDescriptor,
+        .number = GtalkRosterItem_FieldNumber_Ask,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkRosterItem__storage_, ask),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "groupArray",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRosterItem_FieldNumber_GroupArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkRosterItem__storage_, groupArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "quickContact",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRosterItem_FieldNumber_QuickContact,
+        .hasIndex = 4,
+        .offset = 5,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "display",
+        .dataTypeSpecific.enumDescFunc = GtalkRosterItem_DisplayType_EnumDescriptor,
+        .number = GtalkRosterItem_FieldNumber_Display,
+        .hasIndex = 6,
+        .offset = (uint32_t)offsetof(GtalkRosterItem__storage_, display),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "rejected",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRosterItem_FieldNumber_Rejected,
+        .hasIndex = 7,
+        .offset = 8,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkRosterItem class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkRosterItem__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - Enum GtalkRosterItem_SubscriptionType
+
+GPBEnumDescriptor *GtalkRosterItem_SubscriptionType_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "None\000To\000From\000Both\000Remove\000";
+    static const int32_t values[] = {
+        GtalkRosterItem_SubscriptionType_None,
+        GtalkRosterItem_SubscriptionType_To,
+        GtalkRosterItem_SubscriptionType_From,
+        GtalkRosterItem_SubscriptionType_Both,
+        GtalkRosterItem_SubscriptionType_Remove,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkRosterItem_SubscriptionType)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkRosterItem_SubscriptionType_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkRosterItem_SubscriptionType_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkRosterItem_SubscriptionType_None:
+    case GtalkRosterItem_SubscriptionType_To:
+    case GtalkRosterItem_SubscriptionType_From:
+    case GtalkRosterItem_SubscriptionType_Both:
+    case GtalkRosterItem_SubscriptionType_Remove:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - Enum GtalkRosterItem_AskType
+
+GPBEnumDescriptor *GtalkRosterItem_AskType_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Subscribe\000";
+    static const int32_t values[] = {
+        GtalkRosterItem_AskType_Subscribe,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkRosterItem_AskType)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkRosterItem_AskType_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkRosterItem_AskType_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkRosterItem_AskType_Subscribe:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - Enum GtalkRosterItem_DisplayType
+
+GPBEnumDescriptor *GtalkRosterItem_DisplayType_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Blocked\000Hidden\000Pinned\000";
+    static const int32_t values[] = {
+        GtalkRosterItem_DisplayType_Blocked,
+        GtalkRosterItem_DisplayType_Hidden,
+        GtalkRosterItem_DisplayType_Pinned,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkRosterItem_DisplayType)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkRosterItem_DisplayType_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkRosterItem_DisplayType_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkRosterItem_DisplayType_Blocked:
+    case GtalkRosterItem_DisplayType_Hidden:
+    case GtalkRosterItem_DisplayType_Pinned:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GtalkRmqLastId
+
+@implementation GtalkRmqLastId
+
+@dynamic hasId_p, id_p;
+
+typedef struct GtalkRmqLastId__storage_ {
+  uint32_t _has_storage_[1];
+  int64_t id_p;
+} GtalkRmqLastId__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRmqLastId_FieldNumber_Id_p,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkRmqLastId__storage_, id_p),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeInt64,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkRmqLastId class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkRmqLastId__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkRmqAck
+
+@implementation GtalkRmqAck
+
+@dynamic hasId_p, id_p;
+
+typedef struct GtalkRmqAck__storage_ {
+  uint32_t _has_storage_[1];
+  int64_t id_p;
+} GtalkRmqAck__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "id_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkRmqAck_FieldNumber_Id_p,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkRmqAck__storage_, id_p),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeInt64,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkRmqAck class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkRmqAck__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkVCard
+
+@implementation GtalkVCard
+
+@dynamic hasVersion, version;
+@dynamic hasFullName, fullName;
+@dynamic hasPhoto, photo;
+@dynamic hasAvatarHash, avatarHash;
+@dynamic hasModified, modified;
+
+typedef struct GtalkVCard__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *version;
+  NSString *fullName;
+  GtalkPhoto *photo;
+  NSString *avatarHash;
+} GtalkVCard__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "version",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkVCard_FieldNumber_Version,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkVCard__storage_, version),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "fullName",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkVCard_FieldNumber_FullName,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkVCard__storage_, fullName),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "photo",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkPhoto),
+        .number = GtalkVCard_FieldNumber_Photo,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkVCard__storage_, photo),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "avatarHash",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkVCard_FieldNumber_AvatarHash,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkVCard__storage_, avatarHash),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "modified",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkVCard_FieldNumber_Modified,
+        .hasIndex = 4,
+        .offset = 5,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkVCard class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkVCard__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkPhoto
+
+@implementation GtalkPhoto
+
+@dynamic hasType, type;
+@dynamic hasData_p, data_p;
+
+typedef struct GtalkPhoto__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *type;
+  NSString *data_p;
+} GtalkPhoto__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "type",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPhoto_FieldNumber_Type,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkPhoto__storage_, type),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "data_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPhoto_FieldNumber_Data_p,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkPhoto__storage_, data_p),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkPhoto class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkPhoto__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkChatRead
+
+@implementation GtalkChatRead
+
+@dynamic hasUser, user;
+
+typedef struct GtalkChatRead__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *user;
+} GtalkChatRead__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "user",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkChatRead_FieldNumber_User,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkChatRead__storage_, user),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkChatRead class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkChatRead__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkChatClosed
+
+@implementation GtalkChatClosed
+
+@dynamic hasUser, user;
+
+typedef struct GtalkChatClosed__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *user;
+} GtalkChatClosed__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "user",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkChatClosed_FieldNumber_User,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkChatClosed__storage_, user),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkChatClosed class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkChatClosed__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkCapabilities
+
+@implementation GtalkCapabilities
+
+@dynamic hasNode, node;
+@dynamic hasVer, ver;
+@dynamic hasExt, ext;
+@dynamic hasHash_p, hash_p;
+
+typedef struct GtalkCapabilities__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *node;
+  NSString *ver;
+  NSString *ext;
+  NSString *hash_p;
+} GtalkCapabilities__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "node",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkCapabilities_FieldNumber_Node,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkCapabilities__storage_, node),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "ver",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkCapabilities_FieldNumber_Ver,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkCapabilities__storage_, ver),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "ext",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkCapabilities_FieldNumber_Ext,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkCapabilities__storage_, ext),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "hash_p",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkCapabilities_FieldNumber_Hash_p,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkCapabilities__storage_, hash_p),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkCapabilities class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkCapabilities__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkSharedStatus
+
+@implementation GtalkSharedStatus
+
+@dynamic hasStatusMax, statusMax;
+@dynamic hasStatusListMax, statusListMax;
+@dynamic hasStatusListContentsMax, statusListContentsMax;
+@dynamic hasStatus, status;
+@dynamic hasShow, show;
+@dynamic statusListArray, statusListArray_Count;
+@dynamic hasInvisible, invisible;
+@dynamic hasStatusMinVersion, statusMinVersion;
+
+typedef struct GtalkSharedStatus__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t statusMax;
+  int32_t statusListMax;
+  int32_t statusListContentsMax;
+  GtalkSharedStatus_ShowType show;
+  int32_t statusMinVersion;
+  NSString *status;
+  NSMutableArray *statusListArray;
+} GtalkSharedStatus__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "statusMax",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkSharedStatus_FieldNumber_StatusMax,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkSharedStatus__storage_, statusMax),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "statusListMax",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkSharedStatus_FieldNumber_StatusListMax,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GtalkSharedStatus__storage_, statusListMax),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "statusListContentsMax",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkSharedStatus_FieldNumber_StatusListContentsMax,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkSharedStatus__storage_, statusListContentsMax),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "status",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkSharedStatus_FieldNumber_Status,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GtalkSharedStatus__storage_, status),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "show",
+        .dataTypeSpecific.enumDescFunc = GtalkSharedStatus_ShowType_EnumDescriptor,
+        .number = GtalkSharedStatus_FieldNumber_Show,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GtalkSharedStatus__storage_, show),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "statusListArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkSharedStatus_StatusList),
+        .number = GtalkSharedStatus_FieldNumber_StatusListArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkSharedStatus__storage_, statusListArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeGroup,
+      },
+      {
+        .name = "invisible",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkSharedStatus_FieldNumber_Invisible,
+        .hasIndex = 5,
+        .offset = 6,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "statusMinVersion",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkSharedStatus_FieldNumber_StatusMinVersion,
+        .hasIndex = 7,
+        .offset = (uint32_t)offsetof(GtalkSharedStatus__storage_, statusMinVersion),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkSharedStatus class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkSharedStatus__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - Enum GtalkSharedStatus_ShowType
+
+GPBEnumDescriptor *GtalkSharedStatus_ShowType_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "Default\000Dnd\000";
+    static const int32_t values[] = {
+        GtalkSharedStatus_ShowType_Default,
+        GtalkSharedStatus_ShowType_Dnd,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkSharedStatus_ShowType)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkSharedStatus_ShowType_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkSharedStatus_ShowType_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkSharedStatus_ShowType_Default:
+    case GtalkSharedStatus_ShowType_Dnd:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GtalkSharedStatus_StatusList
+
+@implementation GtalkSharedStatus_StatusList
+
+@dynamic hasShow, show;
+@dynamic statusArray, statusArray_Count;
+
+typedef struct GtalkSharedStatus_StatusList__storage_ {
+  uint32_t _has_storage_[1];
+  GtalkSharedStatus_ShowType show;
+  NSMutableArray *statusArray;
+} GtalkSharedStatus_StatusList__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "show",
+        .dataTypeSpecific.enumDescFunc = GtalkSharedStatus_ShowType_EnumDescriptor,
+        .number = GtalkSharedStatus_StatusList_FieldNumber_Show,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkSharedStatus_StatusList__storage_, show),
+        .flags = (GPBFieldFlags)(GPBFieldRequired | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "statusArray",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkSharedStatus_StatusList_FieldNumber_StatusArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkSharedStatus_StatusList__storage_, statusArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkSharedStatus_StatusList class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkSharedStatus_StatusList__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    [localDescriptor setupContainingMessageClassName:GPBStringifySymbol(GtalkSharedStatus)];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkOtrQuery
+
+@implementation GtalkOtrQuery
+
+@dynamic hasNosaveDefault, nosaveDefault;
+@dynamic itemArray, itemArray_Count;
+@dynamic hasEtag, etag;
+@dynamic hasNotModified, notModified;
+
+typedef struct GtalkOtrQuery__storage_ {
+  uint32_t _has_storage_[1];
+  NSMutableArray *itemArray;
+  NSString *etag;
+} GtalkOtrQuery__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "nosaveDefault",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkOtrQuery_FieldNumber_NosaveDefault,
+        .hasIndex = 0,
+        .offset = 1,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "itemArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GtalkOtrItem),
+        .number = GtalkOtrQuery_FieldNumber_ItemArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkOtrQuery__storage_, itemArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "etag",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkOtrQuery_FieldNumber_Etag,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GtalkOtrQuery__storage_, etag),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "notModified",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkOtrQuery_FieldNumber_NotModified,
+        .hasIndex = 3,
+        .offset = 4,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkOtrQuery class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkOtrQuery__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkOtrItem
+
+@implementation GtalkOtrItem
+
+@dynamic hasJid, jid;
+@dynamic hasNosave, nosave;
+@dynamic hasChangedByBuddy, changedByBuddy;
+
+typedef struct GtalkOtrItem__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *jid;
+} GtalkOtrItem__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "jid",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkOtrItem_FieldNumber_Jid,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GtalkOtrItem__storage_, jid),
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "nosave",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkOtrItem_FieldNumber_Nosave,
+        .hasIndex = 1,
+        .offset = 2,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "changedByBuddy",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkOtrItem_FieldNumber_ChangedByBuddy,
+        .hasIndex = 3,
+        .offset = 4,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkOtrItem class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkOtrItem__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkIdle
+
+@implementation GtalkIdle
+
+@dynamic hasIdle, idle;
+@dynamic hasAway, away;
+
+typedef struct GtalkIdle__storage_ {
+  uint32_t _has_storage_[1];
+} GtalkIdle__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "idle",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIdle_FieldNumber_Idle,
+        .hasIndex = 0,
+        .offset = 1,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "away",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkIdle_FieldNumber_Away,
+        .hasIndex = 2,
+        .offset = 3,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkIdle class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkIdle__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkPostAuthBatchQuery
+
+@implementation GtalkPostAuthBatchQuery
+
+@dynamic hasAvailable, available;
+@dynamic hasDeviceIdle, deviceIdle;
+@dynamic hasMobileIndicator, mobileIndicator;
+@dynamic hasSharedStatusVersion, sharedStatusVersion;
+@dynamic hasRosterEtag, rosterEtag;
+@dynamic hasOtrEtag, otrEtag;
+@dynamic hasAvatarHash, avatarHash;
+@dynamic hasVcardQueryStanzaId, vcardQueryStanzaId;
+@dynamic hasCapabilitiesExtFlags, capabilitiesExtFlags;
+
+typedef struct GtalkPostAuthBatchQuery__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t sharedStatusVersion;
+  int32_t capabilitiesExtFlags;
+  NSString *rosterEtag;
+  NSString *otrEtag;
+  NSString *avatarHash;
+  NSString *vcardQueryStanzaId;
+} GtalkPostAuthBatchQuery__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "available",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPostAuthBatchQuery_FieldNumber_Available,
+        .hasIndex = 0,
+        .offset = 1,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldRequired,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "deviceIdle",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPostAuthBatchQuery_FieldNumber_DeviceIdle,
+        .hasIndex = 2,
+        .offset = 3,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "mobileIndicator",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPostAuthBatchQuery_FieldNumber_MobileIndicator,
+        .hasIndex = 4,
+        .offset = 5,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "sharedStatusVersion",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPostAuthBatchQuery_FieldNumber_SharedStatusVersion,
+        .hasIndex = 6,
+        .offset = (uint32_t)offsetof(GtalkPostAuthBatchQuery__storage_, sharedStatusVersion),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "rosterEtag",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPostAuthBatchQuery_FieldNumber_RosterEtag,
+        .hasIndex = 7,
+        .offset = (uint32_t)offsetof(GtalkPostAuthBatchQuery__storage_, rosterEtag),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "otrEtag",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPostAuthBatchQuery_FieldNumber_OtrEtag,
+        .hasIndex = 8,
+        .offset = (uint32_t)offsetof(GtalkPostAuthBatchQuery__storage_, otrEtag),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "avatarHash",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPostAuthBatchQuery_FieldNumber_AvatarHash,
+        .hasIndex = 9,
+        .offset = (uint32_t)offsetof(GtalkPostAuthBatchQuery__storage_, avatarHash),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "vcardQueryStanzaId",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPostAuthBatchQuery_FieldNumber_VcardQueryStanzaId,
+        .hasIndex = 10,
+        .offset = (uint32_t)offsetof(GtalkPostAuthBatchQuery__storage_, vcardQueryStanzaId),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "capabilitiesExtFlags",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkPostAuthBatchQuery_FieldNumber_CapabilitiesExtFlags,
+        .hasIndex = 11,
+        .offset = (uint32_t)offsetof(GtalkPostAuthBatchQuery__storage_, capabilitiesExtFlags),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkPostAuthBatchQuery class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkPostAuthBatchQuery__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - Enum GtalkPostAuthBatchQuery_CapabilitiesExtFlags
+
+GPBEnumDescriptor *GtalkPostAuthBatchQuery_CapabilitiesExtFlags_EnumDescriptor(void) {
+  static GPBEnumDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    static const char *valueNames =
+        "HasVoiceV1\000HasVideoV1\000HasCameraV1\000HasPmu"
+        "cV1\000";
+    static const int32_t values[] = {
+        GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasVoiceV1,
+        GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasVideoV1,
+        GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasCameraV1,
+        GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasPmucV1,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkPostAuthBatchQuery_CapabilitiesExtFlags)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GtalkPostAuthBatchQuery_CapabilitiesExtFlags_IsValidValue];
+    if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GtalkPostAuthBatchQuery_CapabilitiesExtFlags_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasVoiceV1:
+    case GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasVideoV1:
+    case GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasCameraV1:
+    case GtalkPostAuthBatchQuery_CapabilitiesExtFlags_HasPmucV1:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GtalkStreamAck
+
+@implementation GtalkStreamAck
+
+
+typedef struct GtalkStreamAck__storage_ {
+  uint32_t _has_storage_[1];
+} GtalkStreamAck__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkStreamAck class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:NULL
+                                    fieldCount:0
+                                   storageSize:sizeof(GtalkStreamAck__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GtalkSelectiveAck
+
+@implementation GtalkSelectiveAck
+
+@dynamic idArray, idArray_Count;
+
+typedef struct GtalkSelectiveAck__storage_ {
+  uint32_t _has_storage_[1];
+  NSMutableArray *idArray;
+} GtalkSelectiveAck__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "idArray",
+        .dataTypeSpecific.className = NULL,
+        .number = GtalkSelectiveAck_FieldNumber_IdArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GtalkSelectiveAck__storage_, idArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GtalkSelectiveAck class]
+                                     rootClass:[GtalkGtalkExtensionsRoot class]
+                                          file:GtalkGtalkExtensionsRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GtalkSelectiveAck__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Public/FIRMessaging.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Public/FIRMessaging.h
new file mode 100644 (file)
index 0000000..e58a216
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ *  @related FIRMessaging
+ *
+ *  The completion handler invoked when the registration token returns.
+ *  If the call fails we return the appropriate `error code`, described by
+ *  `FIRMessagingError`.
+ *
+ *  @param FCMToken  The valid registration token returned by FCM.
+ *  @param error     The error describing why a token request failed. The error code
+ *                   will match a value from the FIRMessagingError enumeration.
+ */
+typedef void(^FIRMessagingFCMTokenFetchCompletion)(NSString * _Nullable FCMToken,
+    NSError * _Nullable error)
+    NS_SWIFT_NAME(MessagingFCMTokenFetchCompletion);
+
+
+/**
+ *  @related FIRMessaging
+ *
+ *  The completion handler invoked when the registration token deletion request is
+ *  completed. If the call fails we return the appropriate `error code`, described
+ *  by `FIRMessagingError`.
+ *
+ *  @param error The error describing why a token deletion failed. The error code
+ *               will match a value from the FIRMessagingError enumeration.
+ */
+typedef void(^FIRMessagingDeleteFCMTokenCompletion)(NSError * _Nullable error)
+    NS_SWIFT_NAME(MessagingDeleteFCMTokenCompletion);
+
+/**
+ *  Callback to invoke once the HTTP call to FIRMessaging backend for updating
+ *  subscription finishes.
+ *
+ *  @param error  The error which occurred while updating the subscription topic
+ *                on the FIRMessaging server. This will be nil in case the operation
+ *                was successful, or if the operation was cancelled.
+ */
+typedef void (^FIRMessagingTopicOperationCompletion)(NSError *_Nullable error);
+
+/**
+ *  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)
+    NS_SWIFT_NAME(MessagingConnectCompletion)
+    __deprecated_msg("Please listen for the FIRMessagingConnectionStateChangedNotification "
+                     "NSNotification instead.");
+
+#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
+/**
+ *  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 const NSNotificationName FIRMessagingSendSuccessNotification
+    NS_SWIFT_NAME(MessagingSendSuccess);
+
+/**
+ *  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 const NSNotificationName FIRMessagingSendErrorNotification
+    NS_SWIFT_NAME(MessagingSendError);
+
+/**
+ *  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 const NSNotificationName FIRMessagingMessagesDeletedNotification
+    NS_SWIFT_NAME(MessagingMessagesDeleted);
+
+/**
+ *  Notification sent when Firebase Messaging establishes or disconnects from
+ *  an FCM socket connection. You can query the connection state in this
+ *  notification by checking the `isDirectChannelEstablished` property of FIRMessaging.
+ */
+FOUNDATION_EXPORT const NSNotificationName FIRMessagingConnectionStateChangedNotification
+    NS_SWIFT_NAME(MessagingConnectionStateChanged);
+
+/**
+ *  Notification sent when the FCM registration token has been refreshed. Please use the
+ *  FIRMessaging delegate method `messaging:didReceiveRegistrationToken:` to receive current and
+ *  updated tokens.
+ */
+FOUNDATION_EXPORT const NSNotificationName
+    FIRMessagingRegistrationTokenRefreshedNotification
+    NS_SWIFT_NAME(MessagingRegistrationTokenRefreshed);
+#else
+/**
+ *  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 *const FIRMessagingSendSuccessNotification
+    NS_SWIFT_NAME(MessagingSendSuccessNotification);
+
+/**
+ *  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 *const FIRMessagingSendErrorNotification
+    NS_SWIFT_NAME(MessagingSendErrorNotification);
+
+/**
+ *  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 *const FIRMessagingMessagesDeletedNotification
+    NS_SWIFT_NAME(MessagingMessagesDeletedNotification);
+
+/**
+ *  Notification sent when Firebase Messaging establishes or disconnects from
+ *  an FCM socket connection. You can query the connection state in this
+ *  notification by checking the `isDirectChannelEstablished` property of FIRMessaging.
+ */
+FOUNDATION_EXPORT NSString *const FIRMessagingConnectionStateChangedNotification
+    NS_SWIFT_NAME(MessagingConnectionStateChangedNotification);
+
+/**
+ *  Notification sent when the FCM registration token has been refreshed. Please use the
+ *  FIRMessaging delegate method `messaging:didReceiveRegistrationToken:` to receive current and
+ *  updated tokens.
+ */
+FOUNDATION_EXPORT NSString *const FIRMessagingRegistrationTokenRefreshedNotification
+    NS_SWIFT_NAME(MessagingRegistrationTokenRefreshedNotification);
+#endif  // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
+
+/**
+ *  @enum FIRMessagingError
+ */
+typedef NS_ENUM(NSUInteger, FIRMessagingError) {
+  /// Unknown error.
+  FIRMessagingErrorUnknown = 0,
+
+  /// FIRMessaging couldn't validate request from this client.
+  FIRMessagingErrorAuthentication = 1,
+
+  /// InstanceID service cannot be accessed.
+  FIRMessagingErrorNoAccess = 2,
+
+  /// Request to InstanceID backend timed out.
+  FIRMessagingErrorTimeout = 3,
+
+  /// No network available to reach the servers.
+  FIRMessagingErrorNetwork = 4,
+
+  /// Another similar operation in progress, bailing this one.
+  FIRMessagingErrorOperationInProgress = 5,
+
+  /// Some parameters of the request were invalid.
+  FIRMessagingErrorInvalidRequest = 7,
+} NS_SWIFT_NAME(MessagingError);
+
+/// Status for the downstream message received by the app.
+typedef NS_ENUM(NSInteger, FIRMessagingMessageStatus) {
+  /// Unknown status.
+  FIRMessagingMessageStatusUnknown,
+  /// New downstream message received by the app.
+  FIRMessagingMessageStatusNew,
+} NS_SWIFT_NAME(MessagingMessageStatus);
+
+/**
+ *  The APNS token type for the app. If the token type is set to `UNKNOWN`
+ *  Firebase Messaging will implicitly try to figure out what the actual token type
+ *  is from the provisioning profile.
+ *  Unless you really need to specify the type, you should use the `APNSToken`
+ *  property instead.
+ */
+typedef NS_ENUM(NSInteger, FIRMessagingAPNSTokenType) {
+  /// Unknown token type.
+  FIRMessagingAPNSTokenTypeUnknown,
+  /// Sandbox token type.
+  FIRMessagingAPNSTokenTypeSandbox,
+  /// Production token type.
+  FIRMessagingAPNSTokenTypeProd,
+} NS_SWIFT_NAME(MessagingAPNSTokenType);
+
+/// Information about a downstream message received by the app.
+NS_SWIFT_NAME(MessagingMessageInfo)
+@interface FIRMessagingMessageInfo : NSObject
+
+/// The status of the downstream message
+@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.
+ */
+NS_SWIFT_NAME(MessagingRemoteMessage)
+@interface FIRMessagingRemoteMessage : NSObject
+
+/// The downstream message received by the application.
+@property(nonatomic, readonly, strong) NSDictionary *appData;
+@end
+
+@class FIRMessaging;
+/**
+ * A protocol to handle token update or data message delivery from FCM.
+ *
+ */
+NS_SWIFT_NAME(MessagingDelegate)
+@protocol FIRMessagingDelegate <NSObject>
+
+@optional
+/// This method will be called once a token is available, or has been refreshed. Typically it
+/// will be called once per app start, but may be called more often, if token is invalidated or
+/// updated. In this method, you should perform operations such as:
+///
+/// * Uploading the FCM token to your application server, so targeted notifications can be sent.
+///
+/// * Subscribing to any topics.
+- (void)messaging:(FIRMessaging *)messaging
+    didReceiveRegistrationToken:(NSString *)fcmToken
+    NS_SWIFT_NAME(messaging(_:didReceiveRegistrationToken:));
+
+/// This method is called on iOS 10 devices to handle data messages received via FCM through its
+/// direct channel (not via APNS). For iOS 9 and below, the FCM data message is delivered via the
+/// UIApplicationDelegate's -application:didReceiveRemoteNotification: method.
+- (void)messaging:(FIRMessaging *)messaging
+    didReceiveMessage:(FIRMessagingRemoteMessage *)remoteMessage
+    NS_SWIFT_NAME(messaging(_:didReceive:))
+    __IOS_AVAILABLE(10.0);
+
+@end
+
+/**
+ *  Firebase Messaging lets you reliably deliver messages at no cost.
+ *
+ *  To send or receive messages, the app must get a
+ *  registration token from FIRInstanceID. This token authorizes an
+ *  app server to send messages to an app instance.
+ *
+ *  In order to receive FIRMessaging messages, declare `application:didReceiveRemoteNotification:`.
+ */
+NS_SWIFT_NAME(Messaging)
+@interface FIRMessaging : NSObject
+
+/**
+ * Delegate to handle FCM token refreshes, and remote data messages received via FCM for devices
+ * running iOS 10 or above.
+ */
+@property(nonatomic, weak, nullable) id<FIRMessagingDelegate> delegate;
+
+/**
+ *  When set to `YES`, Firebase Messaging will automatically establish a socket-based, direct
+ *  channel to the FCM server. Enable this only if you are sending upstream messages or
+ *  receiving non-APNS, data-only messages in foregrounded apps.
+ *  Default is `NO`.
+ */
+@property(nonatomic) BOOL shouldEstablishDirectChannel;
+
+/**
+ *  Returns `YES` if the direct channel to the FCM server is active, and `NO` otherwise.
+ */
+@property(nonatomic, readonly) BOOL isDirectChannelEstablished;
+
+/**
+ *  FIRMessaging
+ *
+ *  @return An instance of FIRMessaging.
+ */
++ (instancetype)messaging NS_SWIFT_NAME(messaging());
+
+/**
+ *  Unavailable. Use +messaging instead.
+ */
+- (instancetype)init __attribute__((unavailable("Use +messaging instead.")));
+
+#pragma mark - APNS
+
+/**
+ *  This property is used to set the APNS Token received by the application delegate.
+ *
+ *  FIRMessaging uses method swizzling to ensure that the APNS token is set
+ *  automatically. However, if you have disabled swizzling by setting
+ *  `FirebaseAppDelegateProxyEnabled` to `NO` in your app's
+ *  Info.plist, you should manually set the APNS token in your application
+ *  delegate's `-application:didRegisterForRemoteNotificationsWithDeviceToken:`
+ *  method.
+ *
+ *  If you would like to set the type of the APNS token, rather than relying on
+ *  automatic detection, see: `-setAPNSToken:type:`.
+ */
+@property(nonatomic, copy, nullable) NSData *APNSToken NS_SWIFT_NAME(apnsToken);
+
+/**
+ *  Set APNS token for the application. This APNS token will be used to register
+ *  with Firebase Messaging using `FCMToken` or
+ *  `tokenWithAuthorizedEntity:scope:options:handler`.
+ *
+ *  @param apnsToken The APNS token for the application.
+ *  @param type  The type of APNS token. Debug builds should use
+ *  FIRMessagingAPNSTokenTypeSandbox. Alternatively, you can supply
+ *  FIRMessagingAPNSTokenTypeUnknown to have the type automatically
+ *  detected based on your provisioning profile.
+ */
+- (void)setAPNSToken:(NSData *)apnsToken type:(FIRMessagingAPNSTokenType)type;
+
+#pragma mark - FCM Tokens
+
+/**
+ * Is Firebase Messaging token auto generation enabled?  If this flag is disabled,
+ * Firebase Messaging will not generate token automatically for message delivery.
+ *
+ * If this flag is disabled, Firebase Messaging does not generate new tokens automatically for
+ * message delivery. If this flag is enabled, FCM generates a registration token on application
+ * start when there is no existing valid token. FCM also generates a new token when an existing
+ * token is deleted.
+ *
+ * This setting is persisted, and is applied on future
+ * invocations of your application.  Once explicitly set, it overrides any
+ * settings in your Info.plist.
+ *
+ * By default, FCM automatic initialization is enabled.  If you need to change the
+ * default (for example, because you want to prompt the user before getting token)
+ * set FirebaseMessagingAutoInitEnabled to false in your application's Info.plist.
+ */
+@property(nonatomic, assign, getter=isAutoInitEnabled) BOOL autoInitEnabled;
+
+/**
+ *  The FCM token is used to identify this device so that FCM can send notifications to it.
+ *  It is associated with your APNS token when the APNS token is supplied, so that sending
+ *  messages to the FCM token will be delivered over APNS.
+ *
+ *  The FCM token is sometimes refreshed automatically. In your FIRMessaging delegate, the
+ *  delegate method `messaging:didReceiveRegistrationToken:` will be called once a token is
+ *  available, or has been refreshed. Typically it should be called once per app start, but
+ *  may be called more often, if token is invalidated or updated.
+ *
+ *  Once you have an FCM token, you should send it to your application server, so it can use
+ *  the FCM token to send notifications to your device.
+ */
+@property(nonatomic, readonly, nullable) NSString *FCMToken NS_SWIFT_NAME(fcmToken);
+
+
+/**
+ *  Retrieves an FCM registration token for a particular Sender ID. This can be used to allow
+ *  multiple senders to send notifications to the same device. By providing a different Sender
+ *  ID than your default when fetching a token, you can create a new FCM token which you can
+ *  give to a different sender. Both tokens will deliver notifications to your device, and you
+ *  can revoke a token when you need to.
+ *
+ *  This registration token is not cached by FIRMessaging. FIRMessaging should have an APNS
+ *  token set before calling this to ensure that notifications can be delivered via APNS using
+ *  this FCM token. You may re-retrieve the FCM token once you have the APNS token set, to
+ *  associate it with the FCM token. The default FCM token is automatically associated with
+ *  the APNS token, if the APNS token data is available.
+ *
+ *  @param senderID The Sender ID for a particular Firebase project.
+ *  @param completion The completion handler to handle the token request.
+ */
+- (void)retrieveFCMTokenForSenderID:(NSString *)senderID
+                         completion:(FIRMessagingFCMTokenFetchCompletion)completion
+    NS_SWIFT_NAME(retrieveFCMToken(forSenderID:completion:));
+
+
+/**
+ *  Invalidates an FCM token for a particular Sender ID. That Sender ID cannot no longer send
+ *  notifications to that FCM token.
+ *
+ *  @param senderID The senderID for a particular Firebase project.
+ *  @param completion The completion handler to handle the token deletion.
+ */
+- (void)deleteFCMTokenForSenderID:(NSString *)senderID
+                       completion:(FIRMessagingDeleteFCMTokenCompletion)completion
+    NS_SWIFT_NAME(deleteFCMToken(forSenderID:completion:));
+
+
+#pragma mark - Connect
+
+/**
+ *  Create a FIRMessaging data connection which will be used to send the data notifications
+ *  sent 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.
+ *
+ *
+ *  @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:(FIRMessagingConnectCompletion)handler
+    NS_SWIFT_NAME(connect(handler:))
+    __deprecated_msg("Please use the shouldEstablishDirectChannel property instead.");
+
+/**
+ *  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 the background, it is
+ *  prudent to close the connection.
+ */
+- (void)disconnect
+      __deprecated_msg("Please use the shouldEstablishDirectChannel property instead.");
+
+#pragma mark - Topics
+
+/**
+ *  Asynchronously subscribes to a topic.
+ *
+ *  @param topic The name of the topic, for example, @"sports".
+ */
+- (void)subscribeToTopic:(NSString *)topic NS_SWIFT_NAME(subscribe(toTopic:));
+
+/**
+ *  Asynchronously subscribe to the provided topic, retrying on failure.
+ *
+ *  @param topic       The topic name to subscribe to, for example, @"sports".
+ *  @param completion  The completion that is invoked once the subscribe call ends.
+ *                     In case of success, nil error is returned. Otherwise, an
+ *                     appropriate error object is returned.
+ */
+- (void)subscribeToTopic:(nonnull NSString *)topic
+              completion:(nullable FIRMessagingTopicOperationCompletion)completion;
+
+/**
+ *  Asynchronously unsubscribe from a topic.
+ *
+ *  @param topic The name of the topic, for example @"sports".
+ */
+- (void)unsubscribeFromTopic:(NSString *)topic NS_SWIFT_NAME(unsubscribe(fromTopic:));
+
+/**
+ *  Asynchronously unsubscribe from the provided topic, retrying on failure.
+ *
+ *  @param topic       The topic name to unsubscribe from, for example @"sports".
+ *  @param completion  The completion that is invoked once the unsubscribe call ends.
+ *                     In case of success, nil error is returned. Otherwise, an
+ *                     appropriate error object is returned.
+ */
+- (void)unsubscribeFromTopic:(nonnull NSString *)topic
+                  completion:(nullable FIRMessagingTopicOperationCompletion)completion;
+
+#pragma mark - Upstream
+
+/**
+ *  Sends an upstream ("device to cloud") message.
+ *
+ *  The message is queued if we don't have an active connection.
+ *  You can only use the upstream feature if your FCM implementation
+ *  uses the XMPP server protocol.
+ *
+ *  @param message      Key/Value pairs to be sent. Values must be String, any
+ *                      other type will be ignored.
+ *  @param receiver     A string identifying the receiver of the message. For FCM
+ *                      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:(NSDictionary *)message
+                 to:(NSString *)receiver
+      withMessageID:(NSString *)messageID
+         timeToLive:(int64_t)ttl;
+
+#pragma mark - Analytics
+
+/**
+ *  Use this to track message delivery and analytics for messages, typically
+ *  when you receive a notification in `application:didReceiveRemoteNotification:`.
+ *  However, you only need to call this if you set the `FirebaseAppDelegateProxyEnabled`
+ *  flag to `NO` in your Info.plist. If `FirebaseAppDelegateProxyEnabled` is either missing
+ *  or set to `YES` in your Info.plist, the library will call this automatically.
+ *
+ *  @param message The downstream message received by the application.
+ *
+ *  @return Information about the downstream message.
+ */
+- (FIRMessagingMessageInfo *)appDidReceiveMessage:(NSDictionary *)message;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Public/FirebaseMessaging.h b/iOS/Pods/FirebaseMessaging/Firebase/Messaging/Public/FirebaseMessaging.h
new file mode 100755 (executable)
index 0000000..ef081c9
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "FIRMessaging.h"
diff --git a/iOS/Pods/FirebaseMessaging/LICENSE b/iOS/Pods/FirebaseMessaging/LICENSE
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -0,0 +1,202 @@
+
+                                 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/iOS/Pods/FirebaseMessaging/README.md b/iOS/Pods/FirebaseMessaging/README.md
new file mode 100644 (file)
index 0000000..4414b3e
--- /dev/null
@@ -0,0 +1,179 @@
+# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
+
+This repository contains a subset of the Firebase iOS SDK source. It currently
+includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
+FirebaseFunctions, FirebaseMessaging and FirebaseStorage.
+
+The repository also includes GoogleUtilities source. The
+[GoogleUtilities](GoogleUtilities/README.md) pod is
+a set of utilities used by Firebase and other Google products.
+
+Firebase is an app development platform with tools to help you build, grow and
+monetize your app. More information about Firebase can be found at
+[https://firebase.google.com](https://firebase.google.com).
+
+## Installation
+
+See the three subsections for details about three different installation methods.
+1. [Standard pod install](README.md#standard-pod-install)
+1. [Installing from the GitHub repo](README.md#installing-from-github)
+1. [Experimental Carthage](README.md#carthage-ios-only)
+
+### Standard pod install
+
+Go to
+[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
+
+### Installing from GitHub
+
+For releases starting with 5.0.0, the source for each release is also deployed
+to CocoaPods master and available via standard
+[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
+
+These instructions can be used to access the Firebase repo at other branches,
+tags, or commits.
+
+#### Background
+
+See
+[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
+for instructions and options about overriding pod source locations.
+
+#### Accessing Firebase Source Snapshots
+
+All of the official releases are tagged in this repo and available via CocoaPods. To access a local
+source snapshot or unreleased branch, use Podfile directives like the following:
+
+To access FirebaseFirestore via a branch:
+```
+pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
+pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
+```
+
+To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
+
+```
+pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
+pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
+```
+
+### Carthage (iOS only)
+
+An experimental Carthage distribution is now available. See
+[Carthage](Carthage.md).
+
+## Development
+
+Follow the subsequent instructions to develop, debug, unit test, run integration
+tests, and try out reference samples:
+
+```
+$ git clone git@github.com:firebase/firebase-ios-sdk.git
+$ cd firebase-ios-sdk/Example
+$ pod update
+$ open Firebase.xcworkspace
+```
+
+Firestore and Functions have self contained Xcode projects. See
+[Firestore/README.md](Firestore/README.md) and
+[Functions/README.md](Functions/README.md).
+
+### Running Unit Tests
+
+Select a scheme and press Command-u to build a component and run its unit tests.
+
+### Running Sample Apps
+In order to run the sample apps and integration tests, you'll need valid
+`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
+files without real values, but can be replaced with real plist files. To get your own
+`GoogleService-Info.plist` files:
+
+1. Go to the [Firebase Console](https://console.firebase.google.com/)
+2. Create a new Firebase project, if you don't already have one
+3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
+identifier (e.g. `com.google.Database-Example`)
+4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
+(e.g. in [Example/Database/App/](Example/Database/App/));
+
+Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
+special Apple capabilities, and you will have to change the sample app to use a unique bundle
+identifier that you can control in your own Apple Developer account.
+
+## Specific Component Instructions
+See the sections below for any special instructions for those components.
+
+### Firebase Auth
+
+If you're doing specific Firebase Auth development, see
+[AuthSamples/README.md](AuthSamples/README.md) for instructions about
+building and running the FirebaseAuth pod along with various samples and tests.
+
+### Firebase Database
+
+To run the Database Integration tests, make your database authentication rules
+[public](https://firebase.google.com/docs/database/security/quickstart).
+
+### Firebase Storage
+
+To run the Storage Integration tests, follow the instructions in
+[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
+
+#### Push Notifications
+
+Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
+In order to actually test receiving push notifications, you will need to:
+
+1. Change the bundle identifier of the sample app to something you own in your Apple Developer
+account, and enable that App ID for push notifications.
+2. You'll also need to
+[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
+at **Project Settings > Cloud Messaging > [Your Firebase App]**.
+3. Ensure your iOS device is added to your Apple Developer portal as a test device.
+
+#### iOS Simulator
+
+The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
+In order to receive push notifications, you'll have to follow the steps above and run the app on a
+physical device.
+
+## Community Supported Efforts
+
+We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
+very grateful!  We'd like to empower as many developers as we can to be able to use Firebase and
+participate in the Firebase community.
+
+### macOS and tvOS
+FirebaseAuth, FirebaseCore, FirebaseDatabase and FirebaseStorage now compile, run unit tests, and
+work on macOS and tvOS, thanks to contributions from the community. There are a few tweaks needed,
+like ensuring iOS-only, macOS-only, or tvOS-only code is correctly guarded with checks for
+`TARGET_OS_IOS`, `TARGET_OS_OSX` and `TARGET_OS_TV`.
+
+For tvOS, checkout the [Sample](Example/tvOSSample).
+
+Keep in mind that macOS and tvOS are not officially supported by Firebase, and this repository is
+actively developed primarily for iOS. While we can catch basic unit test issues with Travis, there
+may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter
+this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
+
+For installation instructions, see [above](README.md#accessing-firebase-source-snapshots).
+
+Note that the Firebase pod is not available for macOS and tvOS. Install a selection of the
+`FirebaseAuth`, `FirebaseCore`, `FirebaseDatabase` and `FirebaseStorage` CocoaPods.
+
+## Roadmap
+
+See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
+plans and directions.
+
+## Contributing
+
+See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
+iOS SDK.
+
+## License
+
+The contents of this repository is licensed under the
+[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
+
+Your use of Firebase is governed by the
+[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
diff --git a/iOS/Pods/FolioReaderKit/LICENSE b/iOS/Pods/FolioReaderKit/LICENSE
new file mode 100644 (file)
index 0000000..8a61285
--- /dev/null
@@ -0,0 +1,27 @@
+Copyright (c) 2015-2017, Heberti Almeida
+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 FolioReaderKit nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/iOS/Pods/FolioReaderKit/README.md b/iOS/Pods/FolioReaderKit/README.md
new file mode 100644 (file)
index 0000000..e4d580a
--- /dev/null
@@ -0,0 +1,155 @@
+
+![FolioReader logo](https://raw.githubusercontent.com/FolioReader/FolioReaderKit/assets/folioreader.png)
+FolioReaderKit is an ePub reader and parser framework for iOS written in Swift.
+
+![Version](https://img.shields.io/cocoapods/v/FolioReaderKit.svg)
+![Downloads](https://img.shields.io/cocoapods/dt/FolioReaderKit.svg)
+![Apps using](https://img.shields.io/cocoapods/at/FolioReaderKit.svg)
+![License](https://img.shields.io/cocoapods/l/FolioReaderKit.svg)
+
+## Features
+
+- [x] ePub 2 and ePub 3 support
+- [x] Custom Fonts
+- [x] Custom Text Size
+- [x] Text Highlighting
+- [x] List / Edit / Delete Highlights
+- [x] Themes / Day mode / Night mode
+- [x] Handle Internal and External Links
+- [x] Portrait / Landscape
+- [x] Reading Time Left / Pages left
+- [x] In-App Dictionary
+- [x] Media Overlays (Sync text rendering with audio playback)
+- [x] TTS - Text to Speech Support
+- [x] Parse epub cover image
+- [x] RTL Support
+- [x] Vertical or/and Horizontal scrolling
+- [x] Share Custom Image Quotes **<sup>NEW</sup>**
+- [x] Support multiple instances at same time, like parallel reading **<sup>NEW</sup>**
+- [ ] Book Search
+- [ ] Add Notes to a Highlight
+
+## Demo
+
+**Custom Fonts :smirk:**   |  **Text Highlighting :heart_eyes:**
+:-------------------------:|:-------------------------:
+![Custom fonts](https://raw.githubusercontent.com/FolioReader/FolioReaderKit/assets/custom-fonts.gif)  |  ![Highlight](https://raw.githubusercontent.com/FolioReader/FolioReaderKit/assets/highlight.gif)
+
+**Reading Time Left :open_mouth:**   |  **Media Overlays 😭**
+:-------------------------:|:-------------------------:
+![Time left](https://raw.githubusercontent.com/FolioReader/FolioReaderKit/assets/time-left.mov.gif)  |  ![Media Overlays](https://raw.githubusercontent.com/FolioReader/FolioReaderKit/assets/media-overlays.gif)
+
+## Installation
+
+**FolioReaderKit** is available through [CocoaPods](http://cocoapods.org) and [Carthage](https://github.com/Carthage/Carthage). 
+
+### Cocoapods
+
+[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
+
+```bash
+$ gem install cocoapods
+```
+
+To integrate FolioReaderKit 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!
+
+target '<Your Target Name>' do
+    pod 'FolioReaderKit'
+end
+```
+
+Then, run the following command:
+
+```bash
+$ pod install
+```
+
+Alternatively to give it a test run, run the command:
+
+```bash
+$ pod try FolioReaderKit
+```
+
+### Carthage
+
+Add the following to your [Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile)
+
+```ruby
+github "FolioReader/FolioReaderKit"
+```
+
+Run the following command:
+
+```bash
+$ carthage update --platform iOS --no-use-binaries
+```
+
+Then, follow the steps as described in Carthage's [README](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).
+
+## Requirements
+
+- iOS 8.0+
+- Xcode 8.3+
+
+## Basic Usage
+
+To get started, this is a simple usage sample of using the integrated view controller.
+
+```swift
+import FolioReaderKit
+
+func open(sender: AnyObject) {
+    let config = FolioReaderConfig()
+    let bookPath = Bundle.main.path(forResource: "book", ofType: "epub")
+    let folioReader = FolioReader()
+    folioReader.presentReader(parentViewController: self, withEpubPath: bookPath!, andConfig: config)
+}
+```
+
+For more usage examples check the [Example](/Example) folder.
+
+## Storyboard
+
+To get started, here is a simple example how to use the integrated view controller with storyboards.
+
+```swift
+import FolioReaderKit
+
+class StoryboardFolioReaderContrainer: FolioReaderContainer {
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        
+        let config = FolioReaderConfig()
+        config.scrollDirection = .horizontalWithVerticalContent
+        
+        guard let bookPath = Bundle.main.path(forResource: "The Silver Chair", ofType: "epub") else { return }
+        setupConfig(config, epubPath: bookPath)
+    }
+}
+```
+
+Go to your storyboard file, choose or create the view controller that should present the epub reader. In the identity inspector set StoryboardFolioReaderContrainer as class.
+
+## Documentation
+Checkout [Example](/Example) and [API Documentation](http://cocoadocs.org/docsets/FolioReaderKit)
+
+You can always use the header-doc. (use **alt+click** in Xcode)
+
+<img src="https://raw.githubusercontent.com/FolioReader/FolioReaderKit/assets/header-doc.png" width="521px"/>
+
+### Migration
+If you are migrating to a newer version check out [MIGRATION](/MIGRATION.md) and [CHANGELOG](/CHANGELOG.md).
+
+## Author
+[**Heberti Almeida**](https://github.com/hebertialmeida)
+
+- Follow me on **Twitter**: [**@hebertialmeida**](https://twitter.com/hebertialmeida)
+- Contact me on **LinkedIn**: [**hebertialmeida**](http://linkedin.com/in/hebertialmeida)
+
+## License
+FolioReaderKit is available under the BSD license. See the [LICENSE](/LICENSE) file.
diff --git a/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRBook.swift b/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRBook.swift
new file mode 100755 (executable)
index 0000000..bf8887c
--- /dev/null
@@ -0,0 +1,87 @@
+//
+//  FRBook.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 09/04/15.
+//  Extended by Kevin Jantzer on 12/30/15
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+open class FRBook: NSObject {
+    var metadata = FRMetadata()
+    var spine = FRSpine()
+    var smils = FRSmils()
+    var version: Double?
+    
+    public var opfResource: FRResource!
+    public var tocResource: FRResource?
+    public var uniqueIdentifier: String?
+    public var coverImage: FRResource?
+    public var name: String?
+    public var resources = FRResources()
+    public var tableOfContents: [FRTocReference]!
+    public var flatTableOfContents: [FRTocReference]!
+
+    var hasAudio: Bool {
+        return smils.smils.count > 0
+    }
+
+    var title: String? {
+        return metadata.titles.first
+    }
+
+    var authorName: String? {
+        return metadata.creators.first?.name
+    }
+
+    // MARK: - Media Overlay Metadata
+    // http://www.idpf.org/epub/301/spec/epub-mediaoverlays.html#sec-package-metadata
+
+    var duration: String? {
+        return metadata.find(byProperty: "media:duration")?.value
+    }
+
+    var activeClass: String {
+        guard let className = metadata.find(byProperty: "media:active-class")?.value else {
+            return "epub-media-overlay-active"
+        }
+        return className
+    }
+
+    var playbackActiveClass: String {
+        guard let className = metadata.find(byProperty: "media:playback-active-class")?.value else {
+            return "epub-media-overlay-playing"
+        }
+        return className
+    }
+
+    // MARK: - Media Overlay (SMIL) retrieval
+
+    /**
+     Get Smil File from a resource (if it has a media-overlay)
+     */
+    func smilFileForResource(_ resource: FRResource?) -> FRSmilFile? {
+        guard let resource = resource, let mediaOverlay = resource.mediaOverlay else { return nil }
+
+        // lookup the smile resource to get info about the file
+        guard let smilResource = resources.findById(mediaOverlay) else { return nil }
+
+        // use the resource to get the file
+        return smils.findByHref(smilResource.href)
+    }
+
+    func smilFile(forHref href: String) -> FRSmilFile? {
+        return smilFileForResource(resources.findByHref(href))
+    }
+
+    func smilFile(forId ID: String) -> FRSmilFile? {
+        return smilFileForResource(resources.findById(ID))
+    }
+    
+    // @NOTE: should "#" be automatically prefixed with the ID?
+    func duration(for ID: String) -> String? {
+        return metadata.find(byProperty: "media:duration", refinedBy: ID)?.value
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/EPUBCore/FREpubParser.swift b/iOS/Pods/FolioReaderKit/Source/EPUBCore/FREpubParser.swift
new file mode 100755 (executable)
index 0000000..a9879c5
--- /dev/null
@@ -0,0 +1,492 @@
+//
+//  FREpubParser.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 04/05/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+import AEXML
+#if COCOAPODS
+import SSZipArchive
+#else
+import ZipArchive
+#endif
+
+class FREpubParser: NSObject, SSZipArchiveDelegate {
+
+    let book = FRBook()
+    private var resourcesBasePath = ""
+    private var shouldRemoveEpub = true
+    private var epubPathToRemove: String?
+
+    /// Parse the Cover Image from an epub file.
+    ///
+    /// - Parameters:
+    ///   - epubPath: Epub path on the disk.
+    ///   - unzipPath: Path to unzip the compressed epub.
+    /// - Returns: The book cover as UIImage object
+    /// - Throws: `FolioReaderError`
+    func parseCoverImage(_ epubPath: String, unzipPath: String? = nil) throws -> UIImage {
+        guard let book = try? readEpub(epubPath: epubPath, removeEpub: false, unzipPath: unzipPath),
+            let coverImage = book.coverImage else {
+                throw FolioReaderError.coverNotAvailable
+        }
+
+        guard let image = UIImage(contentsOfFile: coverImage.fullHref) else {
+            throw FolioReaderError.invalidImage(path: coverImage.fullHref)
+        }
+
+        return image
+    }
+
+    /// Parse the book title from an epub file.
+    ///
+    /// - Parameters:
+    ///   - epubPath: Epub path on the disk.
+    ///   - unzipPath: Path to unzip the compressed epub.
+    /// - Returns: The book title
+    /// - Throws: `FolioReaderError`
+    func parseTitle(_ epubPath: String, unzipPath: String? = nil) throws -> String {
+        guard let book = try? readEpub(epubPath: epubPath, removeEpub: false, unzipPath: unzipPath), let title = book.title else {
+             throw FolioReaderError.titleNotAvailable
+        }
+        return title
+    }
+
+
+    /// Parse the book Author name from an epub file.
+    ///
+    /// - Parameters:
+    ///   - epubPath: Epub path on the disk.
+    ///   - unzipPath: Path to unzip the compressed epub.
+    /// - Returns: The author name
+    /// - Throws: `FolioReaderError`
+    func parseAuthorName(_ epubPath: String, unzipPath: String? = nil) throws -> String {
+        guard let book = try? readEpub(epubPath: epubPath, removeEpub: false, unzipPath: unzipPath), let authorName = book.authorName else {
+            throw FolioReaderError.authorNameNotAvailable
+        }
+        return authorName
+    }
+
+    /// Unzip, delete and read an epub file.
+    ///
+    /// - Parameters:
+    ///   - withEpubPath: Epub path on the disk
+    ///   - removeEpub: Should remove the original file?
+    ///   - unzipPath: Path to unzip the compressed epub.
+    /// - Returns: `FRBook` Object
+    /// - Throws: `FolioReaderError`
+    func readEpub(epubPath withEpubPath: String, removeEpub: Bool = true, unzipPath: String? = nil) throws -> FRBook {
+        epubPathToRemove = withEpubPath
+        shouldRemoveEpub = removeEpub
+
+        var isDir: ObjCBool = false
+        let fileManager = FileManager.default
+        let bookName = withEpubPath.lastPathComponent
+        var bookBasePath = ""
+
+        if let path = unzipPath, fileManager.fileExists(atPath: path) {
+            bookBasePath = path
+        } else {
+            bookBasePath = kApplicationDocumentsDirectory
+        }
+
+        bookBasePath = bookBasePath.appendingPathComponent(bookName)
+
+        guard fileManager.fileExists(atPath: withEpubPath) else {
+            throw FolioReaderError.bookNotAvailable
+        }
+
+        // Unzip if necessary
+        let needsUnzip = !fileManager.fileExists(atPath: bookBasePath, isDirectory:&isDir) || !isDir.boolValue
+
+        if needsUnzip {
+            SSZipArchive.unzipFile(atPath: withEpubPath, toDestination: bookBasePath, delegate: self)
+        }
+
+        // Skip from backup this folder
+        try addSkipBackupAttributeToItemAtURL(URL(fileURLWithPath: bookBasePath, isDirectory: true))
+
+        book.name = bookName
+        try readContainer(with: bookBasePath)
+        try readOpf(with: bookBasePath)
+        return self.book
+    }
+
+    /// Read and parse container.xml file.
+    ///
+    /// - Parameter bookBasePath: The base book path
+    /// - Throws: `FolioReaderError`
+    private func readContainer(with bookBasePath: String) throws {
+        let containerPath = "META-INF/container.xml"
+        let containerData = try Data(contentsOf: URL(fileURLWithPath: bookBasePath.appendingPathComponent(containerPath)), options: .alwaysMapped)
+        let xmlDoc = try AEXMLDocument(xml: containerData)
+        let opfResource = FRResource()
+        opfResource.href = xmlDoc.root["rootfiles"]["rootfile"].attributes["full-path"]
+        guard let fullPath = xmlDoc.root["rootfiles"]["rootfile"].attributes["full-path"] else {
+            throw FolioReaderError.fullPathEmpty
+        }
+        opfResource.mediaType = MediaType.by(fileName: fullPath)
+        book.opfResource = opfResource
+        resourcesBasePath = bookBasePath.appendingPathComponent(book.opfResource.href.deletingLastPathComponent)
+    }
+
+    /// Read and parse .opf file.
+    ///
+    /// - Parameter bookBasePath: The base book path
+    /// - Throws: `FolioReaderError`
+    private func readOpf(with bookBasePath: String) throws {
+        let opfPath = bookBasePath.appendingPathComponent(book.opfResource.href)
+        var identifier: String?
+
+        let opfData = try Data(contentsOf: URL(fileURLWithPath: opfPath), options: .alwaysMapped)
+        let xmlDoc = try AEXMLDocument(xml: opfData)
+
+        // Base OPF info
+        if let package = xmlDoc.children.first {
+            identifier = package.attributes["unique-identifier"]
+
+            if let version = package.attributes["version"] {
+                book.version = Double(version)
+            }
+        }
+
+        // Parse and save each "manifest item"
+        xmlDoc.root["manifest"]["item"].all?.forEach {
+            let resource = FRResource()
+            resource.id = $0.attributes["id"]
+            resource.properties = $0.attributes["properties"]
+            resource.href = $0.attributes["href"]
+            resource.fullHref = resourcesBasePath.appendingPathComponent(resource.href).removingPercentEncoding
+            resource.mediaType = MediaType.by(name: $0.attributes["media-type"] ?? "", fileName: resource.href)
+            resource.mediaOverlay = $0.attributes["media-overlay"]
+
+            // if a .smil file is listed in resources, go parse that file now and save it on book model
+            if (resource.mediaType != nil && resource.mediaType == .smil) {
+                readSmilFile(resource)
+            }
+
+            book.resources.add(resource)
+        }
+
+        book.smils.basePath = resourcesBasePath
+
+        // Read metadata
+        book.metadata = readMetadata(xmlDoc.root["metadata"].children)
+
+        // Read the book unique identifier
+        if let identifier = identifier, let uniqueIdentifier = book.metadata.find(identifierById: identifier) {
+            book.uniqueIdentifier = uniqueIdentifier.value
+        }
+
+        // Read the cover image
+        let coverImageId = book.metadata.find(byName: "cover")?.content
+        if let coverImageId = coverImageId, let coverResource = book.resources.findById(coverImageId) {
+            book.coverImage = coverResource
+        } else if let coverResource = book.resources.findByProperty("cover-image") {
+            book.coverImage = coverResource
+        }
+
+        // Specific TOC for ePub 2 and 3
+        // Get the first resource with the NCX mediatype
+        if let tocResource = book.resources.findByMediaType(MediaType.ncx) {
+            book.tocResource = tocResource
+        } else if let tocResource = book.resources.findByExtension(MediaType.ncx.defaultExtension) {
+            // Non-standard books may use wrong mediatype, fallback with extension
+            book.tocResource = tocResource
+        } else if let tocResource = book.resources.findByProperty("nav") {
+            book.tocResource = tocResource
+        }
+
+        precondition(book.tocResource != nil, "ERROR: Could not find table of contents resource. The book don't have a TOC resource.")
+
+        // The book TOC
+        book.tableOfContents = findTableOfContents()
+        book.flatTableOfContents = flatTOC
+
+        // Read Spine
+        let spine = xmlDoc.root["spine"]
+        book.spine = readSpine(spine.children)
+
+        // Page progress direction `ltr` or `rtl`
+        if let pageProgressionDirection = spine.attributes["page-progression-direction"] {
+            book.spine.pageProgressionDirection = pageProgressionDirection
+        }
+    }
+
+    /// Reads and parses a .smil file.
+    ///
+    /// - Parameter resource: A `FRResource` to read the smill
+    private func readSmilFile(_ resource: FRResource) {
+        do {
+            let smilData = try Data(contentsOf: URL(fileURLWithPath: resource.fullHref), options: .alwaysMapped)
+            var smilFile = FRSmilFile(resource: resource)
+            let xmlDoc = try AEXMLDocument(xml: smilData)
+
+            let children = xmlDoc.root["body"].children
+
+            if children.count > 0 {
+                smilFile.data.append(contentsOf: readSmilFileElements(children))
+            }
+
+            book.smils.add(smilFile)
+        } catch {
+            print("Cannot read .smil file: "+resource.href)
+        }
+    }
+
+    private func readSmilFileElements(_ children: [AEXMLElement]) -> [FRSmilElement] {
+        var data = [FRSmilElement]()
+
+        // convert each smil element to a FRSmil object
+        children.forEach{
+            let smil = FRSmilElement(name: $0.name, attributes: $0.attributes)
+
+            // if this element has children, convert them to objects too
+            if $0.children.count > 0 {
+                smil.children.append(contentsOf: readSmilFileElements($0.children))
+            }
+
+            data.append(smil)
+        }
+
+        return data
+    }
+
+    /// Read and parse the Table of Contents.
+    ///
+    /// - Returns: A list of toc references
+    private func findTableOfContents() -> [FRTocReference] {
+        var tableOfContent = [FRTocReference]()
+        var tocItems: [AEXMLElement]?
+        guard let tocResource = book.tocResource else { return tableOfContent }
+        let tocPath = resourcesBasePath.appendingPathComponent(tocResource.href)
+
+        do {
+            if tocResource.mediaType == MediaType.ncx {
+                let ncxData = try Data(contentsOf: URL(fileURLWithPath: tocPath), options: .alwaysMapped)
+                let xmlDoc = try AEXMLDocument(xml: ncxData)
+                if let itemsList = xmlDoc.root["navMap"]["navPoint"].all {
+                    tocItems = itemsList
+                }
+            } else {
+                let tocData = try Data(contentsOf: URL(fileURLWithPath: tocPath), options: .alwaysMapped)
+                let xmlDoc = try AEXMLDocument(xml: tocData)
+
+                if let nav = xmlDoc.root["body"]["nav"].first, let itemsList = nav["ol"]["li"].all {
+                    tocItems = itemsList
+                } else if let nav = findNavTag(xmlDoc.root["body"]), let itemsList = nav["ol"]["li"].all {
+                    tocItems = itemsList
+                }
+            }
+        } catch {
+            print("Cannot find Table of Contents.")
+        }
+
+        guard let items = tocItems else { return tableOfContent }
+
+        for item in items {
+            guard let ref = readTOCReference(item) else { continue }
+            tableOfContent.append(ref)
+        }
+
+        return tableOfContent
+    }
+
+    /// Recursively finds a `<nav>` tag on html.
+    ///
+    /// - Parameter element: An `AEXMLElement`, usually the `<body>`
+    /// - Returns: If found the `<nav>` `AEXMLElement`
+    @discardableResult func findNavTag(_ element: AEXMLElement) -> AEXMLElement? {
+        for element in element.children {
+            if let nav = element["nav"].first {
+                return nav
+            } else {
+                findNavTag(element)
+            }
+        }
+        return nil
+    }
+
+    fileprivate func readTOCReference(_ navpointElement: AEXMLElement) -> FRTocReference? {
+        var label = ""
+
+        if book.tocResource?.mediaType == MediaType.ncx {
+            if let labelText = navpointElement["navLabel"]["text"].value {
+                label = labelText
+            }
+
+            guard let reference = navpointElement["content"].attributes["src"] else { return nil }
+            let hrefSplit = reference.split {$0 == "#"}.map { String($0) }
+            let fragmentID = hrefSplit.count > 1 ? hrefSplit[1] : ""
+            let href = hrefSplit[0]
+
+            let resource = book.resources.findByHref(href)
+            let toc = FRTocReference(title: label, resource: resource, fragmentID: fragmentID)
+
+            // Recursively find child
+            if let navPoints = navpointElement["navPoint"].all {
+                for navPoint in navPoints {
+                    guard let item = readTOCReference(navPoint) else { continue }
+                    toc.children.append(item)
+                }
+            }
+            return toc
+        } else {
+            if let labelText = navpointElement["a"].value {
+                label = labelText
+            }
+
+            guard let reference = navpointElement["a"].attributes["href"] else { return nil }
+            let hrefSplit = reference.split {$0 == "#"}.map { String($0) }
+            let fragmentID = hrefSplit.count > 1 ? hrefSplit[1] : ""
+            let href = hrefSplit[0]
+
+            let resource = book.resources.findByHref(href)
+            let toc = FRTocReference(title: label, resource: resource, fragmentID: fragmentID)
+
+            // Recursively find child
+            if let navPoints = navpointElement["ol"]["li"].all {
+                for navPoint in navPoints {
+                    guard let item = readTOCReference(navPoint) else { continue }
+                    toc.children.append(item)
+                }
+            }
+            return toc
+        }
+    }
+
+    // MARK: - Recursive add items to a list
+
+    var flatTOC: [FRTocReference] {
+        var tocItems = [FRTocReference]()
+
+        for item in book.tableOfContents {
+            tocItems.append(item)
+            tocItems.append(contentsOf: countTocChild(item))
+        }
+        return tocItems
+    }
+
+    func countTocChild(_ item: FRTocReference) -> [FRTocReference] {
+        var tocItems = [FRTocReference]()
+
+        item.children.forEach {
+            tocItems.append($0)
+        }
+        return tocItems
+    }
+
+    /// Read and parse <metadata>.
+    ///
+    /// - Parameter tags: XHTML tags
+    /// - Returns: Metadata object
+    fileprivate func readMetadata(_ tags: [AEXMLElement]) -> FRMetadata {
+        let metadata = FRMetadata()
+
+        for tag in tags {
+            if tag.name == "dc:title" {
+                metadata.titles.append(tag.value ?? "")
+            }
+
+            if tag.name == "dc:identifier" {
+                let identifier = Identifier(id: tag.attributes["id"], scheme: tag.attributes["opf:scheme"], value: tag.value)
+                metadata.identifiers.append(identifier)
+            }
+
+            if tag.name == "dc:language" {
+                let language = tag.value ?? metadata.language
+                metadata.language = language != "en" ? language : metadata.language
+            }
+
+            if tag.name == "dc:creator" {
+                metadata.creators.append(Author(name: tag.value ?? "", role: tag.attributes["opf:role"] ?? "", fileAs: tag.attributes["opf:file-as"] ?? ""))
+            }
+
+            if tag.name == "dc:contributor" {
+                metadata.creators.append(Author(name: tag.value ?? "", role: tag.attributes["opf:role"] ?? "", fileAs: tag.attributes["opf:file-as"] ?? ""))
+            }
+
+            if tag.name == "dc:publisher" {
+                metadata.publishers.append(tag.value ?? "")
+            }
+
+            if tag.name == "dc:description" {
+                metadata.descriptions.append(tag.value ?? "")
+            }
+
+            if tag.name == "dc:subject" {
+                metadata.subjects.append(tag.value ?? "")
+            }
+
+            if tag.name == "dc:rights" {
+                metadata.rights.append(tag.value ?? "")
+            }
+
+            if tag.name == "dc:date" {
+                metadata.dates.append(EventDate(date: tag.value ?? "", event: tag.attributes["opf:event"] ?? ""))
+            }
+
+            if tag.name == "meta" {
+                if tag.attributes["name"] != nil {
+                    metadata.metaAttributes.append(Meta(name: tag.attributes["name"], content: tag.attributes["content"]))
+                }
+
+                if tag.attributes["property"] != nil && tag.attributes["id"] != nil {
+                    metadata.metaAttributes.append(Meta(id: tag.attributes["id"], property: tag.attributes["property"], value: tag.value))
+                }
+
+                if tag.attributes["property"] != nil {
+                    metadata.metaAttributes.append(Meta(property: tag.attributes["property"], value: tag.value, refines: tag.attributes["refines"]))
+                }
+            }
+        }
+        return metadata
+    }
+
+    /// Read and parse <spine>.
+    ///
+    /// - Parameter tags: XHTML tags
+    /// - Returns: Spine object
+    fileprivate func readSpine(_ tags: [AEXMLElement]) -> FRSpine {
+        let spine = FRSpine()
+
+        for tag in tags {
+            guard let idref = tag.attributes["idref"] else { continue }
+            var linear = true
+
+            if tag.attributes["linear"] != nil {
+                linear = tag.attributes["linear"] == "yes" ? true : false
+            }
+
+            if book.resources.containsById(idref) {
+                guard let resource = book.resources.findById(idref) else { continue }
+                spine.spineReferences.append(Spine(resource: resource, linear: linear))
+            }
+        }
+        return spine
+    }
+
+    /// Skip a file from iCloud backup.
+    ///
+    /// - Parameter url: File URL
+    /// - Throws: Error if not possible
+    fileprivate func addSkipBackupAttributeToItemAtURL(_ url: URL) throws {
+        assert(FileManager.default.fileExists(atPath: url.path))
+
+        var urlToExclude = url
+        var resourceValues = URLResourceValues()
+        resourceValues.isExcludedFromBackup = true
+        try urlToExclude.setResourceValues(resourceValues)
+    }
+
+    // MARK: - SSZipArchive delegate
+
+    func zipArchiveWillUnzipArchive(atPath path: String, zipInfo: unz_global_info) {
+        guard shouldRemoveEpub else { return }
+        guard let epubPathToRemove = epubPathToRemove else { return }
+        try? FileManager.default.removeItem(atPath: epubPathToRemove)
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRMetadata.swift b/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRMetadata.swift
new file mode 100755 (executable)
index 0000000..91e24da
--- /dev/null
@@ -0,0 +1,117 @@
+//
+//  FRMetadata.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 04/05/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+/**
+ Represents one of the authors of the book.
+ */
+struct Author {
+    var name: String
+    var role: String
+    var fileAs: String
+
+    init(name: String, role: String, fileAs: String) {
+        self.name = name
+        self.role = role
+        self.fileAs = fileAs
+    }
+}
+
+/**
+ A Book's identifier.
+ */
+struct Identifier {
+    var id: String?
+    var scheme: String?
+    var value: String?
+
+    init(id: String?, scheme: String?, value: String?) {
+        self.id = id
+        self.scheme = scheme
+        self.value = value
+    }
+}
+
+/**
+ A date and his event.
+ */
+struct EventDate {
+    var date: String
+    var event: String?
+
+    init(date: String, event: String?) {
+        self.date = date
+        self.event = event
+    }
+}
+
+/**
+ A metadata tag data.
+ */
+struct Meta {
+    var name: String?
+    var content: String?
+    var id: String?
+    var property: String?
+    var value: String?
+    var refines: String?
+
+    init(name: String? = nil, content: String? = nil, id: String? = nil, property: String? = nil,
+         value: String? = nil, refines: String? = nil) {
+        self.name = name
+        self.content = content
+        self.id = id
+        self.property = property
+        self.value = value
+        self.property = property
+        self.value = value
+        self.refines = refines
+    }
+}
+
+/**
+ Manages book metadata.
+ */
+class FRMetadata {
+    var creators = [Author]()
+    var contributors = [Author]()
+    var dates = [EventDate]()
+    var language = "en-US"
+    var titles = [String]()
+    var identifiers = [Identifier]()
+    var subjects = [String]()
+    var descriptions = [String]()
+    var publishers = [String]()
+    var format = MediaType.epub.name
+    var rights = [String]()
+    var metaAttributes = [Meta]()
+
+    /**
+     Find a book unique identifier by ID
+
+     - parameter id: The ID
+     - returns: The unique identifier of a book
+     */
+    func find(identifierById id: String) -> Identifier? {
+        return identifiers.filter({ $0.id == id }).first
+    }
+
+    func find(byName name: String) -> Meta? {
+        return metaAttributes.filter({ $0.name == name }).first
+    }
+
+    func find(byProperty property: String, refinedBy: String? = nil) -> Meta? {
+        return metaAttributes.filter {
+            if let refinedBy = refinedBy {
+                return $0.property == property && $0.refines == refinedBy
+            }
+            return $0.property == property
+        }.first
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRResource.swift b/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRResource.swift
new file mode 100755 (executable)
index 0000000..d7a2a76
--- /dev/null
@@ -0,0 +1,32 @@
+//
+//  FRResource.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 29/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+open class FRResource: NSObject {
+    var id: String!
+    var properties: String?
+    var mediaType: MediaType!
+    var mediaOverlay: String?
+    
+    public var href: String!
+    public var fullHref: String!
+
+    func basePath() -> String! {
+        if href == nil || href.isEmpty { return nil }
+        var paths = fullHref.components(separatedBy: "/")
+        paths.removeLast()
+        return paths.joined(separator: "/")
+    }
+}
+
+// MARK: Equatable
+
+func ==(lhs: FRResource, rhs: FRResource) -> Bool {
+    return lhs.id == rhs.id && lhs.href == rhs.href
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRResources.swift b/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRResources.swift
new file mode 100755 (executable)
index 0000000..b900438
--- /dev/null
@@ -0,0 +1,114 @@
+//
+//  FRResources.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 29/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+open class FRResources: NSObject {
+    
+    var resources = [String: FRResource]()
+
+    /**
+     Adds a resource to the resources.
+     */
+    func add(_ resource: FRResource) {
+        self.resources[resource.href] = resource
+    }
+
+    // MARK: Find
+
+    /**
+     Gets the first resource (random order) with the give mediatype.
+
+     Useful for looking up the table of contents as it's supposed to be the only resource with NCX mediatype.
+     */
+    func findByMediaType(_ mediaType: MediaType) -> FRResource? {
+        for resource in resources.values {
+            if resource.mediaType != nil && resource.mediaType == mediaType {
+                return resource
+            }
+        }
+        return nil
+    }
+
+    /**
+     Gets the first resource (random order) with the give extension.
+
+     Useful for looking up the table of contents as it's supposed to be the only resource with NCX extension.
+     */
+    func findByExtension(_ ext: String) -> FRResource? {
+        for resource in resources.values {
+            if resource.mediaType != nil && resource.mediaType.defaultExtension == ext {
+                return resource
+            }
+        }
+        return nil
+    }
+
+    /**
+     Gets the first resource (random order) with the give properties.
+
+     - parameter properties: ePub 3 properties. e.g. `cover-image`, `nav`
+     - returns: The Resource.
+     */
+    func findByProperty(_ properties: String) -> FRResource? {
+        for resource in resources.values {
+            if resource.properties == properties {
+                return resource
+            }
+        }
+        return nil
+    }
+
+    /**
+     Gets the resource with the given href.
+     */
+    func findByHref(_ href: String) -> FRResource? {
+        guard !href.isEmpty else { return nil }
+
+        // This clean is neede because may the toc.ncx is not located in the root directory
+        let cleanHref = href.replacingOccurrences(of: "../", with: "")
+        return resources[cleanHref]
+    }
+
+    /**
+     Gets the resource with the given href.
+     */
+    func findById(_ id: String?) -> FRResource? {
+        guard let id = id else { return nil }
+
+        for resource in resources.values {
+            if let resourceID = resource.id, resourceID == id {
+                return resource
+            }
+        }
+        return nil
+    }
+
+    /**
+     Whether there exists a resource with the given href.
+     */
+    func containsByHref(_ href: String) -> Bool {
+        guard !href.isEmpty else { return false }
+
+        return resources.keys.contains(href)
+    }
+
+    /**
+     Whether there exists a resource with the given id.
+     */
+    func containsById(_ id: String?) -> Bool {
+        guard let id = id else { return false }
+
+        for resource in resources.values {
+            if let resourceID = resource.id, resourceID == id {
+                return true
+            }
+        }
+        return false
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRSmilElement.swift b/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRSmilElement.swift
new file mode 100644 (file)
index 0000000..f997de0
--- /dev/null
@@ -0,0 +1,113 @@
+//
+//  FRSmil.swift
+//  Pods
+//
+//  Created by Kevin Jantzer on 12/30/15.
+//
+//
+
+import UIKit
+
+// Media Overlay Documentation
+// http://www.idpf.org/accessibility/guidelines/content/overlays/overview.php#mo005-samp
+
+
+class FRSmilElement: NSObject {
+    var name: String // the name of the tag: <seq>, <par>, <text>, <audio>
+    var attributes: [String: String]!
+    var children: [FRSmilElement]
+
+    init(name: String, attributes: [String:String]!) {
+        self.name = name
+        self.attributes = attributes
+        self.children = [FRSmilElement]()
+    }
+
+    // MARK: - Element attributes
+
+    func getId() -> String! {
+        return getAttribute("id")
+    }
+
+    func getSrc() -> String! {
+        return getAttribute("src")
+    }
+
+    /**
+     Returns array of Strings if `epub:type` attribute is set. An array is returned as there can be multiple types specified, seperated by a whitespace
+     */
+    func getType() -> [String]! {
+        let type = getAttribute("epub:type", defaultVal: "")
+        return type!.components(separatedBy: " ")
+    }
+
+    /**
+     Use to determine if this element matches a given type
+
+     **Example**
+
+     epub:type="bodymatter chapter"
+     isType("bodymatter") -> true
+     */
+    func isType(_ aType:String) -> Bool {
+        return getType().contains(aType)
+    }
+
+    func getAttribute(_ name: String, defaultVal: String!) -> String! {
+        return attributes[name] != nil ? attributes[name] : defaultVal;
+    }
+
+    func getAttribute(_ name: String ) -> String! {
+        return getAttribute(name, defaultVal: nil)
+    }
+
+    // MARK: - Retrieving children elements
+
+    // if <par> tag, a <text> is required (http://www.idpf.org/epub/301/spec/epub-mediaoverlays.html#sec-smil-par-elem)
+    func textElement() -> FRSmilElement! {
+        return childWithName("text")
+    }
+
+    func audioElement() -> FRSmilElement! {
+        return childWithName("audio")
+    }
+
+    func videoElement() -> FRSmilElement! {
+        return childWithName("video")
+    }
+
+    func childWithName(_ name:String) -> FRSmilElement! {
+        for el in children {
+            if( el.name == name ){
+                return el
+            }
+        }
+        return nil;
+    }
+
+    func childrenWithNames(_ name:[String]) -> [FRSmilElement]! {
+        var matched = [FRSmilElement]()
+        for el in children {
+            if( name.contains(el.name) ){
+                matched.append(el)
+            }
+        }
+        return matched;
+    }
+
+    func childrenWithName(_ name:String) -> [FRSmilElement]! {
+        return childrenWithNames([name])
+    }
+
+    // MARK: - Audio clip info
+
+    func clipBegin() -> Double {
+        let val = audioElement().getAttribute("clipBegin", defaultVal: "")
+        return val!.clockTimeToSeconds()
+    }
+
+    func clipEnd() -> Double {
+        let val = audioElement().getAttribute("clipEnd", defaultVal: "")
+        return val!.clockTimeToSeconds()
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRSmils.swift b/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRSmils.swift
new file mode 100644 (file)
index 0000000..2052ba4
--- /dev/null
@@ -0,0 +1,143 @@
+//
+//  FRSmils.swift
+//  Pods
+//
+//  Created by Kevin Jantzer on 12/30/15.
+//
+//
+
+
+import UIKit
+
+struct FRSmilFile {
+    var resource: FRResource
+    var data = [FRSmilElement]()
+
+    init(resource: FRResource){
+        self.resource = resource;
+    }
+
+    // MARK: - shortcuts
+
+    func ID() -> String {
+        return self.resource.id;
+    }
+
+    func href() -> String {
+        return self.resource.href;
+    }
+
+    // MARK: - data methods
+
+    /**
+     Returns a smil <par> tag which contains info about parallel audio and text to be played
+     */
+    func parallelAudioForFragment(_ fragment: String!) -> FRSmilElement! {
+        return findParElement(forTextSrc: fragment, inData: data)
+    }
+
+    fileprivate func findParElement(forTextSrc src:String!, inData _data:[FRSmilElement]) -> FRSmilElement! {
+        for el in _data {
+
+            // if its a <par> (parallel) element and has a <text> node with the matching fragment
+            if( el.name == "par" && (src == nil || el.textElement().attributes["src"]?.contains(src) != false ) ){
+                return el
+
+                // if its a <seq> (sequence) element, it should have children (<par>)
+            }else if el.name == "seq" && el.children.count > 0 {
+                let parEl = findParElement(forTextSrc: src, inData: el.children)
+                if parEl != nil { return parEl }
+            }
+        }
+        return nil
+    }
+
+    /**
+     Returns a smil <par> element after the given fragment
+     */
+    func nextParallelAudioForFragment(_ fragment: String) -> FRSmilElement! {
+        return findNextParElement(forTextSrc: fragment, inData: data)
+    }
+
+    fileprivate func findNextParElement(forTextSrc src:String!, inData _data:[FRSmilElement]) -> FRSmilElement! {
+        var foundPrev = false
+        for el in _data {
+
+            if foundPrev { return el }
+
+            // if its a <par> (parallel) element and has a <text> node with the matching fragment
+            if( el.name == "par" && (src == nil || el.textElement().attributes["src"]?.contains(src) != false) ){
+                foundPrev = true
+
+                // if its a <seq> (sequence) element, it should have children (<par>)
+            }else if el.name == "seq" && el.children.count > 0 {
+                let parEl = findNextParElement(forTextSrc: src, inData: el.children)
+                if parEl != nil { return parEl }
+            }
+        }
+        return nil
+    }
+
+
+    func childWithName(_ name:String) -> FRSmilElement! {
+        for el in data {
+            if( el.name == name ){
+                return el
+            }
+        }
+        return nil;
+    }
+
+    func childrenWithNames(_ name:[String]) -> [FRSmilElement]! {
+        var matched = [FRSmilElement]()
+        for el in data {
+            if( name.contains(el.name) ){
+                matched.append(el)
+            }
+        }
+        return matched;
+    }
+
+    func childrenWithName(_ name:String) -> [FRSmilElement]! {
+        return childrenWithNames([name])
+    }
+}
+
+/**
+ Holds array of `FRSmilFile`
+ */
+class FRSmils: NSObject {
+    var basePath            : String!
+    var smils               = [String: FRSmilFile]()
+
+    /**
+     Adds a smil to the smils.
+     */
+    func add(_ smil: FRSmilFile) {
+        self.smils[smil.resource.href] = smil
+    }
+
+    /**
+     Gets the resource with the given href.
+     */
+    func findByHref(_ href: String) -> FRSmilFile? {
+        for smil in smils.values {
+            if smil.resource.href == href {
+                return smil
+            }
+        }
+        return nil
+    }
+
+    /**
+     Gets the resource with the given id.
+     */
+    func findById(_ ID: String) -> FRSmilFile? {
+        for smil in smils.values {
+            if smil.resource.id == ID {
+                return smil
+            }
+        }
+        return nil
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRSpine.swift b/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRSpine.swift
new file mode 100755 (executable)
index 0000000..958774b
--- /dev/null
@@ -0,0 +1,46 @@
+//
+//  FRSpine.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 06/05/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+struct Spine {
+    var linear: Bool
+    var resource: FRResource
+
+    init(resource: FRResource, linear: Bool = true) {
+        self.resource = resource
+        self.linear = linear
+    }
+}
+
+class FRSpine: NSObject {
+    var pageProgressionDirection: String?
+    var spineReferences = [Spine]()
+
+    var isRtl: Bool {
+        if let pageProgressionDirection = pageProgressionDirection , pageProgressionDirection == "rtl" {
+            return true
+        }
+        return false
+    }
+
+    func nextChapter(_ href: String) -> FRResource? {
+        var found = false;
+
+        for item in spineReferences {
+            if(found){
+                return item.resource
+            }
+
+            if(item.resource.href == href) {
+                found = true
+            }
+        }
+        return nil
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRTocReference.swift b/iOS/Pods/FolioReaderKit/Source/EPUBCore/FRTocReference.swift
new file mode 100755 (executable)
index 0000000..e5597ac
--- /dev/null
@@ -0,0 +1,34 @@
+//
+//  FRTocReference.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 06/05/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+open class FRTocReference: NSObject {
+    var children: [FRTocReference]!
+
+    public var title: String!
+    public var resource: FRResource?
+    public var fragmentID: String?
+    
+    convenience init(title: String, resource: FRResource?, fragmentID: String = "") {
+        self.init(title: title, resource: resource, fragmentID: fragmentID, children: [FRTocReference]())
+    }
+
+    init(title: String, resource: FRResource?, fragmentID: String, children: [FRTocReference]) {
+        self.resource = resource
+        self.title = title
+        self.fragmentID = fragmentID
+        self.children = children
+    }
+}
+
+// MARK: Equatable
+
+func ==(lhs: FRTocReference, rhs: FRTocReference) -> Bool {
+    return lhs.title == rhs.title && lhs.fragmentID == rhs.fragmentID
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/EPUBCore/MediaType.swift b/iOS/Pods/FolioReaderKit/Source/EPUBCore/MediaType.swift
new file mode 100755 (executable)
index 0000000..2e368ac
--- /dev/null
@@ -0,0 +1,122 @@
+//
+//  MediaType.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 29/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+/**
+ MediaType is used to tell the type of content a resource is.
+
+ Examples of mediatypes are image/gif, text/css and application/xhtml+xml
+ */
+public struct MediaType: Equatable {
+    public let name: String
+    public let defaultExtension: String
+    public let extensions: [String]
+
+    public init(name: String, defaultExtension: String, extensions: [String] = []) {
+        self.name = name
+        self.defaultExtension = defaultExtension
+        self.extensions = extensions
+    }
+
+}
+
+// MARK: - Equatable
+
+public func == (lhs: MediaType, rhs: MediaType) -> Bool {
+    guard lhs.name == rhs.name else { return false }
+    guard lhs.defaultExtension == rhs.defaultExtension else { return false }
+    guard lhs.extensions == rhs.extensions else { return false }
+    return true
+}
+
+
+/**
+ Manages mediatypes that are used by epubs.
+ */
+extension MediaType {
+    static let xhtml = MediaType(name: "application/xhtml+xml", defaultExtension: "xhtml", extensions: ["htm", "html", "xhtml", "xml"])
+    static let epub = MediaType(name: "application/epub+zip", defaultExtension: "epub")
+    static let ncx = MediaType(name: "application/x-dtbncx+xml", defaultExtension: "ncx")
+    static let opf = MediaType(name: "application/oebps-package+xml", defaultExtension: "opf")
+    static let javaScript = MediaType(name: "text/javascript", defaultExtension: "js")
+    static let css = MediaType(name: "text/css", defaultExtension: "css")
+
+    // images
+    static let jpg = MediaType(name: "image/jpeg", defaultExtension: "jpg", extensions: ["jpg", "jpeg"])
+    static let png = MediaType(name: "image/png", defaultExtension: "png")
+    static let gif = MediaType(name: "image/gif", defaultExtension: "gif")
+    static let svg = MediaType(name: "image/svg+xml", defaultExtension: "svg")
+
+    // fonts
+    static let ttf = MediaType(name: "application/x-font-ttf", defaultExtension: "ttf")
+    static let ttf1 = MediaType(name: "application/x-font-truetype", defaultExtension: "ttf")
+    static let ttf2 = MediaType(name: "application/x-truetype-font", defaultExtension: "ttf")
+    static let openType = MediaType(name: "application/vnd.ms-opentype", defaultExtension: "otf")
+    static let woff = MediaType(name: "application/font-woff", defaultExtension: "woff")
+
+    // audio
+    static let mp3 = MediaType(name: "audio/mpeg", defaultExtension: "mp3")
+    static let mp4 = MediaType(name: "audio/mp4", defaultExtension: "mp4")
+    static let ogg = MediaType(name: "audio/ogg", defaultExtension: "ogg")
+
+    static let smil = MediaType(name: "application/smil+xml", defaultExtension: "smil")
+    static let xpgt = MediaType(name: "application/adobe-page-template+xml", defaultExtension: "xpgt")
+    static let pls = MediaType(name: "application/pls+xml", defaultExtension: "pls")
+
+    static let mediatypes = [xhtml, epub, ncx, opf, jpg, png, gif, javaScript, css, svg, ttf, ttf1, ttf2, openType, woff, mp3, mp4, ogg, smil, xpgt, pls]
+
+    /**
+     Gets the MediaType based on the file mimetype.
+     - parameter name:     The mediaType name
+     - parameter fileName: The file name to extract the extension
+     - returns: A know mediatype or create a new one.
+     */
+    static func by(name: String, fileName: String?) -> MediaType {
+        for mediatype in mediatypes {
+            if mediatype.name == name {
+                return mediatype
+            }
+        }
+        let ext = fileName?.pathExtension ?? ""
+        return MediaType(name: name, defaultExtension: ext)
+    }
+
+    /**
+     Gets the MediaType based on the file extension.
+     */
+    static func by(fileName: String) -> MediaType? {
+        let ext = "." + (fileName as NSString).pathExtension
+        return mediatypes.filter({ $0.extensions.contains(ext) }).first
+    }
+
+    /**
+     Compare if the resource is a image.
+     - returns: `true` if is a image and `false` if not
+     */
+    static func isBitmapImage(_ mediaType: MediaType) -> Bool {
+        return mediaType == jpg || mediaType == png || mediaType == gif
+    }
+
+    /**
+     Gets the MediaType based on the file extension.
+     */
+    static func determineMediaType(_ fileName: String) -> MediaType? {
+        let ext = fileName.pathExtension
+
+        for mediatype in mediatypes {
+            if mediatype.defaultExtension == ext {
+                return mediatype
+            }
+            if mediatype.extensions.contains(ext) {
+                return mediatype
+            }
+        }
+        return nil
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/Extensions.swift b/iOS/Pods/FolioReaderKit/Source/Extensions.swift
new file mode 100644 (file)
index 0000000..9955808
--- /dev/null
@@ -0,0 +1,537 @@
+//
+//  Extensions.swift
+//  Pods
+//
+//  Created by Kevin Delord on 01/04/17.
+//
+//
+
+import Foundation
+import UIKit
+
+extension UICollectionViewScrollDirection {
+    static func direction(withConfiguration readerConfig: FolioReaderConfig) -> UICollectionViewScrollDirection {
+        return readerConfig.isDirection(.vertical, .horizontal, .horizontal)
+    }
+}
+
+extension UICollectionViewScrollPosition {
+    static func direction(withConfiguration readerConfig: FolioReaderConfig) -> UICollectionViewScrollPosition {
+        return readerConfig.isDirection(.top, .left, .left)
+    }
+}
+
+extension CGPoint {
+    func forDirection(withConfiguration readerConfig: FolioReaderConfig, scrollType: ScrollType = .page) -> CGFloat {
+        return readerConfig.isDirection(self.y, self.x, ((scrollType == .page) ? self.y : self.x))
+    }
+}
+
+extension CGSize {
+    func forDirection(withConfiguration readerConfig: FolioReaderConfig) -> CGFloat {
+        return readerConfig.isDirection(height, width, height)
+    }
+
+    func forReverseDirection(withConfiguration readerConfig: FolioReaderConfig) -> CGFloat {
+        return readerConfig.isDirection(width, height, width)
+    }
+}
+
+extension CGRect {
+    func forDirection(withConfiguration readerConfig: FolioReaderConfig) -> CGFloat {
+        return readerConfig.isDirection(height, width, height)
+    }
+}
+
+extension ScrollDirection {
+    static func negative(withConfiguration readerConfig: FolioReaderConfig, scrollType: ScrollType = .page) -> ScrollDirection {
+        return readerConfig.isDirection(.down, .right, .right)
+    }
+
+    static func positive(withConfiguration readerConfig: FolioReaderConfig, scrollType: ScrollType = .page) -> ScrollDirection {
+        return readerConfig.isDirection(.up, .left, .left)
+    }
+}
+
+// MARK: Helpers
+
+/**
+ Delay function
+ From: http://stackoverflow.com/a/24318861/517707
+
+ - parameter delay:   Delay in seconds
+ - parameter closure: Closure
+ */
+func delay(_ delay:Double, closure:@escaping ()->()) {
+    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
+}
+
+
+// MARK: - Extensions
+
+internal extension Bundle {
+    class func frameworkBundle() -> Bundle {
+        return Bundle(for: FolioReader.self)
+    }
+}
+
+internal extension UIColor {
+    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.index(rgba.startIndex, offsetBy: 1)
+            let hex = String(rgba[index...])
+            let scanner = Scanner(string: hex)
+            var hexValue: CUnsignedLongLong = 0
+            if scanner.scanHexInt64(&hexValue) {
+                switch (hex.count) {
+                case 3:
+                    red   = CGFloat((hexValue & 0xF00) >> 8)       / 15.0
+                    green = CGFloat((hexValue & 0x0F0) >> 4)       / 15.0
+                    blue  = CGFloat(hexValue & 0x00F)              / 15.0
+                    break
+                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
+                    break
+                case 6:
+                    red   = CGFloat((hexValue & 0xFF0000) >> 16)   / 255.0
+                    green = CGFloat((hexValue & 0x00FF00) >> 8)    / 255.0
+                    blue  = CGFloat(hexValue & 0x0000FF)           / 255.0
+                    break
+                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
+                    break
+                default:
+                    print("Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8", terminator: "")
+                    break
+                }
+            } else {
+                print("Scan hex error")
+            }
+        } else {
+            print("Invalid RGB string, missing '#' as prefix", terminator: "")
+        }
+        self.init(red:red, green:green, blue:blue, alpha:alpha)
+    }
+
+    //
+    /// Hex string of a UIColor instance.
+    ///
+    /// from: https://github.com/yeahdongcn/UIColor-Hex-Swift
+    ///
+    /// - Parameter includeAlpha: Whether the alpha should be included.
+    /// - Returns: Hexa string
+    func hexString(_ includeAlpha: Bool) -> String {
+        var r: CGFloat = 0
+        var g: CGFloat = 0
+        var b: CGFloat = 0
+        var a: CGFloat = 0
+        self.getRed(&r, green: &g, blue: &b, alpha: &a)
+
+        if (includeAlpha == true) {
+            return String(format: "#%02X%02X%02X%02X", Int(r * 255), Int(g * 255), Int(b * 255), Int(a * 255))
+        } else {
+            return String(format: "#%02X%02X%02X", Int(r * 255), Int(g * 255), Int(b * 255))
+        }
+    }
+
+    // MARK: - color shades
+    // https://gist.github.com/mbigatti/c6be210a6bbc0ff25972
+
+    func highlightColor() -> UIColor {
+
+        var hue : CGFloat = 0
+        var saturation : CGFloat = 0
+        var brightness : CGFloat = 0
+        var alpha : CGFloat = 0
+
+        if getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
+            return UIColor(hue: hue, saturation: 0.30, brightness: 1, alpha: alpha)
+        } else {
+            return self;
+        }
+
+    }
+
+    /**
+     Returns a lighter color by the provided percentage
+
+     :param: lighting percent percentage
+     :returns: lighter UIColor
+     */
+    func lighterColor(_ percent : Double) -> UIColor {
+        return colorWithBrightnessFactor(CGFloat(1 + percent));
+    }
+
+    /**
+     Returns a darker color by the provided percentage
+
+     :param: darking percent percentage
+     :returns: darker UIColor
+     */
+    func darkerColor(_ percent : Double) -> UIColor {
+        return colorWithBrightnessFactor(CGFloat(1 - percent));
+    }
+
+    /**
+     Return a modified color using the brightness factor provided
+
+     :param: factor brightness factor
+     :returns: modified color
+     */
+    func colorWithBrightnessFactor(_ factor: CGFloat) -> UIColor {
+        var hue : CGFloat = 0
+        var saturation : CGFloat = 0
+        var brightness : CGFloat = 0
+        var alpha : CGFloat = 0
+
+        if getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
+            return UIColor(hue: hue, saturation: saturation, brightness: brightness * factor, alpha: alpha)
+        } else {
+            return self;
+        }
+    }
+}
+
+internal extension String {
+    /// Truncates the string to length number of characters and
+    /// appends optional trailing string if longer
+    func truncate(_ length: Int, trailing: String? = nil) -> String {
+        if count > length {
+            let indexOfText = index(startIndex, offsetBy: length)
+            return String(self[..<indexOfText])
+        } else {
+            return self
+        }
+    }
+
+    func stripHtml() -> String {
+        return self.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression)
+    }
+
+    func stripLineBreaks() -> String {
+        return self.replacingOccurrences(of: "\n", with: "", options: .regularExpression)
+    }
+
+    /**
+     Converts a clock time such as `0:05:01.2` to seconds (`Double`)
+
+     Looks for media overlay clock formats as specified [here][1]
+
+     - Note: this may not be the  most efficient way of doing this. It can be improved later on.
+
+     - Returns: seconds as `Double`
+
+     [1]: http://www.idpf.org/epub/301/spec/epub-mediaoverlays.html#app-clock-examples
+     */
+    func clockTimeToSeconds() -> Double {
+
+        let val = self.trimmingCharacters(in: CharacterSet.whitespaces)
+
+        if( val.isEmpty ){ return 0 }
+
+        let formats = [
+            "HH:mm:ss.SSS"  : "^\\d{1,2}:\\d{2}:\\d{2}\\.\\d{1,3}$",
+            "HH:mm:ss"      : "^\\d{1,2}:\\d{2}:\\d{2}$",
+            "mm:ss.SSS"     : "^\\d{1,2}:\\d{2}\\.\\d{1,3}$",
+            "mm:ss"         : "^\\d{1,2}:\\d{2}$",
+            "ss.SSS"         : "^\\d{1,2}\\.\\d{1,3}$",
+            ]
+
+        // search for normal duration formats such as `00:05:01.2`
+        for (format, pattern) in formats {
+
+            if val.range(of: pattern, options: .regularExpression) != nil {
+
+                let formatter = DateFormatter()
+                formatter.dateFormat = format
+                let time = formatter.date(from: val)
+
+                if( time == nil ){ return 0 }
+
+                formatter.dateFormat = "ss.SSS"
+                let seconds = (formatter.string(from: time!) as NSString).doubleValue
+
+                formatter.dateFormat = "mm"
+                let minutes = (formatter.string(from: time!) as NSString).doubleValue
+
+                formatter.dateFormat = "HH"
+                let hours = (formatter.string(from: time!) as NSString).doubleValue
+
+                return seconds + (minutes*60) + (hours*60*60)
+            }
+        }
+
+        // if none of the more common formats match, check for other possible formats
+
+        // 2345ms
+        if val.range(of: "^\\d+ms$", options: .regularExpression) != nil{
+            return (val as NSString).doubleValue / 1000.0
+        }
+
+        // 7.25h
+        if val.range(of: "^\\d+(\\.\\d+)?h$", options: .regularExpression) != nil {
+            return (val as NSString).doubleValue * 60 * 60
+        }
+
+        // 13min
+        if val.range(of: "^\\d+(\\.\\d+)?min$", options: .regularExpression) != nil {
+            return (val as NSString).doubleValue * 60
+        }
+
+        return 0
+    }
+
+    func clockTimeToMinutesString() -> String {
+
+        let val = clockTimeToSeconds()
+
+        let min = floor(val / 60)
+        let sec = floor(val.truncatingRemainder(dividingBy: 60))
+
+        return String(format: "%02.f:%02.f", min, sec)
+    }
+
+    // MARK: - NSString helpers
+
+    var lastPathComponent: String {
+        return (self as NSString).lastPathComponent
+    }
+
+    var deletingLastPathComponent: String {
+        return (self as NSString).deletingLastPathComponent
+    }
+
+    var deletingPathExtension: String {
+        return (self as NSString).deletingPathExtension
+    }
+
+    var pathExtension: String {
+        return (self as NSString).pathExtension
+    }
+
+    var abbreviatingWithTildeInPath: String {
+        return (self as NSString).abbreviatingWithTildeInPath
+    }
+
+    func appendingPathComponent(_ str: String) -> String {
+        return (self as NSString).appendingPathComponent(str)
+    }
+
+    func appendingPathExtension(_ str: String) -> String {
+        return (self as NSString).appendingPathExtension(str) ?? self+"."+str
+    }
+}
+
+internal extension UIImage {
+
+    convenience init?(readerImageNamed: String) {
+        self.init(named: readerImageNamed, in: Bundle.frameworkBundle(), compatibleWith: nil)
+    }
+
+    /// Forces the image to be colored with Reader Config tintColor
+    ///
+    /// - Parameter readerConfig: Current folio reader configuration.
+    /// - Returns: Returns a colored image
+    func ignoreSystemTint(withConfiguration readerConfig: FolioReaderConfig) -> UIImage? {
+        return self.imageTintColor(readerConfig.tintColor)?.withRenderingMode(.alwaysOriginal)
+    }
+
+    /**
+     Colorize the image with a color
+
+     - parameter tintColor: The input color
+     - returns: Returns a colored image
+     */
+    func imageTintColor(_ tintColor: UIColor) -> UIImage? {
+        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
+
+        let context = UIGraphicsGetCurrentContext()
+        context?.translateBy(x: 0, y: self.size.height)
+        context?.scaleBy(x: 1.0, y: -1.0)
+        context?.setBlendMode(CGBlendMode.normal)
+
+        let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height) as CGRect
+        if let cgImage = self.cgImage {
+            context?.clip(to: rect, mask:  cgImage)
+        }
+
+        tintColor.setFill()
+        context?.fill(rect)
+
+        let newImage = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+
+        return newImage
+    }
+
+    /**
+     Generate a image with a color
+
+     - parameter color: The input color
+     - returns: Returns a colored image
+     */
+    class func imageWithColor(_ color: UIColor?) -> UIImage {
+        let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
+        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
+        let context = UIGraphicsGetCurrentContext()
+
+        if let color = color {
+            color.setFill()
+        } else {
+            UIColor.white.setFill()
+        }
+
+        context!.fill(rect)
+        let image = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+
+        return image!
+    }
+
+    /**
+     Generates a image with a `CALayer`
+
+     - parameter layer: The input `CALayer`
+     - returns: Return a rendered image
+     */
+    class func imageWithLayer(_ layer: CALayer) -> UIImage {
+        UIGraphicsBeginImageContextWithOptions(layer.bounds.size, layer.isOpaque, 0.0)
+        layer.render(in: UIGraphicsGetCurrentContext()!)
+        let img = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        return img!
+    }
+
+    /**
+     Generates a image from a `UIView`
+
+     - parameter view: The input `UIView`
+     - returns: Return a rendered image
+     */
+    class func imageWithView(_ view: UIView) -> UIImage {
+        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
+        view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
+        let img = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        return img!
+    }
+}
+
+internal extension UIViewController {
+
+    func setCloseButton(withConfiguration readerConfig: FolioReaderConfig) {
+        let closeImage = UIImage(readerImageNamed: "icon-navbar-close")?.ignoreSystemTint(withConfiguration: readerConfig)
+        self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: closeImage, style: .plain, target: self, action: #selector(dismiss as () -> Void))
+    }
+
+    @objc func dismiss() {
+        self.dismiss(nil)
+    }
+
+    func dismiss(_ completion: (() -> Void)?) {
+        DispatchQueue.main.async {
+            self.dismiss(animated: true, completion: {
+                completion?()
+            })
+        }
+    }
+
+    // MARK: - NavigationBar
+
+    func setTransparentNavigation() {
+        let navBar = self.navigationController?.navigationBar
+        navBar?.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
+        navBar?.hideBottomHairline()
+        navBar?.isTranslucent = true
+    }
+
+    func setTranslucentNavigation(_ translucent: Bool = true, color: UIColor, tintColor: UIColor = UIColor.white, titleColor: UIColor = UIColor.black, andFont font: UIFont = UIFont.systemFont(ofSize: 17)) {
+        let navBar = self.navigationController?.navigationBar
+        navBar?.setBackgroundImage(UIImage.imageWithColor(color), for: UIBarMetrics.default)
+        navBar?.showBottomHairline()
+        navBar?.isTranslucent = translucent
+        navBar?.tintColor = tintColor
+        navBar?.titleTextAttributes = [NSAttributedStringKey.foregroundColor: titleColor, NSAttributedStringKey.font: font]
+    }
+}
+
+internal extension UINavigationBar {
+
+    func hideBottomHairline() {
+        let navigationBarImageView = hairlineImageViewInNavigationBar(self)
+        navigationBarImageView!.isHidden = true
+    }
+
+    func showBottomHairline() {
+        let navigationBarImageView = hairlineImageViewInNavigationBar(self)
+        navigationBarImageView!.isHidden = false
+    }
+
+    fileprivate func hairlineImageViewInNavigationBar(_ view: UIView) -> UIImageView? {
+        if view.isKind(of: UIImageView.self) && view.bounds.height <= 1.0 {
+            return (view as! UIImageView)
+        }
+
+        let subviews = (view.subviews)
+        for subview: UIView in subviews {
+            if let imageView: UIImageView = hairlineImageViewInNavigationBar(subview) {
+                return imageView
+            }
+        }
+        return nil
+    }
+}
+
+extension UINavigationController {
+
+    open override var preferredStatusBarStyle : UIStatusBarStyle {
+        guard let viewController = visibleViewController else { return .default }
+        return viewController.preferredStatusBarStyle
+    }
+
+    open override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
+        guard let viewController = visibleViewController else { return .portrait }
+        return viewController.supportedInterfaceOrientations
+    }
+
+    open override var shouldAutorotate : Bool {
+        guard let viewController = visibleViewController else { return false }
+        return viewController.shouldAutorotate
+    }
+}
+
+/**
+ This fixes iOS 9 crash
+ http://stackoverflow.com/a/32010520/517707
+ */
+extension UIAlertController {
+    open override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
+        return .portrait
+    }
+    
+    open override var shouldAutorotate : Bool {
+        return false
+    }
+}
+
+extension Array {
+    
+    /**
+     Return index if is safe, if not return nil
+     http://stackoverflow.com/a/30593673/517707
+     */
+    subscript(safe index: Int) -> Element? {
+        return indices ~= index ? self[index] : nil
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderAudioPlayer.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderAudioPlayer.swift
new file mode 100644 (file)
index 0000000..7429dfd
--- /dev/null
@@ -0,0 +1,535 @@
+//
+//  FolioReaderAudioPlayer.swift
+//  FolioReaderKit
+//
+//  Created by Kevin Jantzer on 1/4/16.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+import AVFoundation
+import MediaPlayer
+
+open class FolioReaderAudioPlayer: NSObject {
+
+    var isTextToSpeech = false
+    var synthesizer: AVSpeechSynthesizer!
+    var playing = false
+    var player: AVAudioPlayer?
+    var currentHref: String!
+    var currentFragment: String!
+    var currentSmilFile: FRSmilFile!
+    var currentAudioFile: String!
+    var currentBeginTime: Double!
+    var currentEndTime: Double!
+    var playingTimer: Timer!
+    var registeredCommands = false
+    var completionHandler: () -> Void = {}
+    var utteranceRate: Float = 0
+
+    fileprivate var book: FRBook
+    fileprivate var folioReader: FolioReader
+
+    // MARK: Init
+
+    init(withFolioReader folioReader: FolioReader, book: FRBook) {
+        self.book = book
+        self.folioReader = folioReader
+
+        super.init()
+
+        UIApplication.shared.beginReceivingRemoteControlEvents()
+
+        // this is needed to the audio can play even when the "silent/vibrate" toggle is on
+        let session = AVAudioSession.sharedInstance()
+        try? session.setCategory(AVAudioSessionCategoryPlayback)
+        try? session.setActive(true)
+
+        self.updateNowPlayingInfo()
+    }
+
+    deinit {
+        UIApplication.shared.endReceivingRemoteControlEvents()
+    }
+
+    // MARK: Reading speed
+
+    func setRate(_ rate: Int) {
+        if let player = player {
+            switch rate {
+            case 0:
+                player.rate = 0.5
+                break
+            case 1:
+                player.rate = 1.0
+                break
+            case 2:
+                player.rate = 1.5
+                break
+            case 3:
+                player.rate = 2
+                break
+            default:
+                break
+            }
+
+            updateNowPlayingInfo()
+        }
+        if synthesizer != nil {
+            // Need to change between version IOS
+            // http://stackoverflow.com/questions/32761786/ios9-avspeechutterance-rate-for-avspeechsynthesizer-issue
+            if #available(iOS 9, *) {
+                switch rate {
+                case 0:
+                    utteranceRate = 0.42
+                    break
+                case 1:
+                    utteranceRate = 0.5
+                    break
+                case 2:
+                    utteranceRate = 0.53
+                    break
+                case 3:
+                    utteranceRate = 0.56
+                    break
+                default:
+                    break
+                }
+            } else {
+                switch rate {
+                case 0:
+                    utteranceRate = 0
+                    break
+                case 1:
+                    utteranceRate = 0.06
+                    break
+                case 2:
+                    utteranceRate = 0.15
+                    break
+                case 3:
+                    utteranceRate = 0.23
+                    break
+                default:
+                    break
+                }
+            }
+
+            updateNowPlayingInfo()
+        }
+    }
+
+    // MARK: Play, Pause, Stop controls
+
+    func stop(immediate: Bool = false) {
+        playing = false
+        if !isTextToSpeech {
+            if let player = player , player.isPlaying {
+                player.stop()
+            }
+        } else {
+            stopSynthesizer(immediate: immediate, completion: nil)
+        }
+    }
+
+    func stopSynthesizer(immediate: Bool = false, completion: (() -> Void)? = nil) {
+        synthesizer.stopSpeaking(at: immediate ? .immediate : .word)
+        completion?()
+    }
+
+    @objc func pause() {
+        playing = false
+
+        if !isTextToSpeech {
+            if let player = player , player.isPlaying {
+                player.pause()
+            }
+        } else {
+            if synthesizer.isSpeaking {
+                synthesizer.pauseSpeaking(at: .word)
+            }
+        }
+    }
+
+    @objc func togglePlay() {
+        isPlaying() ? pause() : play()
+    }
+
+    @objc func play() {
+        if book.hasAudio {
+            guard let currentPage = self.folioReader.readerCenter?.currentPage else { return }
+            currentPage.webView?.js("playAudio()")
+        } else {
+            self.readCurrentSentence()
+        }
+    }
+
+    func isPlaying() -> Bool {
+        return playing
+    }
+
+    /**
+     Play Audio (href/fragmentID)
+
+     Begins to play audio for the given chapter (href) and text fragment.
+     If this chapter does not have audio, it will delay for a second, then attempt to play the next chapter
+     */
+    func playAudio(_ href: String, fragmentID: String) {
+        isTextToSpeech = false
+
+        self.stop()
+
+        let smilFile = book.smilFile(forHref: href)
+
+        // if no smil file for this href and the same href is being requested, we've hit the end. stop playing
+        if smilFile == nil && currentHref != nil && href == currentHref {
+            return
+        }
+
+        playing = true
+        currentHref = href
+        currentFragment = "#"+fragmentID
+        currentSmilFile = smilFile
+
+        // if no smil file, delay for a second, then move on to the next chapter
+        if smilFile == nil {
+            Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(_autoPlayNextChapter), userInfo: nil, repeats: false)
+            return
+        }
+
+        let fragment = smilFile?.parallelAudioForFragment(currentFragment)
+
+        if fragment != nil {
+            if _playFragment(fragment) {
+                startPlayerTimer()
+            }
+        }
+    }
+
+    @objc func _autoPlayNextChapter() {
+        // if user has stopped playing, dont play the next chapter
+        if isPlaying() == false { return }
+        playNextChapter()
+    }
+
+    @objc func playPrevChapter() {
+        stopPlayerTimer()
+        // Wait for "currentPage" to update, then request to play audio
+        self.folioReader.readerCenter?.changePageToPrevious {
+            if self.isPlaying() {
+                self.play()
+            } else {
+                self.pause()
+            }
+        }
+    }
+
+    @objc func playNextChapter() {
+        stopPlayerTimer()
+        // Wait for "currentPage" to update, then request to play audio
+        self.folioReader.readerCenter?.changePageToNext {
+            if self.isPlaying() {
+                self.play()
+            }
+        }
+    }
+
+
+    /**
+     Play Fragment of audio
+
+     Once an audio fragment begins playing, the audio clip will continue playing until the player timer detects
+     the audio is out of the fragment timeframe.
+     */
+    @discardableResult fileprivate func _playFragment(_ smil: FRSmilElement?) -> Bool {
+
+        guard let smil = smil else {
+            // FIXME: What about the log that the library prints in the console? shouldn’t we disable it? use another library for that or some compiler flags?
+            print("no more parallel audio to play")
+            self.stop()
+            return false
+        }
+
+        let textFragment = smil.textElement().attributes["src"]
+        let audioFile = smil.audioElement().attributes["src"]
+
+        currentBeginTime = smil.clipBegin()
+        currentEndTime = smil.clipEnd()
+
+        // new audio file to play, create the audio player
+        if player == nil || (audioFile != nil && audioFile != currentAudioFile) {
+
+            currentAudioFile = audioFile
+
+            let fileURL = currentSmilFile.resource.basePath() + ("/"+audioFile!)
+            let audioData = try? Data(contentsOf: URL(fileURLWithPath: fileURL))
+
+            do {
+
+                player = try AVAudioPlayer(data: audioData!)
+
+                guard let player = player else { return false }
+
+                setRate(self.folioReader.currentAudioRate)
+                player.enableRate = true
+                player.prepareToPlay()
+                player.delegate = self
+
+                updateNowPlayingInfo()
+            } catch {
+                print("could not read audio file:", audioFile ?? "nil")
+                return false
+            }
+        }
+
+        // if player is initialized properly, begin playing
+        guard let player = player else { return false }
+
+        // the audio may be playing already, so only set the player time if it is NOT already within the fragment timeframe
+        // this is done to mitigate milisecond skips in the audio when changing fragments
+        if player.currentTime < currentBeginTime || ( currentEndTime > 0 && player.currentTime > currentEndTime) {
+            player.currentTime = currentBeginTime;
+            updateNowPlayingInfo()
+        }
+
+        player.play()
+
+        // get the fragment ID so we can "mark" it in the webview
+        let textParts = textFragment!.components(separatedBy: "#")
+        let fragmentID = textParts[1];
+        self.folioReader.readerCenter?.audioMark(href: currentHref, fragmentID: fragmentID)
+
+        return true
+    }
+
+    /**
+     Next Audio Fragment
+
+     Gets the next audio fragment in the current smil file, or moves on to the next smil file
+     */
+    fileprivate func nextAudioFragment() -> FRSmilElement? {
+
+        guard let smilFile = book.smilFile(forHref: currentHref) else {
+            return nil
+        }
+
+        let smil = (self.currentFragment == nil ? smilFile.parallelAudioForFragment(nil) : smilFile.nextParallelAudioForFragment(currentFragment))
+
+        if (smil != nil) {
+            self.currentFragment = smil?.textElement().attributes["src"]
+            return smil
+        }
+
+        self.currentHref = self.book.spine.nextChapter(currentHref)?.href
+        self.currentFragment = nil
+        self.currentSmilFile = smilFile
+
+        guard (self.currentHref != nil) else {
+            return nil
+        }
+
+        return self.nextAudioFragment()
+    }
+
+    func playText(_ href: String, text: String) {
+        isTextToSpeech = true
+        playing = true
+        currentHref = href
+
+        if synthesizer == nil {
+            synthesizer = AVSpeechSynthesizer()
+            synthesizer.delegate = self
+            setRate(self.folioReader.currentAudioRate)
+        }
+
+        let utterance = AVSpeechUtterance(string: text)
+        utterance.rate = utteranceRate
+        utterance.voice = AVSpeechSynthesisVoice(language: self.book.metadata.language)
+
+        if synthesizer.isSpeaking {
+            stopSynthesizer()
+        }
+        synthesizer.speak(utterance)
+
+        updateNowPlayingInfo()
+    }
+
+    // MARK: TTS Sentence
+
+    func speakSentence() {
+        guard
+            let readerCenter = self.folioReader.readerCenter,
+            let currentPage = readerCenter.currentPage else {
+                return
+        }
+
+        let playbackActiveClass = book.playbackActiveClass
+        guard let sentence = currentPage.webView?.js("getSentenceWithIndex('\(playbackActiveClass)')") else {
+            if (readerCenter.isLastPage() == true) {
+                self.stop()
+            } else {
+                readerCenter.changePageToNext()
+            }
+
+            return
+        }
+
+        guard let href = readerCenter.getCurrentChapter()?.href else {
+            return
+        }
+
+        // TODO QUESTION: The previous code made it possible to call `playText` with the parameter `href` being an empty string. Was that valid? should this logic be kept?
+        self.playText(href, text: sentence)
+    }
+
+    func readCurrentSentence() {
+        guard synthesizer != nil else { return speakSentence() }
+
+        if synthesizer.isPaused {
+            playing = true
+            synthesizer.continueSpeaking()
+        } else {
+            if synthesizer.isSpeaking {
+                stopSynthesizer(immediate: false, completion: {
+                    if let currentPage = self.folioReader.readerCenter?.currentPage {
+                        currentPage.webView?.js("resetCurrentSentenceIndex()")
+                    }
+                    self.speakSentence()
+                })
+            } else {
+                speakSentence()
+            }
+        }
+    }
+
+    // MARK: - Audio timing events
+
+    fileprivate func startPlayerTimer() {
+        // we must add the timer in this mode in order for it to continue working even when the user is scrolling a webview
+        playingTimer = Timer(timeInterval: 0.01, target: self, selector: #selector(playerTimerObserver), userInfo: nil, repeats: true)
+        RunLoop.current.add(playingTimer, forMode: RunLoopMode.commonModes)
+    }
+
+    fileprivate func stopPlayerTimer() {
+        if playingTimer != nil {
+            playingTimer.invalidate()
+            playingTimer = nil
+        }
+    }
+
+    @objc func playerTimerObserver() {
+        guard let player = player else { return }
+
+        if currentEndTime != nil && currentEndTime > 0 && player.currentTime > currentEndTime {
+            _playFragment(self.nextAudioFragment())
+        }
+    }
+
+    // MARK: - Now Playing Info and Controls
+
+    /**
+     Update Now Playing info
+
+     Gets the book and audio information and updates on Now Playing Center
+     */
+    func updateNowPlayingInfo() {
+        var songInfo = [String: AnyObject]()
+
+        // Get book Artwork
+        if let coverImage = self.book.coverImage, let artwork = UIImage(contentsOfFile: coverImage.fullHref) {
+            let albumArt = MPMediaItemArtwork(image: artwork)
+            songInfo[MPMediaItemPropertyArtwork] = albumArt
+        }
+
+        // Get book title
+        if let title = self.book.title {
+            songInfo[MPMediaItemPropertyAlbumTitle] = title as AnyObject?
+        }
+
+        // Get chapter name
+        if let chapter = getCurrentChapterName() {
+            songInfo[MPMediaItemPropertyTitle] = chapter as AnyObject?
+        }
+
+        // Get author name
+        if let author = self.book.metadata.creators.first {
+            songInfo[MPMediaItemPropertyArtist] = author.name as AnyObject?
+        }
+
+        // Set player times
+        if let player = player , !isTextToSpeech {
+            songInfo[MPMediaItemPropertyPlaybackDuration] = player.duration as AnyObject?
+            songInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate as AnyObject?
+            songInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime ] = player.currentTime as AnyObject?
+        }
+
+        // Set Audio Player info
+        MPNowPlayingInfoCenter.default().nowPlayingInfo = songInfo
+
+        registerCommandsIfNeeded()
+    }
+
+    /**
+     Get Current Chapter Name
+
+     This is done here and not in ReaderCenter because even though `currentHref` is accurate,
+     the `currentPage` in ReaderCenter may not have updated just yet
+     */
+    func getCurrentChapterName() -> String? {
+        guard let chapter = self.folioReader.readerCenter?.getCurrentChapter() else {
+            return nil
+        }
+
+        currentHref = chapter.href
+
+        for item in (self.book.flatTableOfContents ?? []) {
+            if let resource = item.resource , resource.href == currentHref {
+                return item.title
+            }
+        }
+        return nil
+    }
+
+    /**
+     Register commands if needed, check if it's registered to avoid register twice.
+     */
+    func registerCommandsIfNeeded() {
+
+        guard !registeredCommands else { return }
+
+        let command = MPRemoteCommandCenter.shared()
+        command.previousTrackCommand.isEnabled = true
+        command.previousTrackCommand.addTarget(self, action: #selector(playPrevChapter))
+        command.nextTrackCommand.isEnabled = true
+        command.nextTrackCommand.addTarget(self, action: #selector(playNextChapter))
+        command.pauseCommand.isEnabled = true
+        command.pauseCommand.addTarget(self, action: #selector(pause))
+        command.playCommand.isEnabled = true
+        command.playCommand.addTarget(self, action: #selector(play))
+        command.togglePlayPauseCommand.isEnabled = true
+        command.togglePlayPauseCommand.addTarget(self, action: #selector(togglePlay))
+
+        registeredCommands = true
+    }
+}
+
+// MARK: AVSpeechSynthesizerDelegate
+
+extension FolioReaderAudioPlayer: AVSpeechSynthesizerDelegate {
+    public func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
+        completionHandler()
+    }
+    
+    public func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
+        if isPlaying() {
+            readCurrentSentence()
+        }
+    }
+}
+
+// MARK: AVAudioPlayerDelegate
+
+extension FolioReaderAudioPlayer: AVAudioPlayerDelegate {
+    public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
+        _playFragment(self.nextAudioFragment())
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderCenter.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderCenter.swift
new file mode 100755 (executable)
index 0000000..06766ca
--- /dev/null
@@ -0,0 +1,1559 @@
+//
+//  FolioReaderCenter.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 08/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+import ZFDragableModalTransition
+
+/// Protocol which is used from `FolioReaderCenter`s.
+@objc public protocol FolioReaderCenterDelegate: class {
+
+    /// Notifies that a page appeared. This is triggered when a page is chosen and displayed.
+    ///
+    /// - Parameter page: The appeared page
+    @objc optional func pageDidAppear(_ page: FolioReaderPage)
+
+    /// Passes and returns the HTML content as `String`. Implement this method if you want to modify the HTML content of a `FolioReaderPage`.
+    ///
+    /// - Parameters:
+    ///   - page: The `FolioReaderPage`.
+    ///   - htmlContent: The current HTML content as `String`.
+    /// - Returns: The adjusted HTML content as `String`. This is the content which will be loaded into the given `FolioReaderPage`.
+    @objc optional func htmlContentForPage(_ page: FolioReaderPage, htmlContent: String) -> String
+    
+    /// Notifies that a page changed. This is triggered when collection view cell is changed.
+    ///
+    /// - Parameter pageNumber: The appeared page item
+    @objc optional func pageItemChanged(_ pageNumber: Int)
+
+}
+
+/// The base reader class
+open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
+
+    /// This delegate receives the events from the current `FolioReaderPage`s delegate.
+    open weak var delegate: FolioReaderCenterDelegate?
+
+    /// This delegate receives the events from current page
+    open weak var pageDelegate: FolioReaderPageDelegate?
+
+    /// The base reader container
+    open weak var readerContainer: FolioReaderContainer?
+
+    /// The current visible page on reader
+    open fileprivate(set) var currentPage: FolioReaderPage?
+
+    /// The collection view with pages
+    open var collectionView: UICollectionView!
+    
+    let collectionViewLayout = UICollectionViewFlowLayout()
+    var loadingView: UIActivityIndicatorView!
+    var pages: [String]!
+    var totalPages: Int = 0
+    var tempFragment: String?
+    var animator: ZFModalTransitionAnimator!
+    var pageIndicatorView: FolioReaderPageIndicator?
+    var pageIndicatorHeight: CGFloat = 20
+    var recentlyScrolled = false
+    var recentlyScrolledDelay = 2.0 // 2 second delay until we clear recentlyScrolled
+    var recentlyScrolledTimer: Timer!
+    var scrollScrubber: ScrollScrubber?
+    var activityIndicator = UIActivityIndicatorView()
+    var isScrolling = false
+    var pageScrollDirection = ScrollDirection()
+    var nextPageNumber: Int = 0
+    var previousPageNumber: Int = 0
+    var currentPageNumber: Int = 0
+    var pageWidth: CGFloat = 0.0
+    var pageHeight: CGFloat = 0.0
+    var anchor: String!
+    var lastRow: Int?
+
+    fileprivate var screenBounds: CGRect!
+    fileprivate var pointNow = CGPoint.zero
+    fileprivate var pageOffsetRate: CGFloat = 0
+    fileprivate var tempReference: FRTocReference?
+    fileprivate var isFirstLoad = true
+    fileprivate var currentWebViewScrollPositions = [Int: CGPoint]()
+    fileprivate var currentOrientation: UIInterfaceOrientation?
+    fileprivate var changingChapter = false
+
+    fileprivate var readerConfig: FolioReaderConfig {
+        guard let readerContainer = readerContainer else { return FolioReaderConfig() }
+        return readerContainer.readerConfig
+    }
+
+    fileprivate var book: FRBook {
+        guard let readerContainer = readerContainer else { return FRBook() }
+        return readerContainer.book
+    }
+
+    fileprivate var folioReader: FolioReader {
+        guard let readerContainer = readerContainer else { return FolioReader() }
+        return readerContainer.folioReader
+    }
+
+    // MARK: - Init
+
+    init(withContainer readerContainer: FolioReaderContainer) {
+        self.readerContainer = readerContainer
+        super.init(nibName: nil, bundle: Bundle.frameworkBundle())
+
+        self.initialization()
+    }
+
+    required public init?(coder aDecoder: NSCoder) {
+        fatalError("This class doesn't support NSCoding.")
+    }
+
+    /**
+     Common Initialization
+     */
+    fileprivate func initialization() {
+
+        if (self.readerConfig.hideBars == true) {
+            self.pageIndicatorHeight = 0
+        }
+        
+        self.totalPages = book.spine.spineReferences.count
+
+        // Loading indicator
+        let style: UIActivityIndicatorViewStyle = folioReader.isNight(.white, .gray)
+        loadingView = UIActivityIndicatorView(activityIndicatorStyle: style)
+        loadingView.hidesWhenStopped = true
+        loadingView.startAnimating()
+        self.view.addSubview(loadingView)
+    }
+
+    // MARK: - View life cicle
+
+    override open func viewDidLoad() {
+        super.viewDidLoad()
+
+        screenBounds = self.getScreenBounds()
+        
+        setPageSize(UIApplication.shared.statusBarOrientation)
+
+        // Layout
+        collectionViewLayout.sectionInset = UIEdgeInsets.zero
+        collectionViewLayout.minimumLineSpacing = 0
+        collectionViewLayout.minimumInteritemSpacing = 0
+        collectionViewLayout.scrollDirection = .direction(withConfiguration: self.readerConfig)
+        
+        let background = folioReader.isNight(self.readerConfig.nightModeBackground, UIColor.white)
+        view.backgroundColor = background
+
+        // CollectionView
+        collectionView = UICollectionView(frame: screenBounds, collectionViewLayout: collectionViewLayout)
+        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.isPagingEnabled = true
+        collectionView.showsVerticalScrollIndicator = false
+        collectionView.showsHorizontalScrollIndicator = false
+        collectionView.backgroundColor = background
+        collectionView.decelerationRate = UIScrollViewDecelerationRateFast
+        enableScrollBetweenChapters(scrollEnabled: true)
+        view.addSubview(collectionView)
+
+        if #available(iOS 11.0, *) {
+            collectionView.contentInsetAdjustmentBehavior = .never
+        }
+        
+        // Activity Indicator
+        self.activityIndicator.activityIndicatorViewStyle = .gray
+        self.activityIndicator.hidesWhenStopped = true
+        self.activityIndicator = UIActivityIndicatorView(frame: CGRect(x: screenBounds.size.width/2, y: screenBounds.size.height/2, width: 30, height: 30))
+        self.activityIndicator.backgroundColor = UIColor.gray
+        self.view.addSubview(self.activityIndicator)
+        self.view.bringSubview(toFront: self.activityIndicator)
+
+        if #available(iOS 10.0, *) {
+            collectionView.isPrefetchingEnabled = false
+        }
+
+        // Register cell classes
+        collectionView?.register(FolioReaderPage.self, forCellWithReuseIdentifier: kReuseCellIdentifier)
+
+        // Configure navigation bar and layout
+        automaticallyAdjustsScrollViewInsets = false
+        extendedLayoutIncludesOpaqueBars = true
+        configureNavBar()
+
+        // Page indicator view
+        if (self.readerConfig.hidePageIndicator == false) {
+            let frame = self.frameForPageIndicatorView()
+            pageIndicatorView = FolioReaderPageIndicator(frame: frame, readerConfig: readerConfig, folioReader: folioReader)
+            if let pageIndicatorView = pageIndicatorView {
+                view.addSubview(pageIndicatorView)
+            }
+        }
+
+        guard let readerContainer = readerContainer else { return }
+        self.scrollScrubber = ScrollScrubber(frame: frameForScrollScrubber(), withReaderContainer: readerContainer)
+        self.scrollScrubber?.delegate = self
+        if let scrollScrubber = scrollScrubber {
+            view.addSubview(scrollScrubber.slider)
+        }
+    }
+
+    override open func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+
+        configureNavBar()
+
+        // Update pages
+        pagesForCurrentPage(currentPage)
+        pageIndicatorView?.reloadView(updateShadow: true)
+    }
+
+    override open func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+
+        screenBounds = self.getScreenBounds()
+        loadingView.center = view.center
+
+        setPageSize(UIApplication.shared.statusBarOrientation)
+        updateSubviewFrames()
+    }
+
+    // MARK: Layout
+
+    /**
+     Enable or disable the scrolling between chapters (`FolioReaderPage`s). If this is enabled it's only possible to read the current chapter. If another chapter should be displayed is has to be triggered programmatically with `changePageWith`.
+
+     - parameter scrollEnabled: `Bool` which enables or disables the scrolling between `FolioReaderPage`s.
+     */
+    open func enableScrollBetweenChapters(scrollEnabled: Bool) {
+        self.collectionView.isScrollEnabled = scrollEnabled
+    }
+
+    fileprivate func updateSubviewFrames() {
+        self.pageIndicatorView?.frame = self.frameForPageIndicatorView()
+        self.scrollScrubber?.frame = self.frameForScrollScrubber()
+    }
+
+    fileprivate func frameForPageIndicatorView() -> CGRect {
+        var bounds = CGRect(x: 0, y: screenBounds.size.height-pageIndicatorHeight, width: screenBounds.size.width, height: pageIndicatorHeight)
+        
+        if #available(iOS 11.0, *) {
+            bounds.size.height = bounds.size.height + view.safeAreaInsets.bottom
+        }
+        
+        return bounds
+    }
+
+    fileprivate func frameForScrollScrubber() -> CGRect {
+        let scrubberY: CGFloat = ((self.readerConfig.shouldHideNavigationOnTap == true || self.readerConfig.hideBars == true) ? 50 : 74)
+        return CGRect(x: self.pageWidth + 10, y: scrubberY, width: 40, height: (self.pageHeight - 100))
+    }
+
+    func configureNavBar() {
+        let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
+        let navBackground = folioReader.isNight(self.readerConfig.nightModeMenuBackground, greenColor)
+        let tintColor = folioReader.isNight(greenColor, UIColor.white)// readerConfig.tintColor
+        let navText = tintColor// folioReader.isNight(UIColor.white, UIColor.black)
+        let font = UIFont(name: "Avenir-Light", size: 17)!
+        setTranslucentNavigation(color: navBackground, tintColor: tintColor, titleColor: navText, andFont: font)
+    }
+
+    func configureNavBarButtons() {
+
+        // Navbar buttons
+        let shareIcon = UIImage(readerImageNamed: "icon-navbar-share")//.ignoreSystemTint(withConfiguration: self.readerConfig)
+        let audioIcon = UIImage(readerImageNamed: "icon-navbar-tts")//?.ignoreSystemTint(withConfiguration: self.readerConfig) //man-speech-icon
+        let closeIcon = UIImage(readerImageNamed: "icon-navbar-close")//?.ignoreSystemTint(withConfiguration: self.readerConfig)
+        let tocIcon = UIImage(readerImageNamed: "icon-navbar-toc")//?.ignoreSystemTint(withConfiguration: self.readerConfig)
+        let fontIcon = UIImage(readerImageNamed: "icon-navbar-font")//?.ignoreSystemTint(withConfiguration: self.readerConfig)
+        let space = 70 as CGFloat
+
+        let menu = UIBarButtonItem(image: closeIcon, style: .plain, target: self, action:#selector(closeReader(_:)))
+        let toc = UIBarButtonItem(image: tocIcon, style: .plain, target: self, action:#selector(presentChapterList(_:)))
+
+        navigationItem.leftBarButtonItems = [menu, toc]
+
+        var rightBarIcons = [UIBarButtonItem]()
+
+        if (self.readerConfig.allowSharing == true) {
+            rightBarIcons.append(UIBarButtonItem(image: shareIcon, style: .plain, target: self, action:#selector(shareChapter(_:))))
+        }
+
+        if self.book.hasAudio || self.readerConfig.enableTTS {
+            rightBarIcons.append(UIBarButtonItem(image: audioIcon, style: .plain, target: self, action:#selector(presentPlayerMenu(_:))))
+        }
+
+        let font = UIBarButtonItem(image: fontIcon, style: .plain, target: self, action: #selector(presentFontsMenu))
+        font.width = space
+
+        rightBarIcons.append(contentsOf: [font])
+        navigationItem.rightBarButtonItems = rightBarIcons
+        
+        if(self.readerConfig.displayTitle){
+            navigationItem.title = book.title
+        }
+    }
+
+    func reloadData() {
+        self.loadingView.stopAnimating()
+        self.totalPages = book.spine.spineReferences.count
+
+        self.collectionView.reloadData()
+        self.configureNavBarButtons()
+        self.setCollectionViewProgressiveDirection()
+
+        if self.readerConfig.loadSavedPositionForCurrentBook {
+            guard let position = folioReader.savedPositionForCurrentBook, let pageNumber = position["pageNumber"] as? Int, pageNumber > 0 else {
+                self.currentPageNumber = 1
+                return
+            }
+
+            self.changePageWith(page: pageNumber)
+            self.currentPageNumber = pageNumber
+        }
+    }
+
+    // MARK: Change page progressive direction
+
+    private func transformViewForRTL(_ view: UIView?) {
+        if folioReader.needsRTLChange {
+            view?.transform = CGAffineTransform(scaleX: -1, y: 1)
+        } else {
+            view?.transform = CGAffineTransform.identity
+        }
+    }
+
+    func setCollectionViewProgressiveDirection() {
+        self.transformViewForRTL(self.collectionView)
+    }
+
+    func setPageProgressiveDirection(_ page: FolioReaderPage) {
+        self.transformViewForRTL(page)
+    }
+
+    // MARK: Change layout orientation
+
+    /// Get internal page offset before layout change
+    private func updatePageOffsetRate() {
+        guard let currentPage = self.currentPage, let webView = currentPage.webView else {
+            return
+        }
+
+        let pageScrollView = webView.scrollView
+        let contentSize = pageScrollView.contentSize.forDirection(withConfiguration: self.readerConfig)
+        let contentOffset = pageScrollView.contentOffset.forDirection(withConfiguration: self.readerConfig)
+        self.pageOffsetRate = (contentSize != 0 ? (contentOffset / contentSize) : 0)
+    }
+
+    func setScrollDirection(_ direction: FolioReaderScrollDirection) {
+        guard let currentPage = self.currentPage, let webView = currentPage.webView else {
+            return
+        }
+
+        let pageScrollView = webView.scrollView
+
+        // Get internal page offset before layout change
+        self.updatePageOffsetRate()
+        // Change layout
+        self.readerConfig.scrollDirection = direction
+        self.collectionViewLayout.scrollDirection = .direction(withConfiguration: self.readerConfig)
+        self.currentPage?.setNeedsLayout()
+        self.collectionView.collectionViewLayout.invalidateLayout()
+        self.collectionView.setContentOffset(frameForPage(self.currentPageNumber).origin, animated: false)
+
+        // Page progressive direction
+        self.setCollectionViewProgressiveDirection()
+        delay(0.2) { self.setPageProgressiveDirection(currentPage) }
+
+
+        /**
+         *  This delay is needed because the page will not be ready yet
+         *  so the delay wait until layout finished the changes.
+         */
+        delay(0.1) {
+            var pageOffset = (pageScrollView.contentSize.forDirection(withConfiguration: self.readerConfig) * self.pageOffsetRate)
+
+            // Fix the offset for paged scroll
+            if (self.readerConfig.scrollDirection == .horizontal && self.pageWidth != 0) {
+                let page = round(pageOffset / self.pageWidth)
+                pageOffset = (page * self.pageWidth)
+            }
+
+            let pageOffsetPoint = self.readerConfig.isDirection(CGPoint(x: 0, y: pageOffset), CGPoint(x: pageOffset, y: 0), CGPoint(x: 0, y: pageOffset))
+            pageScrollView.setContentOffset(pageOffsetPoint, animated: true)
+        }
+    }
+
+    // MARK: Status bar and Navigation bar
+
+    func hideBars() {
+        guard self.readerConfig.shouldHideNavigationOnTap == true else {
+            return
+        }
+
+        self.updateBarsStatus(true)
+    }
+
+    func showBars() {
+        self.configureNavBar()
+        self.updateBarsStatus(false)
+    }
+
+    func toggleBars() {
+        guard self.readerConfig.shouldHideNavigationOnTap == true else {
+            return
+        }
+
+        let shouldHide = !self.navigationController!.isNavigationBarHidden
+        if shouldHide == false {
+            self.configureNavBar()
+        }
+
+        self.updateBarsStatus(shouldHide)
+    }
+
+    private func updateBarsStatus(_ shouldHide: Bool, shouldShowIndicator: Bool = false) {
+        guard let readerContainer = readerContainer else { return }
+        readerContainer.shouldHideStatusBar = shouldHide
+
+        UIView.animate(withDuration: 0.25, animations: {
+            readerContainer.setNeedsStatusBarAppearanceUpdate()
+
+            // Show minutes indicator
+            if (shouldShowIndicator == true) {
+                self.pageIndicatorView?.minutesLabel.alpha = shouldHide ? 0 : 1
+            }
+        })
+        self.navigationController?.setNavigationBarHidden(shouldHide, animated: true)
+    }
+
+    // MARK: UICollectionViewDataSource
+
+    open func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 1
+    }
+
+    open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return totalPages
+    }
+
+    open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let reuseableCell = collectionView.dequeueReusableCell(withReuseIdentifier: kReuseCellIdentifier, for: indexPath) as? FolioReaderPage
+        
+        return self.configure(readerPageCell: reuseableCell, atIndexPath: indexPath)
+    }
+
+    private func configure(readerPageCell cell: FolioReaderPage?, atIndexPath indexPath: IndexPath) -> UICollectionViewCell {
+        guard let cell = cell, let readerContainer = readerContainer else {
+            return UICollectionViewCell()
+        }
+        
+        var isNextChapter = true
+        if let lastRow = lastRow {
+            isNextChapter = lastRow < indexPath.row
+        }
+        if changingChapter {
+            isNextChapter = true
+        }
+        lastRow = indexPath.row
+
+        cell.setup(withReaderContainer: readerContainer)
+        cell.pageNumber = indexPath.row+1
+        cell.webView?.scrollView.delegate = self
+        if #available(iOS 11.0, *) {
+            cell.webView?.scrollView.contentInsetAdjustmentBehavior = .never
+        }
+        cell.webView?.setupScrollDirection()
+        cell.webView?.frame = cell.webViewFrame()
+        cell.delegate = self
+        cell.backgroundColor = .clear
+
+        setPageProgressiveDirection(cell)
+
+        // Configure the cell
+        let resource = self.book.spine.spineReferences[indexPath.row].resource
+        guard var html = try? String(contentsOfFile: resource.fullHref, encoding: String.Encoding.utf8) else {
+            return cell
+        }
+
+        let mediaOverlayStyleColors = "\"\(self.readerConfig.mediaOverlayColor.hexString(false))\", \"\(self.readerConfig.mediaOverlayColor.highlightColor().hexString(false))\""
+
+        // Inject CSS
+        let jsFilePath = Bundle.frameworkBundle().path(forResource: "Bridge", ofType: "js")
+        let cssFilePath = Bundle.frameworkBundle().path(forResource: "Style", ofType: "css")
+        let cssTag = "<link rel=\"stylesheet\" type=\"text/css\" href=\"\(cssFilePath!)\">"
+        let jsTag = "<script type=\"text/javascript\" src=\"\(jsFilePath!)\"></script>" +
+        "<script type=\"text/javascript\">setMediaOverlayStyleColors(\(mediaOverlayStyleColors))</script>"
+
+        let toInject = "\n\(cssTag)\n\(jsTag)\n</head>"
+        html = html.replacingOccurrences(of: "</head>", with: toInject)
+
+        // Font class name
+        var classes = folioReader.currentFont.cssIdentifier
+        classes += " " + folioReader.currentMediaOverlayStyle.className()
+
+        // Night mode
+        if folioReader.nightMode {
+            classes += " nightMode"
+        }
+
+        // Font Size
+        classes += " \(folioReader.currentFontSize.cssIdentifier(sliderType: .font))"
+        classes += " \(folioReader.currentMarginSize.cssIdentifier(sliderType: .margin))"
+        classes += " \(folioReader.currentInterlineSize.cssIdentifier(sliderType: .interline))"
+
+        
+        
+        html = html.replacingOccurrences(of: "<html ", with: "<html class=\"\(classes)\"")
+
+        // Let the delegate adjust the html string
+        if let modifiedHtmlContent = self.delegate?.htmlContentForPage?(cell, htmlContent: html) {
+            html = modifiedHtmlContent
+        }
+
+//        print("\n\nhtmlhtml\n\n\(html)")
+        
+        cell.loadHTMLString(html, baseURL: URL(fileURLWithPath: resource.fullHref.deletingLastPathComponent), isNextChapter: isNextChapter)
+        return cell
+    }
+
+    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+        var size = CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
+        
+        if #available(iOS 11.0, *) {
+            let orientation = UIDevice.current.orientation
+            
+            if orientation == .portrait || orientation == .portraitUpsideDown {
+                if readerConfig.scrollDirection == .horizontal {
+                    size.height = size.height - view.safeAreaInsets.bottom
+                }
+            }
+        }
+        
+        return size
+    }
+    
+    // MARK: - Device rotation
+
+    override open func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
+        guard folioReader.isReaderReady else { return }
+
+        setPageSize(toInterfaceOrientation)
+        updateCurrentPage()
+
+        if self.currentOrientation == nil || (self.currentOrientation?.isPortrait != toInterfaceOrientation.isPortrait) {
+            var pageIndicatorFrame = pageIndicatorView?.frame
+            pageIndicatorFrame?.origin.y = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.height - pageIndicatorHeight) : (self.collectionView.frame.width - pageIndicatorHeight))
+            pageIndicatorFrame?.origin.x = 0
+            pageIndicatorFrame?.size.width = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.width) : (self.collectionView.frame.height))
+            pageIndicatorFrame?.size.height = pageIndicatorHeight
+
+            var scrollScrubberFrame = scrollScrubber?.slider.frame;
+            scrollScrubberFrame?.origin.x = ((screenBounds.size.height < screenBounds.size.width) ? (screenBounds.size.width - 100) : (screenBounds.size.height + 10))
+            scrollScrubberFrame?.size.height = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.height - 100) : (self.collectionView.frame.width - 100))
+
+            self.collectionView.collectionViewLayout.invalidateLayout()
+
+            UIView.animate(withDuration: duration, animations: {
+                // Adjust page indicator view
+                if let pageIndicatorFrame = pageIndicatorFrame {
+                    self.pageIndicatorView?.frame = pageIndicatorFrame
+                    self.pageIndicatorView?.reloadView(updateShadow: true)
+                }
+
+                // Adjust scroll scrubber slider
+                if let scrollScrubberFrame = scrollScrubberFrame {
+                    self.scrollScrubber?.slider.frame = scrollScrubberFrame
+                }
+
+                // Adjust collectionView
+                self.collectionView.contentSize = self.readerConfig.isDirection(
+                    CGSize(width: self.pageWidth, height: self.pageHeight * CGFloat(self.totalPages)),
+                    CGSize(width: self.pageWidth * CGFloat(self.totalPages), height: self.pageHeight),
+                    CGSize(width: self.pageWidth * CGFloat(self.totalPages), height: self.pageHeight)
+                )
+                self.collectionView.setContentOffset(self.frameForPage(self.currentPageNumber).origin, animated: false)
+                self.collectionView.collectionViewLayout.invalidateLayout()
+
+                // Adjust internal page offset
+                self.updatePageOffsetRate()
+            })
+        }
+
+        self.currentOrientation = toInterfaceOrientation
+    }
+
+    override open func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
+        guard folioReader.isReaderReady == true, let currentPage = currentPage else {
+            return
+        }
+
+        // Update pages
+        pagesForCurrentPage(currentPage)
+        currentPage.refreshPageMode()
+
+        scrollScrubber?.setSliderVal()
+
+        // After rotation fix internal page offset
+        var pageOffset = (currentPage.webView?.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig) ?? 0) * pageOffsetRate
+
+        // Fix the offset for paged scroll
+        if (self.readerConfig.scrollDirection == .horizontal && self.pageWidth != 0) {
+            let page = round(pageOffset / self.pageWidth)
+            pageOffset = page * self.pageWidth
+        }
+
+        let pageOffsetPoint = self.readerConfig.isDirection(CGPoint(x: 0, y: pageOffset), CGPoint(x: pageOffset, y: 0), CGPoint(x: 0, y: pageOffset))
+        currentPage.webView?.scrollView.setContentOffset(pageOffsetPoint, animated: true)
+    }
+
+    override open func willAnimateRotation(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
+        guard folioReader.isReaderReady else {
+            return
+        }
+
+        self.collectionView.scrollToItem(at: IndexPath(row: self.currentPageNumber - 1, section: 0), at: UICollectionViewScrollPosition(), animated: false)
+        if (self.currentPageNumber + 1) >= totalPages {
+            UIView.animate(withDuration: duration, animations: {
+                self.collectionView.setContentOffset(self.frameForPage(self.currentPageNumber).origin, animated: false)
+            })
+        }
+    }
+
+    // MARK: - Page
+
+    func setPageSize(_ orientation: UIInterfaceOrientation) {
+        guard orientation.isPortrait else {
+            if screenBounds.size.width > screenBounds.size.height {
+                self.pageWidth = screenBounds.size.width
+                self.pageHeight = screenBounds.size.height
+            } else {
+                self.pageWidth = screenBounds.size.height
+                self.pageHeight = screenBounds.size.width
+            }
+            return
+        }
+
+        if screenBounds.size.width < screenBounds.size.height {
+            self.pageWidth = screenBounds.size.width
+            self.pageHeight = screenBounds.size.height
+        } else {
+            self.pageWidth = screenBounds.size.height
+            self.pageHeight = screenBounds.size.width
+        }
+    }
+
+    func updateCurrentPage(_ page: FolioReaderPage? = nil, completion: (() -> Void)? = nil) {
+        if let page = page {
+            currentPage = page
+            self.previousPageNumber = page.pageNumber-1
+            self.currentPageNumber = page.pageNumber
+        } else {
+            let currentIndexPath = getCurrentIndexPath()
+            currentPage = collectionView.cellForItem(at: currentIndexPath) as? FolioReaderPage
+
+            self.previousPageNumber = currentIndexPath.row
+            self.currentPageNumber = currentIndexPath.row+1
+        }
+
+        self.nextPageNumber = (((self.currentPageNumber + 1) <= totalPages) ? (self.currentPageNumber + 1) : self.currentPageNumber)
+
+        // Set pages
+        guard let currentPage = currentPage else {
+            completion?()
+            return
+        }
+
+        scrollScrubber?.setSliderVal()
+
+        if let readingTime = currentPage.webView?.js("getReadingTime()") {
+            pageIndicatorView?.totalMinutes = Int(readingTime)!
+        } else {
+            pageIndicatorView?.totalMinutes = 0
+        }
+        pagesForCurrentPage(currentPage)
+
+        delegate?.pageDidAppear?(currentPage)
+        delegate?.pageItemChanged?(self.getCurrentPageItemNumber())
+
+        completion?()
+    }
+
+    func pagesForCurrentPage(_ page: FolioReaderPage?) {
+        guard let page = page, let webView = page.webView else { return }
+
+        let pageSize = self.readerConfig.isDirection(pageHeight, self.pageWidth, pageHeight)
+        let contentSize = page.webView?.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig) ?? 0
+        self.pageIndicatorView?.totalPages = ((pageSize != 0) ? Int(ceil(contentSize / pageSize)) : 0)
+
+        let pageOffSet = self.readerConfig.isDirection(webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.y)
+        let webViewPage = pageForOffset(pageOffSet, pageHeight: pageSize)
+
+        self.pageIndicatorView?.currentPage = webViewPage
+    }
+
+    func pageForOffset(_ offset: CGFloat, pageHeight height: CGFloat) -> Int {
+        guard (height != 0) else {
+            return 0
+        }
+
+        let page = Int(ceil(offset / height))+1
+        return page
+    }
+
+    func getCurrentIndexPath() -> IndexPath {
+        let indexPaths = collectionView.indexPathsForVisibleItems
+        var indexPath = IndexPath()
+
+        if indexPaths.count > 1 {
+            let first = indexPaths.first!
+            let last = indexPaths.last!
+
+            switch self.pageScrollDirection {
+            case .up, .left:
+                if first.compare(last) == .orderedAscending {
+                    indexPath = last
+                } else {
+                    indexPath = first
+                }
+            default:
+                if first.compare(last) == .orderedAscending {
+                    indexPath = first
+                } else {
+                    indexPath = last
+                }
+            }
+        } else {
+            indexPath = indexPaths.first ?? IndexPath(row: 0, section: 0)
+        }
+
+        return indexPath
+    }
+
+    func frameForPage(_ page: Int) -> CGRect {
+        return self.readerConfig.isDirection(
+            CGRect(x: 0, y: self.pageHeight * CGFloat(page-1), width: self.pageWidth, height: self.pageHeight),
+            CGRect(x: self.pageWidth * CGFloat(page-1), y: 0, width: self.pageWidth, height: self.pageHeight),
+            CGRect(x: 0, y: self.pageHeight * CGFloat(page-1), width: self.pageWidth, height: self.pageHeight)
+        )
+    }
+
+    open func changePageWith(page: Int, andFragment fragment: String, animated: Bool = false, completion: (() -> Void)? = nil) {
+        if (self.currentPageNumber == page) {
+            if let currentPage = currentPage , fragment != "" {
+                currentPage.handleAnchor(fragment, avoidBeginningAnchors: true, animated: animated)
+            }
+            completion?()
+        } else {
+            tempFragment = fragment
+            changePageWith(page: page, animated: animated, completion: { () -> Void in
+                self.updateCurrentPage {
+                    completion?()
+                }
+            })
+        }
+    }
+
+    open func changePageWith(href: String, animated: Bool = false, completion: (() -> Void)? = nil) {
+        let item = findPageByHref(href)
+        let indexPath = IndexPath(row: item, section: 0)
+        changePageWith(indexPath: indexPath, animated: animated, completion: { () -> Void in
+            self.updateCurrentPage {
+                completion?()
+            }
+        })
+    }
+
+    open func changePageWith(href: String, andAudioMarkID markID: String) {
+        if recentlyScrolled { return } // if user recently scrolled, do not change pages or scroll the webview
+        guard let currentPage = currentPage else { return }
+
+        let item = findPageByHref(href)
+        let pageUpdateNeeded = item+1 != currentPage.pageNumber
+        let indexPath = IndexPath(row: item, section: 0)
+        changePageWith(indexPath: indexPath, animated: true) { () -> Void in
+            if pageUpdateNeeded {
+                self.updateCurrentPage {
+                    currentPage.audioMarkID(markID)
+                }
+            } else {
+                currentPage.audioMarkID(markID)
+            }
+        }
+    }
+
+    open func changePageWith(indexPath: IndexPath, animated: Bool = false, completion: (() -> Void)? = nil) {
+        guard indexPathIsValid(indexPath) else {
+            print("ERROR: Attempt to scroll to invalid index path")
+            completion?()
+            return
+        }
+
+        UIView.animate(withDuration: animated ? 0.3 : 0, delay: 0, options: UIViewAnimationOptions(), animations: { () -> Void in
+            self.collectionView.scrollToItem(at: indexPath, at: .direction(withConfiguration: self.readerConfig), animated: false)
+        }) { (finished: Bool) -> Void in
+            completion?()
+        }
+    }
+    
+    open func changePageWith(href: String, pageItem: Int, animated: Bool = false, completion: (() -> Void)? = nil) {
+        changePageWith(href: href, animated: animated) {
+            self.changePageItem(to: pageItem)
+        }
+    }
+
+    func indexPathIsValid(_ indexPath: IndexPath) -> Bool {
+        let section = indexPath.section
+        let row = indexPath.row
+        let lastSectionIndex = numberOfSections(in: collectionView) - 1
+
+        //Make sure the specified section exists
+        if section > lastSectionIndex {
+            return false
+        }
+
+        let rowCount = self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) - 1
+        return row <= rowCount
+    }
+
+    open func isLastPage() -> Bool{
+        return (currentPageNumber == self.nextPageNumber)
+    }
+
+    public func changePageToNext(_ completion: (() -> Void)? = nil) {
+        changePageWith(page: self.nextPageNumber, animated: true) { () -> Void in
+            completion?()
+        }
+    }
+
+    public func changePageToPrevious(_ completion: (() -> Void)? = nil) {
+        changePageWith(page: self.previousPageNumber, animated: true) { () -> Void in
+            completion?()
+        }
+    }
+    
+    public func changePageItemToNext(_ completion: (() -> Void)? = nil) {
+        // TODO: It was implemented for horizontal orientation.
+        // Need check page orientation (v/h) and make correct calc for vertical
+        guard
+            let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
+            let contentOffset = cell.webView?.scrollView.contentOffset,
+            let contentOffsetXLimit = cell.webView?.scrollView.contentSize.width else {
+                completion?()
+                return
+        }
+        
+        let cellSize = cell.frame.size
+        let contentOffsetX = contentOffset.x + cellSize.width
+        
+        if contentOffsetX >= contentOffsetXLimit {
+            changePageToNext(completion)
+        } else {
+            cell.scrollPageToOffset(contentOffsetX, animated: true)
+        }
+        
+        completion?()
+    }
+
+    public func getCurrentPageItemNumber() -> Int {
+        guard let page = currentPage, let webView = page.webView else { return 0 }
+        
+        let pageSize = readerConfig.isDirection(pageHeight, pageWidth, pageHeight)
+        let pageOffSet = readerConfig.isDirection(webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.y)
+        let webViewPage = pageForOffset(pageOffSet, pageHeight: pageSize)
+        
+        return webViewPage
+    }
+
+    public func changePageItemToPrevious(_ completion: (() -> Void)? = nil) {
+        // TODO: It was implemented for horizontal orientation.
+        // Need check page orientation (v/h) and make correct calc for vertical
+        guard
+            let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
+            let contentOffset = cell.webView?.scrollView.contentOffset else {
+                completion?()
+                return
+        }
+        
+        let cellSize = cell.frame.size
+        let contentOffsetX = contentOffset.x - cellSize.width
+        
+        if contentOffsetX < 0 {
+            changePageToPrevious(completion)
+        } else {
+            cell.scrollPageToOffset(contentOffsetX, animated: true)
+        }
+        
+        completion?()
+    }
+
+    public func changePageItemToLast(animated: Bool = true, _ completion: (() -> Void)? = nil) {
+        // TODO: It was implemented for horizontal orientation.
+        // Need check page orientation (v/h) and make correct calc for vertical
+        guard
+            let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
+            let contentSize = cell.webView?.scrollView.contentSize else {
+                completion?()
+                return
+        }
+        
+        let cellSize = cell.frame.size
+        var contentOffsetX: CGFloat = 0.0
+        
+        if contentSize.width > 0 && cellSize.width > 0 {
+            contentOffsetX = (cellSize.width * (contentSize.width / cellSize.width)) - cellSize.width
+        }
+        
+        if contentOffsetX < 0 {
+            contentOffsetX = 0
+        }
+        
+        cell.scrollPageToOffset(contentOffsetX, animated: animated)
+        
+        completion?()
+    }
+
+    public func changePageItem(to: Int, animated: Bool = true, completion: (() -> Void)? = nil) {
+        // TODO: It was implemented for horizontal orientation.
+        // Need check page orientation (v/h) and make correct calc for vertical
+        guard
+            let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
+            let contentSize = cell.webView?.scrollView.contentSize else {
+                delegate?.pageItemChanged?(getCurrentPageItemNumber())
+                completion?()
+                return
+        }
+        
+        let cellSize = cell.frame.size
+        var contentOffsetX: CGFloat = 0.0
+        
+        if contentSize.width > 0 && cellSize.width > 0 {
+            contentOffsetX = (cellSize.width * CGFloat(to)) - cellSize.width
+        }
+        
+        if contentOffsetX > contentSize.width {
+            contentOffsetX = contentSize.width - cellSize.width
+        }
+        
+        if contentOffsetX < 0 {
+            contentOffsetX = 0
+        }
+        
+        UIView.animate(withDuration: animated ? 0.3 : 0, delay: 0, options: UIViewAnimationOptions(), animations: { () -> Void in
+            cell.scrollPageToOffset(contentOffsetX, animated: animated)
+        }) { (finished: Bool) -> Void in
+            self.updateCurrentPage {
+                completion?()
+            }
+        }
+    }
+
+    /**
+     Find a page by FRTocReference.
+     */
+    public func findPageByResource(_ reference: FRTocReference) -> Int {
+        var count = 0
+        for item in self.book.spine.spineReferences {
+            if let resource = reference.resource, item.resource == resource {
+                return count
+            }
+            count += 1
+        }
+        return count
+    }
+
+    /**
+     Find a page by href.
+     */
+    public func findPageByHref(_ href: String) -> Int {
+        var count = 0
+        for item in self.book.spine.spineReferences {
+            if item.resource.href == href {
+                return count
+            }
+            count += 1
+        }
+        return count
+    }
+
+    /**
+     Find and return the current chapter resource.
+     */
+    public func getCurrentChapter() -> FRResource? {
+        for item in self.book.flatTableOfContents {
+            if
+                let reference = self.book.spine.spineReferences[safe: (self.currentPageNumber - 1)],
+                let resource = item.resource,
+                (resource == reference.resource) {
+                return item.resource
+            }
+        }
+        return nil
+    }
+
+    /**
+     Return the current chapter progress based on current chapter and total of chapters.
+     */
+    public func getCurrentChapterProgress() -> CGFloat {
+        let total = totalPages
+        let current = currentPageNumber
+        
+        if total == 0 {
+            return 0
+        }
+        
+        return CGFloat((100 * current) / total)
+    }
+
+    /**
+     Find and return the current chapter name.
+     */
+    public func getCurrentChapterName() -> String? {
+        for item in self.book.flatTableOfContents {
+            guard
+                let reference = self.book.spine.spineReferences[safe: (self.currentPageNumber - 1)],
+                let resource = item.resource,
+                (resource == reference.resource),
+                let title = item.title else {
+                    continue
+            }
+
+            return title
+        }
+
+        return nil
+    }
+
+    // MARK: Public page methods
+
+    /**
+     Changes the current page of the reader.
+
+     - parameter page: The target page index. Note: The page index starts at 1 (and not 0).
+     - parameter animated: En-/Disables the animation of the page change.
+     - parameter completion: A Closure which is called if the page change is completed.
+     */
+    public func changePageWith(page: Int, animated: Bool = false, completion: (() -> Void)? = nil) {
+        if page > 0 && page-1 < totalPages {
+            let indexPath = IndexPath(row: page-1, section: 0)
+            changePageWith(indexPath: indexPath, animated: animated, completion: { () -> Void in
+                self.updateCurrentPage {
+                    completion?()
+                }
+            })
+        }
+    }
+
+    // MARK: - Audio Playing
+
+    func audioMark(href: String, fragmentID: String) {
+        changePageWith(href: href, andAudioMarkID: fragmentID)
+    }
+
+    // MARK: - Sharing
+
+    /**
+     Sharing chapter method.
+     */
+    @objc func shareChapter(_ sender: UIBarButtonItem) {
+        guard let currentPage = currentPage else { return }
+
+        if let chapterText = currentPage.webView?.js("getBodyText()") {
+            let htmlText = chapterText.replacingOccurrences(of: "[\\n\\r]+", with: "<br />", options: .regularExpression)
+            var subject = readerConfig.localizedShareChapterSubject
+            var html = ""
+            var text = ""
+            var bookTitle = ""
+            var chapterName = ""
+            var authorName = ""
+            var shareItems = [AnyObject]()
+
+            // Get book title
+            if let title = self.book.title {
+                bookTitle = title
+                subject += " “\(title)”"
+            }
+
+            // Get chapter name
+            if let chapter = getCurrentChapterName() {
+                chapterName = chapter
+            }
+
+            // Get author name
+            if let author = self.book.metadata.creators.first {
+                authorName = author.name
+            }
+
+            // Sharing html and text
+            html = "<html><body>"
+            html += "<br /><hr> <p>\(htmlText)</p> <hr><br />"
+            html += "<center><p style=\"color:gray\">"+readerConfig.localizedShareAllExcerptsFrom+"</p>"
+            html += "<b>\(bookTitle)</b><br />"
+            html += readerConfig.localizedShareBy+" <i>\(authorName)</i><br />"
+
+            if let bookShareLink = readerConfig.localizedShareWebLink {
+                html += "<a href=\"\(bookShareLink.absoluteString)\">\(bookShareLink.absoluteString)</a>"
+                shareItems.append(bookShareLink as AnyObject)
+            }
+
+            html += "</center></body></html>"
+            text = "\(chapterName)\n\n“\(chapterText)” \n\n\(bookTitle) \n\(readerConfig.localizedShareBy) \(authorName)"
+
+            let act = FolioReaderSharingProvider(subject: subject, text: text, html: html)
+            shareItems.insert(contentsOf: [act, "" as AnyObject], at: 0)
+
+            let activityViewController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
+            activityViewController.excludedActivityTypes = [UIActivityType.print, UIActivityType.postToVimeo]
+
+            // Pop style on iPad
+            if let actv = activityViewController.popoverPresentationController {
+                actv.barButtonItem = sender
+            }
+
+            present(activityViewController, animated: true, completion: nil)
+        }
+    }
+
+    /**
+     Sharing highlight method.
+     */
+    func shareHighlight(_ string: String, rect: CGRect) {
+        var subject = readerConfig.localizedShareHighlightSubject
+        var html = ""
+        var text = ""
+        var bookTitle = ""
+        var chapterName = ""
+        var authorName = ""
+        var shareItems = [AnyObject]()
+
+        // Get book title
+        if let title = self.book.title {
+            bookTitle = title
+            subject += " “\(title)”"
+        }
+
+        // Get chapter name
+        if let chapter = getCurrentChapterName() {
+            chapterName = chapter
+        }
+
+        // Get author name
+        if let author = self.book.metadata.creators.first {
+            authorName = author.name
+        }
+
+        // Sharing html and text
+        html = "<html><body>"
+        html += "<br /><hr> <p>\(chapterName)</p>"
+        html += "<p>\(string)</p> <hr><br />"
+        html += "<center><p style=\"color:gray\">"+readerConfig.localizedShareAllExcerptsFrom+"</p>"
+        html += "<b>\(bookTitle)</b><br />"
+        html += readerConfig.localizedShareBy+" <i>\(authorName)</i><br />"
+
+        if let bookShareLink = readerConfig.localizedShareWebLink {
+            html += "<a href=\"\(bookShareLink.absoluteString)\">\(bookShareLink.absoluteString)</a>"
+            shareItems.append(bookShareLink as AnyObject)
+        }
+
+        html += "</center></body></html>"
+        text = "\(chapterName)\n\n“\(string)” \n\n\(bookTitle) \n\(readerConfig.localizedShareBy) \(authorName)"
+
+        let act = FolioReaderSharingProvider(subject: subject, text: text, html: html)
+        shareItems.insert(contentsOf: [act, "" as AnyObject], at: 0)
+
+        let activityViewController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
+        activityViewController.excludedActivityTypes = [UIActivityType.print, UIActivityType.postToVimeo]
+
+        // Pop style on iPad
+        if let actv = activityViewController.popoverPresentationController {
+            actv.sourceView = currentPage
+            actv.sourceRect = rect
+        }
+
+        present(activityViewController, animated: true, completion: nil)
+    }
+
+    // MARK: - ScrollView Delegate
+
+    open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
+        self.isScrolling = true
+        clearRecentlyScrolled()
+        recentlyScrolled = true
+        pointNow = scrollView.contentOffset
+
+        if let currentPage = currentPage {
+            currentPage.webView?.createMenu(options: true)
+            currentPage.webView?.setMenuVisible(false)
+        }
+
+        scrollScrubber?.scrollViewWillBeginDragging(scrollView)
+    }
+
+    open func scrollViewDidScroll(_ scrollView: UIScrollView) {
+
+        if (navigationController?.isNavigationBarHidden == false) {
+            self.toggleBars()
+        }
+
+        scrollScrubber?.scrollViewDidScroll(scrollView)
+
+        let isCollectionScrollView = (scrollView is UICollectionView)
+        let scrollType: ScrollType = ((isCollectionScrollView == true) ? .chapter : .page)
+
+        // Update current reading page
+        if (isCollectionScrollView == false), let page = currentPage, let webView = page.webView {
+
+            let pageSize = self.readerConfig.isDirection(self.pageHeight, self.pageWidth, self.pageHeight)
+            let contentOffset = webView.scrollView.contentOffset.forDirection(withConfiguration: self.readerConfig)
+            let contentSize = webView.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig)
+            if (contentOffset + pageSize <= contentSize) {
+
+                let webViewPage = pageForOffset(contentOffset, pageHeight: pageSize)
+
+                if (readerConfig.scrollDirection == .horizontalWithVerticalContent) {
+                    let currentIndexPathRow = (page.pageNumber - 1)
+
+                    // if the cell reload doesn't save the top position offset
+                    if let oldOffSet = self.currentWebViewScrollPositions[currentIndexPathRow], (abs(oldOffSet.y - scrollView.contentOffset.y) > 100) {
+                        // Do nothing
+                    } else {
+                        self.currentWebViewScrollPositions[currentIndexPathRow] = scrollView.contentOffset
+                    }
+                }
+
+                if (pageIndicatorView?.currentPage != webViewPage) {
+                    pageIndicatorView?.currentPage = webViewPage
+                }
+                
+                self.delegate?.pageItemChanged?(webViewPage)
+            }
+        }
+
+        self.updatePageScrollDirection(inScrollView: scrollView, forScrollType: scrollType)
+    }
+
+    private func updatePageScrollDirection(inScrollView scrollView: UIScrollView, forScrollType scrollType: ScrollType) {
+        
+        let scrollViewContentOffsetForDirection = scrollView.contentOffset.forDirection(withConfiguration: self.readerConfig, scrollType: scrollType)
+        let pointNowForDirection = pointNow.forDirection(withConfiguration: self.readerConfig, scrollType: scrollType)
+        // The movement is either positive or negative. This happens if the page change isn't completed. Toggle to the other scroll direction then.
+        let isCurrentlyPositive = (self.pageScrollDirection == .left || self.pageScrollDirection == .up)
+
+        if (scrollViewContentOffsetForDirection < pointNowForDirection) {
+            self.pageScrollDirection = .negative(withConfiguration: self.readerConfig, scrollType: scrollType)
+        } else if (scrollViewContentOffsetForDirection > pointNowForDirection) {
+            self.pageScrollDirection = .positive(withConfiguration: self.readerConfig, scrollType: scrollType)
+        } else if (isCurrentlyPositive == true) {
+            self.pageScrollDirection = .negative(withConfiguration: self.readerConfig, scrollType: scrollType)
+        } else {
+            self.pageScrollDirection = .positive(withConfiguration: self.readerConfig, scrollType: scrollType)
+        }
+    }
+
+    open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
+        self.isScrolling = false
+
+        // Perform the page after a short delay as the collection view hasn't completed it's transition if this method is called (the index paths aren't right during fast scrolls).
+        delay(0.2, closure: { [weak self] in
+            if (self?.readerConfig.scrollDirection == .horizontalWithVerticalContent),
+                let cell = ((scrollView.superview as? UIWebView)?.delegate as? FolioReaderPage) {
+                let currentIndexPathRow = cell.pageNumber - 1
+                self?.currentWebViewScrollPositions[currentIndexPathRow] = scrollView.contentOffset
+            }
+
+            if (scrollView is UICollectionView) {
+                guard let instance = self else {
+                    return
+                }
+                
+                if instance.totalPages > 0 {
+                    instance.updateCurrentPage()
+                    instance.delegate?.pageItemChanged?(instance.getCurrentPageItemNumber())
+                }
+            } else {
+                self?.scrollScrubber?.scrollViewDidEndDecelerating(scrollView)
+            }
+        })
+    }
+
+    open func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
+        if scrollView == collectionView{            
+            scrollView.isUserInteractionEnabled = false
+            delay(0.5, closure: { [weak self] in
+                scrollView.isUserInteractionEnabled = true
+            })
+        }
+      
+        recentlyScrolledTimer = Timer(timeInterval:recentlyScrolledDelay, target: self, selector: #selector(FolioReaderCenter.clearRecentlyScrolled), userInfo: nil, repeats: false)
+        RunLoop.current.add(recentlyScrolledTimer, forMode: RunLoopMode.commonModes)
+    }
+
+    @objc func clearRecentlyScrolled() {
+        if(recentlyScrolledTimer != nil) {
+            recentlyScrolledTimer.invalidate()
+            recentlyScrolledTimer = nil
+        }
+        recentlyScrolled = false
+    }
+
+    open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
+        scrollScrubber?.scrollViewDidEndScrollingAnimation(scrollView)
+    }
+
+    // MARK: NavigationBar Actions
+
+    @objc func closeReader(_ sender: UIBarButtonItem) {
+        dismiss()
+        folioReader.close()
+    }
+
+    //PD: changed
+    /**
+     Present chapter list
+     */
+    @objc func presentChapterList(_ sender: UIBarButtonItem) {
+        folioReader.saveReaderState()
+
+        let chapter = FolioReaderChapterList(folioReader: folioReader, readerConfig: readerConfig, book: book, delegate: self)
+        chapter.title = "Spis treści"
+//        let highlight = FolioReaderHighlightList(folioReader: folioReader, readerConfig: readerConfig)
+//        let pageController = PageViewController(folioReader: folioReader, readerConfig: readerConfig)
+
+//        pageController.viewControllerOne = chapter
+//        pageController.viewControllerTwo = highlight
+//        pageController.segmentedControlItems = [readerConfig.localizedContentsTitle, readerConfig.localizedHighlightsTitle]
+
+        let vc = FolioReaderModalViewController(folioReader: folioReader, readerConfig: readerConfig)
+        
+        vc.viewControllerOne = chapter
+        let nav = UINavigationController(rootViewController:vc /*pageController*/)
+        present(nav, animated: true, completion: nil)
+    }
+    
+    /**
+     Present fonts and settings menu
+     */
+    @objc func presentFontsMenu() {
+        folioReader.saveReaderState()
+        hideBars()
+
+        let menu = FolioReaderFontsMenu(folioReader: folioReader, readerConfig: readerConfig)
+        menu.modalPresentationStyle = .custom
+
+        animator = ZFModalTransitionAnimator(modalViewController: menu)
+        animator.isDragable = false
+        animator.bounces = false
+        animator.behindViewAlpha = 0.4
+        animator.behindViewScale = 1
+        animator.transitionDuration = 0.6
+        animator.direction = ZFModalTransitonDirection.bottom
+
+        menu.transitioningDelegate = animator
+        self.present(menu, animated: true, completion: nil)
+    }
+
+    /**
+     Present audio player menu
+     */
+    @objc func presentPlayerMenu(_ sender: UIBarButtonItem) {
+        folioReader.saveReaderState()
+        hideBars()
+
+        let menu = FolioReaderPlayerMenu(folioReader: folioReader, readerConfig: readerConfig)
+        menu.modalPresentationStyle = .custom
+
+        animator = ZFModalTransitionAnimator(modalViewController: menu)
+        animator.isDragable = true
+        animator.bounces = false
+        animator.behindViewAlpha = 0.4
+        animator.behindViewScale = 1
+        animator.transitionDuration = 0.6
+        animator.direction = ZFModalTransitonDirection.bottom
+
+        menu.transitioningDelegate = animator
+        present(menu, animated: true, completion: nil)
+    }
+
+    /**
+     Present Quote Share
+     */
+    func presentQuoteShare(_ string: String) {
+        let quoteShare = FolioReaderQuoteShare(initWithText: string, readerConfig: readerConfig, folioReader: folioReader, book: book)
+        let nav = UINavigationController(rootViewController: quoteShare)
+
+        if UIDevice.current.userInterfaceIdiom == .pad {
+            nav.modalPresentationStyle = .formSheet
+        }
+        present(nav, animated: true, completion: nil)
+    }
+}
+
+// MARK: FolioPageDelegate
+
+extension FolioReaderCenter: FolioReaderPageDelegate {
+
+    public func pageDidLoad(_ page: FolioReaderPage) {
+        if self.readerConfig.loadSavedPositionForCurrentBook, let position = folioReader.savedPositionForCurrentBook {
+            let pageNumber = position["pageNumber"] as? Int
+            let offset = self.readerConfig.isDirection(position["pageOffsetY"], position["pageOffsetX"], position["pageOffsetY"]) as? CGFloat
+            let pageOffset = offset
+
+            if isFirstLoad {
+                updateCurrentPage(page)
+                isFirstLoad = false
+
+                if (self.currentPageNumber == pageNumber && pageOffset > 0) {
+                    page.scrollPageToOffset(pageOffset!, animated: false)
+                }
+            } else if (self.isScrolling == false && folioReader.needsRTLChange == true) {
+                page.scrollPageToBottom()
+            }
+        } else if isFirstLoad {
+            updateCurrentPage(page)
+            isFirstLoad = false
+        }
+
+        // Go to fragment if needed
+        if let fragmentID = tempFragment, let currentPage = currentPage , fragmentID != "" {
+            currentPage.handleAnchor(fragmentID, avoidBeginningAnchors: true, animated: true)
+            tempFragment = nil
+        }
+        
+        if (readerConfig.scrollDirection == .horizontalWithVerticalContent),
+            let offsetPoint = self.currentWebViewScrollPositions[page.pageNumber - 1] {
+            page.webView?.scrollView.setContentOffset(offsetPoint, animated: false)
+        }
+        
+        // Pass the event to the centers `pageDelegate`
+        pageDelegate?.pageDidLoad?(page)
+    }
+    
+    public func pageWillLoad(_ page: FolioReaderPage) {
+        // Pass the event to the centers `pageDelegate`
+        pageDelegate?.pageWillLoad?(page)
+    }
+    
+    public func pageTap(_ recognizer: UITapGestureRecognizer) {
+        // Pass the event to the centers `pageDelegate`
+        pageDelegate?.pageTap?(recognizer)
+    }
+    
+}
+
+// MARK: FolioReaderChapterListDelegate
+
+extension FolioReaderCenter: FolioReaderChapterListDelegate {
+    
+    func chapterList(_ chapterList: FolioReaderChapterList, didSelectRowAtIndexPath indexPath: IndexPath, withTocReference reference: FRTocReference) {
+        let item = findPageByResource(reference)
+        
+        if item < totalPages {
+            let indexPath = IndexPath(row: item, section: 0)
+            changingChapter = true
+            changePageWith(indexPath: indexPath, animated: false, completion: { () -> Void in
+                self.updateCurrentPage()
+                self.changingChapter = false
+            })
+            tempReference = reference
+        } else {
+            print("Failed to load book because the requested resource is missing.")
+        }
+    }
+    
+    func chapterList(didDismissedChapterList chapterList: FolioReaderChapterList) {
+        updateCurrentPage()
+        
+        // Move to #fragment
+        if let reference = tempReference {
+            if let fragmentID = reference.fragmentID, let currentPage = currentPage , fragmentID != "" {
+                currentPage.handleAnchor(reference.fragmentID!, avoidBeginningAnchors: true, animated: true)
+            }
+            tempReference = nil
+        }
+    }
+    
+    func getScreenBounds() -> CGRect {
+        var bounds = view.frame
+        
+        if #available(iOS 11.0, *) {
+            bounds.size.height = bounds.size.height - view.safeAreaInsets.bottom
+        }
+        
+        return bounds
+    }
+    
+}
+
+class FolioReaderModalViewController: UIViewController{
+    
+    var viewControllerOne: UIViewController!
+    fileprivate var readerConfig: FolioReaderConfig
+    fileprivate var folioReader: FolioReader
+    
+    // MARK: Init
+    
+    init(folioReader: FolioReader, readerConfig: FolioReaderConfig) {
+        self.folioReader = folioReader
+        self.readerConfig = readerConfig
+        super.init(nibName: nil, bundle: nil)
+        self.edgesForExtendedLayout = UIRectEdge()
+        self.extendedLayoutIncludesOpaqueBars = true
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("storyboards are incompatible with truth and beauty")
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        self.navigationItem.title = viewControllerOne.title
+        
+        addChildViewController(viewControllerOne)
+        viewControllerOne.view.frame = view.bounds
+        view.addSubview(viewControllerOne.view)
+        viewControllerOne.view.translatesAutoresizingMaskIntoConstraints = false
+        if #available(iOS 9.0, *) {
+            viewControllerOne.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
+            viewControllerOne.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
+            viewControllerOne.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
+            viewControllerOne.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
+        } else {
+            // Fallback on earlier versions
+        }
+        viewControllerOne.didMove(toParentViewController: self)
+        
+//        self.view.backgroundColor = UIColor.white
+//        self.setViewControllers([viewList[index]], direction: .forward, animated: false, completion: nil)
+        
+        //PD: changed
+        let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
+        let tintColor = folioReader.isNight(greenColor, UIColor.white)
+        
+        let closeImage = UIImage(readerImageNamed: "icon-navbar-close")?.imageTintColor(tintColor)?.withRenderingMode(.alwaysOriginal)// ignoreSystemTint(withConfiguration: readerConfig)
+        self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: closeImage, style: .plain, target: self, action: #selector(dismiss as () -> Void))
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        configureNavBar()
+    }
+    
+    func configureNavBar() {
+        
+        let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
+        let navBackground = folioReader.isNight(self.readerConfig.nightModeMenuBackground, greenColor)
+        let tintColor = folioReader.isNight(greenColor, UIColor.white)
+        let navText = tintColor
+        let font = UIFont(name: "Avenir-Light", size: 17)!
+        setTranslucentNavigation(color: navBackground, tintColor: tintColor, titleColor: navText, andFont: font)
+    }
+    
+    //PD: changed
+    override var preferredStatusBarStyle : UIStatusBarStyle {
+        return .lightContent //self.folioReader.isNight(.lightContent, .default)
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderChapterList.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderChapterList.swift
new file mode 100755 (executable)
index 0000000..bdf27ca
--- /dev/null
@@ -0,0 +1,119 @@
+//
+//  FolioReaderChapterList.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 15/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+/// Table Of Contents delegate
+@objc protocol FolioReaderChapterListDelegate: class {
+    /**
+     Notifies when the user selected some item on menu.
+     */
+    func chapterList(_ chapterList: FolioReaderChapterList, didSelectRowAtIndexPath indexPath: IndexPath, withTocReference reference: FRTocReference)
+
+    /**
+     Notifies when chapter list did totally dismissed.
+     */
+    func chapterList(didDismissedChapterList chapterList: FolioReaderChapterList)
+}
+
+class FolioReaderChapterList: UITableViewController {
+
+    weak var delegate: FolioReaderChapterListDelegate?
+    fileprivate var tocItems = [FRTocReference]()
+    fileprivate var book: FRBook
+    fileprivate var readerConfig: FolioReaderConfig
+    fileprivate var folioReader: FolioReader
+
+    init(folioReader: FolioReader, readerConfig: FolioReaderConfig, book: FRBook, delegate: FolioReaderChapterListDelegate?) {
+        self.readerConfig = readerConfig
+        self.folioReader = folioReader
+        self.delegate = delegate
+        self.book = book
+
+        super.init(style: UITableViewStyle.plain)
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init with coder not supported")
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        // Register cell classes
+        self.tableView.register(FolioReaderChapterListCell.self, forCellReuseIdentifier: kReuseCellIdentifier)
+        self.tableView.separatorInset = UIEdgeInsets.zero
+        self.tableView.backgroundColor = self.folioReader.isNight(self.readerConfig.nightModeMenuBackground, self.readerConfig.menuBackgroundColor)
+        self.tableView.separatorColor = self.folioReader.isNight(self.readerConfig.nightModeSeparatorColor, self.readerConfig.menuSeparatorColor)
+
+        self.tableView.rowHeight = UITableViewAutomaticDimension
+        self.tableView.estimatedRowHeight = 50
+
+        // Create TOC list
+        self.tocItems = self.book.flatTableOfContents
+    }
+
+    // MARK: - Table view data source
+
+    override func numberOfSections(in tableView: UITableView) -> Int {
+        return 1
+    }
+
+    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return tocItems.count
+    }
+
+    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: kReuseCellIdentifier, for: indexPath) as! FolioReaderChapterListCell
+
+        cell.setup(withConfiguration: self.readerConfig)
+        let tocReference = tocItems[(indexPath as NSIndexPath).row]
+        let isSection = tocReference.children.count > 0
+
+        cell.indexLabel?.text = tocReference.title.trimmingCharacters(in: .whitespacesAndNewlines)
+
+        // Add audio duration for Media Ovelay
+        if let resource = tocReference.resource {
+            if let mediaOverlay = resource.mediaOverlay {
+                let duration = self.book.duration(for: "#"+mediaOverlay)
+
+                if let durationFormatted = (duration != nil ? duration : "")?.clockTimeToMinutesString() {
+                    let text = cell.indexLabel?.text ?? ""
+                    cell.indexLabel?.text = text + (duration != nil ? (" - " + durationFormatted) : "")
+                }
+            }
+        }
+
+        // Mark current reading chapter
+        if
+            let currentPageNumber = self.folioReader.readerCenter?.currentPageNumber,
+            let reference = self.book.spine.spineReferences[safe: currentPageNumber - 1],
+            (tocReference.resource != nil) {
+            let resource = reference.resource
+            cell.indexLabel?.textColor = (tocReference.resource == resource ? self.readerConfig.tintColor : self.readerConfig.menuTextColor)
+        }
+
+        cell.layoutMargins = UIEdgeInsets.zero
+        cell.preservesSuperviewLayoutMargins = false
+        cell.contentView.backgroundColor = isSection ? UIColor(white: 0.7, alpha: 0.1) : UIColor.clear
+        cell.backgroundColor = UIColor.clear
+        return cell
+    }
+
+    // MARK: - Table view delegate
+
+    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let tocReference = tocItems[(indexPath as NSIndexPath).row]
+        delegate?.chapterList(self, didSelectRowAtIndexPath: indexPath, withTocReference: tocReference)
+        
+        tableView.deselectRow(at: indexPath, animated: true)
+        dismiss { 
+            self.delegate?.chapterList(didDismissedChapterList: self)
+        }
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderChapterListCell.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderChapterListCell.swift
new file mode 100755 (executable)
index 0000000..75f2f7f
--- /dev/null
@@ -0,0 +1,57 @@
+//
+//  FolioReaderChapterListCell.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 07/05/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+class FolioReaderChapterListCell: UITableViewCell {
+    var indexLabel: UILabel?
+
+    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+
+        self.indexLabel = UILabel()
+    }
+
+    func setup(withConfiguration readerConfig: FolioReaderConfig) {
+
+        self.indexLabel?.lineBreakMode = .byWordWrapping
+        self.indexLabel?.numberOfLines = 0
+        self.indexLabel?.translatesAutoresizingMaskIntoConstraints = false
+        self.indexLabel?.font = UIFont(name: "Avenir-Light", size: 17)
+        self.indexLabel?.textColor = readerConfig.menuTextColor
+
+        if let label = self.indexLabel {
+            self.contentView.addSubview(label)
+
+            // Configure cell contraints
+            var constraints = [NSLayoutConstraint]()
+            let views = ["label": label]
+
+            NSLayoutConstraint.constraints(withVisualFormat: "H:|-15-[label]-15-|", options: [], metrics: nil, views: views).forEach {
+                constraints.append($0 as NSLayoutConstraint)
+            }
+
+            NSLayoutConstraint.constraints(withVisualFormat: "V:|-16-[label]-16-|", options: [], metrics: nil, views: views).forEach {
+                constraints.append($0 as NSLayoutConstraint)
+            }
+
+            self.contentView.addConstraints(constraints)
+        }
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("storyboards are incompatible with truth and beauty")
+    }
+    
+    override func prepareForReuse() {
+        super.prepareForReuse()
+        
+        // As the `setup` is called at each reuse, make sure the label is added only once to the view hierarchy.
+        self.indexLabel?.removeFromSuperview()
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderConfig.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderConfig.swift
new file mode 100755 (executable)
index 0000000..2f1be73
--- /dev/null
@@ -0,0 +1,245 @@
+//
+//  FolioReaderConfig.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 08/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+import RealmSwift
+
+// MARK: - FolioReaderScrollDirection
+
+/// Defines the Reader scrolling direction
+///
+/// - vertical: Section and content scroll on vertical.
+/// - horizontal: Section and content scroll on horizontal.
+/// - horizontalWithVerticalContent: Sections scroll horizontal and content scroll on vertical.
+/// - defaultVertical: The default scroll direction, if not overridden; works as .vertical.
+public enum FolioReaderScrollDirection: Int {
+    case vertical
+    case horizontal
+    case horizontalWithVerticalContent
+    case defaultVertical
+
+    /// The current scroll direction
+    ///
+    /// - Returns: Returns `UICollectionViewScrollDirection`
+    func collectionViewScrollDirection() -> UICollectionViewScrollDirection {
+        switch self {
+        case .vertical, .defaultVertical:
+            return .vertical
+        case .horizontal, .horizontalWithVerticalContent:
+            return .horizontal
+        }
+    }
+}
+
+// MARK: - ClassBasedOnClickListener
+
+/**
+ A `ClassBasedOnClickListener` takes a closure which is performed if a given html `class` is clicked. The closure will reveice the content of the specified parameter.
+
+ Eg. A ClassBasedOnClickListener with the className `quote` and parameterName `id` with the given epub html content `<section class="quote" id="12345">` would call the given closure on a click on this section with the String `12345` as parameter.
+ */
+public struct ClassBasedOnClickListener {
+
+    /// The name of the URL scheme which should be used. Note: Make sure that the given `String` is a valid as scheme name.
+    public var schemeName: String
+
+    /// The query selector for the elements which the listener should be added to. See https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector for further information about query selectors.
+    public var querySelector: String
+
+    /// The name of the attribute whose content should be passed to the `onClickAction` action.
+    public var attributeName: String
+
+    /// Whether the listener should be added to all found elements or only to the first one. See https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll for further information. The default value is `true`.
+    public var selectAll: Bool
+
+    /// The closure which will be called if the specified class was clicked. `attributeContent` contains the string content of the specified attribute and `touchPointRelativeToWebView` reprsents the touch point relative to the web view.
+    public var onClickAction: ((_ attributeContent: String?, _ touchPointRelativeToWebView: CGPoint) -> Void)
+
+    /**
+     Initializes a `ClassBasedOnClickListener` instance. Append it to the `classBasedOnClickListeners` property from the `FolioReaderConfig` to receive on click events. The default `selectAll` value is `true`.
+
+     - parameter schemeName:    The name of the URL scheme which should be used. Note: Make sure that the given `String` is a valid as scheme name.
+     - parameter querySelector: The query selector for the elements which the listener should be added to. See https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector for further information about query selectors.
+     - parameter attributeName: The name of the attribute whose content should be passed to the `onClickAction` action.
+     - parameter selectAll:     Whether the listener should be added to all found elements or only to the first one. See https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll for further information. The default value is `true`.
+     - parameter onClickAction: The closure which will be called if the specified class was clicked. `attributeContent` contains the string content of the specified attribute and `touchPointRelativeToWebView` reprsents the touch point relative to the web view.
+     */
+    public init(schemeName: String, querySelector: String, attributeName: String, selectAll: Bool = true, onClickAction: @escaping ((_ attributeContent: String?, _ touchPointRelativeToWebView: CGPoint) -> Void)) {
+        self.schemeName = schemeName.lowercased()
+        self.querySelector = querySelector
+        self.attributeName = attributeName
+        self.selectAll = selectAll
+        self.onClickAction = onClickAction
+    }
+}
+
+// MARK: - FolioReaderConfig
+
+/**
+ Defines the Reader custom configuration
+ */
+open class FolioReaderConfig: NSObject {
+
+    // MARK: ClassBasedOnClickListener
+
+    /**
+     Array of `ClassBasedOnClickListener` objects. A `ClassBasedOnClickListener` takes a closure which is performed if a given html `class` is clicked. The closure will reveice the content of the specified parameter.
+
+     Eg. A ClassBasedOnClickListener with the className `quote` and parameterName `id` with the given epub html content `<section class="quote" id="12345">` would call the given closure on a click on this section with the String `12345` as parameter.
+     */
+    open var classBasedOnClickListeners = [ClassBasedOnClickListener]()
+
+    // MARK: Colors
+
+    /// Base header custom TintColor
+    open var tintColor = UIColor(rgba: "#6ACC50")
+
+    /// Menu background color
+    open var menuBackgroundColor = UIColor.white
+
+    /// Menu separator Color
+    open var menuSeparatorColor = UIColor(rgba: "#D7D7D7")
+
+    /// Menu text color
+    open var menuTextColor = UIColor(rgba: "#767676")
+
+    /// Night mode background color
+    open var nightModeBackground = UIColor(rgba: "#131313")
+
+    /// Night mode menu background color
+    open var nightModeMenuBackground = UIColor(rgba: "#1E1E1E")
+
+    /// Night mode separator color
+    open var nightModeSeparatorColor = UIColor(white: 0.5, alpha: 0.2)
+
+    /// Media overlay or TTS selection color
+    open lazy var mediaOverlayColor: UIColor! = self.tintColor
+
+    // MARK: Custom actions
+
+    /// hide the navigation bar and the bottom status view
+    open var hideBars = false
+
+    /// If `canChangeScrollDirection` is `true` it will be overrided by user's option.
+    open var scrollDirection: FolioReaderScrollDirection = .defaultVertical
+
+    /// Enable or disable hability to user change scroll direction on menu.
+    open var canChangeScrollDirection = true
+
+    /// Should hide navigation bar on user tap
+    open var shouldHideNavigationOnTap = true
+
+    /// Allow sharing option, if `false` will hide all sharing icons and options
+    open var allowSharing = true
+
+    /// Enable TTS (Text To Speech)
+    open var enableTTS = true
+    
+    /// Display book title in navbar
+    open var displayTitle = false
+
+    /// Hide the page indicator
+    open var hidePageIndicator = false
+
+    /// Go to saved position when open a book
+    open var loadSavedPositionForCurrentBook = true
+    
+    // MARK: Quote image share
+
+    /// Custom Quote logo
+    open var quoteCustomLogoImage       = UIImage(readerImageNamed: "icon-logo")
+
+    /// Add custom backgrounds and font colors to Quote Images
+    open var quoteCustomBackgrounds     = [QuoteImage]()
+
+    /// Enable or disable default Quote Image backgrounds
+    open var quotePreserveDefaultBackgrounds    = true
+
+    // MARK: Realm
+
+    /// Realm configuration for storing highlights
+    open var realmConfiguration         = Realm.Configuration()
+
+    // MARK: Localized strings
+
+    /// Localizes Highlight title
+    open var localizedHighlightsTitle   = NSLocalizedString("Highlights", comment: "")
+
+    /// Localizes Content title
+    open var localizedContentsTitle     = NSLocalizedString("Contents", comment: "")
+
+    /// Use the readers `UIMenuController` which enables the highlighting etc. The default is `true`. If set to false it's possible to modify the shared `UIMenuController` for yourself. Note: This doesn't disable the text selection in the web view.
+    open var useReaderMenuController    = true
+
+    /// Used to distinguish between multiple or different reader instances. The content of the user defaults (font settings etc.) depends on this identifier. The default is `nil`.
+    open var identifier: String?
+
+    /// Localizes Highlight date format. This is a `dateFormat` from `NSDateFormatter`, so be careful 🤔
+    open var localizedHighlightsDateFormat = "MMM dd, YYYY | HH:mm"
+    open var localizedHighlightMenu = NSLocalizedString("Highlight", comment: "")
+    open var localizedDefineMenu = NSLocalizedString("Define", comment: "")
+    open var localizedPlayMenu = NSLocalizedString("Play", comment: "")
+    open var localizedPauseMenu = NSLocalizedString("Pause", comment: "")
+    open var localizedFontMenuNight = NSLocalizedString("Night", comment: "")
+    open var localizedPlayerMenuStyle = NSLocalizedString("Style", comment: "")
+    open var localizedFontMenuDay = NSLocalizedString("Day", comment: "")
+    open var localizedLayoutHorizontal = NSLocalizedString("Horizontal", comment: "")
+    open var localizedLayoutVertical = NSLocalizedString("Vertical", comment: "")
+    open var localizedReaderOnePageLeft = NSLocalizedString("1 page left", comment: "")
+    open var localizedReaderManyPagesLeft = NSLocalizedString("pages left", comment: "")
+    open var localizedReaderManyMinutes = NSLocalizedString("minutes", comment: "")
+    open var localizedReaderOneMinute = NSLocalizedString("1 minute", comment: "")
+    open var localizedReaderLessThanOneMinute = NSLocalizedString("Less than a minute", comment: "")
+    open var localizedShareWebLink: URL? = nil
+    open var localizedShareChapterSubject = NSLocalizedString("Check out this chapter from", comment: "")
+    open var localizedShareHighlightSubject = NSLocalizedString("Notes from", comment: "")
+    open var localizedShareAllExcerptsFrom = NSLocalizedString("All excerpts from", comment: "")
+    open var localizedShareBy = NSLocalizedString("by", comment: "")
+    open var localizedCancel = NSLocalizedString("Cancel", comment: "")
+    open var localizedShare = NSLocalizedString("Share", comment: "")
+    open var localizedChooseExisting = NSLocalizedString("Choose existing", comment: "")
+    open var localizedTakePhoto = NSLocalizedString("Take Photo", comment: "")
+    open var localizedShareImageQuote = NSLocalizedString("Share image quote", comment: "")
+    open var localizedShareTextQuote = NSLocalizedString("Share text quote", comment: "")
+
+    public convenience init(withIdentifier identifier: String) {
+        self.init()
+
+        self.identifier = identifier
+    }
+
+    /**
+     Simplify attibution of values based on direction, basically is to avoid too much usage of `switch`,
+     `if` and `else` statements to check. So basically this is like a shorthand version of the `switch` verification.
+
+     For example:
+     ```
+     let pageOffsetPoint = readerConfig.isDirection(CGPoint(x: 0, y: pageOffset), CGPoint(x: pageOffset, y: 0), CGPoint(x: 0, y: pageOffset))
+     ```
+
+     As usually the `vertical` direction and `horizontalContentVertical` has similar statements you can basically hide the last
+     value and it will assume the value from `vertical` as fallback.
+     ```
+     let pageOffsetPoint = readerConfig.isDirection(CGPoint(x: 0, y: pageOffset), CGPoint(x: pageOffset, y: 0))
+     ```
+
+     - parameter vertical:                  Value for `vertical` direction
+     - parameter horizontal:                Value for `horizontal` direction
+     - parameter horizontalContentVertical: Value for `horizontalWithVerticalContent` direction, if nil will fallback to `vertical` value
+
+     - returns: The right value based on direction.
+     */
+    func isDirection<T> (_ vertical: T, _ horizontal: T, _ horizontalContentVertical: T) -> T {
+        switch self.scrollDirection {
+        case .vertical, .defaultVertical:       return vertical
+        case .horizontal:                       return horizontal
+        case .horizontalWithVerticalContent:    return horizontalContentVertical
+        }
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderContainer.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderContainer.swift
new file mode 100755 (executable)
index 0000000..427eeef
--- /dev/null
@@ -0,0 +1,229 @@
+//
+//  FolioReaderContainer.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 15/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+import FontBlaster
+
+/// Reader container
+open class FolioReaderContainer: UIViewController {
+    var shouldHideStatusBar = true
+    var shouldRemoveEpub = true
+    
+    // Mark those property as public so they can accessed from other classes/subclasses.
+    public var epubPath: String
+       public var unzipPath: String?
+    public var book: FRBook
+    
+    public var centerNavigationController: UINavigationController?
+    public var centerViewController: FolioReaderCenter?
+    public var audioPlayer: FolioReaderAudioPlayer?
+    
+    public var readerConfig: FolioReaderConfig
+    public var folioReader: FolioReader
+
+    fileprivate var errorOnLoad = false
+
+    // MARK: - Init
+
+    /// Init a Folio Reader Container
+    ///
+    /// - Parameters:
+    ///   - config: Current Folio Reader configuration
+    ///   - folioReader: Current instance of the FolioReader kit.
+    ///   - path: The ePub path on system. Must not be nil nor empty string.
+       ///   - unzipPath: Path to unzip the compressed epub.
+    ///   - removeEpub: Should delete the original file after unzip? Default to `true` so the ePub will be unziped only once.
+    public init(withConfig config: FolioReaderConfig, folioReader: FolioReader, epubPath path: String, unzipPath: String? = nil, removeEpub: Bool = true) {
+        self.readerConfig = config
+        self.folioReader = folioReader
+        self.epubPath = path
+               self.unzipPath = unzipPath
+        self.shouldRemoveEpub = removeEpub
+        self.book = FRBook()
+
+        super.init(nibName: nil, bundle: Bundle.frameworkBundle())
+
+        // Configure the folio reader.
+        self.folioReader.readerContainer = self
+
+        // Initialize the default reader options.
+        if self.epubPath != "" {
+            self.initialization()
+        }
+    }
+
+    required public init?(coder aDecoder: NSCoder) {
+        // When a FolioReaderContainer object is instantiated from the storyboard this function is called before.
+        // At this moment, we need to initialize all non-optional objects with default values.
+        // The function `setupConfig(config:epubPath:removeEpub:)` MUST be called afterward.
+        // See the ExampleFolioReaderContainer.swift for more information?
+        self.readerConfig = FolioReaderConfig()
+        self.folioReader = FolioReader()
+        self.epubPath = ""
+        self.shouldRemoveEpub = false
+        self.book = FRBook()
+
+        super.init(coder: aDecoder)
+
+        // Configure the folio reader.
+        self.folioReader.readerContainer = self
+    }
+
+    /// Common Initialization
+    fileprivate func initialization() {
+        // Register custom fonts
+        FontBlaster.blast(bundle: Bundle.frameworkBundle())
+
+        // Register initial defaults
+        self.folioReader.register(defaults: [
+            kCurrentFontFamily: FolioReaderFont.andada.rawValue,
+            kNightMode: false,
+            kCurrentFontSize: 2,
+            kCurrentMarginSize: 2,
+            kCurrentInterlineSize: 2,
+            kCurrentAudioRate: 1,
+            kCurrentHighlightStyle: 0,
+            kCurrentTOCMenu: 0,
+            kCurrentMediaOverlayStyle: MediaOverlayStyle.default.rawValue,
+            kCurrentScrollDirection: FolioReaderScrollDirection.defaultVertical.rawValue
+            ])
+    }
+
+    /// Set the `FolioReaderConfig` and epubPath.
+    ///
+    /// - Parameters:
+    ///   - config: Current Folio Reader configuration
+    ///   - path: The ePub path on system. Must not be nil nor empty string.
+       ///   - unzipPath: Path to unzip the compressed epub.
+    ///   - removeEpub: Should delete the original file after unzip? Default to `true` so the ePub will be unziped only once.
+    open func setupConfig(_ config: FolioReaderConfig, epubPath path: String, unzipPath: String? = nil, removeEpub: Bool = true) {
+        self.readerConfig = config
+        self.folioReader = FolioReader()
+        self.folioReader.readerContainer = self
+        self.epubPath = path
+               self.unzipPath = unzipPath
+        self.shouldRemoveEpub = removeEpub
+    }
+
+    // MARK: - View life cicle
+
+    override open func viewDidLoad() {
+        super.viewDidLoad()
+
+        let canChangeScrollDirection = self.readerConfig.canChangeScrollDirection
+        self.readerConfig.canChangeScrollDirection = self.readerConfig.isDirection(canChangeScrollDirection, canChangeScrollDirection, false)
+
+        // If user can change scroll direction use the last saved
+        if self.readerConfig.canChangeScrollDirection == true {
+            var scrollDirection = FolioReaderScrollDirection(rawValue: self.folioReader.currentScrollDirection) ?? .vertical
+            if (scrollDirection == .defaultVertical && self.readerConfig.scrollDirection != .defaultVertical) {
+                scrollDirection = self.readerConfig.scrollDirection
+            }
+
+            self.readerConfig.scrollDirection = scrollDirection
+        }
+
+        let hideBars = readerConfig.hideBars
+        self.readerConfig.shouldHideNavigationOnTap = ((hideBars == true) ? true : self.readerConfig.shouldHideNavigationOnTap)
+
+        self.centerViewController = FolioReaderCenter(withContainer: self)
+
+        if let rootViewController = self.centerViewController {
+            self.centerNavigationController = UINavigationController(rootViewController: rootViewController)
+        }
+
+        self.centerNavigationController?.setNavigationBarHidden(self.readerConfig.shouldHideNavigationOnTap, animated: false)
+        if let _centerNavigationController = self.centerNavigationController {
+            self.view.addSubview(_centerNavigationController.view)
+            self.addChildViewController(_centerNavigationController)
+        }
+        self.centerNavigationController?.didMove(toParentViewController: self)
+
+        if (self.readerConfig.hideBars == true) {
+            self.readerConfig.shouldHideNavigationOnTap = false
+            self.navigationController?.navigationBar.isHidden = true
+            self.centerViewController?.pageIndicatorHeight = 0
+        }
+
+        // Read async book
+        guard (self.epubPath.isEmpty == false) else {
+            print("Epub path is nil.")
+            self.errorOnLoad = true
+            return
+        }
+
+        DispatchQueue.global(qos: .userInitiated).async {
+
+            do {
+                let parsedBook = try FREpubParser().readEpub(epubPath: self.epubPath, removeEpub: self.shouldRemoveEpub, unzipPath: self.unzipPath)
+                self.book = parsedBook
+                self.folioReader.isReaderOpen = true
+
+                // Reload data
+                DispatchQueue.main.async {
+                    // Add audio player if needed
+                    if self.book.hasAudio || self.readerConfig.enableTTS {
+                        self.addAudioPlayer()
+                    }
+                    self.centerViewController?.reloadData()
+                    self.folioReader.isReaderReady = true
+                    self.folioReader.delegate?.folioReader?(self.folioReader, didFinishedLoading: self.book)
+                }
+            } catch {
+                self.errorOnLoad = true
+                self.alert(message: error.localizedDescription)
+            }
+        }
+    }
+
+    override open func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+
+        if (self.errorOnLoad == true) {
+            self.dismiss()
+        }
+    }
+
+    /**
+     Initialize the media player
+     */
+    func addAudioPlayer() {
+        self.audioPlayer = FolioReaderAudioPlayer(withFolioReader: self.folioReader, book: self.book)
+        self.folioReader.readerAudioPlayer = audioPlayer
+    }
+
+    // MARK: - Status Bar
+
+    override open var prefersStatusBarHidden: Bool {
+        return (self.readerConfig.shouldHideNavigationOnTap == false ? false : self.shouldHideStatusBar)
+    }
+
+    override open var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
+        return UIStatusBarAnimation.slide
+    }
+
+    override open var preferredStatusBarStyle: UIStatusBarStyle {
+        return .lightContent // self.folioReader.isNight(.lightContent, .default)
+    }
+}
+
+extension FolioReaderContainer {
+    func alert(message: String) {
+        let alertController = UIAlertController(
+            title: "Error",
+            message: message,
+            preferredStyle: UIAlertControllerStyle.alert
+        )
+        let action = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel) { [weak self]
+            (result : UIAlertAction) -> Void in
+            self?.dismiss()
+        }
+        alertController.addAction(action)
+        self.present(alertController, animated: true, completion: nil)
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderFontsMenu.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderFontsMenu.swift
new file mode 100644 (file)
index 0000000..4f8760a
--- /dev/null
@@ -0,0 +1,442 @@
+//
+//  FolioReaderFontsMenu.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 27/08/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+public enum FolioReaderFont: Int {
+    case andada = 0
+    case lato
+    case lora
+    case raleway
+
+    public static func folioReaderFont(fontName: String) -> FolioReaderFont? {
+        var font: FolioReaderFont?
+        switch fontName {
+        case "andada": font = .andada
+        case "lato": font = .lato
+        case "lora": font = .lora
+        case "raleway": font = .raleway
+        default: break
+        }
+        return font
+    }
+
+    public var cssIdentifier: String {
+        switch self {
+        case .andada: return "andada"
+        case .lato: return "lato"
+        case .lora: return "lora"
+        case .raleway: return "raleway"
+        }
+    }
+}
+
+public enum SliderType: Int{
+    case font
+    case margin
+    case interline
+    
+    var paramText: String{
+        switch self {
+        case .font:
+            return "text"
+        case .margin:
+            return "margin"
+        case .interline:
+            return "interline"
+        }
+    }
+    
+    var leftImage: UIImage{
+        switch self {
+        case .font:
+            return #imageLiteral(resourceName: "reader_font-small")
+        case .margin:
+            return #imageLiteral(resourceName: "reader_margin-small")
+        case .interline:
+            return #imageLiteral(resourceName: "reader_leading-small")
+        }
+    }
+    
+    var rightImage: UIImage{
+        switch self {
+        case .font:
+            return #imageLiteral(resourceName: "reader_font-big")
+        case .margin:
+            return #imageLiteral(resourceName: "reader_margin-big")
+        case .interline:
+            return #imageLiteral(resourceName: "reader_leading-big")
+        }
+    }
+}
+
+public enum FolioReaderSliderParamSize: Int {
+    case xs = 0
+    case s
+    case m
+    case l
+    case xl
+
+    public static func folioReaderParamSize(fontSizeStringRepresentation: String, sliderType: SliderType) -> FolioReaderSliderParamSize? {
+        var paramSize: FolioReaderSliderParamSize?
+        let paramText = sliderType.paramText
+        switch fontSizeStringRepresentation {
+        case "\(paramText)SizeOne": paramSize = .xs
+        case "\(paramText)SizeTwo": paramSize = .s
+        case "\(paramText)SizeThree": paramSize = .m
+        case "\(paramText)SizeFour": paramSize = .l
+        case "\(paramText)SizeFive": paramSize = .xl
+        default: break
+        }
+        return paramSize
+    }
+
+    public func cssIdentifier(sliderType: SliderType) -> String {
+        let paramText = sliderType.paramText
+        switch self {
+        case .xs: return "\(paramText)SizeOne"
+        case .s: return "\(paramText)SizeTwo"
+        case .m: return "\(paramText)SizeThree"
+        case .l: return "\(paramText)SizeFour"
+        case .xl: return "\(paramText)SizeFive"
+        }
+    }
+}
+
+class FolioReaderFontsMenu: UIViewController, SMSegmentViewDelegate, UIGestureRecognizerDelegate {
+    var menuView: UIView!
+
+    fileprivate var readerConfig: FolioReaderConfig
+    fileprivate var folioReader: FolioReader
+
+    init(folioReader: FolioReader, readerConfig: FolioReaderConfig) {
+        self.readerConfig = readerConfig
+        self.folioReader = folioReader
+
+        super.init(nibName: nil, bundle: nil)
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        // Do any additional setup after loading the view.
+        self.view.backgroundColor = UIColor.clear
+
+        // Tap gesture
+        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(FolioReaderFontsMenu.tapGesture))
+        tapGesture.numberOfTapsRequired = 1
+        tapGesture.delegate = self
+        view.addGestureRecognizer(tapGesture)
+
+        // Menu view
+//        let visibleHeight: CGFloat = self.readerConfig.canChangeScrollDirection ? 222 : 170
+        let visibleHeight: CGFloat = self.readerConfig.canChangeScrollDirection ? 222 + 114 : 170 + 114
+        menuView = UIView(frame: CGRect(x: 0, y: view.frame.height-visibleHeight, width: view.frame.width, height: view.frame.height))
+        menuView.backgroundColor = self.folioReader.isNight(self.readerConfig.nightModeMenuBackground, UIColor.white)
+        menuView.autoresizingMask = .flexibleWidth
+        menuView.layer.shadowColor = UIColor.black.cgColor
+        menuView.layer.shadowOffset = CGSize(width: 0, height: 0)
+        menuView.layer.shadowOpacity = 0.3
+        menuView.layer.shadowRadius = 6
+        menuView.layer.shadowPath = UIBezierPath(rect: menuView.bounds).cgPath
+        menuView.layer.rasterizationScale = UIScreen.main.scale
+        menuView.layer.shouldRasterize = true
+        view.addSubview(menuView)
+
+        let normalColor = UIColor(white: 0.5, alpha: 0.7)
+        let selectedColor = self.readerConfig.tintColor
+        let sun = UIImage(readerImageNamed: "icon-sun")
+        let moon = UIImage(readerImageNamed: "icon-moon")
+        let fontSmall = UIImage(readerImageNamed: "icon-font-small")
+        let fontBig = UIImage(readerImageNamed: "icon-font-big")
+
+        let sunNormal = sun?.imageTintColor(normalColor)?.withRenderingMode(.alwaysOriginal)
+        let moonNormal = moon?.imageTintColor(normalColor)?.withRenderingMode(.alwaysOriginal)
+
+        let sunSelected = sun?.imageTintColor(selectedColor)?.withRenderingMode(.alwaysOriginal)
+        let moonSelected = moon?.imageTintColor(selectedColor)?.withRenderingMode(.alwaysOriginal)
+
+        // Day night mode
+        let dayNight = SMSegmentView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 55),
+                                     separatorColour: self.readerConfig.nightModeSeparatorColor,
+                                     separatorWidth: 1,
+                                     segmentProperties:  [
+                                        keySegmentTitleFont: UIFont(name: "Avenir-Light", size: 17)!,
+                                        keySegmentOnSelectionColour: UIColor.clear,
+                                        keySegmentOffSelectionColour: UIColor.clear,
+                                        keySegmentOnSelectionTextColour: selectedColor,
+                                        keySegmentOffSelectionTextColour: normalColor,
+                                        keyContentVerticalMargin: 17 as AnyObject
+            ])
+        dayNight.delegate = self
+        dayNight.tag = 1
+        dayNight.addSegmentWithTitle(self.readerConfig.localizedFontMenuDay, onSelectionImage: sunSelected, offSelectionImage: sunNormal)
+        dayNight.addSegmentWithTitle(self.readerConfig.localizedFontMenuNight, onSelectionImage: moonSelected, offSelectionImage: moonNormal)
+        dayNight.selectSegmentAtIndex(self.folioReader.nightMode.hashValue)
+        menuView.addSubview(dayNight)
+
+
+        // Separator
+        let line = UIView(frame: CGRect(x: 0, y: dayNight.frame.height+dayNight.frame.origin.y, width: view.frame.width, height: 1))
+        line.backgroundColor = self.readerConfig.nightModeSeparatorColor
+        menuView.addSubview(line)
+
+        // Fonts adjust
+        let fontName = SMSegmentView(frame: CGRect(x: 15, y: line.frame.height+line.frame.origin.y, width: view.frame.width-30, height: 55),
+                                     separatorColour: UIColor.clear,
+                                     separatorWidth: 0,
+                                     segmentProperties:  [
+                                        keySegmentOnSelectionColour: UIColor.clear,
+                                        keySegmentOffSelectionColour: UIColor.clear,
+                                        keySegmentOnSelectionTextColour: selectedColor,
+                                        keySegmentOffSelectionTextColour: normalColor,
+                                        keyContentVerticalMargin: 17 as AnyObject
+            ])
+        fontName.delegate = self
+        fontName.tag = 2
+
+        fontName.addSegmentWithTitle("Andada", onSelectionImage: nil, offSelectionImage: nil)
+        fontName.addSegmentWithTitle("Lato", onSelectionImage: nil, offSelectionImage: nil)
+        fontName.addSegmentWithTitle("Lora", onSelectionImage: nil, offSelectionImage: nil)
+        fontName.addSegmentWithTitle("Raleway", onSelectionImage: nil, offSelectionImage: nil)
+
+//        fontName.segments[0].titleFont = UIFont(name: "Andada-Regular", size: 18)!
+//        fontName.segments[1].titleFont = UIFont(name: "Lato-Regular", size: 18)!
+//        fontName.segments[2].titleFont = UIFont(name: "Lora-Regular", size: 18)!
+//        fontName.segments[3].titleFont = UIFont(name: "Raleway-Regular", size: 18)!
+
+        fontName.selectSegmentAtIndex(self.folioReader.currentFont.rawValue)
+        menuView.addSubview(fontName)
+
+        // Separator 2
+        let line2 = UIView(frame: CGRect(x: 0, y: fontName.frame.height+fontName.frame.origin.y, width: view.frame.width, height: 1))
+        line2.backgroundColor = self.readerConfig.nightModeSeparatorColor
+        menuView.addSubview(line2)
+
+        addSlider(sliderType: .font, topY: line2.frame.origin.y, selectedColor: selectedColor)
+        
+        addSlider(sliderType: .margin, topY: line2.frame.origin.y + 57, selectedColor: selectedColor)
+
+        addSlider(sliderType: .interline, topY: line2.frame.origin.y + 114, selectedColor: selectedColor)
+
+//        // Font slider size
+//        let slider = HADiscreteSlider(frame: CGRect(x: 60, y: line2.frame.origin.y+2, width: view.frame.width-120, height: 55))
+//        slider.tickStyle = ComponentStyle.rounded
+//        slider.tickCount = 5
+//        slider.tickSize = CGSize(width: 8, height: 8)
+//
+//        slider.thumbStyle = ComponentStyle.rounded
+//        slider.thumbSize = CGSize(width: 28, height: 28)
+//        slider.thumbShadowOffset = CGSize(width: 0, height: 2)
+//        slider.thumbShadowRadius = 3
+//        slider.thumbColor = selectedColor
+//
+//        slider.backgroundColor = UIColor.clear
+//        slider.tintColor = self.readerConfig.nightModeSeparatorColor
+//        slider.minimumValue = 0
+//        slider.value = CGFloat(self.folioReader.currentFontSize.rawValue)
+//        slider.addTarget(self, action: #selector(FolioReaderFontsMenu.sliderValueChanged(_:)), for: UIControlEvents.valueChanged)
+//
+//        // Force remove fill color
+//        slider.layer.sublayers?.forEach({ layer in
+//            layer.backgroundColor = UIColor.clear.cgColor
+//        })
+//
+//        menuView.addSubview(slider)
+//
+//        // Font icons
+//        let fontSmallView = UIImageView(frame: CGRect(x: 20, y: line2.frame.origin.y+14, width: 30, height: 30))
+//        fontSmallView.image = fontSmallNormal
+//        fontSmallView.contentMode = UIViewContentMode.center
+//        menuView.addSubview(fontSmallView)
+//
+//        let fontBigView = UIImageView(frame: CGRect(x: view.frame.width-50, y: line2.frame.origin.y+14, width: 30, height: 30))
+//        fontBigView.image = fontBigNormal
+//        fontBigView.contentMode = UIViewContentMode.center
+//        menuView.addSubview(fontBigView)
+
+        // Only continues if user can change scroll direction
+        guard (self.readerConfig.canChangeScrollDirection == true) else {
+            return
+        }
+
+        // Separator 3
+//        let line3 = UIView(frame: CGRect(x: 0, y: line2.frame.origin.y+56, width: view.frame.width, height: 1))
+        let line3 = UIView(frame: CGRect(x: 0, y: line2.frame.origin.y+171, width: view.frame.width, height: 1))
+        line3.backgroundColor = self.readerConfig.nightModeSeparatorColor
+        menuView.addSubview(line3)
+
+        let vertical = UIImage(readerImageNamed: "icon-menu-vertical")
+        let horizontal = UIImage(readerImageNamed: "icon-menu-horizontal")
+        let verticalNormal = vertical?.imageTintColor(normalColor)?.withRenderingMode(.alwaysOriginal)
+        let horizontalNormal = horizontal?.imageTintColor(normalColor)?.withRenderingMode(.alwaysOriginal)
+        let verticalSelected = vertical?.imageTintColor(selectedColor)?.withRenderingMode(.alwaysOriginal)
+        let horizontalSelected = horizontal?.imageTintColor(selectedColor)?.withRenderingMode(.alwaysOriginal)
+
+        // Layout direction
+        let layoutDirection = SMSegmentView(frame: CGRect(x: 0, y: line3.frame.origin.y, width: view.frame.width, height: 55),
+                                            separatorColour: self.readerConfig.nightModeSeparatorColor,
+                                            separatorWidth: 1,
+                                            segmentProperties:  [
+                                                keySegmentTitleFont: UIFont(name: "Avenir-Light", size: 17)!,
+                                                keySegmentOnSelectionColour: UIColor.clear,
+                                                keySegmentOffSelectionColour: UIColor.clear,
+                                                keySegmentOnSelectionTextColour: selectedColor,
+                                                keySegmentOffSelectionTextColour: normalColor,
+                                                keyContentVerticalMargin: 17 as AnyObject
+            ])
+        layoutDirection.delegate = self
+        layoutDirection.tag = 3
+        layoutDirection.addSegmentWithTitle(self.readerConfig.localizedLayoutVertical, onSelectionImage: verticalSelected, offSelectionImage: verticalNormal)
+        layoutDirection.addSegmentWithTitle(self.readerConfig.localizedLayoutHorizontal, onSelectionImage: horizontalSelected, offSelectionImage: horizontalNormal)
+
+        var scrollDirection = FolioReaderScrollDirection(rawValue: self.folioReader.currentScrollDirection)
+
+        if scrollDirection == .defaultVertical && self.readerConfig.scrollDirection != .defaultVertical {
+            scrollDirection = self.readerConfig.scrollDirection
+        }
+
+        switch scrollDirection ?? .vertical {
+        case .vertical, .defaultVertical:
+            layoutDirection.selectSegmentAtIndex(FolioReaderScrollDirection.vertical.rawValue)
+        case .horizontal, .horizontalWithVerticalContent:
+            layoutDirection.selectSegmentAtIndex(FolioReaderScrollDirection.horizontal.rawValue)
+        }
+        menuView.addSubview(layoutDirection)
+    }
+    
+    func addSlider(sliderType: SliderType, topY: CGFloat, selectedColor: UIColor){
+        
+        let normalColor = UIColor(white: 0.5, alpha: 0.7)
+        
+        // Font slider size
+        let slider = HADiscreteSlider(frame: CGRect(x: 60, y: topY+2, width: view.frame.width-120, height: 55))
+        slider.tickStyle = ComponentStyle.rounded
+        slider.tickCount = 5
+        slider.tickSize = CGSize(width: 8, height: 8)
+        
+        slider.thumbStyle = ComponentStyle.rounded
+        slider.thumbSize = CGSize(width: 28, height: 28)
+        slider.thumbShadowOffset = CGSize(width: 0, height: 2)
+        slider.thumbShadowRadius = 3
+        slider.thumbColor = selectedColor
+        
+        slider.backgroundColor = UIColor.clear
+        slider.tintColor = self.readerConfig.nightModeSeparatorColor
+        slider.minimumValue = 0
+        slider.tag = sliderType.rawValue
+        
+        switch sliderType {
+        case .font:
+            slider.value = CGFloat(self.folioReader.currentFontSize.rawValue)
+        case .margin:
+            slider.value = CGFloat(self.folioReader.currentMarginSize.rawValue)
+        case .interline:
+            slider.value = CGFloat(self.folioReader.currentInterlineSize.rawValue)
+        }
+        
+        slider.addTarget(self, action: #selector(FolioReaderFontsMenu.sliderValueChanged(_:)), for: UIControlEvents.valueChanged)
+        
+        // Force remove fill color
+        slider.layer.sublayers?.forEach({ layer in
+            layer.backgroundColor = UIColor.clear.cgColor
+        })
+        
+        menuView.addSubview(slider)
+        
+        // Font icons
+        let fontSmallView = UIImageView(frame: CGRect(x: 20, y: topY+14, width: 30, height: 30))
+        fontSmallView.image = sliderType.leftImage.imageTintColor(normalColor)
+        fontSmallView.contentMode = UIViewContentMode.center
+        menuView.addSubview(fontSmallView)
+        
+        let fontBigView = UIImageView(frame: CGRect(x: view.frame.width-50, y: topY+14, width: 30, height: 30))
+        fontSmallView.image = sliderType.rightImage.imageTintColor(normalColor)
+        fontBigView.contentMode = UIViewContentMode.center
+        menuView.addSubview(fontBigView)
+    }
+    
+    // MARK: - SMSegmentView delegate
+
+    func segmentView(_ segmentView: SMSegmentView, didSelectSegmentAtIndex index: Int) {
+        guard (self.folioReader.readerCenter?.currentPage) != nil else { return }
+
+        if segmentView.tag == 1 {
+
+            self.folioReader.nightMode = Bool(index == 1)
+
+            UIView.animate(withDuration: 0.6, animations: {
+                self.menuView.backgroundColor = (self.folioReader.nightMode ? self.readerConfig.nightModeBackground : UIColor.white)
+            })
+
+        } else if segmentView.tag == 2 {
+
+            self.folioReader.currentFont = FolioReaderFont(rawValue: index)!
+
+        }  else if segmentView.tag == 3 {
+
+            guard self.folioReader.currentScrollDirection != index else {
+                return
+            }
+
+            self.folioReader.currentScrollDirection = index
+        }
+    }
+    
+    // MARK: - Font slider changed
+    
+    @objc func sliderValueChanged(_ sender: HADiscreteSlider) {
+        guard
+            (self.folioReader.readerCenter?.currentPage != nil), let sliderType = SliderType(rawValue: sender.tag)
+             else {
+                return
+        }
+        
+        switch sliderType {
+        case .font:
+            if let fontSize = FolioReaderSliderParamSize(rawValue: Int(sender.value)){
+                self.folioReader.currentFontSize = fontSize
+            }
+        case .margin:
+            if let marginSize = FolioReaderSliderParamSize(rawValue: Int(sender.value)){
+                self.folioReader.currentMarginSize = marginSize
+            }
+        case .interline:
+            if let interlineSize = FolioReaderSliderParamSize(rawValue: Int(sender.value)){
+                self.folioReader.currentInterlineSize = interlineSize
+            }
+        }
+    }
+    
+    // MARK: - Gestures
+    
+    @objc func tapGesture() {
+        dismiss()
+        
+        if (self.readerConfig.shouldHideNavigationOnTap == false) {
+            self.folioReader.readerCenter?.showBars()
+        }
+    }
+    
+    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
+        if gestureRecognizer is UITapGestureRecognizer && touch.view == view {
+            return true
+        }
+        return false
+    }
+    
+    // MARK: - Status Bar
+    
+    override var prefersStatusBarHidden : Bool {
+        return (self.readerConfig.shouldHideNavigationOnTap == true)
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderHighlightList.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderHighlightList.swift
new file mode 100644 (file)
index 0000000..7b9f6bb
--- /dev/null
@@ -0,0 +1,172 @@
+//
+//  FolioReaderHighlightList.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 01/09/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+class FolioReaderHighlightList: UITableViewController {
+
+    fileprivate var highlights = [Highlight]()
+    fileprivate var readerConfig: FolioReaderConfig
+    fileprivate var folioReader: FolioReader
+
+    init(folioReader: FolioReader, readerConfig: FolioReaderConfig) {
+        self.readerConfig = readerConfig
+        self.folioReader = folioReader
+
+        super.init(style: UITableViewStyle.plain)
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init with coder not supported")
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: kReuseCellIdentifier)
+        self.tableView.separatorInset = UIEdgeInsets.zero
+        self.tableView.backgroundColor = self.folioReader.isNight(self.readerConfig.nightModeMenuBackground, self.readerConfig.menuBackgroundColor)
+        self.tableView.separatorColor = self.folioReader.isNight(self.readerConfig.nightModeSeparatorColor, self.readerConfig.menuSeparatorColor)
+
+        guard let bookId = (self.folioReader.readerContainer?.book.name as NSString?)?.deletingPathExtension else {
+            self.highlights = []
+            return
+        }
+
+        self.highlights = Highlight.allByBookId(withConfiguration: self.readerConfig, bookId: bookId)
+    }
+
+    // MARK: - Table view data source
+
+    override func numberOfSections(in tableView: UITableView) -> Int {
+        return 1
+    }
+
+    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return highlights.count
+    }
+
+    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: kReuseCellIdentifier, for: indexPath)
+        cell.backgroundColor = UIColor.clear
+
+        let highlight = highlights[(indexPath as NSIndexPath).row]
+
+        // Format date
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateFormat = self.readerConfig.localizedHighlightsDateFormat
+        let dateString = dateFormatter.string(from: highlight.date)
+
+        // Date
+        var dateLabel: UILabel!
+        if cell.contentView.viewWithTag(456) == nil {
+            dateLabel = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.width-40, height: 16))
+            dateLabel.tag = 456
+            dateLabel.autoresizingMask = UIViewAutoresizing.flexibleWidth
+            dateLabel.font = UIFont(name: "Avenir-Medium", size: 12)
+            cell.contentView.addSubview(dateLabel)
+        } else {
+            dateLabel = cell.contentView.viewWithTag(456) as! UILabel
+        }
+
+        dateLabel.text = dateString.uppercased()
+        dateLabel.textColor = self.folioReader.isNight(UIColor(white: 5, alpha: 0.3), UIColor.lightGray)
+        dateLabel.frame = CGRect(x: 20, y: 20, width: view.frame.width-40, height: dateLabel.frame.height)
+
+        // Text
+        let cleanString = highlight.content.stripHtml().truncate(250, trailing: "...").stripLineBreaks()
+        let text = NSMutableAttributedString(string: cleanString)
+        let range = NSRange(location: 0, length: text.length)
+        let paragraph = NSMutableParagraphStyle()
+        paragraph.lineSpacing = 3
+        let textColor = self.folioReader.isNight(self.readerConfig.menuTextColor, UIColor.black)
+
+        text.addAttribute(NSAttributedStringKey.paragraphStyle, value: paragraph, range: range)
+        text.addAttribute(NSAttributedStringKey.font, value: UIFont(name: "Avenir-Light", size: 16)!, range: range)
+        text.addAttribute(NSAttributedStringKey.foregroundColor, value: textColor, range: range)
+
+        if (highlight.type == HighlightStyle.underline.rawValue) {
+            text.addAttribute(NSAttributedStringKey.backgroundColor, value: UIColor.clear, range: range)
+            text.addAttribute(NSAttributedStringKey.underlineColor, value: HighlightStyle.colorForStyle(highlight.type, nightMode: self.folioReader.nightMode), range: range)
+            text.addAttribute(NSAttributedStringKey.underlineStyle, value: NSNumber(value: NSUnderlineStyle.styleSingle.rawValue as Int), range: range)
+        } else {
+            text.addAttribute(NSAttributedStringKey.backgroundColor, value: HighlightStyle.colorForStyle(highlight.type, nightMode: self.folioReader.nightMode), range: range)
+        }
+
+        // Text
+        var highlightLabel: UILabel!
+        if cell.contentView.viewWithTag(123) == nil {
+            highlightLabel = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.width-40, height: 0))
+            highlightLabel.tag = 123
+            highlightLabel.autoresizingMask = UIViewAutoresizing.flexibleWidth
+            highlightLabel.numberOfLines = 0
+            highlightLabel.textColor = UIColor.black
+            cell.contentView.addSubview(highlightLabel)
+        } else {
+            highlightLabel = cell.contentView.viewWithTag(123) as! UILabel
+        }
+
+        highlightLabel.attributedText = text
+        highlightLabel.sizeToFit()
+        highlightLabel.frame = CGRect(x: 20, y: 46, width: view.frame.width-40, height: highlightLabel.frame.height)
+
+        cell.layoutMargins = UIEdgeInsets.zero
+        cell.preservesSuperviewLayoutMargins = false
+        return cell
+    }
+
+    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        let highlight = highlights[(indexPath as NSIndexPath).row]
+
+        let cleanString = highlight.content.stripHtml().truncate(250, trailing: "...").stripLineBreaks()
+        let text = NSMutableAttributedString(string: cleanString)
+        let range = NSRange(location: 0, length: text.length)
+        let paragraph = NSMutableParagraphStyle()
+        paragraph.lineSpacing = 3
+        text.addAttribute(NSAttributedStringKey.paragraphStyle, value: paragraph, range: range)
+        text.addAttribute(NSAttributedStringKey.font, value: UIFont(name: "Avenir-Light", size: 16)!, range: range)
+
+        let s = text.boundingRect(with: CGSize(width: view.frame.width-40, height: CGFloat.greatestFiniteMagnitude),
+                                  options: [NSStringDrawingOptions.usesLineFragmentOrigin, NSStringDrawingOptions.usesFontLeading],
+                                  context: nil)
+
+        return s.size.height + 66
+    }
+
+    // MARK: - Table view delegate
+
+    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        guard let highlight = highlights[safe: (indexPath as NSIndexPath).row] else {
+            return
+        }
+
+        self.folioReader.readerCenter?.changePageWith(page: highlight.page, andFragment: highlight.highlightId)
+        self.dismiss()
+    }
+
+    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
+        if editingStyle == .delete {
+            let highlight = highlights[(indexPath as NSIndexPath).row]
+
+            if (highlight.page == self.folioReader.readerCenter?.currentPageNumber),
+                let page = self.folioReader.readerCenter?.currentPage {
+                Highlight.removeFromHTMLById(withinPage: page, highlightId: highlight.highlightId) // Remove from HTML
+            }
+
+            highlight.remove(withConfiguration: self.readerConfig) // Remove from Database
+            highlights.remove(at: (indexPath as NSIndexPath).row)
+            tableView.deleteRows(at: [indexPath], with: .fade)
+        }
+    }
+    
+    // MARK: - Handle rotation transition
+    
+    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
+        tableView.reloadData()
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderKit.h b/iOS/Pods/FolioReaderKit/Source/FolioReaderKit.h
new file mode 100644 (file)
index 0000000..9a3a0d1
--- /dev/null
@@ -0,0 +1,19 @@
+//
+//  FolioReaderKit.h
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 08/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+//! Project version number for FolioReaderKit.
+FOUNDATION_EXPORT double FolioReaderKitVersionNumber;
+
+//! Project version string for FolioReaderKit.
+FOUNDATION_EXPORT const unsigned char FolioReaderKitVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <FolioReaderKit/PublicHeader.h>
+
+
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderKit.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderKit.swift
new file mode 100755 (executable)
index 0000000..ad1d07d
--- /dev/null
@@ -0,0 +1,421 @@
+//
+//  FolioReaderKit.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 08/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+// MARK: - Internal constants
+
+internal let kApplicationDocumentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
+internal let kCurrentFontFamily = "com.folioreader.kCurrentFontFamily"
+internal let kCurrentFontSize = "com.folioreader.kCurrentFontSize"
+internal let kCurrentMarginSize = "com.folioreader.kCurrentMarginSize"
+internal let kCurrentInterlineSize = "com.folioreader.kCurrentInterlineSize"
+internal let kCurrentAudioRate = "com.folioreader.kCurrentAudioRate"
+internal let kCurrentHighlightStyle = "com.folioreader.kCurrentHighlightStyle"
+internal let kCurrentMediaOverlayStyle = "com.folioreader.kMediaOverlayStyle"
+internal let kCurrentScrollDirection = "com.folioreader.kCurrentScrollDirection"
+internal let kNightMode = "com.folioreader.kNightMode"
+internal let kCurrentTOCMenu = "com.folioreader.kCurrentTOCMenu"
+internal let kHighlightRange = 30
+internal let kReuseCellIdentifier = "com.folioreader.Cell.ReuseIdentifier"
+
+public enum FolioReaderError: Error, LocalizedError {
+    case bookNotAvailable
+    case errorInContainer
+    case errorInOpf
+    case authorNameNotAvailable
+    case coverNotAvailable
+    case invalidImage(path: String)
+    case titleNotAvailable
+    case fullPathEmpty
+
+    public var errorDescription: String? {
+        switch self {
+        case .bookNotAvailable:
+            return "Book not found"
+        case .errorInContainer, .errorInOpf:
+            return "Invalid book format"
+        case .authorNameNotAvailable:
+            return "Author name not available"
+        case .coverNotAvailable:
+            return "Cover image not available"
+        case let .invalidImage(path):
+            return "Invalid image at path: " + path
+        case .titleNotAvailable:
+            return "Book title not available"
+        case .fullPathEmpty:
+            return "Book corrupted"
+        }
+    }
+}
+
+/// Defines the media overlay and TTS selection
+///
+/// - `default`: The background is colored
+/// - underline: The underlined is colored
+/// - textColor: The text is colored
+public enum MediaOverlayStyle: Int {
+    case `default`
+    case underline
+    case textColor
+
+    init() {
+        self = .default
+    }
+
+    func className() -> String {
+        return "mediaOverlayStyle\(self.rawValue)"
+    }
+}
+
+/// FolioReader actions delegate
+@objc public protocol FolioReaderDelegate: class {
+    
+    /// Did finished loading book.
+    ///
+    /// - Parameters:
+    ///   - folioReader: The FolioReader instance
+    ///   - book: The Book instance
+    @objc optional func folioReader(_ folioReader: FolioReader, didFinishedLoading book: FRBook)
+    
+    /// Called when reader did closed.
+    ///
+    /// - Parameter folioReader: The FolioReader instance
+    @objc optional func folioReaderDidClose(_ folioReader: FolioReader)
+    
+    /// Called when reader did closed.
+    @available(*, deprecated, message: "Use 'folioReaderDidClose(_ folioReader: FolioReader)' instead.")
+    @objc optional func folioReaderDidClosed()
+}
+
+/// Main Library class with some useful constants and methods
+open class FolioReader: NSObject {
+
+    public override init() { }
+
+    deinit {
+        removeObservers()
+    }
+
+    /// Custom unzip path
+    open var unzipPath: String?
+
+    /// FolioReaderDelegate
+    open weak var delegate: FolioReaderDelegate?
+    
+    open weak var readerContainer: FolioReaderContainer?
+    open weak var readerAudioPlayer: FolioReaderAudioPlayer?
+    open weak var readerCenter: FolioReaderCenter? {
+        return self.readerContainer?.centerViewController
+    }
+
+    /// Check if reader is open
+    var isReaderOpen = false
+
+    /// Check if reader is open and ready
+    var isReaderReady = false
+
+    /// Check if layout needs to change to fit Right To Left
+    var needsRTLChange: Bool {
+        return (self.readerContainer?.book.spine.isRtl == true && self.readerContainer?.readerConfig.scrollDirection == .horizontal)
+    }
+
+    func isNight<T>(_ f: T, _ l: T) -> T {
+        return (self.nightMode == true ? f : l)
+    }
+
+    /// UserDefault for the current ePub file.
+    fileprivate var defaults: FolioReaderUserDefaults {
+        return FolioReaderUserDefaults(withIdentifier: self.readerContainer?.readerConfig.identifier)
+    }
+
+    // Add necessary observers
+    fileprivate func addObservers() {
+        removeObservers()
+        NotificationCenter.default.addObserver(self, selector: #selector(saveReaderState), name: .UIApplicationWillResignActive, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(saveReaderState), name: .UIApplicationWillTerminate, object: nil)
+    }
+
+    /// Remove necessary observers
+    fileprivate func removeObservers() {
+        NotificationCenter.default.removeObserver(self, name: .UIApplicationWillResignActive, object: nil)
+        NotificationCenter.default.removeObserver(self, name: .UIApplicationWillTerminate, object: nil)
+    }
+    
+    public func getProgressValues() -> (currentPage: Int, totalPages: Int)? {
+        guard let center = readerCenter else { return nil}
+        let totalPages = center.totalPages
+        let currentPage = center.currentPageNumber
+        return (currentPage, totalPages)
+    }
+
+}
+
+// MARK: - Present FolioReader
+
+extension FolioReader {
+
+    /// Present a Folio Reader Container modally on a Parent View Controller.
+    ///
+    /// - Parameters:
+    ///   - parentViewController: View Controller that will present the reader container.
+    ///   - epubPath: String representing the path on the disk of the ePub file. Must not be nil nor empty string.
+       ///   - unzipPath: Path to unzip the compressed epub.
+    ///   - config: FolioReader configuration.
+    ///   - shouldRemoveEpub: Boolean to remove the epub or not. Default true.
+    ///   - animated: Pass true to animate the presentation; otherwise, pass false.
+    open func presentReader(parentViewController: UIViewController, withEpubPath epubPath: String, unzipPath: String? = nil, andConfig config: FolioReaderConfig, shouldRemoveEpub: Bool = true, animated:
+        Bool = true) {
+        let readerContainer = FolioReaderContainer(withConfig: config, folioReader: self, epubPath: epubPath, unzipPath: unzipPath, removeEpub: shouldRemoveEpub)
+        self.readerContainer = readerContainer
+        parentViewController.present(readerContainer, animated: animated, completion: nil)
+        addObservers()
+    }
+}
+
+// MARK: -  Getters and setters for stored values
+
+extension FolioReader {
+
+    public func register(defaults: [String: Any]) {
+        self.defaults.register(defaults: defaults)
+    }
+
+    /// Check if current theme is Night mode
+    open var nightMode: Bool {
+        get { return self.defaults.bool(forKey: kNightMode) }
+        set (value) {
+            self.defaults.set(value, forKey: kNightMode)
+
+            if let readerCenter = self.readerCenter {
+                UIView.animate(withDuration: 0.6, animations: {
+                    _ = readerCenter.currentPage?.webView?.js("nightMode(\(self.nightMode))")
+                    readerCenter.pageIndicatorView?.reloadColors()
+                    readerCenter.configureNavBar()
+                    readerCenter.scrollScrubber?.reloadColors()
+                    readerCenter.collectionView.backgroundColor = (self.nightMode == true ? self.readerContainer?.readerConfig.nightModeBackground : UIColor.white)
+                }, completion: { (finished: Bool) in
+                    NotificationCenter.default.post(name: Notification.Name(rawValue: "needRefreshPageMode"), object: nil)
+                })
+            }
+        }
+    }
+
+    /// Check current font name. Default .andada
+    open var currentFont: FolioReaderFont {
+        get {
+            guard
+                let rawValue = self.defaults.value(forKey: kCurrentFontFamily) as? Int,
+                let font = FolioReaderFont(rawValue: rawValue) else {
+                    return .andada
+            }
+
+            return font
+        }
+        set (font) {
+            self.defaults.set(font.rawValue, forKey: kCurrentFontFamily)
+            _ = self.readerCenter?.currentPage?.webView?.js("setFontName('\(font.cssIdentifier)')")
+        }
+    }
+
+    /// Check current font size. Default .m
+    open var currentFontSize: FolioReaderSliderParamSize {
+        get {
+            guard
+                let rawValue = self.defaults.value(forKey: kCurrentFontSize) as? Int,
+                let size = FolioReaderSliderParamSize(rawValue: rawValue) else {
+                    return .m
+            }
+
+            return size
+        }
+        set (value) {
+            self.defaults.set(value.rawValue, forKey: kCurrentFontSize)
+
+            guard let currentPage = self.readerCenter?.currentPage else {
+                return
+            }
+
+            currentPage.webView?.js("setFontSize('\(currentFontSize.cssIdentifier(sliderType: SliderType.font) )')")
+        }
+    }
+    
+    /// Check current margin size. Default .m
+    open var currentMarginSize: FolioReaderSliderParamSize {
+        get {
+            guard
+                let rawValue = self.defaults.value(forKey: kCurrentMarginSize) as? Int,
+                let size = FolioReaderSliderParamSize(rawValue: rawValue) else {
+                    return .m
+            }
+            
+            return size
+        }
+        set (value) {
+            self.defaults.set(value.rawValue, forKey: kCurrentMarginSize)
+            
+            guard let currentPage = self.readerCenter?.currentPage else {
+                return
+            }
+            
+            currentPage.webView?.js("setMarginSize('\(currentMarginSize.cssIdentifier(sliderType: SliderType.margin))')")
+        }
+    }
+
+    /// Check current interline size. Default .m
+    open var currentInterlineSize: FolioReaderSliderParamSize {
+        get {
+            guard
+                let rawValue = self.defaults.value(forKey: kCurrentInterlineSize) as? Int,
+                let size = FolioReaderSliderParamSize(rawValue: rawValue) else {
+                    return .m
+            }
+            
+            return size
+        }
+        set (value) {
+            self.defaults.set(value.rawValue, forKey: kCurrentInterlineSize)
+            
+            guard let currentPage = self.readerCenter?.currentPage else {
+                return
+            }
+            
+            currentPage.webView?.js("setInterlineSize('\(currentInterlineSize.cssIdentifier(sliderType: SliderType.interline))')")
+            
+//            print("\n\nhtmllllll\n\n" + currentPage.webView!.stringByEvaluatingJavaScript(from: "document.documentElement.outerHTML")!)
+
+        }
+    }
+
+    /// Check current audio rate, the speed of speech voice. Default 0
+    open var currentAudioRate: Int {
+        get { return self.defaults.integer(forKey: kCurrentAudioRate) }
+        set (value) {
+            self.defaults.set(value, forKey: kCurrentAudioRate)
+        }
+    }
+
+    /// Check the current highlight style.Default 0
+    open var currentHighlightStyle: Int {
+        get { return self.defaults.integer(forKey: kCurrentHighlightStyle) }
+        set (value) {
+            self.defaults.set(value, forKey: kCurrentHighlightStyle)
+        }
+    }
+
+    /// Check the current Media Overlay or TTS style
+    open var currentMediaOverlayStyle: MediaOverlayStyle {
+        get {
+            guard let rawValue = self.defaults.value(forKey: kCurrentMediaOverlayStyle) as? Int,
+                let style = MediaOverlayStyle(rawValue: rawValue) else {
+                return MediaOverlayStyle.default
+            }
+            return style
+        }
+        set (value) {
+            self.defaults.set(value.rawValue, forKey: kCurrentMediaOverlayStyle)
+        }
+    }
+
+    /// Check the current scroll direction. Default .defaultVertical
+    open var currentScrollDirection: Int {
+        get {
+            guard let value = self.defaults.value(forKey: kCurrentScrollDirection) as? Int else {
+                return FolioReaderScrollDirection.defaultVertical.rawValue
+            }
+
+            return value
+        }
+        set (value) {
+            self.defaults.set(value, forKey: kCurrentScrollDirection)
+
+            let direction = (FolioReaderScrollDirection(rawValue: currentScrollDirection) ?? .defaultVertical)
+            self.readerCenter?.setScrollDirection(direction)
+        }
+    }
+
+    open var currentMenuIndex: Int {
+        get { return self.defaults.integer(forKey: kCurrentTOCMenu) }
+        set (value) {
+            self.defaults.set(value, forKey: kCurrentTOCMenu)
+        }
+    }
+
+    open var savedPositionForCurrentBook: [String: Any]? {
+        get {
+            guard let bookId = self.readerContainer?.book.name else {
+                return nil
+            }
+            return self.defaults.value(forKey: bookId) as? [String : Any]
+        }
+        set {
+            guard let bookId = self.readerContainer?.book.name else {
+                return
+            }
+            self.defaults.set(newValue, forKey: bookId)
+        }
+    }
+}
+
+// MARK: - Metadata
+
+extension FolioReader {
+
+    // TODO QUESTION: The static `getCoverImage` function used the shared instance before and ignored the `unzipPath` parameter.
+    // Should we properly implement the parameter (what has been done now) or should change the API to only use the current FolioReader instance?
+
+    /**
+     Read Cover Image and Return an `UIImage`
+     */
+    open class func getCoverImage(_ epubPath: String, unzipPath: String? = nil) throws -> UIImage {
+        return try FREpubParser().parseCoverImage(epubPath, unzipPath: unzipPath)
+    }
+
+    open class func getTitle(_ epubPath: String, unzipPath: String? = nil) throws -> String {
+        return try FREpubParser().parseTitle(epubPath, unzipPath: unzipPath)
+    }
+
+    open class func getAuthorName(_ epubPath: String, unzipPath: String? = nil) throws-> String {
+        return try FREpubParser().parseAuthorName(epubPath, unzipPath: unzipPath)
+    }
+}
+
+// MARK: - Exit, save and close FolioReader
+
+extension FolioReader {
+
+    /// Save Reader state, book, page and scroll offset.
+    @objc open func saveReaderState() {
+        guard isReaderOpen else {
+            return
+        }
+
+        guard let currentPage = self.readerCenter?.currentPage, let webView = currentPage.webView else {
+            return
+        }
+
+        let position = [
+            "pageNumber": (self.readerCenter?.currentPageNumber ?? 0),
+            "pageOffsetX": webView.scrollView.contentOffset.x,
+            "pageOffsetY": webView.scrollView.contentOffset.y
+            ] as [String : Any]
+
+        self.savedPositionForCurrentBook = position
+    }
+
+    /// Closes and save the reader current instance.
+    open func close() {
+        self.saveReaderState()
+        self.isReaderOpen = false
+        self.isReaderReady = false
+        self.readerAudioPlayer?.stop(immediate: true)
+        self.defaults.set(0, forKey: kCurrentTOCMenu)
+        self.delegate?.folioReaderDidClose?(self)
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderPage.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderPage.swift
new file mode 100755 (executable)
index 0000000..a1a8fea
--- /dev/null
@@ -0,0 +1,586 @@
+//
+//  FolioReaderPage.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 10/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+import SafariServices
+import MenuItemKit
+import JSQWebViewController
+
+/// Protocol which is used from `FolioReaderPage`s.
+@objc public protocol FolioReaderPageDelegate: class {
+
+    /**
+     Notify that the page will be loaded. Note: The webview content itself is already loaded at this moment. But some java script operations like the adding of class based on click listeners will happen right after this method. If you want to perform custom java script before this happens this method is the right choice. If you want to modify the html content (and not run java script) you have to use `htmlContentForPage()` from the `FolioReaderCenterDelegate`.
+
+     - parameter page: The loaded page
+     */
+    @objc optional func pageWillLoad(_ page: FolioReaderPage)
+
+    /**
+     Notifies that page did load. A page load doesn't mean that this page is displayed right away, use `pageDidAppear` to get informed about the appearance of a page.
+
+     - parameter page: The loaded page
+     */
+    @objc optional func pageDidLoad(_ page: FolioReaderPage)
+    
+    /**
+     Notifies that page receive tap gesture.
+     
+     - parameter recognizer: The tap recognizer
+     */
+    @objc optional func pageTap(_ recognizer: UITapGestureRecognizer)
+}
+
+open class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGestureRecognizerDelegate {
+    weak var delegate: FolioReaderPageDelegate?
+    weak var readerContainer: FolioReaderContainer?
+
+    /// The index of the current page. Note: The index start at 1!
+    open var pageNumber: Int!
+    open var webView: FolioReaderWebView?
+
+    fileprivate var colorView: UIView!
+    fileprivate var shouldShowBar = true
+    fileprivate var menuIsVisible = false
+    fileprivate var isNextChapter = true
+    fileprivate var anchorTapped = false
+    private(set) var pageLoaded = false
+    
+    fileprivate var readerConfig: FolioReaderConfig {
+        guard let readerContainer = readerContainer else { return FolioReaderConfig() }
+        return readerContainer.readerConfig
+    }
+
+    fileprivate var book: FRBook {
+        guard let readerContainer = readerContainer else { return FRBook() }
+        return readerContainer.book
+    }
+
+    fileprivate var folioReader: FolioReader {
+        guard let readerContainer = readerContainer else { return FolioReader() }
+        return readerContainer.folioReader
+    }
+
+    // MARK: - View life cicle
+
+    public override init(frame: CGRect) {
+        // Init explicit attributes with a default value. The `setup` function MUST be called to configure the current object with valid attributes.
+        self.readerContainer = FolioReaderContainer(withConfig: FolioReaderConfig(), folioReader: FolioReader(), epubPath: "")
+        super.init(frame: frame)
+        self.backgroundColor = UIColor.clear
+
+        NotificationCenter.default.addObserver(self, selector: #selector(refreshPageMode), name: NSNotification.Name(rawValue: "needRefreshPageMode"), object: nil)
+    }
+
+    public func setup(withReaderContainer readerContainer: FolioReaderContainer) {
+        self.readerContainer = readerContainer
+        guard let readerContainer = self.readerContainer else { return }
+
+        if webView == nil {
+            webView = FolioReaderWebView(frame: webViewFrame(), readerContainer: readerContainer)
+            webView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+            webView?.dataDetectorTypes = .link
+            webView?.scrollView.showsVerticalScrollIndicator = false
+            webView?.scrollView.showsHorizontalScrollIndicator = false
+            webView?.backgroundColor = .clear
+            self.contentView.addSubview(webView!)
+        }
+        webView?.delegate = self
+
+        if colorView == nil {
+            colorView = UIView()
+            colorView.backgroundColor = self.readerConfig.nightModeBackground
+            webView?.scrollView.addSubview(colorView)
+        }
+
+        // Remove all gestures before adding new one
+        webView?.gestureRecognizers?.forEach({ gesture in
+            webView?.removeGestureRecognizer(gesture)
+        })
+        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
+        tapGestureRecognizer.numberOfTapsRequired = 1
+        tapGestureRecognizer.delegate = self
+        webView?.addGestureRecognizer(tapGestureRecognizer)
+    }
+
+    required public init?(coder aDecoder: NSCoder) {
+        fatalError("storyboards are incompatible with truth and beauty")
+    }
+
+    deinit {
+        webView?.scrollView.delegate = nil
+        webView?.delegate = nil
+        NotificationCenter.default.removeObserver(self)
+    }
+
+    override open func layoutSubviews() {
+        super.layoutSubviews()
+
+        webView?.setupScrollDirection()
+        webView?.frame = webViewFrame()
+    }
+
+    func webViewFrame() -> CGRect {
+        guard (self.readerConfig.hideBars == false) else {
+            return bounds
+        }
+
+        let statusbarHeight = UIApplication.shared.statusBarFrame.size.height
+        let navBarHeight = self.folioReader.readerCenter?.navigationController?.navigationBar.frame.size.height ?? CGFloat(0)
+        let navTotal = self.readerConfig.shouldHideNavigationOnTap ? 0 : statusbarHeight + navBarHeight
+        let paddingTop: CGFloat = 20
+        let paddingBottom: CGFloat = 30
+
+        return CGRect(
+            x: bounds.origin.x,
+            y: self.readerConfig.isDirection(bounds.origin.y + navTotal, bounds.origin.y + navTotal + paddingTop, bounds.origin.y + navTotal),
+            width: bounds.width,
+            height: self.readerConfig.isDirection(bounds.height - navTotal, bounds.height - navTotal - paddingTop - paddingBottom, bounds.height - navTotal)
+        )
+    }
+
+    func loadHTMLString(_ htmlContent: String!, baseURL: URL!, isNextChapter: Bool) {
+        // Insert the stored highlights to the HTML
+        if self.anchorTapped { // dont scroll to bottom, when anchor tapped,
+            self.anchorTapped = false
+            self.isNextChapter = true
+        }
+        else {
+            self.isNextChapter = isNextChapter
+        }
+        let tempHtmlContent = htmlContentWithInsertHighlights(htmlContent)
+        // Load the html into the webview
+       
+        self.pageLoaded = false
+        print("pageStartLoading")
+
+        webView?.alpha = 0
+        webView?.loadHTMLString(tempHtmlContent, baseURL: baseURL)
+    }
+
+    // MARK: - Highlights
+
+    fileprivate func htmlContentWithInsertHighlights(_ htmlContent: String) -> String {
+        var tempHtmlContent = htmlContent as NSString
+        // Restore highlights
+        guard let bookId = (self.book.name as NSString?)?.deletingPathExtension else {
+            return tempHtmlContent as String
+        }
+
+        let highlights = Highlight.allByBookId(withConfiguration: self.readerConfig, bookId: bookId, andPage: pageNumber as NSNumber?)
+
+        if (highlights.count > 0) {
+            for item in highlights {
+                let style = HighlightStyle.classForStyle(item.type)
+                let tag = "<highlight id=\"\(item.highlightId!)\" onclick=\"callHighlightURL(this);\" class=\"\(style)\">\(item.content!)</highlight>"
+                var locator = item.contentPre + item.content
+                locator += item.contentPost
+                locator = Highlight.removeSentenceSpam(locator) /// Fix for Highlights
+                let range: NSRange = tempHtmlContent.range(of: locator, options: .literal)
+
+                if range.location != NSNotFound {
+                    let newRange = NSRange(location: range.location + item.contentPre.count, length: item.content.count)
+                    tempHtmlContent = tempHtmlContent.replacingCharacters(in: newRange, with: tag) as NSString
+                } else {
+                    print("highlight range not found")
+                }
+            }
+        }
+        return tempHtmlContent as String
+    }
+
+    // MARK: - UIWebView Delegate
+
+    open func webViewDidFinishLoad(_ webView: UIWebView) {
+        guard let webView = webView as? FolioReaderWebView else {
+            return
+        }
+
+        let anchorFromURL = self.folioReader.readerCenter?.anchor
+        if anchorFromURL != nil {
+            handleAnchor(anchorFromURL!, avoidBeginningAnchors: false, animated: true)
+            self.folioReader.readerCenter?.anchor = nil
+        }
+        
+        delegate?.pageWillLoad?(self)
+
+        // Add the custom class based onClick listener
+        self.setupClassBasedOnClickListeners()
+
+        refreshPageMode()
+
+        if self.readerConfig.enableTTS && !self.book.hasAudio {
+            webView.js("wrappingSentencesWithinPTags()")
+
+            if let audioPlayer = self.folioReader.readerAudioPlayer, (audioPlayer.isPlaying() == true) {
+                audioPlayer.readCurrentSentence()
+            }
+        }
+
+       /*
+        let direction: ScrollDirection = self.folioReader.needsRTLChange ? .positive(withConfiguration: self.readerConfig) : .negative(withConfiguration: self.readerConfig)
+        print("direction \(direction), pageScrollDirection \(self.folioReader.readerCenter?.pageScrollDirection)")
+        //TODO: TUTAJ PORAWIAC. scrollPageToBottom() powinno wywoływać się tylko gdy scrollujemy w górę do następnego chaptera i gdy scrollujemy w lewo do następnego chaptera
+        if (self.folioReader.readerCenter?.pageScrollDirection == direction &&
+            self.folioReader.readerCenter?.isScrolling == true &&
+            self.readerConfig.scrollDirection != .horizontalWithVerticalContent) {
+            
+            print("scrollPageToBottom()")
+
+            scrollPageToBottom()
+        }
+        else {
+            print("no scrollPageToBottom()")
+
+        }
+ */
+        if !isNextChapter {
+            scrollPageToBottom()
+        }
+
+        UIView.animate(withDuration: 0.2, animations: {webView.alpha = 1}, completion: { finished in
+            webView.isColors = false
+            self.webView?.createMenu(options: false)
+        })
+
+        
+        print("pageEndLoading")
+        self.pageLoaded = true
+        
+        delegate?.pageDidLoad?(self)
+    }
+
+    open func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
+        guard
+            let webView = webView as? FolioReaderWebView,
+            let scheme = request.url?.scheme else {
+                return true
+        }
+
+        guard let url = request.url else { return false }
+
+        if scheme == "highlight" {
+
+            shouldShowBar = false
+
+            guard let decoded = url.absoluteString.removingPercentEncoding else { return false }
+            let index = decoded.index(decoded.startIndex, offsetBy: 12)
+            let rect = CGRectFromString(String(decoded[index...]))
+
+            webView.createMenu(options: true)
+            webView.setMenuVisible(true, andRect: rect)
+            menuIsVisible = true
+
+            return false
+        } else if scheme == "play-audio" {
+            guard let decoded = url.absoluteString.removingPercentEncoding else { return false }
+            let index = decoded.index(decoded.startIndex, offsetBy: 13)
+            let playID = String(decoded[index...])
+            let chapter = self.folioReader.readerCenter?.getCurrentChapter()
+            let href = chapter?.href ?? ""
+            self.folioReader.readerAudioPlayer?.playAudio(href, fragmentID: playID)
+
+            return false
+        } else if scheme == "file" {
+
+            let anchorFromURL = url.fragment
+
+            // Handle internal url
+            if !url.pathExtension.isEmpty {
+                let pathComponent = (self.book.opfResource.href as NSString?)?.deletingLastPathComponent
+                guard let base = ((pathComponent == nil || pathComponent?.isEmpty == true) ? self.book.name : pathComponent) else {
+                    return true
+                }
+
+                let path = url.path
+                let splitedPath = path.components(separatedBy: base)
+
+                // Return to avoid crash
+                if (splitedPath.count <= 1 || splitedPath[1].isEmpty) {
+                    return true
+                }
+
+                let href = splitedPath[1].trimmingCharacters(in: CharacterSet(charactersIn: "/"))
+                let hrefPage = (self.folioReader.readerCenter?.findPageByHref(href) ?? 0) + 1
+
+                if (hrefPage == pageNumber) {
+                    // Handle internal #anchor
+                    if anchorFromURL != nil {
+                        handleAnchor(anchorFromURL!, avoidBeginningAnchors: false, animated: true)
+                        return false
+                    }
+                } else {
+                    anchorTapped = true
+                    self.folioReader.readerCenter?.anchor = anchorFromURL
+
+                    self.folioReader.readerCenter?.changePageWith(href: href, animated: true)
+                }
+                return false
+            }
+
+            // Handle internal #anchor
+            if anchorFromURL != nil {
+                handleAnchor(anchorFromURL!, avoidBeginningAnchors: false, animated: true)
+                return false
+            }
+
+            return true
+        } else if scheme == "mailto" {
+            print("Email")
+            return true
+        } else if url.absoluteString != "about:blank" && scheme.contains("http") && navigationType == .linkClicked {
+
+            if #available(iOS 9.0, *) {
+                let safariVC = SFSafariViewController(url: request.url!)
+                safariVC.view.tintColor = self.readerConfig.tintColor
+                self.folioReader.readerCenter?.present(safariVC, animated: true, completion: nil)
+            } else {
+                let webViewController = WebViewController(url: request.url!)
+                let nav = UINavigationController(rootViewController: webViewController)
+                nav.view.tintColor = self.readerConfig.tintColor
+                self.folioReader.readerCenter?.present(nav, animated: true, completion: nil)
+            }
+            return false
+        } else {
+            // Check if the url is a custom class based onClick listerner
+            var isClassBasedOnClickListenerScheme = false
+            for listener in self.readerConfig.classBasedOnClickListeners {
+
+                if scheme == listener.schemeName,
+                    let absoluteURLString = request.url?.absoluteString,
+                    let range = absoluteURLString.range(of: "/clientX=") {
+                    let baseURL = String(absoluteURLString[..<range.lowerBound])
+                    let positionString = String(absoluteURLString[range.lowerBound...])
+                    if let point = getEventTouchPoint(fromPositionParameterString: positionString) {
+                        let attributeContentString = (baseURL.replacingOccurrences(of: "\(scheme)://", with: "").removingPercentEncoding)
+                        // Call the on click action block
+                        listener.onClickAction(attributeContentString, point)
+                        // Mark the scheme as class based click listener scheme
+                        isClassBasedOnClickListenerScheme = true
+                    }
+                }
+            }
+
+            if isClassBasedOnClickListenerScheme == false {
+                // Try to open the url with the system if it wasn't a custom class based click listener
+                if UIApplication.shared.canOpenURL(url) {
+                    UIApplication.shared.openURL(url)
+                    return false
+                }
+            } else {
+                return false
+            }
+        }
+
+        return true
+    }
+
+    fileprivate func getEventTouchPoint(fromPositionParameterString positionParameterString: String) -> CGPoint? {
+        // Remove the parameter names: "/clientX=188&clientY=292" -> "188&292"
+        var positionParameterString = positionParameterString.replacingOccurrences(of: "/clientX=", with: "")
+        positionParameterString = positionParameterString.replacingOccurrences(of: "clientY=", with: "")
+        // Separate both position values into an array: "188&292" -> [188],[292]
+        let positionStringValues = positionParameterString.components(separatedBy: "&")
+        // Multiply the raw positions with the screen scale and return them as CGPoint
+        if
+            positionStringValues.count == 2,
+            let xPos = Int(positionStringValues[0]),
+            let yPos = Int(positionStringValues[1]) {
+            return CGPoint(x: xPos, y: yPos)
+        }
+        return nil
+    }
+
+    // MARK: Gesture recognizer
+
+    open func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+        if gestureRecognizer.view is FolioReaderWebView {
+            if otherGestureRecognizer is UILongPressGestureRecognizer {
+                if UIMenuController.shared.isMenuVisible {
+                    webView?.setMenuVisible(false)
+                }
+                return false
+            }
+            return true
+        }
+        return false
+    }
+
+    @objc open func handleTapGesture(_ recognizer: UITapGestureRecognizer) {
+        self.delegate?.pageTap?(recognizer)
+        
+        if let _navigationController = self.folioReader.readerCenter?.navigationController, (_navigationController.isNavigationBarHidden == true) {
+            let selected = webView?.js("getSelectedText()")
+            
+            guard (selected == nil || selected?.isEmpty == true) else {
+                return
+            }
+
+            let delay = 0.4 * Double(NSEC_PER_SEC) // 0.4 seconds * nanoseconds per seconds
+            let dispatchTime = (DispatchTime.now() + (Double(Int64(delay)) / Double(NSEC_PER_SEC)))
+            
+            DispatchQueue.main.asyncAfter(deadline: dispatchTime, execute: {
+                if (self.shouldShowBar == true && self.menuIsVisible == false) {
+                    self.folioReader.readerCenter?.toggleBars()
+                }
+            })
+        } else if (self.readerConfig.shouldHideNavigationOnTap == true) {
+            self.folioReader.readerCenter?.hideBars()
+            self.menuIsVisible = false
+        }
+    }
+
+    // MARK: - Public scroll postion setter
+
+    /**
+     Scrolls the page to a given offset
+
+     - parameter offset:   The offset to scroll
+     - parameter animated: Enable or not scrolling animation
+     */
+    open func scrollPageToOffset(_ offset: CGFloat, animated: Bool) {
+        let pageOffsetPoint = self.readerConfig.isDirection(CGPoint(x: 0, y: offset), CGPoint(x: offset, y: 0), CGPoint(x: 0, y: offset))
+        webView?.scrollView.setContentOffset(pageOffsetPoint, animated: animated)
+    }
+
+    /**
+     Scrolls the page to bottom
+     */
+    open func scrollPageToBottom() {
+        guard let webView = webView else { return }
+        let bottomOffset = self.readerConfig.isDirection(
+            CGPoint(x: 0, y: webView.scrollView.contentSize.height - webView.scrollView.bounds.height),
+            CGPoint(x: webView.scrollView.contentSize.width - webView.scrollView.bounds.width, y: 0),
+            CGPoint(x: webView.scrollView.contentSize.width - webView.scrollView.bounds.width, y: 0)
+        )
+
+        if bottomOffset.forDirection(withConfiguration: self.readerConfig) >= 0 {
+            DispatchQueue.main.async {
+                self.webView?.scrollView.setContentOffset(bottomOffset, animated: false)
+            }
+        }
+    }
+
+    /**
+     Handdle #anchors in html, get the offset and scroll to it
+
+     - parameter anchor:                The #anchor
+     - parameter avoidBeginningAnchors: Sometimes the anchor is on the beggining of the text, there is not need to scroll
+     - parameter animated:              Enable or not scrolling animation
+     */
+    open func handleAnchor(_ anchor: String,  avoidBeginningAnchors: Bool, animated: Bool) {
+        if !anchor.isEmpty {
+            let offset = getAnchorOffset(anchor)
+
+            switch self.readerConfig.scrollDirection {
+            case .vertical, .defaultVertical:
+                let isBeginning = (offset < frame.forDirection(withConfiguration: self.readerConfig) * 0.5)
+
+                if !avoidBeginningAnchors {
+                    scrollPageToOffset(offset, animated: animated)
+                } else if avoidBeginningAnchors && !isBeginning {
+                    scrollPageToOffset(offset, animated: animated)
+                }
+            case .horizontal, .horizontalWithVerticalContent:
+                scrollPageToOffset(offset, animated: animated)
+            }
+        }
+    }
+
+    // MARK: Helper
+
+    /**
+     Get the #anchor offset in the page
+
+     - parameter anchor: The #anchor id
+     - returns: The element offset ready to scroll
+     */
+    func getAnchorOffset(_ anchor: String) -> CGFloat {
+        let horizontal = self.readerConfig.scrollDirection == .horizontal
+        
+
+        if let strOffset = webView?.js("getAnchorOffset('\(anchor)', \(horizontal.description))") {
+            return CGFloat((strOffset as NSString).floatValue)
+        }
+
+        return CGFloat(0)
+    }
+
+    // MARK: Mark ID
+
+    /**
+     Audio Mark ID - marks an element with an ID with the given class and scrolls to it
+
+     - parameter identifier: The identifier
+     */
+    func audioMarkID(_ identifier: String) {
+        guard let currentPage = self.folioReader.readerCenter?.currentPage else {
+            return
+        }
+
+        let playbackActiveClass = self.book.playbackActiveClass
+        currentPage.webView?.js("audioMarkID('\(playbackActiveClass)','\(identifier)')")
+    }
+
+    // MARK: UIMenu visibility
+
+    override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
+        guard let webView = webView else { return false }
+
+        if UIMenuController.shared.menuItems?.count == 0 {
+            webView.isColors = false
+            webView.createMenu(options: false)
+        }
+
+        if !webView.isShare && !webView.isColors {
+            if let result = webView.js("getSelectedText()") , result.components(separatedBy: " ").count == 1 {
+                webView.isOneWord = true
+                webView.createMenu(options: false)
+            } else {
+                webView.isOneWord = false
+            }
+        }
+
+        return super.canPerformAction(action, withSender: sender)
+    }
+
+    // MARK: ColorView fix for horizontal layout
+    @objc func refreshPageMode() {
+        guard let webView = webView else { return }
+
+        if (self.folioReader.nightMode == true) {
+            // omit create webView and colorView
+            let script = "document.documentElement.offsetHeight"
+            let contentHeight = webView.stringByEvaluatingJavaScript(from: script)
+            let frameHeight = webView.frame.height
+            let lastPageHeight = frameHeight * CGFloat(webView.pageCount) - CGFloat(Double(contentHeight!)!)
+            colorView.frame = CGRect(x: webView.frame.width * CGFloat(webView.pageCount-1), y: webView.frame.height - lastPageHeight, width: webView.frame.width, height: lastPageHeight)
+        } else {
+            colorView.frame = CGRect.zero
+        }
+    }
+    
+    // MARK: - Class based click listener
+    
+    fileprivate func setupClassBasedOnClickListeners() {
+        for listener in self.readerConfig.classBasedOnClickListeners {
+            self.webView?.js("addClassBasedOnClickListener(\"\(listener.schemeName)\", \"\(listener.querySelector)\", \"\(listener.attributeName)\", \"\(listener.selectAll)\")");
+        }
+    }
+    
+    // MARK: - Public Java Script injection
+    
+    /** 
+     Runs a JavaScript script and returns it result. The result of running the JavaScript script passed in the script parameter, or nil if the script fails.
+     
+     - returns: The result of running the JavaScript script passed in the script parameter, or nil if the script fails.
+     */
+    open func performJavaScript(_ javaScriptCode: String) -> String? {
+        return webView?.js(javaScriptCode)
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderPageIndicator.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderPageIndicator.swift
new file mode 100644 (file)
index 0000000..f6ca2b1
--- /dev/null
@@ -0,0 +1,132 @@
+//
+//  FolioReaderPageIndicator.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 10/09/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+class FolioReaderPageIndicator: UIView {
+    var pagesLabel: UILabel!
+    var minutesLabel: UILabel!
+    var totalMinutes: Int!
+    var totalPages: Int!
+    var currentPage: Int = 1 {
+        didSet { self.reloadViewWithPage(self.currentPage) }
+    }
+
+    fileprivate var readerConfig: FolioReaderConfig
+    fileprivate var folioReader: FolioReader
+
+    init(frame: CGRect, readerConfig: FolioReaderConfig, folioReader: FolioReader) {
+        self.readerConfig = readerConfig
+        self.folioReader = folioReader
+
+        super.init(frame: frame)
+
+        let color = self.folioReader.isNight(self.readerConfig.nightModeBackground, UIColor.white)
+        backgroundColor = color
+        layer.shadowColor = color.cgColor
+        layer.shadowOffset = CGSize(width: 0, height: -6)
+        layer.shadowOpacity = 1
+        layer.shadowRadius = 4
+        layer.shadowPath = UIBezierPath(rect: bounds).cgPath
+        layer.rasterizationScale = UIScreen.main.scale
+        layer.shouldRasterize = true
+
+        pagesLabel = UILabel(frame: CGRect.zero)
+        pagesLabel.font = UIFont(name: "Avenir-Light", size: 10)!
+        pagesLabel.textAlignment = NSTextAlignment.right
+        addSubview(pagesLabel)
+
+        minutesLabel = UILabel(frame: CGRect.zero)
+        minutesLabel.font = UIFont(name: "Avenir-Light", size: 10)!
+        minutesLabel.textAlignment = NSTextAlignment.right
+        //        minutesLabel.alpha = 0
+        addSubview(minutesLabel)
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("storyboards are incompatible with truth and beauty")
+    }
+
+    func reloadView(updateShadow: Bool) {
+        minutesLabel.sizeToFit()
+        pagesLabel.sizeToFit()
+
+        let fullW = pagesLabel.frame.width + minutesLabel.frame.width
+        minutesLabel.frame.origin = CGPoint(x: frame.width/2-fullW/2, y: 2)
+        pagesLabel.frame.origin = CGPoint(x: minutesLabel.frame.origin.x+minutesLabel.frame.width, y: 2)
+        
+        if updateShadow {
+            layer.shadowPath = UIBezierPath(rect: bounds).cgPath
+            self.reloadColors()
+        }
+    }
+
+    func reloadColors() {
+        let color = self.folioReader.isNight(self.readerConfig.nightModeBackground, UIColor.white)
+        backgroundColor = color
+
+        // Animate the shadow color change
+        let animation = CABasicAnimation(keyPath: "shadowColor")
+        let currentColor = UIColor(cgColor: layer.shadowColor!)
+        animation.fromValue = currentColor.cgColor
+        animation.toValue = color.cgColor
+        animation.fillMode = kCAFillModeForwards
+        animation.isRemovedOnCompletion = false
+        animation.duration = 0.6
+        animation.delegate = self
+        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
+        layer.add(animation, forKey: "shadowColor")
+
+        minutesLabel.textColor = self.folioReader.isNight(UIColor(white: 1, alpha: 0.3), UIColor(white: 0, alpha: 0.6))
+        pagesLabel.textColor = self.folioReader.isNight(UIColor(white: 1, alpha: 0.6), UIColor(white: 0, alpha: 0.9))
+    }
+
+    fileprivate func reloadViewWithPage(_ page: Int) {
+        let pagesRemaining = self.folioReader.needsRTLChange ? totalPages-(totalPages-page+1) : totalPages-page
+
+        if pagesRemaining == 1 {
+            pagesLabel.text = " " + self.readerConfig.localizedReaderOnePageLeft
+        }
+//        else {
+//            pagesLabel.text = " \(pagesRemaining) " + self.readerConfig.localizedReaderManyPagesLeft
+//        }
+        else if pagesRemaining == 0 {
+            pagesLabel.text = "0 stron do końca"
+        }
+        else if pagesRemaining < 5 {
+            pagesLabel.text = " \(pagesRemaining) " + self.readerConfig.localizedReaderManyPagesLeft
+        }
+        else {
+            pagesLabel.text = " \(pagesRemaining) " + "stron do końca"
+        }
+        
+        // PD: Changed
+        
+        minutesLabel.text = ""
+//        let minutesRemaining = Int(ceil(CGFloat((pagesRemaining * totalMinutes)/totalPages)))
+//        if minutesRemaining > 1 {
+//            minutesLabel.text = "\(minutesRemaining) " + self.readerConfig.localizedReaderManyMinutes+" ·"
+//        } else if minutesRemaining == 1 {
+//            minutesLabel.text = self.readerConfig.localizedReaderOneMinute+" ·"
+//        } else {
+//            minutesLabel.text = self.readerConfig.localizedReaderLessThanOneMinute+" ·"
+//        }
+
+        reloadView(updateShadow: false)
+    }
+}
+
+extension FolioReaderPageIndicator: CAAnimationDelegate {
+    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
+        // Set the shadow color to the final value of the animation is done
+        if let keyPath = anim.value(forKeyPath: "keyPath") as? String , keyPath == "shadowColor" {
+            let color = self.folioReader.isNight(self.readerConfig.nightModeBackground, UIColor.white)
+            layer.shadowColor = color.cgColor
+        }
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderPlayerMenu.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderPlayerMenu.swift
new file mode 100644 (file)
index 0000000..af0c6bd
--- /dev/null
@@ -0,0 +1,295 @@
+//
+//  FolioReaderFontsMenu.swift
+//  FolioReaderKit
+//
+//  Created by Kevin Jantzer on 1/6/16.
+//  Copyright (c) 2016 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+class FolioReaderPlayerMenu: UIViewController, SMSegmentViewDelegate, UIGestureRecognizerDelegate {
+
+    var menuView: UIView!
+    var playPauseBtn: UIButton!
+    var styleOptionBtns = [UIButton]()
+    var viewDidAppear = false
+
+    fileprivate var readerConfig: FolioReaderConfig
+    fileprivate var folioReader: FolioReader
+
+    init(folioReader: FolioReader, readerConfig: FolioReaderConfig) {
+        self.readerConfig = readerConfig
+        self.folioReader = folioReader
+
+        super.init(nibName: nil, bundle: nil)
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        // Do any additional setup after loading the view.
+        self.view.backgroundColor = UIColor.clear
+
+        // Tap gesture
+        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(FolioReaderPlayerMenu.tapGesture))
+        tapGesture.numberOfTapsRequired = 1
+        tapGesture.delegate = self
+        view.addGestureRecognizer(tapGesture)
+
+        // Menu view
+        menuView = UIView(frame: CGRect(x: 0, y: view.frame.height-165, width: view.frame.width, height: view.frame.height))
+        menuView.backgroundColor = self.folioReader.isNight(self.readerConfig.nightModeMenuBackground, UIColor.white)
+        menuView.autoresizingMask = .flexibleWidth
+        menuView.layer.shadowColor = UIColor.black.cgColor
+        menuView.layer.shadowOffset = CGSize(width: 0, height: 0)
+        menuView.layer.shadowOpacity = 0.3
+        menuView.layer.shadowRadius = 6
+        menuView.layer.shadowPath = UIBezierPath(rect: menuView.bounds).cgPath
+        menuView.layer.rasterizationScale = UIScreen.main.scale
+        menuView.layer.shouldRasterize = true
+        view.addSubview(menuView)
+
+        let normalColor = UIColor(white: 0.5, alpha: 0.7)
+        let selectedColor = self.readerConfig.tintColor
+        let size = 55
+        let padX = 32
+        // @NOTE: could this be improved/simplified with autolayout?
+        let gutterX = (Int(view.frame.width) - (size * 3 ) - (padX * 4) ) / 2
+
+        //let btnX = (Int(view.frame.width) - (size * 3)) / 4
+
+        // get icon images
+        let play = UIImage(readerImageNamed: "play-icon")
+        let pause = UIImage(readerImageNamed: "pause-icon")
+        let prev = UIImage(readerImageNamed: "prev-icon")
+        let next = UIImage(readerImageNamed: "next-icon")
+        let playSelected = play?.imageTintColor(selectedColor)?.withRenderingMode(.alwaysOriginal)
+        let pauseSelected = pause?.imageTintColor(selectedColor)?.withRenderingMode(.alwaysOriginal)
+
+        let prevNormal = prev?.imageTintColor(normalColor)?.withRenderingMode(.alwaysOriginal)
+        let nextNormal = next?.imageTintColor(normalColor)?.withRenderingMode(.alwaysOriginal)
+        let prevSelected = prev?.imageTintColor(selectedColor)?.withRenderingMode(.alwaysOriginal)
+        let nextSelected = next?.imageTintColor(selectedColor)?.withRenderingMode(.alwaysOriginal)
+
+        // prev button
+        let prevBtn = UIButton(frame: CGRect(x: gutterX + padX, y: 0, width: size, height: size))
+        prevBtn.setImage(prevNormal, for: UIControlState())
+        prevBtn.setImage(prevSelected, for: .selected)
+        prevBtn.addTarget(self, action: #selector(FolioReaderPlayerMenu.prevChapter(_:)), for: .touchUpInside)
+        menuView.addSubview(prevBtn)
+
+        // play / pause button
+        let playPauseBtn = UIButton(frame: CGRect(x: Int(prevBtn.frame.origin.x) + padX + size, y: 0, width: size, height: size))
+        playPauseBtn.setTitleColor(selectedColor, for: UIControlState())
+        playPauseBtn.setTitleColor(selectedColor, for: .selected)
+        playPauseBtn.setImage(playSelected, for: UIControlState())
+        playPauseBtn.setImage(pauseSelected, for: .selected)
+        playPauseBtn.titleLabel!.font = UIFont(name: "Avenir", size: 22)!
+        playPauseBtn.addTarget(self, action: #selector(FolioReaderPlayerMenu.togglePlay(_:)), for: .touchUpInside)
+        menuView.addSubview(playPauseBtn)
+
+        if let audioPlayer = self.folioReader.readerAudioPlayer , audioPlayer.isPlaying() {
+            playPauseBtn.isSelected = true
+        }
+
+        // next button
+        let nextBtn = UIButton(frame: CGRect(x: Int(playPauseBtn.frame.origin.x) + padX + size, y: 0, width: size, height: size))
+        nextBtn.setImage(nextNormal, for: UIControlState())
+        nextBtn.setImage(nextSelected, for: .selected)
+        nextBtn.addTarget(self, action: #selector(FolioReaderPlayerMenu.nextChapter(_:)), for: .touchUpInside)
+        menuView.addSubview(nextBtn)
+
+
+        // Separator
+        let line = UIView(frame: CGRect(x: 0, y: playPauseBtn.frame.height+playPauseBtn.frame.origin.y, width: view.frame.width, height: 1))
+        line.backgroundColor = self.readerConfig.nightModeSeparatorColor
+        menuView.addSubview(line)
+
+        // audio playback rate adjust
+        let playbackRate = SMSegmentView(frame: CGRect(x: 15, y: line.frame.height+line.frame.origin.y, width: view.frame.width-30, height: 55),
+                                         separatorColour: UIColor.clear,
+                                         separatorWidth: 0,
+                                         segmentProperties:  [
+                                            keySegmentOnSelectionColour: UIColor.clear,
+                                            keySegmentOffSelectionColour: UIColor.clear,
+                                            keySegmentOnSelectionTextColour: selectedColor,
+                                            keySegmentOffSelectionTextColour: normalColor,
+                                            keyContentVerticalMargin: 17 as AnyObject
+            ])
+        playbackRate.delegate = self
+        playbackRate.tag = 2
+        playbackRate.addSegmentWithTitle("½x", onSelectionImage: nil, offSelectionImage: nil)
+        playbackRate.addSegmentWithTitle("1x", onSelectionImage: nil, offSelectionImage: nil)
+        playbackRate.addSegmentWithTitle("1½x", onSelectionImage: nil, offSelectionImage: nil)
+        playbackRate.addSegmentWithTitle("2x", onSelectionImage: nil, offSelectionImage: nil)
+        playbackRate.segmentTitleFont = UIFont(name: "Avenir-Light", size: 17)!
+        playbackRate.selectSegmentAtIndex(Int(self.folioReader.currentAudioRate))
+        menuView.addSubview(playbackRate)
+
+
+        // Separator
+        let line2 = UIView(frame: CGRect(x: 0, y: playbackRate.frame.height+playbackRate.frame.origin.y, width: view.frame.width, height: 1))
+        line2.backgroundColor = self.readerConfig.nightModeSeparatorColor
+        menuView.addSubview(line2)
+
+
+        // Media overlay highlight styles
+        let style0 = UIButton(frame: CGRect(x: 0, y: line2.frame.height+line2.frame.origin.y, width: view.frame.width/3, height: 55))
+        style0.titleLabel!.textAlignment = .center
+        style0.titleLabel!.font = UIFont(name: "Avenir-Light", size: 17)
+        style0.setTitleColor(self.folioReader.isNight(self.readerConfig.nightModeMenuBackground, UIColor.white), for: UIControlState())
+        style0.setTitleColor(self.folioReader.isNight(self.readerConfig.nightModeMenuBackground, UIColor.white), for: .selected)
+        style0.setTitle(self.readerConfig.localizedPlayerMenuStyle, for: UIControlState())
+        menuView.addSubview(style0);
+        style0.titleLabel?.sizeToFit()
+        let style0Bgd = UIView(frame: style0.titleLabel!.frame)
+        style0Bgd.center = CGPoint(x: style0.frame.size.width  / 2, y: style0.frame.size.height / 2);
+        style0Bgd.frame.size.width += 8
+        style0Bgd.frame.origin.x -= 4
+        style0Bgd.backgroundColor = normalColor;
+        style0Bgd.layer.cornerRadius = 3.0;
+        style0Bgd.isUserInteractionEnabled = false
+        style0.insertSubview(style0Bgd, belowSubview: style0.titleLabel!)
+
+        let style1 = UIButton(frame: CGRect(x: view.frame.width/3, y: line2.frame.height+line2.frame.origin.y, width: view.frame.width/3, height: 55))
+        style1.titleLabel!.textAlignment = .center
+        style1.titleLabel!.font = UIFont(name: "Avenir-Light", size: 17)
+        style1.setTitleColor(normalColor, for: UIControlState())
+        style1.setAttributedTitle(NSAttributedString(string: "Style", attributes: [
+            NSAttributedStringKey.foregroundColor: normalColor,
+            NSAttributedStringKey.underlineStyle: NSUnderlineStyle.patternDot.rawValue|NSUnderlineStyle.styleSingle.rawValue,
+            NSAttributedStringKey.underlineColor: normalColor
+            ]), for: UIControlState())
+        style1.setAttributedTitle(NSAttributedString(string: self.readerConfig.localizedPlayerMenuStyle, attributes: [
+            NSAttributedStringKey.foregroundColor: self.folioReader.isNight(UIColor.white, UIColor.black),
+            NSAttributedStringKey.underlineStyle: NSUnderlineStyle.patternDot.rawValue|NSUnderlineStyle.styleSingle.rawValue,
+            NSAttributedStringKey.underlineColor: selectedColor
+            ]), for: .selected)
+        menuView.addSubview(style1);
+
+        let style2 = UIButton(frame: CGRect(x: view.frame.width/1.5, y: line2.frame.height+line2.frame.origin.y, width: view.frame.width/3, height: 55))
+        style2.titleLabel!.textAlignment = .center
+        style2.titleLabel!.font = UIFont(name: "Avenir-Light", size: 17)
+        style2.setTitleColor(normalColor, for: UIControlState())
+        style2.setTitleColor(selectedColor, for: .selected)
+        style2.setTitle(self.readerConfig.localizedPlayerMenuStyle, for: UIControlState())
+        menuView.addSubview(style2);
+
+        // add line dividers between style buttons
+        let style1line = UIView(frame: CGRect(x: style1.frame.origin.x, y: style1.frame.origin.y, width: 1, height: style1.frame.height))
+        style1line.backgroundColor = self.readerConfig.nightModeSeparatorColor
+        menuView.addSubview(style1line)
+        let style2line = UIView(frame: CGRect(x: style2.frame.origin.x, y: style2.frame.origin.y, width: 1, height: style2.frame.height))
+        style2line.backgroundColor = self.readerConfig.nightModeSeparatorColor
+        menuView.addSubview(style2line)
+
+        // select the current style
+        style0.isSelected = (self.folioReader.currentMediaOverlayStyle == .default)
+        style1.isSelected = (self.folioReader.currentMediaOverlayStyle == .underline)
+        style2.isSelected = (self.folioReader.currentMediaOverlayStyle == .textColor)
+        if style0.isSelected { style0Bgd.backgroundColor = selectedColor }
+
+        // hook up button actions
+        style0.tag = MediaOverlayStyle.default.rawValue
+        style1.tag = MediaOverlayStyle.underline.rawValue
+        style2.tag = MediaOverlayStyle.textColor.rawValue
+        style0.addTarget(self, action: #selector(FolioReaderPlayerMenu.changeStyle(_:)), for: .touchUpInside)
+        style1.addTarget(self, action: #selector(FolioReaderPlayerMenu.changeStyle(_:)), for: .touchUpInside)
+        style2.addTarget(self, action: #selector(FolioReaderPlayerMenu.changeStyle(_:)), for: .touchUpInside)
+
+        // store ref to buttons
+        styleOptionBtns.append(style0)
+        styleOptionBtns.append(style1)
+        styleOptionBtns.append(style2)
+    }
+
+
+    override func viewDidAppear(_ animated: Bool) {
+        viewDidAppear = true
+    }
+
+    override func viewDidDisappear(_ animated: Bool) {
+        viewDidAppear = false
+    }
+
+    override func didReceiveMemoryWarning() {
+        super.didReceiveMemoryWarning()
+        // Dispose of any resources that can be recreated.
+    }
+
+    // MARK: - Status Bar
+
+    override var prefersStatusBarHidden : Bool {
+        return (self.readerConfig.shouldHideNavigationOnTap == true)
+    }
+
+    // MARK: - SMSegmentView delegate
+
+    func segmentView(_ segmentView: SMSegmentView, didSelectSegmentAtIndex index: Int) {
+        guard viewDidAppear else { return }
+
+        if let audioPlayer = self.folioReader.readerAudioPlayer, (segmentView.tag == 2) {
+            audioPlayer.setRate(index)
+            self.folioReader.currentAudioRate = index
+        }
+    }
+
+    @objc func prevChapter(_ sender: UIButton!) {
+        self.folioReader.readerAudioPlayer?.playPrevChapter()
+    }
+
+    @objc func nextChapter(_ sender: UIButton!) {
+        self.folioReader.readerAudioPlayer?.playNextChapter()
+    }
+
+    @objc func togglePlay(_ sender: UIButton!) {
+        sender.isSelected = sender.isSelected != true
+        self.folioReader.readerAudioPlayer?.togglePlay()
+        closeView()
+    }
+
+    @objc func changeStyle(_ sender: UIButton!) {
+        self.folioReader.currentMediaOverlayStyle = MediaOverlayStyle(rawValue: sender.tag)!
+
+        // select the proper style button
+        for btn in styleOptionBtns {
+            btn.isSelected = btn == sender
+
+            if btn.tag == MediaOverlayStyle.default.rawValue {
+                btn.subviews.first?.backgroundColor = (btn.isSelected ? self.readerConfig.tintColor : UIColor(white: 0.5, alpha: 0.7))
+            }
+        }
+
+        // update the current page style
+        if let currentPage = self.folioReader.readerCenter?.currentPage {
+            currentPage.webView?.js("setMediaOverlayStyle(\"\(self.folioReader.currentMediaOverlayStyle.className())\")")
+        }
+    }
+
+    func closeView() {
+        self.dismiss()
+
+        if (self.readerConfig.shouldHideNavigationOnTap == false) {
+            self.folioReader.readerCenter?.showBars()
+        }
+    }
+    
+    // MARK: - Gestures
+    
+    @objc func tapGesture() {
+        closeView()
+    }
+    
+    
+    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
+        if gestureRecognizer is UITapGestureRecognizer && touch.view == view {
+            return true
+        }
+        return false
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderQuoteShare.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderQuoteShare.swift
new file mode 100644 (file)
index 0000000..64dea3f
--- /dev/null
@@ -0,0 +1,427 @@
+//
+//  FolioReaderQuoteShare.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 8/11/16.
+//  Copyright (c) 2016 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+class FolioReaderQuoteShare: UIViewController {
+    var quoteText: String!
+    var filterImage: UIView!
+    var imageView: UIImageView!
+    var quoteLabel: UILabel!
+    var titleLabel: UILabel!
+    var authorLabel: UILabel!
+    var logoImageView: UIImageView!
+    var collectionView: UICollectionView!
+    let collectionViewLayout = UICollectionViewFlowLayout()
+    let itemSize: CGFloat = 90
+    var dataSource = [QuoteImage]()
+//    let imagePicker = UIImagePickerController()
+    var selectedIndex = 0
+
+    fileprivate var book: FRBook
+    fileprivate var folioReader: FolioReader
+    fileprivate var readerConfig: FolioReaderConfig
+
+    // MARK: Init
+
+    init(initWithText shareText: String, readerConfig: FolioReaderConfig, folioReader: FolioReader, book: FRBook) {
+        self.folioReader = folioReader
+        self.readerConfig = readerConfig
+        self.quoteText = shareText.stripLineBreaks().stripHtml()
+        self.book = book
+
+        super.init(nibName: nil, bundle: Bundle.frameworkBundle())
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("storyboards are incompatible with truth and beauty")
+    }
+
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        setNeedsStatusBarAppearanceUpdate()
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        self.setCloseButton(withConfiguration: self.readerConfig)
+        configureNavBar()
+
+        let titleAttrs = [NSAttributedStringKey.foregroundColor: self.readerConfig.tintColor]
+        let share = UIBarButtonItem(title: self.readerConfig.localizedShare, style: .plain, target: self, action: #selector(shareQuote(_:)))
+        share.setTitleTextAttributes(titleAttrs, for: UIControlState())
+        navigationItem.rightBarButtonItem = share
+
+        let isPad = (UIDevice.current.userInterfaceIdiom == .pad)
+        if (isPad == true) {
+            preferredContentSize = CGSize(width: 400, height: 600)
+        }
+        let screenBounds = (isPad == true ? preferredContentSize : UIScreen.main.bounds.size)
+
+        self.filterImage = UIView(frame: CGRect(x: 0, y: 0, width: screenBounds.width, height: screenBounds.width))
+        self.filterImage.backgroundColor = self.readerConfig.menuSeparatorColor
+        view.addSubview(self.filterImage)
+
+        imageView = UIImageView(frame: filterImage.bounds)
+        filterImage.addSubview(imageView)
+
+        quoteLabel = UILabel()
+        quoteLabel.text = quoteText
+        quoteLabel.textAlignment = .center
+        quoteLabel.font = UIFont(name: "Andada-Regular", size: 26)
+        quoteLabel.textColor = UIColor.white
+        quoteLabel.numberOfLines = 0
+        quoteLabel.baselineAdjustment = .alignCenters
+        quoteLabel.translatesAutoresizingMaskIntoConstraints = false
+        quoteLabel.adjustsFontSizeToFitWidth = true
+        quoteLabel.minimumScaleFactor = 0.3
+        quoteLabel.setContentCompressionResistancePriority(UILayoutPriority(100), for: .vertical)
+        filterImage.addSubview(quoteLabel)
+
+        var bookTitle = ""
+        var authorName = ""
+
+        if let title = self.book.title {
+            bookTitle = title
+        }
+
+        if let author = self.book.metadata.creators.first {
+            authorName = author.name
+        }
+
+        titleLabel = UILabel()
+        titleLabel.text = bookTitle
+        titleLabel.font = UIFont(name: "Lato-Bold", size: 15)
+        titleLabel.textAlignment = .center
+        titleLabel.textColor = UIColor.white
+        titleLabel.numberOfLines = 1
+        titleLabel.translatesAutoresizingMaskIntoConstraints = false
+        titleLabel.adjustsFontSizeToFitWidth = true
+        titleLabel.minimumScaleFactor = 0.8
+        titleLabel.setContentCompressionResistancePriority(UILayoutPriority(600), for: .vertical)
+        filterImage.addSubview(titleLabel)
+
+        // Attributed author
+        let attrs = [NSAttributedStringKey.font: UIFont(name: "Lato-Italic", size: 15)!]
+        let attributedString = NSMutableAttributedString(string:"\(self.readerConfig.localizedShareBy) ", attributes: attrs)
+
+        let attrs1 = [NSAttributedStringKey.font: UIFont(name: "Lato-Regular", size: 15)!]
+        let boldString = NSMutableAttributedString(string: authorName, attributes:attrs1)
+        attributedString.append(boldString)
+
+        authorLabel = UILabel()
+        authorLabel.attributedText = attributedString
+        authorLabel.textAlignment = .center
+        authorLabel.textColor = UIColor.white
+        authorLabel.numberOfLines = 1
+        authorLabel.translatesAutoresizingMaskIntoConstraints = false
+        authorLabel.adjustsFontSizeToFitWidth = true
+        authorLabel.minimumScaleFactor = 0.5
+        filterImage.addSubview(authorLabel)
+
+        let logoImage = self.readerConfig.quoteCustomLogoImage
+        let logoHeight = logoImage?.size.height ?? 0
+        logoImageView = UIImageView(image: logoImage)
+        logoImageView.contentMode = .center
+        logoImageView.translatesAutoresizingMaskIntoConstraints = false
+        filterImage.addSubview(logoImageView)
+
+        // Configure layout contraints
+        var constraints = [NSLayoutConstraint]()
+        let views = [
+            "quoteLabel": self.quoteLabel,
+            "titleLabel": self.titleLabel,
+            "authorLabel": self.authorLabel,
+            "logoImageView": self.logoImageView
+            ] as [String : Any]
+
+        NSLayoutConstraint.constraints(withVisualFormat: "V:|-40-[quoteLabel]-20-[titleLabel]", options: [], metrics: nil, views: views).forEach { constraints.append($0) }
+        NSLayoutConstraint.constraints(withVisualFormat: "V:[titleLabel]-0-[authorLabel]", options: [], metrics: nil, views: views).forEach { constraints.append($0) }
+        NSLayoutConstraint.constraints(withVisualFormat: "V:[authorLabel]-25-[logoImageView(\(Int(logoHeight)))]-18-|", options: [], metrics: nil, views: views).forEach { constraints.append($0) }
+
+        NSLayoutConstraint.constraints(withVisualFormat: "H:|-15-[quoteLabel]-15-|", options: [], metrics: nil, views: views).forEach { constraints.append($0) }
+        NSLayoutConstraint.constraints(withVisualFormat: "H:|-15-[titleLabel]-15-|", options: [], metrics: nil, views: views).forEach { constraints.append($0) }
+        NSLayoutConstraint.constraints(withVisualFormat: "H:|-15-[authorLabel]-15-|", options: [], metrics: nil, views: views).forEach { constraints.append($0) }
+        NSLayoutConstraint.constraints(withVisualFormat: "H:|-15-[logoImageView]-15-|", options: [], metrics: nil, views: views).forEach { constraints.append($0) }
+
+        filterImage.addConstraints(constraints)
+
+        // Layout
+        collectionViewLayout.sectionInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
+        collectionViewLayout.minimumLineSpacing = 15
+        collectionViewLayout.minimumInteritemSpacing = 0
+        collectionViewLayout.scrollDirection = .horizontal
+
+        let background = self.folioReader.isNight(self.readerConfig.nightModeBackground, UIColor.white)
+        view.backgroundColor = background
+
+        // CollectionView
+        let collectionFrame = CGRect(x: 0, y: filterImage.frame.height+15, width: screenBounds.width, height: itemSize)
+        collectionView = UICollectionView(frame: collectionFrame, collectionViewLayout: collectionViewLayout)
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.showsHorizontalScrollIndicator = false
+        collectionView.backgroundColor = background
+        collectionView.decelerationRate = UIScrollViewDecelerationRateFast
+        view.addSubview(collectionView)
+
+        if (UIDevice.current.userInterfaceIdiom == .phone) {
+            collectionView.autoresizingMask = [.flexibleWidth]
+        }
+
+        // Register cell classes
+        collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: kReuseCellIdentifier)
+
+        // Create images
+        dataSource = self.readerConfig.quoteCustomBackgrounds
+        if (self.readerConfig.quotePreserveDefaultBackgrounds == true) {
+            createDefaultImages()
+        }
+
+        // Picker delegate
+//        imagePicker.delegate = self
+
+        // Select first item
+        selectIndex(0)
+    }
+
+    func configureNavBar() {
+        let navBackground = self.folioReader.isNight(self.readerConfig.nightModeMenuBackground, UIColor.white)
+        let tintColor = self.readerConfig.tintColor
+        let navText = self.folioReader.isNight(UIColor.white, UIColor.black)
+        let font = UIFont(name: "Avenir-Light", size: 17)!
+        setTranslucentNavigation(false, color: navBackground, tintColor: tintColor, titleColor: navText, andFont: font)
+    }
+
+    func createDefaultImages() {
+        let color1 = QuoteImage(withColor: UIColor(rgba: "#FA7B67"))
+        let color2 = QuoteImage(withColor: UIColor(rgba: "#78CAB6"))
+        let color3 = QuoteImage(withColor: UIColor(rgba: "#71B630"))
+        let color4 = QuoteImage(withColor: UIColor(rgba: "#4D5B49"))
+        let color5 = QuoteImage(withColor: UIColor(rgba: "#959D92"), textColor: UIColor(rgba: "#4D5B49"))
+
+        var gradient = CAGradientLayer()
+        gradient.colors = [UIColor(rgba: "#2989C9").cgColor, UIColor(rgba: "#21B8C2").cgColor]
+        gradient.startPoint = CGPoint(x: 0, y: 1)
+        gradient.endPoint = CGPoint(x: 1, y: 0)
+        let gradient1 = QuoteImage(withGradient: gradient)
+
+        gradient = CAGradientLayer()
+        gradient.colors = [UIColor(rgba: "#FAD961").cgColor, UIColor(rgba: "#F76B1C").cgColor]
+        let gradient2 = QuoteImage(withGradient: gradient)
+
+        gradient = CAGradientLayer()
+        gradient.colors = [UIColor(rgba: "#B4EC51").cgColor, UIColor(rgba: "#429321").cgColor]
+        let gradient3 = QuoteImage(withGradient: gradient)
+
+        dataSource.append(contentsOf: [color1, color2, color3, color4, color5, gradient1, gradient2, gradient3])
+    }
+
+    func selectIndex(_ index: Int) {
+        let quoteImage = dataSource[index]
+        let row = index+1
+
+        filterImage.backgroundColor = quoteImage.backgroundColor
+        imageView.alpha = quoteImage.alpha
+
+        UIView.transition(with: filterImage, duration: 0.4, options: .transitionCrossDissolve, animations: {
+            self.imageView.image = quoteImage.image
+            self.quoteLabel.textColor = quoteImage.textColor
+            self.titleLabel.textColor = quoteImage.textColor
+            self.authorLabel.textColor = quoteImage.textColor
+            self.logoImageView.image = self.logoImageView.image?.imageTintColor(quoteImage.textColor)
+        }, completion: nil)
+
+        //
+        let prevSelectedIndex = selectedIndex
+        selectedIndex = row
+
+        guard prevSelectedIndex != selectedIndex else { return }
+
+        collectionView.performBatchUpdates({
+            self.collectionView.reloadItems(at: [
+                IndexPath(item: self.selectedIndex, section: 0),
+                IndexPath(item: prevSelectedIndex, section: 0)
+                ])
+        }, completion: nil)
+    }
+
+    // MARK: Share
+
+    @objc func shareQuote(_ sender: UIBarButtonItem) {
+        var subject = self.readerConfig.localizedShareHighlightSubject
+        var text = ""
+        var bookTitle = ""
+        var authorName = ""
+        var shareItems = [AnyObject]()
+
+        // Get book title
+        if let title = self.book.title {
+            bookTitle = title
+            subject += " “\(title)”"
+        }
+
+        // Get author name
+        if let author = self.book.metadata.creators.first {
+            authorName = author.name
+        }
+
+        text = "\(bookTitle) \n\(self.readerConfig.localizedShareBy) \(authorName)"
+
+        let imageQuote = UIImage.imageWithView(filterImage)
+        shareItems.append(imageQuote)
+
+        if let bookShareLink = self.readerConfig.localizedShareWebLink {
+            text += "\n\(bookShareLink.absoluteString)"
+        }
+
+        let act = FolioReaderSharingProvider(subject: subject, text: text)
+        shareItems.insert(act, at: 0)
+
+        let activityViewController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
+        activityViewController.excludedActivityTypes = [UIActivityType.print, UIActivityType.postToVimeo]
+
+        // Pop style on iPad
+        if let actv = activityViewController.popoverPresentationController {
+            actv.barButtonItem = sender
+        }
+
+        present(activityViewController, animated: true, completion: nil)
+    }
+
+    // MARK: Status Bar
+
+    override var preferredStatusBarStyle : UIStatusBarStyle {
+        return self.folioReader.isNight(.lightContent, .default)
+    }
+
+    override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
+        return .portrait
+    }
+
+    override var shouldAutorotate : Bool {
+        return false
+    }
+}
+
+// MARK: UICollectionViewDataSource
+
+extension FolioReaderQuoteShare: UICollectionViewDataSource {
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return dataSource.count + 1
+    }
+
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kReuseCellIdentifier, for: indexPath)
+        let imageView: UIImageView!
+        let tag = 9999
+
+        cell.backgroundColor = UIColor.clear
+        cell.contentView.backgroundColor = UIColor.clear
+        cell.contentView.layer.borderWidth = 1
+
+        if let view = cell.contentView.viewWithTag(tag) as? UIImageView {
+            imageView = view
+        } else {
+            imageView = UIImageView(frame: cell.bounds)
+            imageView.tag = tag
+            cell.contentView.addSubview(imageView)
+        }
+
+        // Camera
+        guard ((indexPath as NSIndexPath).row > 0) else {
+
+            // Image color
+            let normalColor = UIColor(white: 0.5, alpha: 0.7)
+            let camera = UIImage(readerImageNamed: "icon-camera")
+            let dash = UIImage(readerImageNamed: "border-dashed-pattern")
+            let cameraNormal = camera?.imageTintColor(normalColor)
+
+            imageView.contentMode = .center
+            imageView.image = cameraNormal
+            if let dashNormal = dash?.imageTintColor(normalColor) {
+                cell.contentView.layer.borderColor = UIColor(patternImage: dashNormal).cgColor
+            }
+            return cell
+        }
+
+        if (selectedIndex == (indexPath as NSIndexPath).row) {
+            cell.contentView.layer.borderColor = self.readerConfig.tintColor.cgColor
+            cell.contentView.layer.borderWidth = 3
+        } else {
+            cell.contentView.layer.borderColor = UIColor(white: 0.5, alpha: 0.2).cgColor
+        }
+
+        let quoteImage = dataSource[(indexPath as NSIndexPath).row-1]
+        imageView.image = quoteImage.image
+        imageView.alpha = quoteImage.alpha
+        imageView.contentMode = .scaleAspectFit
+        cell.contentView.backgroundColor = quoteImage.backgroundColor
+        return cell
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
+        return CGSize(width: itemSize, height: itemSize)
+    }
+}
+
+// MARK: UICollectionViewDelegate
+
+extension FolioReaderQuoteShare: UICollectionViewDelegate {
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+
+        guard (indexPath as NSIndexPath).row > 0 else {
+            let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
+
+            let takePhoto = UIAlertAction(title: self.readerConfig.localizedTakePhoto, style: .default, handler: { (action) -> Void in
+//                self.imagePicker.sourceType = .camera
+//                self.imagePicker.allowsEditing = true
+//                self.present(self.imagePicker, animated: true, completion: nil)
+            })
+
+            let existingPhoto = UIAlertAction(title: self.readerConfig.localizedChooseExisting, style: .default) { (action) -> Void in
+//                self.imagePicker.sourceType = .photoLibrary
+//                self.imagePicker.allowsEditing = true
+//                self.present(self.imagePicker, animated: true, completion: nil)
+            }
+
+            let cancel = UIAlertAction(title: self.readerConfig.localizedCancel, style: .cancel, handler: nil)
+
+            alertController.addAction(takePhoto)
+            alertController.addAction(existingPhoto)
+            alertController.addAction(cancel)
+
+            present(alertController, animated: true, completion: nil)
+            return
+        }
+
+        selectIndex((indexPath as NSIndexPath).row-1)
+    }
+}
+
+// MARK: ImagePicker delegate
+
+//extension FolioReaderQuoteShare: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
+//    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
+//        if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
+//
+//            let quoteImage = QuoteImage(withImage: image, alpha: 0.6, backgroundColor: UIColor.black)
+//
+//            collectionView.performBatchUpdates({
+//                self.dataSource.insert(quoteImage, at: 0)
+//                self.collectionView.insertItems(at: [IndexPath(item: 1, section: 0)])
+//                self.collectionView.reloadItems(at: [IndexPath(item: self.selectedIndex, section: 0)])
+//            }, completion: { (finished) in
+//                self.selectIndex(0)
+//            })
+//        }
+//
+//        self.dismiss(animated: true, completion: nil)
+//    }
+//}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderSharingProvider.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderSharingProvider.swift
new file mode 100644 (file)
index 0000000..307bbf5
--- /dev/null
@@ -0,0 +1,41 @@
+//
+//  FolioReaderSharingProvider.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 02/09/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+class FolioReaderSharingProvider: UIActivityItemProvider {
+    var subject: String
+    var text: String
+    var html: String?
+    var image: UIImage?
+
+    init(subject: String, text: String, html: String? = nil, image: UIImage? = nil) {
+        self.subject = subject
+        self.text = text
+        self.html = html
+        self.image = image
+
+        super.init(placeholderItem: "")
+    }
+
+    override func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivityType?) -> String {
+        return subject
+    }
+    
+    override func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType?) -> Any? {
+        if let html = html , activityType == UIActivityType.mail {
+            return html
+        }
+
+        if let image = image , activityType == UIActivityType.postToFacebook {
+            return image
+        }
+
+        return text
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderUserDefaults.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderUserDefaults.swift
new file mode 100644 (file)
index 0000000..4312b1e
--- /dev/null
@@ -0,0 +1,108 @@
+//
+//  FolioReaderUserDefaults.swift
+//  Pods
+//
+//  Created by Kevin Delord on 01/04/17.
+//
+//
+
+import Foundation
+
+class FolioReaderUserDefaults {
+
+    /// User Defaults which are dependend on an identifier. If no identifier is given the default standard user defaults are used.
+    fileprivate var userDefaults = [String: Any]()
+
+    fileprivate var identifier: String?
+
+    fileprivate var useStandardUserDefaultsDirectly: Bool {
+        return (self.identifier == nil)
+    }
+
+    init(withIdentifier identifier: String?) {
+        if let _identifier = identifier {
+            self.identifier = "folioreader.userdefaults.identifier.\(_identifier)"
+        }
+
+        guard
+            let prefixedIdentifier = self.identifier,
+            let defaults = UserDefaults.standard.value(forKey: prefixedIdentifier) as? [String: Any] else {
+                return
+        }
+
+        self.userDefaults = defaults
+    }
+
+    public func synchronize() {
+        if let identifier = self.identifier {
+            // Add the keys to to the user defaults it they are identifier dependend
+            UserDefaults.standard.set(self.userDefaults, forKey: identifier)
+        }
+
+        UserDefaults.standard.synchronize()
+    }
+}
+
+// MARK: - Getter
+
+extension FolioReaderUserDefaults {
+
+    internal func bool(forKey key: String) -> Bool {
+        guard (self.useStandardUserDefaultsDirectly == false) else {
+                return ((UserDefaults.standard.object(forKey: key) as? Bool) ?? false)
+        }
+
+        guard let value = self.userDefaults[key] as? Bool else {
+            return false
+        }
+
+        return value
+    }
+
+    internal func integer(forKey key: String) -> Int {
+        guard (self.useStandardUserDefaultsDirectly == false) else {
+            return ((UserDefaults.standard.object(forKey: key) as? Int) ?? 0)
+        }
+
+        guard let value = self.userDefaults[key] as? Int else {
+            return 0
+        }
+
+        return value
+    }
+
+    internal func value(forKey key: String) -> Any? {
+        guard (self.useStandardUserDefaultsDirectly == false) else {
+            return UserDefaults.standard.object(forKey: key)
+        }
+
+        return self.userDefaults[key]
+    }
+}
+// MARK: - Setter
+
+extension FolioReaderUserDefaults {
+    
+    internal func register(defaults: [String: Any]) {
+        guard (self.useStandardUserDefaultsDirectly == false) else {
+            UserDefaults.standard.register(defaults: defaults)
+            return
+        }
+
+        for (key, value) in defaults where (self.userDefaults[key] == nil) {
+            self.userDefaults[key] = value
+        }
+
+        self.synchronize()
+    }
+
+    internal func set(_ value: Any?, forKey key: String) {
+        if (self.useStandardUserDefaultsDirectly == true) {
+            UserDefaults.standard.set(value, forKey: key)
+        } else {
+            self.userDefaults[key] = value
+        }
+
+        self.synchronize()
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderWebView.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderWebView.swift
new file mode 100644 (file)
index 0000000..7c1a3c3
--- /dev/null
@@ -0,0 +1,339 @@
+//
+//  FolioReaderWebView.swift
+//  FolioReaderKit
+//
+//  Created by Hans Seiffert on 21.09.16.
+//  Copyright (c) 2016 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+/// The custom WebView used in each page
+open class FolioReaderWebView: UIWebView {
+    var isColors = false
+    var isShare = false
+    var isOneWord = false
+
+    fileprivate weak var readerContainer: FolioReaderContainer?
+
+    fileprivate var readerConfig: FolioReaderConfig {
+        guard let readerContainer = readerContainer else { return FolioReaderConfig() }
+        return readerContainer.readerConfig
+    }
+
+    fileprivate var book: FRBook {
+        guard let readerContainer = readerContainer else { return FRBook() }
+        return readerContainer.book
+    }
+
+    fileprivate var folioReader: FolioReader {
+        guard let readerContainer = readerContainer else { return FolioReader() }
+        return readerContainer.folioReader
+    }
+
+    override init(frame: CGRect) {
+        fatalError("use init(frame:readerConfig:book:) instead.")
+    }
+
+    init(frame: CGRect, readerContainer: FolioReaderContainer) {
+        self.readerContainer = readerContainer
+
+        super.init(frame: frame)
+    }
+
+    required public init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    // MARK: - UIMenuController
+
+    open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
+        guard readerConfig.useReaderMenuController else {
+            return super.canPerformAction(action, withSender: sender)
+        }
+
+        if isShare {
+            return false
+        } else if isColors {
+            return false
+        } else {
+            if action == #selector(highlight(_:))
+                || (action == #selector(define(_:)) && isOneWord)
+                || (action == #selector(play(_:)) && (book.hasAudio || readerConfig.enableTTS))
+                || (action == #selector(share(_:)) && readerConfig.allowSharing)
+                || (action == #selector(copy(_:)) && readerConfig.allowSharing) {
+                return true
+            }
+            return false
+        }
+    }
+
+    // MARK: - UIMenuController - Actions
+
+    @objc func share(_ sender: UIMenuController) {
+        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
+
+        let shareImage = UIAlertAction(title: self.readerConfig.localizedShareImageQuote, style: .default, handler: { (action) -> Void in
+            if self.isShare {
+                if let textToShare = self.js("getHighlightContent()") {
+                    self.folioReader.readerCenter?.presentQuoteShare(textToShare)
+                }
+            } else {
+                if let textToShare = self.js("getSelectedText()") {
+                    self.folioReader.readerCenter?.presentQuoteShare(textToShare)
+
+                    self.clearTextSelection()
+                }
+            }
+            self.setMenuVisible(false)
+        })
+
+        let shareText = UIAlertAction(title: self.readerConfig.localizedShareTextQuote, style: .default) { (action) -> Void in
+            if self.isShare {
+                if let textToShare = self.js("getHighlightContent()") {
+                    self.folioReader.readerCenter?.shareHighlight(textToShare, rect: sender.menuFrame)
+                }
+            } else {
+                if let textToShare = self.js("getSelectedText()") {
+                    self.folioReader.readerCenter?.shareHighlight(textToShare, rect: sender.menuFrame)
+                }
+            }
+            self.setMenuVisible(false)
+        }
+
+        let cancel = UIAlertAction(title: self.readerConfig.localizedCancel, style: .cancel, handler: nil)
+
+        alertController.addAction(shareImage)
+        alertController.addAction(shareText)
+        alertController.addAction(cancel)
+
+        if let alert = alertController.popoverPresentationController {
+            alert.sourceView = self.folioReader.readerCenter?.currentPage
+            alert.sourceRect = sender.menuFrame
+        }
+
+        self.folioReader.readerCenter?.present(alertController, animated: true, completion: nil)
+    }
+
+    func colors(_ sender: UIMenuController?) {
+        isColors = true
+        createMenu(options: false)
+        setMenuVisible(true)
+    }
+
+    func remove(_ sender: UIMenuController?) {
+        if let removedId = js("removeThisHighlight()") {
+            Highlight.removeById(withConfiguration: self.readerConfig, highlightId: removedId)
+        }
+        setMenuVisible(false)
+    }
+
+    @objc func highlight(_ sender: UIMenuController?) {
+        let highlightAndReturn = js("highlightString('\(HighlightStyle.classForStyle(self.folioReader.currentHighlightStyle))')")
+        let jsonData = highlightAndReturn?.data(using: String.Encoding.utf8)
+
+        do {
+            let json = try JSONSerialization.jsonObject(with: jsonData!, options: []) as! NSArray
+            let dic = json.firstObject as! [String: String]
+            let rect = CGRectFromString(dic["rect"]!)
+            guard let startOffset = dic["startOffset"] else {
+                return
+            }
+            guard let endOffset = dic["endOffset"] else {
+                return
+            }
+
+            createMenu(options: true)
+            setMenuVisible(true, andRect: rect)
+
+            // Persist
+            guard
+                let html = js("getHTML()"),
+                let identifier = dic["id"],
+                let bookId = (self.book.name as NSString?)?.deletingPathExtension else {
+                    return
+            }
+
+            let pageNumber = folioReader.readerCenter?.currentPageNumber ?? 0
+            let match = Highlight.MatchingHighlight(text: html, id: identifier, startOffset: startOffset, endOffset: endOffset, bookId: bookId, currentPage: pageNumber)
+            let highlight = Highlight.matchHighlight(match)
+            highlight?.persist(withConfiguration: self.readerConfig)
+
+        } catch {
+            print("Could not receive JSON")
+        }
+    }
+
+    @objc func define(_ sender: UIMenuController?) {
+        guard let selectedText = js("getSelectedText()") else {
+            return
+        }
+
+        self.setMenuVisible(false)
+        self.clearTextSelection()
+
+        let vc = UIReferenceLibraryViewController(term: selectedText)
+        vc.view.tintColor = self.readerConfig.tintColor
+        guard let readerContainer = readerContainer else { return }
+        readerContainer.show(vc, sender: nil)
+    }
+
+    @objc func play(_ sender: UIMenuController?) {
+        self.folioReader.readerAudioPlayer?.play()
+
+        self.clearTextSelection()
+    }
+
+    func setYellow(_ sender: UIMenuController?) {
+        changeHighlightStyle(sender, style: .yellow)
+    }
+
+    func setGreen(_ sender: UIMenuController?) {
+        changeHighlightStyle(sender, style: .green)
+    }
+
+    func setBlue(_ sender: UIMenuController?) {
+        changeHighlightStyle(sender, style: .blue)
+    }
+
+    func setPink(_ sender: UIMenuController?) {
+        changeHighlightStyle(sender, style: .pink)
+    }
+
+    func setUnderline(_ sender: UIMenuController?) {
+        changeHighlightStyle(sender, style: .underline)
+    }
+
+    func changeHighlightStyle(_ sender: UIMenuController?, style: HighlightStyle) {
+        self.folioReader.currentHighlightStyle = style.rawValue
+
+        if let updateId = js("setHighlightStyle('\(HighlightStyle.classForStyle(style.rawValue))')") {
+            Highlight.updateById(withConfiguration: self.readerConfig, highlightId: updateId, type: style)
+        }
+    }
+
+    // MARK: - Create and show menu
+
+    func createMenu(options: Bool) {
+        guard (self.readerConfig.useReaderMenuController == true) else {
+            return
+        }
+
+        isShare = options
+
+        let colors = UIImage(readerImageNamed: "colors-marker")
+        let share = UIImage(readerImageNamed: "share-marker")
+        let remove = UIImage(readerImageNamed: "no-marker")
+        let yellow = UIImage(readerImageNamed: "yellow-marker")
+        let green = UIImage(readerImageNamed: "green-marker")
+        let blue = UIImage(readerImageNamed: "blue-marker")
+        let pink = UIImage(readerImageNamed: "pink-marker")
+        let underline = UIImage(readerImageNamed: "underline-marker")
+
+        let menuController = UIMenuController.shared
+
+        let highlightItem = UIMenuItem(title: self.readerConfig.localizedHighlightMenu, action: #selector(highlight(_:)))
+        let playAudioItem = UIMenuItem(title: self.readerConfig.localizedPlayMenu, action: #selector(play(_:)))
+        let defineItem = UIMenuItem(title: self.readerConfig.localizedDefineMenu, action: #selector(define(_:)))
+        let colorsItem = UIMenuItem(title: "C", image: colors) { [weak self] _ in
+            self?.colors(menuController)
+        }
+        let shareItem = UIMenuItem(title: "S", image: share) { [weak self] _ in
+            self?.share(menuController)
+        }
+        let removeItem = UIMenuItem(title: "R", image: remove) { [weak self] _ in
+            self?.remove(menuController)
+        }
+        let yellowItem = UIMenuItem(title: "Y", image: yellow) { [weak self] _ in
+            self?.setYellow(menuController)
+        }
+        let greenItem = UIMenuItem(title: "G", image: green) { [weak self] _ in
+            self?.setGreen(menuController)
+        }
+        let blueItem = UIMenuItem(title: "B", image: blue) { [weak self] _ in
+            self?.setBlue(menuController)
+        }
+        let pinkItem = UIMenuItem(title: "P", image: pink) { [weak self] _ in
+            self?.setPink(menuController)
+        }
+        let underlineItem = UIMenuItem(title: "U", image: underline) { [weak self] _ in
+            self?.setUnderline(menuController)
+        }
+
+        var menuItems = [shareItem]
+
+        // menu on existing highlight
+        if isShare {
+            menuItems = [colorsItem, removeItem]
+            if (self.readerConfig.allowSharing == true) {
+                menuItems.append(shareItem)
+            }
+        } else if isColors {
+            // menu for selecting highlight color
+            menuItems = [yellowItem, greenItem, blueItem, pinkItem, underlineItem]
+        } else {
+            // default menu
+            // PD: changed
+            menuItems = [highlightItem/*, defineItem*/, shareItem]
+
+            if self.book.hasAudio || self.readerConfig.enableTTS {
+                menuItems.insert(playAudioItem, at: 0)
+            }
+
+            if (self.readerConfig.allowSharing == false) {
+                menuItems.removeLast()
+            }
+        }
+        
+        menuController.menuItems = menuItems
+    }
+    
+    open func setMenuVisible(_ menuVisible: Bool, animated: Bool = true, andRect rect: CGRect = CGRect.zero) {
+        if !menuVisible && isShare || !menuVisible && isColors {
+            isColors = false
+            isShare = false
+        }
+        
+        if menuVisible  {
+            if !rect.equalTo(CGRect.zero) {
+                UIMenuController.shared.setTargetRect(rect, in: self)
+            }
+        }
+        
+        UIMenuController.shared.setMenuVisible(menuVisible, animated: animated)
+    }
+    
+    // MARK: - Java Script Bridge
+    
+    @discardableResult open func js(_ script: String) -> String? {
+        let callback = self.stringByEvaluatingJavaScript(from: script)
+        if callback!.isEmpty { return nil }
+        return callback
+    }
+    
+    // MARK: WebView
+    
+    func clearTextSelection() {
+        // Forces text selection clearing
+        // @NOTE: this doesn't seem to always work
+        
+        self.isUserInteractionEnabled = false
+        self.isUserInteractionEnabled = true
+    }
+    
+    func setupScrollDirection() {
+        switch self.readerConfig.scrollDirection {
+        case .vertical, .defaultVertical, .horizontalWithVerticalContent:
+            scrollView.isPagingEnabled = false
+            paginationMode = .unpaginated
+            scrollView.bounces = true
+            break
+        case .horizontal:
+            scrollView.isPagingEnabled = true
+            paginationMode = .leftToRight
+            paginationBreakingMode = .page
+            scrollView.bounces = false
+            break
+        }
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/Models/Highlight+Helper.swift b/iOS/Pods/FolioReaderKit/Source/Models/Highlight+Helper.swift
new file mode 100644 (file)
index 0000000..30a4198
--- /dev/null
@@ -0,0 +1,313 @@
+//
+//  Highlight+Helper.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 06/07/16.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import Foundation
+import RealmSwift
+
+/**
+ HighlightStyle type, default is .Yellow.
+ */
+public enum HighlightStyle: Int {
+    case yellow
+    case green
+    case blue
+    case pink
+    case underline
+
+    public init () {
+        // Default style is `.yellow`
+        self = .yellow
+    }
+
+    /**
+     Return HighlightStyle for CSS class.
+     */
+    public static func styleForClass(_ className: String) -> HighlightStyle {
+        switch className {
+        case "highlight-yellow":    return .yellow
+        case "highlight-green":     return .green
+        case "highlight-blue":      return .blue
+        case "highlight-pink":      return .pink
+        case "highlight-underline": return .underline
+        default:                    return .yellow
+        }
+    }
+
+    /**
+     Return CSS class for HighlightStyle.
+     */
+    public static func classForStyle(_ style: Int) -> String {
+
+        let enumStyle = (HighlightStyle(rawValue: style) ?? HighlightStyle())
+        switch enumStyle {
+        case .yellow:       return "highlight-yellow"
+        case .green:        return "highlight-green"
+        case .blue:         return "highlight-blue"
+        case .pink:         return "highlight-pink"
+        case .underline:    return "highlight-underline"
+        }
+    }
+
+    /// Color components for the style
+    ///
+    /// - Returns: Tuple of all color compnonents.
+    private func colorComponents() -> (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
+        switch self {
+        case .yellow:       return (red: 255, green: 235, blue: 107, alpha: 0.9)
+        case .green:        return (red: 192, green: 237, blue: 114, alpha: 0.9)
+        case .blue:         return (red: 173, green: 216, blue: 255, alpha: 0.9)
+        case .pink:         return (red: 255, green: 176, blue: 202, alpha: 0.9)
+        case .underline:    return (red: 240, green: 40, blue: 20, alpha: 0.6)
+        }
+    }
+
+    /**
+     Return CSS class for HighlightStyle.
+     */
+    public static func colorForStyle(_ style: Int, nightMode: Bool = false) -> UIColor {
+        let enumStyle = (HighlightStyle(rawValue: style) ?? HighlightStyle())
+        let colors = enumStyle.colorComponents()
+        return UIColor(red: colors.red/255, green: colors.green/255, blue: colors.blue/255, alpha: (nightMode ? colors.alpha : 1))
+    }
+}
+
+/// Completion block
+public typealias Completion = (_ error: NSError?) -> ()
+
+extension Highlight {
+
+    /// Save a Highlight with completion block
+    ///
+    /// - Parameters:
+    ///   - readerConfig: Current folio reader configuration.
+    ///   - completion: Completion block.
+    public func persist(withConfiguration readerConfig: FolioReaderConfig, completion: Completion? = nil) {
+        do {
+            let realm = try Realm(configuration: readerConfig.realmConfiguration)
+            realm.beginWrite()
+            realm.add(self, update: true)
+            try realm.commitWrite()
+            completion?(nil)
+        } catch let error as NSError {
+            print("Error on persist highlight: \(error)")
+            completion?(error)
+        }
+    }
+
+    /// Remove a Highlight
+    ///
+    /// - Parameter readerConfig: Current folio reader configuration.
+    public func remove(withConfiguration readerConfig: FolioReaderConfig) {
+        do {
+            guard let realm = try? Realm(configuration: readerConfig.realmConfiguration) else {
+                return
+            }
+            try realm.write {
+                realm.delete(self)
+                try realm.commitWrite()
+            }
+        } catch let error as NSError {
+            print("Error on remove highlight: \(error)")
+        }
+    }
+
+    /// Remove a Highlight by ID
+    ///
+    /// - Parameters:
+    ///   - readerConfig: Current folio reader configuration.
+    ///   - highlightId: The ID to be removed
+    public static func removeById(withConfiguration readerConfig: FolioReaderConfig, highlightId: String) {
+        var highlight: Highlight?
+        let predicate = NSPredicate(format:"highlightId = %@", highlightId)
+
+        do {
+            let realm = try Realm(configuration: readerConfig.realmConfiguration)
+            highlight = realm.objects(Highlight.self).filter(predicate).toArray(Highlight.self).first
+            highlight?.remove(withConfiguration: readerConfig)
+        } catch let error as NSError {
+            print("Error on remove highlight by id: \(error)")
+        }
+    }
+
+    /// Update a Highlight by ID
+    ///
+    /// - Parameters:
+    ///   - readerConfig: Current folio reader configuration.
+    ///   - highlightId: The ID to be removed
+    ///   - type: The `HighlightStyle`
+    public static func updateById(withConfiguration readerConfig: FolioReaderConfig, highlightId: String, type: HighlightStyle) {
+        var highlight: Highlight?
+        let predicate = NSPredicate(format:"highlightId = %@", highlightId)
+        do {
+            let realm = try Realm(configuration: readerConfig.realmConfiguration)
+            highlight = realm.objects(Highlight.self).filter(predicate).toArray(Highlight.self).first
+            realm.beginWrite()
+
+            highlight?.type = type.hashValue
+
+            try realm.commitWrite()
+            
+        } catch let error as NSError {
+            print("Error on updateById: \(error)")
+        }
+
+    }
+
+    /// Return a list of Highlights with a given ID
+    ///
+    /// - Parameters:
+    ///   - readerConfig: Current folio reader configuration.
+    ///   - bookId: Book ID
+    ///   - page: Page number
+    /// - Returns: Return a list of Highlights
+    public static func allByBookId(withConfiguration readerConfig: FolioReaderConfig, bookId: String, andPage page: NSNumber? = nil) -> [Highlight] {
+        var highlights: [Highlight]?
+        var predicate = NSPredicate(format: "bookId = %@", bookId)
+        if let page = page {
+            predicate = NSPredicate(format: "bookId = %@ && page = %@", bookId, page)
+        }
+
+        do {
+            let realm = try Realm(configuration: readerConfig.realmConfiguration)
+            highlights = realm.objects(Highlight.self).filter(predicate).toArray(Highlight.self)
+            return (highlights ?? [])
+        } catch let error as NSError {
+            print("Error on fetch all by book Id: \(error)")
+            return []
+        }
+    }
+
+    /// Return all Highlights
+    ///
+    /// - Parameter readerConfig: - readerConfig: Current folio reader configuration.
+    /// - Returns: Return all Highlights
+    public static func all(withConfiguration readerConfig: FolioReaderConfig) -> [Highlight] {
+        var highlights: [Highlight]?
+        do {
+            let realm = try Realm(configuration: readerConfig.realmConfiguration)
+            highlights = realm.objects(Highlight.self).toArray(Highlight.self)
+            return (highlights ?? [])
+        } catch let error as NSError {
+            print("Error on fetch all: \(error)")
+            return []
+        }
+    }
+}
+
+// MARK: - HTML Methods
+
+extension Highlight {
+
+    public struct MatchingHighlight {
+        var text: String
+        var id: String
+        var startOffset: String
+        var endOffset: String
+        var bookId: String
+        var currentPage: Int
+    }
+
+    /**
+     Match a highlight on string.
+     */
+    public static func matchHighlight(_ matchingHighlight: MatchingHighlight) -> Highlight? {
+        let pattern = "<highlight id=\"\(matchingHighlight.id)\" onclick=\".*?\" class=\"(.*?)\">((.|\\s)*?)</highlight>"
+        let regex = try? NSRegularExpression(pattern: pattern, options: [])
+        let matches = regex?.matches(in: matchingHighlight.text, options: [], range: NSRange(location: 0, length: matchingHighlight.text.utf16.count))
+        let str = (matchingHighlight.text as NSString)
+
+        let mapped = matches?.map { (match) -> Highlight in
+            var contentPre = str.substring(with: NSRange(location: match.range.location-kHighlightRange, length: kHighlightRange))
+            var contentPost = str.substring(with: NSRange(location: match.range.location + match.range.length, length: kHighlightRange))
+
+            // Normalize string before save
+            contentPre = Highlight.subString(ofContent: contentPre, fromRangeOfString: ">", withPattern: "((?=[^>]*$)(.|\\s)*$)")
+            contentPost = Highlight.subString(ofContent: contentPost, fromRangeOfString: "<", withPattern: "^((.|\\s)*?)(?=<)")
+
+            let highlight = Highlight()
+            highlight.highlightId = matchingHighlight.id
+            highlight.type = HighlightStyle.styleForClass(str.substring(with: match.range(at: 1))).rawValue
+            highlight.date = Date()
+            highlight.content = Highlight.removeSentenceSpam(str.substring(with: match.range(at: 2)))
+            highlight.contentPre = Highlight.removeSentenceSpam(contentPre)
+            highlight.contentPost = Highlight.removeSentenceSpam(contentPost)
+            highlight.page = matchingHighlight.currentPage
+            highlight.bookId = matchingHighlight.bookId
+            highlight.startOffset = (Int(matchingHighlight.startOffset) ?? -1)
+            highlight.endOffset = (Int(matchingHighlight.endOffset) ?? -1)
+
+            return highlight
+        }
+
+        return mapped?.first
+    }
+
+    private static func subString(ofContent content: String, fromRangeOfString rangeString: String, withPattern pattern: String) -> String {
+        var updatedContent = content
+        if updatedContent.range(of: rangeString) != nil {
+            let regex = try? NSRegularExpression(pattern: pattern, options: [])
+            let searchString = regex?.firstMatch(in: updatedContent, options: .reportProgress, range: NSRange(location: 0, length: updatedContent.count))
+
+            if let string = searchString, (string.range.location != NSNotFound) {
+                updatedContent = (updatedContent as NSString).substring(with: string.range)
+            }
+        }
+
+        return updatedContent
+    }
+
+    /// Remove a Highlight from HTML by ID
+    ///
+    /// - Parameters:
+    ///   - page: The page containing the HTML.
+    ///   - highlightId: The ID to be removed
+    /// - Returns: The removed id
+    @discardableResult public static func removeFromHTMLById(withinPage page: FolioReaderPage?, highlightId: String) -> String? {
+        guard let currentPage = page else { return nil }
+        
+        if let removedId = currentPage.webView?.js("removeHighlightById('\(highlightId)')") {
+            return removedId
+        } else {
+            print("Error removing Highlight from page")
+            return nil
+        }
+    }
+    
+    /**
+     Remove span tag before store the highlight, this span is added on JavaScript.
+     <span class=\"sentence\"></span>
+     
+     - parameter text: Text to analise
+     - returns: Striped text
+     */
+    public static func removeSentenceSpam(_ text: String) -> String {
+        
+        // Remove from text
+        func removeFrom(_ text: String, withPattern pattern: String) -> String {
+            var locator = text
+            let regex = try? NSRegularExpression(pattern: pattern, options: [])
+            let matches = regex?.matches(in: locator, options: [], range: NSRange(location: 0, length: locator.utf16.count))
+            let str = (locator as NSString)
+            
+            var newLocator = ""
+            matches?.forEach({ (match: NSTextCheckingResult) in
+                newLocator += str.substring(with: match.range(at: 1))
+            })
+            
+            if (matches?.count > 0 && newLocator.isEmpty == false) {
+                locator = newLocator
+            }
+            
+            return locator
+        }
+        
+        let pattern = "<span class=\"sentence\">((.|\\s)*?)</span>"
+        let cleanText = removeFrom(text, withPattern: pattern)
+        return cleanText
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/Models/Highlight.swift b/iOS/Pods/FolioReaderKit/Source/Models/Highlight.swift
new file mode 100644 (file)
index 0000000..925fe61
--- /dev/null
@@ -0,0 +1,34 @@
+//
+//  Highlight.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 11/08/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import Foundation
+import RealmSwift
+
+/// A Highlight object
+open class Highlight: Object {
+    @objc open dynamic var bookId: String!
+    @objc open dynamic var content: String!
+    @objc open dynamic var contentPost: String!
+    @objc open dynamic var contentPre: String!
+    @objc open dynamic var date: Date!
+    @objc open dynamic var highlightId: String!
+    @objc open dynamic var page: Int = 0
+    @objc open dynamic var type: Int = 0
+    @objc open dynamic var startOffset: Int = -1
+    @objc open dynamic var endOffset: Int = -1
+
+    override open class func primaryKey()-> String {
+        return "highlightId"
+    }
+}
+
+extension Results {
+    func toArray<T>(_ ofType: T.Type) -> [T] {
+        return flatMap { $0 as? T }
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/PageViewController.swift b/iOS/Pods/FolioReaderKit/Source/PageViewController.swift
new file mode 100644 (file)
index 0000000..97e34cd
--- /dev/null
@@ -0,0 +1,147 @@
+//
+//  PageViewController.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 14/07/16.
+//  Copyright © 2016 FolioReader. All rights reserved.
+//
+
+import UIKit
+
+class PageViewController: UIPageViewController {
+
+    var segmentedControl: UISegmentedControl!
+    var viewList = [UIViewController]()
+    var segmentedControlItems = [String]()
+    var viewControllerOne: UIViewController!
+    var viewControllerTwo: UIViewController!
+    var index: Int
+    fileprivate var readerConfig: FolioReaderConfig
+    fileprivate var folioReader: FolioReader
+
+    // MARK: Init
+
+    init(folioReader: FolioReader, readerConfig: FolioReaderConfig) {
+        self.folioReader = folioReader
+        self.readerConfig = readerConfig
+        self.index = self.folioReader.currentMenuIndex
+        super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
+
+        self.edgesForExtendedLayout = UIRectEdge()
+        self.extendedLayoutIncludesOpaqueBars = true
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("storyboards are incompatible with truth and beauty")
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        segmentedControl = UISegmentedControl(items: segmentedControlItems)
+        segmentedControl.addTarget(self, action: #selector(PageViewController.didSwitchMenu(_:)), for: UIControlEvents.valueChanged)
+        segmentedControl.selectedSegmentIndex = index
+        segmentedControl.setWidth(100, forSegmentAt: 0)
+        segmentedControl.setWidth(100, forSegmentAt: 1)
+        self.navigationItem.titleView = segmentedControl
+
+        viewList = [viewControllerOne, viewControllerTwo]
+
+        viewControllerOne.didMove(toParentViewController: self)
+        viewControllerTwo.didMove(toParentViewController: self)
+
+        self.delegate = self
+        self.dataSource = self
+
+        self.view.backgroundColor = UIColor.white
+        self.setViewControllers([viewList[index]], direction: .forward, animated: false, completion: nil)
+
+        // FIXME: This disable scroll because of highlight swipe to delete, if you can fix this would be awesome
+        for view in self.view.subviews {
+            if view is UIScrollView {
+                let scroll = view as! UIScrollView
+                scroll.bounces = false
+            }
+        }
+        
+        //PD: changed
+        let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
+        let tintColor = folioReader.isNight(greenColor, UIColor.white)
+
+        let closeImage = UIImage(readerImageNamed: "icon-navbar-close")?.imageTintColor(tintColor)?.withRenderingMode(.alwaysOriginal)// ignoreSystemTint(withConfiguration: readerConfig)
+        self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: closeImage, style: .plain, target: self, action: #selector(dismiss as () -> Void))
+    }
+
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        configureNavBar()
+    }
+
+    //PD: changed
+    func configureNavBar() {
+        
+        let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
+        let navBackground = folioReader.isNight(self.readerConfig.nightModeMenuBackground, greenColor)
+        let tintColor = folioReader.isNight(greenColor, UIColor.white)
+        let navText = tintColor
+        let font = UIFont(name: "Avenir-Light", size: 17)!
+        setTranslucentNavigation(color: navBackground, tintColor: tintColor, titleColor: navText, andFont: font)
+    }
+    
+    // MARK: - Segmented control changes
+
+    @objc func didSwitchMenu(_ sender: UISegmentedControl) {
+        self.index = sender.selectedSegmentIndex
+        let direction: UIPageViewControllerNavigationDirection = (index == 0 ? .reverse : .forward)
+        setViewControllers([viewList[index]], direction: direction, animated: true, completion: nil)
+        self.folioReader.currentMenuIndex = index
+    }
+
+    // MARK: - Status Bar
+
+    //PD: changed
+    override var preferredStatusBarStyle : UIStatusBarStyle {
+        return .lightContent //self.folioReader.isNight(.lightContent, .default)
+    }
+}
+
+// MARK: UIPageViewControllerDelegate
+
+extension PageViewController: UIPageViewControllerDelegate {
+
+    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
+
+        if finished && completed {
+            let viewController = pageViewController.viewControllers?.last
+            segmentedControl.selectedSegmentIndex = viewList.index(of: viewController!)!
+        }
+    }
+}
+
+// MARK: UIPageViewControllerDataSource
+
+extension PageViewController: UIPageViewControllerDataSource {
+
+    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
+
+        let index = viewList.index(of: viewController)!
+        if index == viewList.count - 1 {
+            return nil
+        }
+
+        self.index = self.index + 1
+        return viewList[self.index]
+    }
+
+    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
+
+        let index = viewList.index(of: viewController)!
+        if index == 0 {
+            return nil
+        }
+
+        self.index = self.index - 1
+        return viewList[self.index]
+    }
+}
+
diff --git a/iOS/Pods/FolioReaderKit/Source/QuoteImage.swift b/iOS/Pods/FolioReaderKit/Source/QuoteImage.swift
new file mode 100644 (file)
index 0000000..7693c3e
--- /dev/null
@@ -0,0 +1,72 @@
+//
+//  QuoteImage.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 8/31/16.
+//  Copyright (c) 2016 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+/**
+ Defines a custom Quote image, can be a square `UIImage`, solid `UIColor` or `CAGradientLayer`.
+ */
+public struct QuoteImage {
+    public var image: UIImage!
+    public var alpha: CGFloat!
+    public var textColor: UIColor!
+    public var backgroundColor: UIColor!
+    
+    /**
+     Quote image from `UIImage`
+     
+     - parameter image:           An `UIImage` to be used as background.
+     - parameter alpha:           The image opacity. Defaults to 1.
+     - parameter textColor:       The color of quote text and custom logo. Defaults to white.
+     - parameter backgroundColor: The filter background color, if the image has a opacity this will appear. Defaults to white.
+     
+     - returns: A newly initialized `QuoteImage` object.
+     */
+    public init(withImage image: UIImage, alpha: CGFloat = 1, textColor: UIColor = UIColor.white, backgroundColor: UIColor = UIColor.white) {
+        self.image = image
+        self.alpha = alpha
+        self.textColor = textColor
+        self.backgroundColor = backgroundColor
+    }
+    
+    /**
+     Quote image from `CAGradientLayer`
+     
+     - parameter gradient:        A custom `CAGradientLayer` to make a gradient background.
+     - parameter alpha:           The image opacity. Defaults to 1.
+     - parameter textColor:       The color of quote text and custom logo. Defaults to white.
+     - parameter backgroundColor: The filter background color, if the image has a opacity this will appear. Defaults to white.
+     
+     - returns: A newly initialized `QuoteImage` object.
+     */
+    public init(withGradient gradient: CAGradientLayer, alpha: CGFloat = 1, textColor: UIColor = UIColor.white, backgroundColor: UIColor = UIColor.white) {
+        let screenBounds = UIScreen.main.bounds
+        gradient.frame = CGRect(x: 0, y: 0, width: screenBounds.width, height: screenBounds.width)
+        self.image = UIImage.imageWithLayer(gradient)
+        self.alpha = alpha
+        self.textColor = textColor
+        self.backgroundColor = backgroundColor
+    }
+    
+    /**
+     Quote image from `UIColor`
+     
+     - parameter color:           A custom `UIColor`
+     - parameter alpha:           The image opacity. Defaults to 1.
+     - parameter textColor:       The color of quote text and custom logo. Defaults to white.
+     - parameter backgroundColor: The filter background color, if the image has a opacity this will appear. Defaults to white.
+     
+     - returns: A newly initialized `QuoteImage` object.
+     */
+    public init(withColor color: UIColor, alpha: CGFloat = 1, textColor: UIColor = UIColor.white, backgroundColor: UIColor = UIColor.white) {
+        self.image = UIImage.imageWithColor(color)
+        self.alpha = alpha
+        self.textColor = textColor
+        self.backgroundColor = backgroundColor
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Bridge.js b/iOS/Pods/FolioReaderKit/Source/Resources/Bridge.js
new file mode 100755 (executable)
index 0000000..41baa45
--- /dev/null
@@ -0,0 +1,639 @@
+//
+//  Bridge.js
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 06/05/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+var thisHighlight;
+var audioMarkClass;
+var wordsPerMinute = 180;
+
+document.addEventListener("DOMContentLoaded", function(event) {
+//    var lnk = document.getElementsByClassName("lnk");
+//    for (var i=0; i<lnk.length; i++) {
+//        lnk[i].setAttribute("onclick","return callVerseURL(this);");
+//    }
+});
+
+// Generate a GUID
+function guid() {
+    function s4() {
+        return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
+    }
+    var guid = s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
+    return guid.toUpperCase();
+}
+
+// Get All HTML
+function getHTML() {
+    return document.documentElement.outerHTML;
+}
+
+// Class manipulation
+function hasClass(ele,cls) {
+  return !!ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
+}
+
+function addClass(ele,cls) {
+  if (!hasClass(ele,cls)) ele.className += " "+cls;
+}
+
+function removeClass(ele,cls) {
+  if (hasClass(ele,cls)) {
+    var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
+    ele.className=ele.className.replace(reg,' ');
+  }
+}
+
+// Font name class
+function setFontName(cls) {
+    var elm = document.documentElement;
+    removeClass(elm, "andada");
+    removeClass(elm, "lato");
+    removeClass(elm, "lora");
+    removeClass(elm, "raleway");
+    addClass(elm, cls);
+}
+
+// Toggle night mode
+function nightMode(enable) {
+    var elm = document.documentElement;
+    if(enable) {
+        addClass(elm, "nightMode");
+    } else {
+        removeClass(elm, "nightMode");
+    }
+}
+
+// Set font size
+function setFontSize(cls) {
+    var elm = document.documentElement;
+    removeClass(elm, "textSizeOne");
+    removeClass(elm, "textSizeTwo");
+    removeClass(elm, "textSizeThree");
+    removeClass(elm, "textSizeFour");
+    removeClass(elm, "textSizeFive");
+    addClass(elm, cls);
+}
+
+// Set font size
+function setMarginSize(cls) {
+    var elm = document.documentElement;
+    removeClass(elm, "marginSizeOne");
+    removeClass(elm, "marginSizeTwo");
+    removeClass(elm, "marginSizeThree");
+    removeClass(elm, "marginSizeFour");
+    removeClass(elm, "marginSizeFive");
+    addClass(elm, cls);
+}
+
+// Set font size
+function setInterlineSize(cls) {
+    var elm = document.documentElement;
+    removeClass(elm, "interlineSizeOne");
+    removeClass(elm, "interlineSizeTwo");
+    removeClass(elm, "interlineSizeThree");
+    removeClass(elm, "interlineSizeFour");
+    removeClass(elm, "interlineSizeFive");
+    addClass(elm, cls);
+}
+
+/*
+ *     Native bridge Highlight text
+ */
+function highlightString(style) {
+    var range = window.getSelection().getRangeAt(0);
+    var startOffset = range.startOffset;
+    var endOffset = range.endOffset;
+    var selectionContents = range.extractContents();
+    var elm = document.createElement("highlight");
+    var id = guid();
+    
+    elm.appendChild(selectionContents);
+    elm.setAttribute("id", id);
+    elm.setAttribute("onclick","callHighlightURL(this);");
+    elm.setAttribute("class", style);
+    
+    range.insertNode(elm);
+    thisHighlight = elm;
+    
+    var params = [];
+    params.push({id: id, rect: getRectForSelectedText(elm), startOffset: startOffset.toString(), endOffset: endOffset.toString()});
+    
+    return JSON.stringify(params);
+}
+
+// Menu colors
+function setHighlightStyle(style) {
+    thisHighlight.className = style;
+    return thisHighlight.id;
+}
+
+function removeThisHighlight() {
+    thisHighlight.outerHTML = thisHighlight.innerHTML;
+    return thisHighlight.id;
+}
+
+function removeHighlightById(elmId) {
+    var elm = document.getElementById(elmId);
+    elm.outerHTML = elm.innerHTML;
+    return elm.id;
+}
+
+function getHighlightContent() {
+    return thisHighlight.textContent
+}
+
+function getBodyText() {
+    return document.body.innerText;
+}
+
+// Method that returns only selected text plain
+var getSelectedText = function() {
+    return window.getSelection().toString();
+}
+
+// Method that gets the Rect of current selected text
+// and returns in a JSON format
+var getRectForSelectedText = function(elm) {
+    if (typeof elm === "undefined") elm = window.getSelection().getRangeAt(0);
+    
+    var rect = elm.getBoundingClientRect();
+    return "{{" + rect.left + "," + rect.top + "}, {" + rect.width + "," + rect.height + "}}";
+}
+
+// Method that call that a hightlight was clicked
+// with URL scheme and rect informations
+var callHighlightURL = function(elm) {
+       event.stopPropagation();
+       var URLBase = "highlight://";
+    var currentHighlightRect = getRectForSelectedText(elm);
+    thisHighlight = elm;
+    
+    window.location = URLBase + encodeURIComponent(currentHighlightRect);
+}
+
+// Reading time
+function getReadingTime() {
+    var text = document.body.innerText;
+    var totalWords = text.trim().split(/\s+/g).length;
+    var wordsPerSecond = wordsPerMinute / 60; //define words per second based on words per minute
+    var totalReadingTimeSeconds = totalWords / wordsPerSecond; //define total reading time in seconds
+    var readingTimeMinutes = Math.round(totalReadingTimeSeconds / 60);
+
+    return readingTimeMinutes;
+}
+
+/**
+ Get Vertical or Horizontal paged #anchor offset
+ */
+var getAnchorOffset = function(target, horizontal) {
+    var elem = document.getElementById(target);
+    
+    if (!elem) {
+        elem = document.getElementsByName(target)[0];
+    }
+    
+    if (horizontal) {
+        return window.innerWidth * Math.floor(elem.offsetTop / window.innerHeight);
+    }
+    
+    return elem.offsetTop;
+}
+
+function findElementWithID(node) {
+    if( !node || node.tagName == "BODY")
+        return null
+    else if( node.id )
+        return node
+    else
+        return findElementWithID(node)
+}
+
+function findElementWithIDInView() {
+
+    if(audioMarkClass) {
+        // attempt to find an existing "audio mark"
+        var el = document.querySelector("."+audioMarkClass)
+
+        // if that existing audio mark exists and is in view, use it
+        if( el && el.offsetTop > document.body.scrollTop && el.offsetTop < (window.innerHeight + document.body.scrollTop))
+            return el
+    }
+
+    // @NOTE: is `span` too limiting?
+    var els = document.querySelectorAll("span[id]")
+
+    for(indx in els) {
+        var element = els[indx];
+
+        // Horizontal scroll
+        if (document.body.scrollTop == 0) {
+            var elLeft = window.innerWidth * Math.floor(element.offsetTop / window.innerHeight);
+            // document.body.scrollLeft = elLeft;
+
+            if (elLeft == document.body.scrollLeft) {
+                return element;
+            }
+
+        // Vertical
+        } else if(element.offsetTop > document.body.scrollTop) {
+            return element;
+        }
+    }
+
+    return null
+}
+
+
+/**
+ Play Audio - called by native UIMenuController when a user selects a bit of text and presses "play"
+ */
+function playAudio() {
+    var sel = getSelection();
+    var node = null;
+
+    // user selected text? start playing from the selected node
+    if (sel.toString() != "") {
+        node = sel.anchorNode ? findElementWithID(sel.anchorNode.parentNode) : null;
+
+    // find the first ID'd element that is within view (it will
+    } else {
+        node = findElementWithIDInView()
+    }
+
+    playAudioFragmentID(node ? node.id : null)
+}
+
+
+/**
+ Play Audio Fragment ID - tells page controller to begin playing audio from the following ID
+ */
+function playAudioFragmentID(fragmentID) {
+    var URLBase = "play-audio://";
+    window.location = URLBase + (fragmentID?encodeURIComponent(fragmentID):"")
+}
+
+/**
+ Go To Element - scrolls the webview to the requested element
+ */
+function goToEl(el) {
+    var top = document.body.scrollTop;
+    var elTop = el.offsetTop - 20;
+    var bottom = window.innerHeight + document.body.scrollTop;
+    var elBottom = el.offsetHeight + el.offsetTop + 60
+
+    if(elBottom > bottom || elTop < top) {
+        document.body.scrollTop = el.offsetTop - 20
+    }
+    
+    /* Set scroll left in case horz scroll is activated.
+    
+        The following works because el.offsetTop accounts for each page turned
+        as if the document was scrolling vertical. We then divide by the window
+        height to figure out what page the element should appear on and set scroll left
+        to scroll to that page.
+    */
+    if( document.body.scrollTop == 0 ){
+        var elLeft = window.innerWidth * Math.floor(el.offsetTop / window.innerHeight);
+        document.body.scrollLeft = elLeft;
+    }
+
+    return el;
+}
+
+/**
+ Remove All Classes - removes the given class from all elements in the DOM
+ */
+function removeAllClasses(className) {
+    var els = document.body.getElementsByClassName(className)
+    if( els.length > 0 )
+    for( i = 0; i <= els.length; i++) {
+        els[i].classList.remove(className);
+    }
+}
+
+/**
+ Audio Mark ID - marks an element with an ID with the given class and scrolls to it
+ */
+function audioMarkID(className, id) {
+    if (audioMarkClass)
+        removeAllClasses(audioMarkClass);
+
+    audioMarkClass = className
+    var el = document.getElementById(id);
+
+    goToEl(el);
+    el.classList.add(className)
+}
+
+function setMediaOverlayStyle(style){
+    document.documentElement.classList.remove("mediaOverlayStyle0", "mediaOverlayStyle1", "mediaOverlayStyle2")
+    document.documentElement.classList.add(style)
+}
+
+function setMediaOverlayStyleColors(color, colorHighlight) {
+    var stylesheet = document.styleSheets[document.styleSheets.length-1];
+    stylesheet.insertRule(".mediaOverlayStyle0 span.epub-media-overlay-playing { background: "+colorHighlight+" !important }")
+    stylesheet.insertRule(".mediaOverlayStyle1 span.epub-media-overlay-playing { border-color: "+color+" !important }")
+    stylesheet.insertRule(".mediaOverlayStyle2 span.epub-media-overlay-playing { color: "+color+" !important }")
+}
+
+var currentIndex = -1;
+
+
+function findSentenceWithIDInView(els) {
+    // @NOTE: is `span` too limiting?
+    for(indx in els) {
+        var element = els[indx];
+
+        // Horizontal scroll
+        if (document.body.scrollTop == 0) {
+            var elLeft = window.innerWidth * Math.floor(element.offsetTop / window.innerHeight);
+            // document.body.scrollLeft = elLeft;
+
+            if (elLeft == document.body.scrollLeft) {
+                currentIndex = indx;
+                return element;
+            }
+
+        // Vertical
+        } else if(element.offsetTop > document.body.scrollTop) {
+            currentIndex = indx;
+            return element;
+        }
+    }
+    
+    return null
+}
+
+function findNextSentenceInArray(els) {
+    if(currentIndex >= 0) {
+        currentIndex ++;
+        return els[currentIndex];
+    }
+    
+    return null
+}
+
+function resetCurrentSentenceIndex() {
+    currentIndex = -1;
+}
+
+function getSentenceWithIndex(className) {
+    var sentence;
+    var sel = getSelection();
+    var node = null;
+    var elements = document.querySelectorAll("span.sentence");
+
+    // Check for a selected text, if found start reading from it
+    if (sel.toString() != "") {
+        console.log(sel.anchorNode.parentNode);
+        node = sel.anchorNode.parentNode;
+
+        if (node.className == "sentence") {
+            sentence = node
+
+            for(var i = 0, len = elements.length; i < len; i++) {
+                if (elements[i] === sentence) {
+                    currentIndex = i;
+                    break;
+                }
+            }
+        } else {
+            sentence = findSentenceWithIDInView(elements);
+        }
+    } else if (currentIndex < 0) {
+        sentence = findSentenceWithIDInView(elements);
+    } else {
+        sentence = findNextSentenceInArray(elements);
+    }
+
+    var text = sentence.innerText || sentence.textContent;
+    
+    goToEl(sentence);
+    
+    if (audioMarkClass){
+        removeAllClasses(audioMarkClass);
+    }
+    
+    audioMarkClass = className;
+    sentence.classList.add(className)
+    return text;
+}
+
+function wrappingSentencesWithinPTags(){
+    currentIndex = -1;
+    "use strict";
+    
+    var rxOpen = new RegExp("<[^\\/].+?>"),
+    rxClose = new RegExp("<\\/.+?>"),
+    rxSupStart = new RegExp("^<sup\\b[^>]*>"),
+    rxSupEnd = new RegExp("<\/sup>"),
+    sentenceEnd = [],
+    rxIndex;
+    
+    sentenceEnd.push(new RegExp("[^\\d][\\.!\\?]+"));
+    sentenceEnd.push(new RegExp("(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*?$)"));
+    sentenceEnd.push(new RegExp("(?![^\\(]*?\\))"));
+    sentenceEnd.push(new RegExp("(?![^\\[]*?\\])"));
+    sentenceEnd.push(new RegExp("(?![^\\{]*?\\})"));
+    sentenceEnd.push(new RegExp("(?![^\\|]*?\\|)"));
+    sentenceEnd.push(new RegExp("(?![^\\\\]*?\\\\)"));
+    //sentenceEnd.push(new RegExp("(?![^\\/.]*\\/)")); // all could be a problem, but this one is problematic
+    
+    rxIndex = new RegExp(sentenceEnd.reduce(function (previousValue, currentValue) {
+                                            return previousValue + currentValue.source;
+                                            }, ""));
+    
+    function indexSentenceEnd(html) {
+        var index = html.search(rxIndex);
+        
+        if (index !== -1) {
+            index += html.match(rxIndex)[0].length - 1;
+        }
+        
+        return index;
+    }
+
+    function pushSpan(array, className, string, classNameOpt) {
+        if (!string.match('[a-zA-Z0-9]+')) {
+            array.push(string);
+        } else {
+            array.push('<span class="' + className + '">' + string + '</span>');
+        }
+    }
+    
+    function addSupToPrevious(html, array) {
+        var sup = html.search(rxSupStart),
+        end = 0,
+        last;
+        
+        if (sup !== -1) {
+            end = html.search(rxSupEnd);
+            if (end !== -1) {
+                last = array.pop();
+                end = end + 6;
+                array.push(last.slice(0, -7) + html.slice(0, end) + last.slice(-7));
+            }
+        }
+        
+        return html.slice(end);
+    }
+    
+    function paragraphIsSentence(html, array) {
+        var index = indexSentenceEnd(html);
+        
+        if (index === -1 || index === html.length) {
+            pushSpan(array, "sentence", html, "paragraphIsSentence");
+            html = "";
+        }
+        
+        return html;
+    }
+    
+    function paragraphNoMarkup(html, array) {
+        var open = html.search(rxOpen),
+        index = 0;
+        
+        if (open === -1) {
+            index = indexSentenceEnd(html);
+            if (index === -1) {
+                index = html.length;
+            }
+            
+            pushSpan(array, "sentence", html.slice(0, index += 1), "paragraphNoMarkup");
+        }
+        
+        return html.slice(index);
+    }
+    
+    function sentenceUncontained(html, array) {
+        var open = html.search(rxOpen),
+        index = 0,
+        close;
+        
+        if (open !== -1) {
+            index = indexSentenceEnd(html);
+            if (index === -1) {
+                index = html.length;
+            }
+            
+            close = html.search(rxClose);
+            if (index < open || index > close) {
+                pushSpan(array, "sentence", html.slice(0, index += 1), "sentenceUncontained");
+            } else {
+                index = 0;
+            }
+        }
+        
+        return html.slice(index);
+    }
+    
+    function sentenceContained(html, array) {
+        var open = html.search(rxOpen),
+        index = 0,
+        close,
+        count;
+        
+        if (open !== -1) {
+            index = indexSentenceEnd(html);
+            if (index === -1) {
+                index = html.length;
+            }
+            
+            close = html.search(rxClose);
+            if (index > open && index < close) {
+                count = html.match(rxClose)[0].length;
+                pushSpan(array, "sentence", html.slice(0, close + count), "sentenceContained");
+                index = close + count;
+            } else {
+                index = 0;
+            }
+        }
+        
+        return html.slice(index);
+    }
+    
+    function anythingElse(html, array) {
+        pushSpan(array, "sentence", html, "anythingElse");
+        
+        return "";
+    }
+    
+    function guessSenetences() {
+        var paragraphs = document.getElementsByTagName("p");
+
+        Array.prototype.forEach.call(paragraphs, function (paragraph) {
+            var html = paragraph.innerHTML,
+                length = html.length,
+                array = [],
+                safety = 100;
+
+            while (length && safety) {
+                html = addSupToPrevious(html, array);
+                if (html.length === length) {
+                    if (html.length === length) {
+                        html = paragraphIsSentence(html, array);
+                        if (html.length === length) {
+                            html = paragraphNoMarkup(html, array);
+                            if (html.length === length) {
+                                html = sentenceUncontained(html, array);
+                                if (html.length === length) {
+                                    html = sentenceContained(html, array);
+                                    if (html.length === length) {
+                                        html = anythingElse(html, array);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                length = html.length;
+                safety -= 1;
+            }
+
+            paragraph.innerHTML = array.join("");
+        });
+    }
+    
+    guessSenetences();
+}
+
+// Class based onClick listener
+
+function addClassBasedOnClickListener(schemeName, querySelector, attributeName, selectAll) {
+       if (selectAll) {
+               // Get all elements with the given query selector
+               var elements = document.querySelectorAll(querySelector);
+               for (elementIndex = 0; elementIndex < elements.length; elementIndex++) {
+                       var element = elements[elementIndex];
+                       addClassBasedOnClickListenerToElement(element, schemeName, attributeName);
+               }
+       } else {
+               // Get the first element with the given query selector
+               var element = document.querySelector(querySelector);
+               addClassBasedOnClickListenerToElement(element, schemeName, attributeName);
+       }
+}
+
+function addClassBasedOnClickListenerToElement(element, schemeName, attributeName) {
+       // Get the content from the given attribute name
+       var attributeContent = element.getAttribute(attributeName);
+       // Add the on click logic
+       element.setAttribute("onclick", "onClassBasedListenerClick(\"" + schemeName + "\", \"" + encodeURIComponent(attributeContent) + "\");");
+}
+
+var onClassBasedListenerClick = function(schemeName, attributeContent) {
+       // Prevent the browser from performing the default on click behavior
+       event.preventDefault();
+       // Don't pass the click event to other elemtents
+       event.stopPropagation();
+       // Create parameters containing the click position inside the web view.
+       var positionParameterString = "/clientX=" + event.clientX + "&clientY=" + event.clientY;
+       // Set the custom link URL to the event
+       window.location = schemeName + "://" + attributeContent + positionParameterString;
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Bold.otf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Bold.otf
new file mode 100755 (executable)
index 0000000..31c0c7f
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Bold.otf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-BoldItalic.otf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-BoldItalic.otf
new file mode 100755 (executable)
index 0000000..e8bc479
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-BoldItalic.otf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Italic.otf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Italic.otf
new file mode 100755 (executable)
index 0000000..38a558d
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Italic.otf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Regular.otf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Regular.otf
new file mode 100755 (executable)
index 0000000..0532524
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Andada/Andada-Regular.otf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Bold.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Bold.ttf
new file mode 100644 (file)
index 0000000..59c4843
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Bold.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-BoldItalic.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-BoldItalic.ttf
new file mode 100644 (file)
index 0000000..3371a4b
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-BoldItalic.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Italic.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Italic.ttf
new file mode 100644 (file)
index 0000000..9babe6a
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Italic.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Regular.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Regular.ttf
new file mode 100644 (file)
index 0000000..f01f558
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lato/Lato-Regular.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Bold.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Bold.ttf
new file mode 100755 (executable)
index 0000000..e48c426
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Bold.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-BoldItalic.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-BoldItalic.ttf
new file mode 100755 (executable)
index 0000000..19b0322
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-BoldItalic.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Italic.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Italic.ttf
new file mode 100755 (executable)
index 0000000..5956856
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Italic.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Regular.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Regular.ttf
new file mode 100755 (executable)
index 0000000..233ae75
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Lora/Lora-Regular.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Bold.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Bold.ttf
new file mode 100755 (executable)
index 0000000..7aa37f0
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Bold.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-BoldItalic.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-BoldItalic.ttf
new file mode 100755 (executable)
index 0000000..1d1c6dd
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-BoldItalic.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Italic.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Italic.ttf
new file mode 100755 (executable)
index 0000000..e46ac30
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Italic.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Regular.ttf b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Regular.ttf
new file mode 100755 (executable)
index 0000000..c6ec2f0
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Fonts/Raleway/Raleway-Regular.ttf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/Contents.json
new file mode 100644 (file)
index 0000000..da4a164
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/Contents.json
new file mode 100644 (file)
index 0000000..9c59c3c
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "blue-marker.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "blue-marker@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/blue-marker.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/blue-marker.png
new file mode 100644 (file)
index 0000000..f42ec09
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/blue-marker.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/blue-marker@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/blue-marker@2x.png
new file mode 100644 (file)
index 0000000..0a9e0af
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/blue-marker.imageset/blue-marker@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/border-dashed-pattern.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/border-dashed-pattern.imageset/Contents.json
new file mode 100644 (file)
index 0000000..c9fb7f9
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "border-dashed-pattern.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/border-dashed-pattern.imageset/border-dashed-pattern.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/border-dashed-pattern.imageset/border-dashed-pattern.pdf
new file mode 100644 (file)
index 0000000..c235ba0
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/border-dashed-pattern.imageset/border-dashed-pattern.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/Contents.json
new file mode 100644 (file)
index 0000000..34038b5
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "btn-navbar-share.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "btn-navbar-share@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "btn-navbar-share@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share.png
new file mode 100644 (file)
index 0000000..a7f8f28
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share@2x.png
new file mode 100644 (file)
index 0000000..116f29a
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share@3x.png
new file mode 100644 (file)
index 0000000..5712316
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/btn-navbar-share.imageset/btn-navbar-share@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/Contents.json
new file mode 100644 (file)
index 0000000..7aed584
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "colors-marker.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "colors-marker@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "colors-marker@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker.png
new file mode 100644 (file)
index 0000000..409328d
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker@2x.png
new file mode 100644 (file)
index 0000000..ecf58d4
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker@3x.png
new file mode 100644 (file)
index 0000000..562b18d
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/colors-marker.imageset/colors-marker@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/Contents.json
new file mode 100644 (file)
index 0000000..edbfac4
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "green-marker.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "green-marker@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/green-marker.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/green-marker.png
new file mode 100644 (file)
index 0000000..f2b5015
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/green-marker.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/green-marker@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/green-marker@2x.png
new file mode 100644 (file)
index 0000000..2f47b52
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/green-marker.imageset/green-marker@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-camera.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-camera.imageset/Contents.json
new file mode 100644 (file)
index 0000000..d066e92
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-camera.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-camera.imageset/icon-camera.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-camera.imageset/icon-camera.pdf
new file mode 100644 (file)
index 0000000..aed5f1a
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-camera.imageset/icon-camera.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/Contents.json
new file mode 100644 (file)
index 0000000..1fa9665
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-font-big.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon-font-big@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon-font-big@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big.png
new file mode 100644 (file)
index 0000000..0c11a16
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big@2x.png
new file mode 100644 (file)
index 0000000..d630d81
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big@3x.png
new file mode 100644 (file)
index 0000000..9ba10c3
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-big.imageset/icon-font-big@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/Contents.json
new file mode 100644 (file)
index 0000000..c560a11
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-font-small.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon-font-small@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon-font-small@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small.png
new file mode 100644 (file)
index 0000000..f1c6ac4
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small@2x.png
new file mode 100644 (file)
index 0000000..7e54db6
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small@3x.png
new file mode 100644 (file)
index 0000000..7f2f02f
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-font-small.imageset/icon-font-small@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-logo.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-logo.imageset/Contents.json
new file mode 100644 (file)
index 0000000..804a2d5
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-logo.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-logo.imageset/icon-logo.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-logo.imageset/icon-logo.pdf
new file mode 100644 (file)
index 0000000..3e73110
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-logo.imageset/icon-logo.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-horizontal.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-horizontal.imageset/Contents.json
new file mode 100644 (file)
index 0000000..7cd1d2e
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-menu-horizontal.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-horizontal.imageset/icon-menu-horizontal.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-horizontal.imageset/icon-menu-horizontal.pdf
new file mode 100644 (file)
index 0000000..866a0f6
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-horizontal.imageset/icon-menu-horizontal.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-vertical.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-vertical.imageset/Contents.json
new file mode 100644 (file)
index 0000000..4d556bb
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-menu-vertical.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-vertical.imageset/icon-menu-vertical.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-vertical.imageset/icon-menu-vertical.pdf
new file mode 100644 (file)
index 0000000..a4030ee
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-menu-vertical.imageset/icon-menu-vertical.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/Contents.json
new file mode 100644 (file)
index 0000000..c421138
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-moon.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon-moon@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon-moon@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon.png
new file mode 100644 (file)
index 0000000..21cb0ea
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon@2x.png
new file mode 100644 (file)
index 0000000..47cf887
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon@3x.png
new file mode 100644 (file)
index 0000000..9458f0c
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-moon.imageset/icon-moon@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-close.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-close.imageset/Contents.json
new file mode 100644 (file)
index 0000000..53853d1
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-navbar-close.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-close.imageset/icon-navbar-close.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-close.imageset/icon-navbar-close.pdf
new file mode 100644 (file)
index 0000000..bf834ac
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-close.imageset/icon-navbar-close.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-font.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-font.imageset/Contents.json
new file mode 100644 (file)
index 0000000..47d8cd2
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-navbar-font.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-font.imageset/icon-navbar-font.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-font.imageset/icon-navbar-font.pdf
new file mode 100644 (file)
index 0000000..2a4432b
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-font.imageset/icon-navbar-font.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-search.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-search.imageset/Contents.json
new file mode 100644 (file)
index 0000000..df7d0b4
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-navbar-search.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-search.imageset/icon-navbar-search.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-search.imageset/icon-navbar-search.pdf
new file mode 100644 (file)
index 0000000..aa1390a
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-search.imageset/icon-navbar-search.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-share.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-share.imageset/Contents.json
new file mode 100644 (file)
index 0000000..8713774
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-navbar-share.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-share.imageset/icon-navbar-share.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-share.imageset/icon-navbar-share.pdf
new file mode 100644 (file)
index 0000000..68a5a84
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-share.imageset/icon-navbar-share.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-toc.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-toc.imageset/Contents.json
new file mode 100644 (file)
index 0000000..245fa15
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-navbar-toc.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-toc.imageset/icon-navbar-toc.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-toc.imageset/icon-navbar-toc.pdf
new file mode 100644 (file)
index 0000000..c83b31f
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-toc.imageset/icon-navbar-toc.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-tts.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-tts.imageset/Contents.json
new file mode 100644 (file)
index 0000000..94fca57
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-navbar-tts.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-tts.imageset/icon-navbar-tts.pdf b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-tts.imageset/icon-navbar-tts.pdf
new file mode 100644 (file)
index 0000000..690b225
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-navbar-tts.imageset/icon-navbar-tts.pdf differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/Contents.json
new file mode 100644 (file)
index 0000000..ce5bdcb
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon-sun.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon-sun@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon-sun@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun.png
new file mode 100644 (file)
index 0000000..6a71da0
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun@2x.png
new file mode 100644 (file)
index 0000000..32e5c96
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun@3x.png
new file mode 100644 (file)
index 0000000..d713041
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/icon-sun.imageset/icon-sun@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/Contents.json
new file mode 100644 (file)
index 0000000..3a1f1dd
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "knob.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "knob@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "knob@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob.png
new file mode 100644 (file)
index 0000000..79c67e9
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob@2x.png
new file mode 100644 (file)
index 0000000..cbeb8bf
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob@3x.png
new file mode 100644 (file)
index 0000000..ae871de
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/knob.imageset/knob@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/Contents.json
new file mode 100644 (file)
index 0000000..3c8c297
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "man-speech-icon.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "man-speech-icon@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "man-speech-icon@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon.png
new file mode 100644 (file)
index 0000000..374ac8e
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon@2x.png
new file mode 100644 (file)
index 0000000..c6f6d9a
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon@3x.png
new file mode 100644 (file)
index 0000000..13e7a58
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/man-speech-icon.imageset/man-speech-icon@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/Contents.json
new file mode 100644 (file)
index 0000000..36d4c94
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "next-icon.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "next-icon@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "next-icon@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon.png
new file mode 100644 (file)
index 0000000..085fa44
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon@2x.png
new file mode 100644 (file)
index 0000000..43c6985
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon@3x.png
new file mode 100644 (file)
index 0000000..3f664b2
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/next-icon.imageset/next-icon@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/Contents.json
new file mode 100644 (file)
index 0000000..7d1b83a
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "no-marker.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "no-marker@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "no-marker@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker.png
new file mode 100644 (file)
index 0000000..ab9bd5b
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker@2x.png
new file mode 100644 (file)
index 0000000..ec487e5
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker@3x.png
new file mode 100644 (file)
index 0000000..c20926a
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/no-marker.imageset/no-marker@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/Contents.json
new file mode 100644 (file)
index 0000000..6b51e60
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "pause-icon.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "pause-icon@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "pause-icon@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon.png
new file mode 100644 (file)
index 0000000..336e9cc
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon@2x.png
new file mode 100644 (file)
index 0000000..58f772c
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon@3x.png
new file mode 100644 (file)
index 0000000..1f221d4
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pause-icon.imageset/pause-icon@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/Contents.json
new file mode 100644 (file)
index 0000000..a2422d9
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "pink-marker.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "pink-marker@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/pink-marker.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/pink-marker.png
new file mode 100644 (file)
index 0000000..88cf448
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/pink-marker.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/pink-marker@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/pink-marker@2x.png
new file mode 100644 (file)
index 0000000..4834604
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/pink-marker.imageset/pink-marker@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/Contents.json
new file mode 100644 (file)
index 0000000..550c58a
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "play-icon.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "play-icon@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "play-icon@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon.png
new file mode 100644 (file)
index 0000000..e5ab1eb
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon@2x.png
new file mode 100644 (file)
index 0000000..bc3409a
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon@3x.png
new file mode 100644 (file)
index 0000000..a62958f
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/play-icon.imageset/play-icon@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/Contents.json
new file mode 100644 (file)
index 0000000..1739606
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "prev-icon.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "prev-icon@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "prev-icon@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon.png
new file mode 100644 (file)
index 0000000..226cf76
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon@2x.png
new file mode 100644 (file)
index 0000000..97bcc99
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon@3x.png
new file mode 100644 (file)
index 0000000..a2d9159
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/prev-icon.imageset/prev-icon@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/Contents.json
new file mode 100644 (file)
index 0000000..b6e2b49
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "share-marker.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "share-marker@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "share-marker@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker.png
new file mode 100644 (file)
index 0000000..decf3ff
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker@2x.png
new file mode 100644 (file)
index 0000000..0a3d1e0
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker@3x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker@3x.png
new file mode 100644 (file)
index 0000000..8e522e6
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/share-marker.imageset/share-marker@3x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/Contents.json
new file mode 100644 (file)
index 0000000..fa4d851
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "underline-marker.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "underline-marker@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/underline-marker.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/underline-marker.png
new file mode 100644 (file)
index 0000000..a40faa5
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/underline-marker.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/underline-marker@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/underline-marker@2x.png
new file mode 100644 (file)
index 0000000..601a06f
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/underline-marker.imageset/underline-marker@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/Contents.json b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/Contents.json
new file mode 100644 (file)
index 0000000..974df90
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "yellow-marker.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "yellow-marker@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/yellow-marker.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/yellow-marker.png
new file mode 100644 (file)
index 0000000..4f9fe02
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/yellow-marker.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/yellow-marker@2x.png b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/yellow-marker@2x.png
new file mode 100644 (file)
index 0000000..de18c71
Binary files /dev/null and b/iOS/Pods/FolioReaderKit/Source/Resources/Images.xcassets/yellow-marker.imageset/yellow-marker@2x.png differ
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Style.css b/iOS/Pods/FolioReaderKit/Source/Resources/Style.css
new file mode 100755 (executable)
index 0000000..63af8dd
--- /dev/null
@@ -0,0 +1,307 @@
+/**
+ *  Style.css
+ *  FolioReaderKit
+ *
+ *  Created by Heberti Almeida on 06/05/15.
+ *  Copyright (c) 2015 Folio Reader. All rights reserved.
+ */
+
+/* CSS Reset */
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+    margin: 0;
+    vertical-align: baseline;
+}
+
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+    display: block;
+}
+
+/* ePUB */
+html {
+    -webkit-text-size-adjust: none; /* Never autoresize text */
+    padding: 0 0 !important;
+    overflow: hidden;
+}
+
+body {
+    padding: 40px 20px !important;
+    overflow: hidden !important;
+}
+
+* {
+    page-break-before: initial !important; /* Reset page breaks to default */
+}
+
+/* Custom padding for tablets */
+@media only screen and (min-device-width: 768px){
+    body {
+        padding: 60px 80px !important;
+    }
+}
+
+/* Table */
+table {
+    border-collapse: collapse;
+    border-spacing: 0;
+}
+tbody, tfoot, thead {
+    vertical-align: middle !important;
+}
+td, th, tr {
+    vertical-align: inherit !important;
+}
+
+/* List */
+dd, dir, menu, ol, ul { margin-left: 30px !important; }
+ol { list-style-type: decimal !important; }
+li { display: list-item !important; }
+ol ol, ol ul, ul ol, ul ul {
+    margin-bottom: 0 !important;
+    margin-top: 0 !important;
+}
+
+/* Links */
+a { -webkit-touch-callout: none; } /* Disable link callback */
+* { -webkit-user-select: text; }
+img { -webkit-user-select: none; }
+p {
+    margin: 1.3em 0 1.5em 0;
+    line-height: 1.40em !important;
+    text-indent: 1.25em;
+}
+
+b, strong, th {font-weight: bolder !important;}
+
+/* Forced font overrides */
+code, kbd, pre, samp, tt {
+    font-family: monospace, monospace !important;
+    font-size: 1em;
+}
+button, input, select, textarea { display: inline-block !important; }
+/*h1, h2, h3, h4, h5, h6 { font-weight: 400!important; }*/
+del, s, strike { text-decoration: line-through!important; }
+hr {
+    background-color: rgba(0,0,0,.1) !important;
+    border: none !important;
+    height: 1px !important;
+}
+
+
+/* Sub and Super */
+big { font-size: 1.15em !important; }
+small, sub, sup { font-size: .65em !important; }
+sub { vertical-align: sub !important; }
+sup {
+    font-family: monospace !important;
+    vertical-align: super !important;
+}
+
+
+/* iBooks like */
+a { text-decoration: none; }
+pre { white-space: pre-wrap; }
+@page { margin: 0 0 !important; }
+table, ol, il { text-align: -webkit-auto; }
+h1 ,h2 ,h3 ,h4 ,h5 ,h6 {
+    text-align: -webkit-auto;
+    text-rendering: optimizelegibility;
+}
+
+/* allow breaking of words on headers and anchors as they tend to be larger font size or contain longer words */
+a, h1, h2, h3, h4, h5, h6 {
+    word-break: break-word !important;
+    -webkit-hyphens: none !important;
+    hyphens: none !important;
+}
+
+/* Begin Ted */
+img, svg, audio, video {
+    max-height: 95% !important;
+    max-width: 100% !important;
+    box-sizing: border-box;
+    object-fit: contain;
+    page-break-inside: avoid;
+}
+
+/* End Ted */
+
+/* Divs are also used to size images so make sure the authors get what they intended */
+/* which is for the images boxed in them to be completely visible on screen */
+div { max-width: 100%; }
+aside[epub|type~="footnote"] { display: none !important; }
+ruby > rt, ruby > rp { -webkit-user-select: none; }
+* { -webkit-font-smoothing: subpixel-antialiased }
+
+
+/*
+ *
+ * Highlight classes
+ *
+ */
+
+highlight {
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+}
+
+/* Remove tap highlight */
+input, textarea, button, highlight, select, a {
+    -webkit-tap-highlight-color: rgba(0,0,0,0);
+}
+
+/* Highlight styles */
+html .highlight-yellow {background:rgb(255, 235, 107)}
+html .highlight-green {background:#C0ED72}
+html .highlight-blue {background:#ADD8FF}
+html .highlight-pink {background:#FFB0CA}
+html .highlight-underline {
+    text-decoration: none;
+    border-bottom: 2px solid #F02814;
+}
+
+html .highlight-yellow, html .highlight-green, html .highlight-blue, html .highlight-pink, span.epub-media-overlay-playing {
+    border-radius: 3px;
+    padding: 0 2px;
+    margin: 0 -2px;
+}
+
+/* default media overlay style */
+.mediaOverlayStyle0 span.epub-media-overlay-playing {
+    background: #ccc
+}
+
+.mediaOverlayStyle1 .epub-media-overlay-playing {
+    border-bottom: dotted 2px transparent;
+    border-radius: 0;
+}
+
+
+
+/*
+ *
+ * Night mode
+ *
+ */
+
+html, body {
+    -webkit-transition: all 0.6s ease;
+}
+
+html {
+    background-color: #FFFFFF !important;
+}
+
+body {
+    background-color: transparent !important;
+}
+
+html.nightMode, html.nightMode body {
+    background-color: #131313 !important;
+}
+
+.nightMode p, .nightMode div, .nightMode span:not(.epub-media-overlay-playing) {
+    color: #767676 !important;
+    background-color: transparent !important;
+}
+
+.nightMode h1, .nightMode h2, .nightMode h3, .nightMode h4, .nightMode h5, .nightMode h6 {
+    color: #848484 !important;
+}
+
+.nightMode a {
+    color: white;
+}
+
+html.nightMode .highlight-yellow {background:rgba(255, 235, 107, 0.9)}
+html.nightMode .highlight-green {background:rgba(192, 237, 114, 0.9)}
+html.nightMode .highlight-blue {background:rgba(173, 216, 255, 0.9)}
+html.nightMode .highlight-pink {background:rgba(255, 176, 202, 0.9)}
+html.nightMode .highlight-underline {border-bottom: 2px solid rgba(240, 40, 20, 0.6)}
+
+
+/*
+ *
+ * Font classes
+ *
+ */
+
+.andada, .andada p, .andada span, .andada div { font-family: "Andada", sans-serif !important; }
+.lato, .lato p, .lato span, .lato div { font-family: "Lato", serif !important; }
+.lora, .lora p, .lora span, .lora div { font-family: "Lora", serif !important; }
+.raleway, .raleway p, .raleway span, .raleway div { font-family: "Raleway", sans-serif !important; }
+
+html.textSizeOne, .textSizeOne body { font-size: 13px !important; }
+html.textSizeTwo, .textSizeTwo body { font-size: 15px !important; }
+html.textSizeThree, .textSizeThree body { font-size: 17px !important; }
+html.textSizeFour, .textSizeFour body { font-size: 19px !important; }
+html.textSizeFive, .textSizeFive body { font-size: 21px !important; }
+
+body.marginSizeOne, .marginSizeOne body { padding: 20px 10px !important; }
+body.marginSizeTwo, .marginSizeTwo body { padding: 20px 20px !important; }
+body.marginSizeThree, .marginSizeThree body { padding: 20px 30px !important; }
+body.marginSizeFour, .marginSizeFour body { padding: 20px 40px !important; }
+body.marginSizeFive, .marginSizeFive body { padding: 20px 50px !important; }
+
+html.interlineSizeOne p { line-height: 1.20em !important; }
+html.interlineSizeTwo p { line-height: 1.40em !important; }
+html.interlineSizeThree p { line-height: 1.60em !important; }
+html.interlineSizeFour p { line-height: 2.00em !important; }
+html.interlineSizeFive p { line-height: 2.40em !important; }
+
+h1 {
+    font-size: 2em;
+    line-height: 1.2;
+}
+h2 {
+    font-size: 1.5em;
+    line-height: 1.2;
+}
+h3 {
+    font-size: 1.17em;
+    line-height: 1.2;
+}
+h4 {
+    font-size: 1em;
+    line-height: 1.2;
+}
+h5 {
+    font-size: 0.83em;
+    line-height: 1.2;
+}
+h6 {
+    font-size: 0.67em;
+    line-height: 1.2;
+}
+body {
+    word-break: break-word !important;
+    -webkit-hyphens: auto !important;
+    hyphens: auto !important;
+}
+p, span, div {
+    font-size: 1em;
+/*    line-height: 1.5 !important;*/
+}
+@media only screen and (min-device-width: 600px) {
+    div {
+        font-size: 1em;
+        line-height: 1.438em !important;
+    }
+    body {
+        -webkit-hyphens: none !important;
+        hyphens: none !important;
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Source/ScrollScrubber.swift b/iOS/Pods/FolioReaderKit/Source/ScrollScrubber.swift
new file mode 100644 (file)
index 0000000..7f73b42
--- /dev/null
@@ -0,0 +1,263 @@
+//
+//  ScrollScrubber.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 7/14/16.
+//  Copyright © 2016 FolioReader. All rights reserved.
+//
+
+import UIKit
+
+func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
+    switch (lhs, rhs) {
+    case let (l?, r?):
+        return l < r
+    case (nil, _?):
+        return true
+    default:
+        return false
+    }
+}
+
+func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
+    switch (lhs, rhs) {
+    case let (l?, r?):
+        return l > r
+    default:
+        return rhs < lhs
+    }
+}
+
+enum ScrollType: Int {
+    case page
+    // `chapter` is only for the collection view if vertical with horizontal content is used
+    case chapter
+}
+
+enum ScrollDirection: Int {
+    case none
+    case right
+    case left
+    case up
+    case down
+
+    init() {
+        self = .none
+    }
+}
+
+class ScrollScrubber: NSObject, UIScrollViewDelegate {
+    weak var delegate: FolioReaderCenter?
+    var showSpeed = 0.6
+    var hideSpeed = 0.6
+    var hideDelay = 1.0
+
+    var visible = false
+    var usingSlider = false
+    var slider: UISlider!
+    var hideTimer: Timer!
+    var scrollStart: CGFloat!
+    var scrollDelta: CGFloat!
+    var scrollDeltaTimer: Timer!
+
+    fileprivate weak var readerContainer: FolioReaderContainer?
+
+    fileprivate var readerConfig: FolioReaderConfig {
+        guard let readerContainer = readerContainer else { return FolioReaderConfig() }
+        return readerContainer.readerConfig
+    }
+
+    fileprivate var folioReader: FolioReader {
+        guard let readerContainer = readerContainer else { return FolioReader() }
+        return readerContainer.folioReader
+    }
+
+    var frame: CGRect {
+        didSet {
+            self.slider.frame = frame
+        }
+    }
+
+    init(frame:CGRect, withReaderContainer readerContainer: FolioReaderContainer) {
+        self.frame = frame
+        self.readerContainer = readerContainer
+
+        super.init()
+
+        slider = UISlider()
+        slider.layer.anchorPoint = CGPoint(x: 0, y: 0)
+        slider.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2))
+        slider.alpha = 0
+        self.reloadColors()
+
+        // less obtrusive knob and fixes jump: http://stackoverflow.com/a/22301039/484780
+        let thumbImg = UIImage(readerImageNamed: "knob")
+        let thumbImgColor = thumbImg?.imageTintColor(readerConfig.tintColor)?.withRenderingMode(.alwaysOriginal)
+        slider.setThumbImage(thumbImgColor, for: UIControlState())
+        slider.setThumbImage(thumbImgColor, for: .selected)
+        slider.setThumbImage(thumbImgColor, for: .highlighted)
+
+        slider.addTarget(self, action: #selector(ScrollScrubber.sliderChange(_:)), for: .valueChanged)
+        slider.addTarget(self, action: #selector(ScrollScrubber.sliderTouchDown(_:)), for: .touchDown)
+        slider.addTarget(self, action: #selector(ScrollScrubber.sliderTouchUp(_:)), for: .touchUpInside)
+        slider.addTarget(self, action: #selector(ScrollScrubber.sliderTouchUp(_:)), for: .touchUpOutside)
+    }
+
+    func reloadColors() {
+        slider.minimumTrackTintColor = readerConfig.tintColor
+        slider.maximumTrackTintColor = folioReader.isNight(readerConfig.nightModeSeparatorColor, readerConfig.menuSeparatorColor)
+    }
+
+    // MARK: - slider events
+
+    @objc func sliderTouchDown(_ slider:UISlider) {
+        usingSlider = true
+        show()
+    }
+
+    @objc func sliderTouchUp(_ slider:UISlider) {
+        usingSlider = false
+        hideAfterDelay()
+    }
+
+    @objc func sliderChange(_ slider:UISlider) {
+        let movePosition = (height() * CGFloat(slider.value))
+        let offset = readerConfig.isDirection(CGPoint(x: 0, y: movePosition), CGPoint(x: movePosition, y: 0), CGPoint(x: 0, y: movePosition))
+        scrollView()?.setContentOffset(offset, animated: false)
+    }
+
+    // MARK: - show / hide
+
+    func show() {
+        cancelHide()
+
+        visible = true
+
+        if slider.alpha <= 0 {
+            UIView.animate(withDuration: showSpeed, animations: {
+
+                self.slider.alpha = 1
+
+            }, completion: { (Bool) -> Void in
+                self.hideAfterDelay()
+            })
+        } else {
+            slider.alpha = 1
+            if usingSlider == false {
+                hideAfterDelay()
+            }
+        }
+    }
+
+
+    @objc func hide() {
+        visible = false
+        resetScrollDelta()
+        UIView.animate(withDuration: hideSpeed, animations: {
+            self.slider.alpha = 0
+        })
+    }
+
+    func hideAfterDelay() {
+        cancelHide()
+        hideTimer = Timer.scheduledTimer(timeInterval: hideDelay, target: self, selector: #selector(ScrollScrubber.hide), userInfo: nil, repeats: false)
+    }
+
+    func cancelHide() {
+
+        if hideTimer != nil {
+            hideTimer.invalidate()
+            hideTimer = nil
+        }
+
+        if visible == false {
+            slider.layer.removeAllAnimations()
+        }
+
+        visible = true
+    }
+
+    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
+
+        if scrollDeltaTimer != nil {
+            scrollDeltaTimer.invalidate()
+            scrollDeltaTimer = nil
+        }
+
+        if scrollStart == nil {
+            scrollStart = scrollView.contentOffset.forDirection(withConfiguration: readerConfig)
+        }
+    }
+
+    func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        guard (readerConfig.scrollDirection == .vertical ||
+            readerConfig.scrollDirection == .defaultVertical ||
+            readerConfig.scrollDirection == .horizontalWithVerticalContent) else {
+                return
+        }
+
+        if visible && usingSlider == false {
+            setSliderVal()
+        }
+
+        if (slider.alpha > 0) {
+            self.show()
+        } else if delegate?.currentPage != nil && scrollStart != nil {
+            scrollDelta = scrollView.contentOffset.forDirection(withConfiguration: readerConfig) - scrollStart
+
+            guard let pageHeight = folioReader.readerCenter?.pageHeight,
+                (scrollDeltaTimer == nil && scrollDelta > (pageHeight * 0.2 ) || (scrollDelta * -1) > (pageHeight * 0.2)) else {
+                    return
+            }
+
+            self.show()
+            self.resetScrollDelta()
+        }
+    }
+
+    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
+        resetScrollDelta()
+    }
+
+    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
+        scrollDeltaTimer = Timer(timeInterval:0.5, target: self, selector: #selector(ScrollScrubber.resetScrollDelta), userInfo: nil, repeats: false)
+        RunLoop.current.add(scrollDeltaTimer, forMode: RunLoopMode.commonModes)
+    }
+
+    @objc func resetScrollDelta() {
+        if scrollDeltaTimer != nil {
+            scrollDeltaTimer.invalidate()
+            scrollDeltaTimer = nil
+        }
+
+        scrollStart = (scrollView()?.contentOffset.forDirection(withConfiguration: readerConfig) ?? 0)
+        scrollDelta = 0
+    }
+
+    func setSliderVal() {
+        slider.value = Float(scrollTop() / height())
+    }
+
+    // MARK: - utility methods
+
+    fileprivate func scrollView() -> UIScrollView? {
+        return delegate?.currentPage?.webView?.scrollView
+    }
+
+    fileprivate func height() -> CGFloat {
+        guard let currentPage = delegate?.currentPage,
+            let pageHeight = folioReader.readerCenter?.pageHeight,
+            let webView = currentPage.webView else {
+                return 0
+        }
+
+        return webView.scrollView.contentSize.height - pageHeight + 44
+    }
+    
+    fileprivate func scrollTop() -> CGFloat {
+        guard let currentPage = delegate?.currentPage, let webView = currentPage.webView else {
+            return 0
+        }
+        return webView.scrollView.contentOffset.forDirection(withConfiguration: readerConfig)
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Vendor/HAControls/HADiscreteSlider.swift b/iOS/Pods/FolioReaderKit/Vendor/HAControls/HADiscreteSlider.swift
new file mode 100644 (file)
index 0000000..8c65c5b
--- /dev/null
@@ -0,0 +1,444 @@
+//
+//  HADiscreteSlider.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 12/02/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+enum ComponentStyle: Int {
+    case ios
+    case rectangular
+    case rounded
+    case invisible
+    case image
+}
+
+let iOSThumbShadowRadius: CGFloat = 4.0
+let iosThumbShadowOffset = CGSize(width: 0, height: 3)
+
+class HADiscreteSlider : UIControl {
+
+    func ticksDistanceChanged(_ ticksDistance: CGFloat, sender: AnyObject) { }
+    func valueChanged(_ value: CGFloat) { }
+    
+    // MARK: properties
+    
+    var tickStyle: ComponentStyle =  ComponentStyle.rectangular {
+        didSet { self.layoutTrack() }
+    }
+    
+    var tickSize: CGSize = CGSize(width: 1.0, height: 4.0) {
+        willSet (value) {
+            self.tickSize.width = max(0, value.width)
+            self.tickSize.height = max(0, value.height)
+            self.layoutTrack()
+        }
+    }
+    
+    var tickCount: Int = 11 {
+        willSet (value) {
+            self.tickCount = max(2, value)
+            self.layoutTrack()
+        }
+    }
+    
+    var ticksDistance: CGFloat {
+        get {
+            assert(self.tickCount > 1, "2 ticks minimum \(self.tickCount)")
+            let segments = CGFloat(max(1, self.tickCount-1))
+            return (self.trackRectangle!.size.width/segments)
+        }
+    }
+    
+    var tickImage: String? {
+        didSet { self.layoutTrack() }
+    }
+    
+    var trackStyle: ComponentStyle = ComponentStyle.ios {
+        didSet { self.layoutTrack() }
+    }
+    
+    var trackThickness: CGFloat = 2.0 {
+        willSet (value) {
+            self.trackThickness = max(0, value)
+            self.layoutTrack()
+        }
+    }
+    
+    var trackImage: String? {
+        didSet { self.layoutTrack() }
+    }
+    
+    var thumbStyle: ComponentStyle = ComponentStyle.ios {
+        didSet { self.layoutTrack() }
+    }
+    
+    var thumbSize: CGSize = CGSize(width: 10.0, height: 10.0) {
+        willSet (value) {
+            self.thumbSize.width = max(1, value.width)
+            self.thumbSize.height = max(1, value.height)
+            self.layoutTrack()
+        }
+    }
+    
+    var thumbShadowRadius: CGFloat = 0.0 {
+        didSet { self.layoutTrack() }
+    }
+    
+    var thumbImage: String? {
+        willSet (value) {
+            self.thumbImage = value
+            // Associate image to layer
+            if let imageName = value {
+                let image: UIImage = UIImage(named: imageName)!
+                self.thumbLayer!.contents = image.cgImage
+            }
+            self.layoutTrack()
+        }
+    }
+    
+    // AKA: UISlider value (as CGFloat for compatibility with UISlider API, but expected to contain integers)
+    var minimumValue: CGFloat {
+        get { return CGFloat(self._intMinimumValue!) } // calculated property, with a float-to-int adapter
+        set (value) {
+            _intMinimumValue = Int(value);
+            self.layoutTrack()
+        }
+    }
+    
+    var value: CGFloat {
+        get { return CGFloat(self._intValue!) }
+        set (value) {
+            let rootValue = ((value - self.minimumValue) / self.incrementValue)
+            _intValue = Int(self.minimumValue+(rootValue * self.incrementValue))
+            self.layoutTrack()
+        }
+    }
+    
+    var incrementValue: CGFloat = 1 {
+        willSet (value) {
+            self.incrementValue = value
+            if 0 == incrementValue {
+                self.incrementValue = 1 // nonZeroIncrement
+            }
+            self.layoutTrack()
+        }
+    }
+    
+    var thumbColor: UIColor?
+    var thumbShadowOffset: CGSize?
+    var _intValue: Int?
+       var _intMinimumValue: Int?
+       var ticksAbscisses = [CGPoint]()
+       var thumbAbscisse: CGFloat?
+       var thumbLayer: CALayer?
+       var colorTrackLayer: CALayer?
+       var trackRectangle: CGRect!
+       
+       // When bounds change, recalculate layout
+//    func setBounds(bounds: CGRect) {
+//             super.bounds = bounds
+//             self.layoutTrack()
+//             self.setNeedsDisplay()
+//     }
+       
+       override init(frame: CGRect) {
+        super.init(frame: frame)
+        self.initProperties()
+       }
+
+       required init?(coder aDecoder: NSCoder) {
+           fatalError("init(coder:) has not been implemented")
+       }
+       
+       override func draw(_ rect: CGRect) {
+               self.drawTrack()
+               self.drawThumb()
+       }
+       
+       func sendActionsForControlEvents() {
+        self.sendActions(for: UIControlEvents.valueChanged)
+       }
+       
+       // MARK: HADiscreteSlider
+    
+       func initProperties() {
+               self.thumbColor = UIColor.lightGray
+               self.thumbShadowOffset = CGSize.zero
+               _intMinimumValue = -5
+               _intValue = 0
+               self.thumbAbscisse = 0.0
+               self.trackRectangle = CGRect.zero
+               // In case we need a colored track, initialize it now
+               // There may be a more elegant way to do this than with a CALayer,
+               // but then again CALayer brings free animation and will animate along the thumb
+               self.colorTrackLayer = CALayer()
+               self.colorTrackLayer!.backgroundColor = UIColor(hue: 211.0/360.0, saturation: 1, brightness: 1, alpha: 1).cgColor
+               self.colorTrackLayer!.cornerRadius = 2.0
+               self.layer.addSublayer(self.colorTrackLayer!)
+               // The thumb is its own CALayer, which brings in free animation
+               self.thumbLayer = CALayer()
+               self.layer.addSublayer(self.thumbLayer!)
+               self.isMultipleTouchEnabled = false
+               self.layoutTrack()
+       }
+       
+       func drawTrack() {
+               let ctx = UIGraphicsGetCurrentContext()
+               // Track
+               switch self.trackStyle {
+        case .rectangular:
+            ctx!.addRect(self.trackRectangle)
+        break
+        case .image:
+        
+            // Draw image if exists
+            if let imageName = self.trackImage {
+                let image = UIImage(named:imageName)!
+                let centered = CGRect(x: (self.frame.size.width/2)-(image.size.width/2), y: (self.frame.size.height/2)-(image.size.height/2), width: image.size.width, height: image.size.height)
+                    ctx!.draw(image.cgImage!, in: centered)
+            }
+            break
+        
+        case .invisible, .rounded, .ios:
+            let path: UIBezierPath = UIBezierPath(roundedRect: self.trackRectangle, cornerRadius: self.trackRectangle.size.height/2)
+            ctx!.addPath(path.cgPath)
+            break
+               }
+        
+               // Ticks
+               if .ios != self.tickStyle {
+            for originValue in self.ticksAbscisses {
+                let originPoint = originValue
+                let rectangle = CGRect(x: originPoint.x-(self.tickSize.width/2), y: originPoint.y-(self.tickSize.height/2), width: self.tickSize.width, height: self.tickSize.height)
+                switch self.tickStyle {
+                case .rounded:
+                    let path = UIBezierPath(roundedRect: rectangle, cornerRadius: rectangle.size.height/2)
+                    ctx!.addPath(path.cgPath)
+                    break
+                case .rectangular:
+                    ctx!.addRect(rectangle)
+                    break
+                case .image:
+                    // Draw image if exists
+                    
+                    if let imageName = self.tickImage {
+                        let image = UIImage(named: imageName)!
+                        let centered = CGRect(x: rectangle.origin.x+(rectangle.size.width/2)-(image.size.width/2), y: rectangle.origin.y+(rectangle.size.height/2)-(image.size.height/2), width: image.size.width, height: image.size.height)
+                            ctx!.draw(image.cgImage!, in: centered)
+                    }
+                    break
+                
+                case .invisible: break
+                case .ios: break
+                }
+            }
+               }
+        
+               // iOS UISlider aka .IOS does not have ticks
+               ctx!.setFillColor(self.tintColor.cgColor)
+               ctx!.fillPath()
+               // For colored track, we overlay a CALayer, which will animate along with the cursor
+               if .ios == self.trackStyle {
+                       var frame = self.trackRectangle
+                       frame?.size.width = self.thumbAbscisse!-self.trackRectangle.minX
+                       self.colorTrackLayer!.frame = frame!
+               } else {
+                       self.colorTrackLayer!.frame = CGRect.zero
+               }
+       }
+       
+       func drawThumb() {
+               if self.value >= self.minimumValue {
+                       // Feature: hide the thumb when below range
+                       let thumbSizeForStyle = self.thumbSizeIncludingShadow()
+                       let thumbWidth = thumbSizeForStyle.width
+                       let thumbHeight = thumbSizeForStyle.height
+                       let rectangle = CGRect(x: self.thumbAbscisse!-(thumbWidth/2), y: (self.frame.size.height-thumbHeight)/2, width: thumbWidth, height: thumbHeight)
+                       let shadowRadius = ((self.thumbStyle == .ios) ? iOSThumbShadowRadius : self.thumbShadowRadius)
+                       let shadowOffset = ((self.thumbStyle == .ios) ? iosThumbShadowOffset : self.thumbShadowOffset)
+                       // Ignore offset if there is no shadow
+                       self.thumbLayer!.frame = ((shadowRadius != 0.0) ? rectangle.insetBy(dx: shadowRadius+shadowOffset!.width, dy: shadowRadius+shadowOffset!.height) : rectangle.insetBy(dx: shadowRadius, dy: shadowRadius))
+                       switch self.thumbStyle {
+            case .rounded:
+                // A rounded thumb is circular
+                self.thumbLayer!.backgroundColor = self.thumbColor!.cgColor
+                self.thumbLayer!.borderColor = UIColor.clear.cgColor
+                self.thumbLayer!.borderWidth = 0.0
+                self.thumbLayer!.cornerRadius = self.thumbLayer!.frame.size.width/2
+                self.thumbLayer!.allowsEdgeAntialiasing = true
+                break
+                               
+            case .image:
+                // image is set using layer.contents
+                self.thumbLayer!.backgroundColor = UIColor.clear.cgColor
+                self.thumbLayer!.borderColor = UIColor.clear.cgColor
+                self.thumbLayer!.borderWidth = 0.0
+                self.thumbLayer!.cornerRadius = 0.0
+                self.thumbLayer!.allowsEdgeAntialiasing = false
+                break
+                               
+            case .rectangular:
+                self.thumbLayer!.backgroundColor = self.thumbColor!.cgColor
+                               self.thumbLayer!.borderColor = UIColor.clear.cgColor
+                               self.thumbLayer!.borderWidth = 0.0
+                               self.thumbLayer!.cornerRadius = 0.0
+                               self.thumbLayer!.allowsEdgeAntialiasing = false
+                               break
+                
+            case .invisible:
+                               self.thumbLayer!.backgroundColor = UIColor.clear.cgColor
+                               self.thumbLayer!.cornerRadius = 0.0
+                               break
+                
+            case .ios:
+                self.thumbLayer!.backgroundColor = UIColor.white.cgColor
+                self.thumbLayer!.borderColor = UIColor(hue: 0, saturation: 0, brightness: 0.8, alpha: 1).cgColor
+                self.thumbLayer!.borderWidth = 0.5
+                self.thumbLayer!.cornerRadius = self.thumbLayer!.frame.size.width/2
+                self.thumbLayer!.allowsEdgeAntialiasing = true
+                break
+                       }
+            
+            
+                       // Shadow
+                       if shadowRadius != 0.0 {
+                               self.thumbLayer!.shadowOffset = shadowOffset!
+                               self.thumbLayer!.shadowRadius = shadowRadius
+                               self.thumbLayer!.shadowColor = UIColor.black.cgColor
+                               self.thumbLayer!.shadowOpacity = 0.15
+                       } else {
+                               self.thumbLayer!.shadowRadius = 0.0
+                               self.thumbLayer!.shadowOffset = CGSize.zero
+                               self.thumbLayer!.shadowColor = UIColor.clear.cgColor
+                               self.thumbLayer!.shadowOpacity = 0.0
+                       }
+               }
+       }
+       
+       func layoutTrack() {
+               assert(self.tickCount > 1, "2 ticks minimum \(self.tickCount)")
+               let segments = max(1, self.tickCount-1)
+               let thumbWidth = self.thumbSizeIncludingShadow().width
+               
+        // Calculate the track ticks positions
+               let trackHeight = ((.ios == self.trackStyle) ? 2.0 : self.trackThickness)
+               var trackSize = CGSize(width: self.frame.size.width-thumbWidth, height: trackHeight)
+               if .image == self.trackStyle {
+                       if let imageName = self.trackImage {
+                               let image = UIImage(named: imageName)!
+                trackSize.width = image.size.width-thumbWidth
+                       }
+               }
+               self.trackRectangle = CGRect(x: (self.frame.size.width-trackSize.width)/2, y: (self.frame.size.height-trackSize.height)/2, width: trackSize.width, height: trackSize.height)
+               let trackY = self.frame.size.height/2
+               
+        self.ticksAbscisses.removeAll()
+               
+        for i in 0...segments {
+            let ratio = Double(i) / Double(segments)
+            let originX = self.trackRectangle.origin.x+(trackSize.width * CGFloat(ratio))
+            let point = CGPoint(x:originX, y:trackY)
+            self.ticksAbscisses.append(point)
+        }
+        
+               self.layoutThumb()
+       }
+       
+       func layoutThumb() {
+               assert(self.tickCount > 1, "2 ticks minimum \(self.tickCount)")
+               let segments = max(1, self.tickCount-1)
+               // Calculate the thumb position
+               var thumbRatio = (self.value-self.minimumValue) / CGFloat(segments) * self.incrementValue
+               thumbRatio = max(0.0, min(thumbRatio, 1.0))
+               // Normalized
+               self.thumbAbscisse = self.trackRectangle.origin.x+(self.trackRectangle.size.width*thumbRatio)
+       }
+       
+       func thumbSizeIncludingShadow() -> CGSize {
+               switch self.thumbStyle {
+        case .invisible: break
+        case .rectangular: break
+        case .rounded:
+            return ((self.thumbShadowRadius != 0.0) ? CGSize(width: self.thumbSize.width+(self.thumbShadowRadius*2)+(self.thumbShadowOffset!.width*2), height: self.thumbSize.height+(self.thumbShadowRadius*2)+(self.thumbShadowOffset!.height*2)) : self.thumbSize)
+        case .ios:
+            return CGSize(width: 33.0+(iOSThumbShadowRadius*2)+(iosThumbShadowOffset.width*2), height: 33.0+(iOSThumbShadowRadius*2)+(iosThumbShadowOffset.height*2))
+        case .image:
+            if let imageName = self.thumbImage {
+                return UIImage(named: imageName)!.size
+            }
+               }
+        return CGSize(width: 33.0, height: 33.0)
+       }
+       
+       // MARK: Touches
+    
+    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
+               self.touchDown(touches, duration: 0.1)
+       }
+       
+    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
+               self.touchDown(touches, duration: 0.0)
+       }
+       
+    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
+        self.touchUp(touches)
+       }
+       
+    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
+        self.touchUp(touches)
+       }
+       
+       func touchDown(_ touches: Set<UITouch>, duration: TimeInterval) {
+        guard let touch = touches.first else { return }
+        let location = touch.location(in: touch.view)
+        self.moveThumbTo(location.x, duration: duration)
+       }
+       
+       func touchUp(_ touches: Set<UITouch>) {
+        guard let touch = touches.first else { return }
+        let location = touch.location(in: touch.view)
+        let tick = self.pickTickFromSliderPosition(location.x)
+        self.moveThumbToTick(tick)
+       }
+       
+       // MARK: Notifications
+    
+       func moveThumbToTick(_ tick: Int) {
+               let intValue = Int(self.minimumValue)+(tick * Int(self.incrementValue))
+               if intValue != _intValue {
+                       _intValue = intValue
+                       self.sendActionsForControlEvents()
+               }
+               self.layoutThumb()
+               self.setNeedsDisplay()
+       }
+       
+       func moveThumbTo(_ abscisse: CGFloat, duration: CFTimeInterval) {
+               let leftMost = self.trackRectangle.minX
+               let rightMost = self.trackRectangle.maxX
+               self.thumbAbscisse = max(leftMost, min(abscisse, rightMost))
+               CATransaction.setAnimationDuration(duration)
+               let tick = self.pickTickFromSliderPosition(self.thumbAbscisse!)
+               let intValue = Int(self.minimumValue)+(tick * Int(self.incrementValue))
+               if intValue != _intValue {
+                       _intValue = intValue
+                       self.sendActionsForControlEvents()
+               }
+               self.setNeedsDisplay()
+       }
+       
+       func pickTickFromSliderPosition(_ abscisse: CGFloat) -> Int {
+               let leftMost = self.trackRectangle.minX
+               let rightMost = self.trackRectangle.maxX
+               let clampedAbscisse = max(leftMost, min(abscisse, rightMost))
+               let ratio = Double(clampedAbscisse-leftMost) / Double(rightMost-leftMost)
+               let segments = Double(max(1, self.tickCount-1))
+               return Int(round(segments*ratio))
+       }
+       
+}
+
diff --git a/iOS/Pods/FolioReaderKit/Vendor/SMSegmentView/SMSegment.swift b/iOS/Pods/FolioReaderKit/Vendor/SMSegmentView/SMSegment.swift
new file mode 100755 (executable)
index 0000000..e153581
--- /dev/null
@@ -0,0 +1,221 @@
+//
+//  SMSegment.swift
+//
+//  Created by Si MA on 03/01/2015.
+//  Copyright (c) 2015 Si Ma. All rights reserved.
+//
+
+import UIKit
+
+protocol SMSegmentDelegate: class {
+    func selectSegment(_ segment: SMSegment)
+}
+
+class SMSegment: UIView {
+    
+    weak var delegate: SMSegmentDelegate?
+    
+    fileprivate(set) var isSelected: Bool = false
+    fileprivate var shouldResponse: Bool!
+    var index: Int = 0
+    var verticalMargin: CGFloat = 5.0 {
+        didSet {
+            self.resetContentFrame()
+        }
+    }
+    
+    var separatorWidth: CGFloat
+    
+    // Segment Colour
+    var onSelectionColour: UIColor = UIColor.darkGray {
+        didSet {
+            if self.isSelected == true {
+                self.backgroundColor = self.onSelectionColour
+            }
+        }
+    }
+    var offSelectionColour: UIColor = UIColor.white {
+        didSet {
+            if self.isSelected == false {
+                self.backgroundColor = self.offSelectionColour
+            }
+        }
+    }
+    fileprivate var willOnSelectionColour: UIColor! {
+        get {
+            var hue: CGFloat = 0.0
+            var saturation: CGFloat = 0.0
+            var brightness: CGFloat = 0.0
+            var alpha: CGFloat = 0.0
+            self.onSelectionColour.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
+            return UIColor(hue: hue, saturation: saturation*0.5, brightness: min(brightness*1.5, 1.0), alpha: alpha)
+        }
+    }
+    
+    // Segment Title Text & Colour & Font
+    var title: String? {
+        didSet {
+            self.label.text = self.title
+            
+            if let titleText = self.label.text as NSString? {
+                self.labelWidth = titleText.boundingRect(with: CGSize(width: self.frame.size.width, height: self.frame.size.height), options:NSStringDrawingOptions.usesLineFragmentOrigin , attributes: [NSAttributedStringKey.font: self.label.font], context: nil).size.width
+            }
+            else {
+                self.labelWidth = 0.0
+            }
+            
+            self.resetContentFrame()
+        }
+    }
+    var onSelectionTextColour: UIColor = UIColor.white {
+        didSet {
+            if self.isSelected == true {
+                self.label.textColor = self.onSelectionTextColour
+            }
+        }
+    }
+    var offSelectionTextColour: UIColor = UIColor.darkGray {
+        didSet {
+            if self.isSelected == false {
+                self.label.textColor = self.offSelectionTextColour
+            }
+        }
+    }
+    var titleFont: UIFont = UIFont.systemFont(ofSize: 17.0) {
+        didSet {
+            self.label.font = self.titleFont
+            
+            if let titleText = self.label.text as NSString? {
+                self.labelWidth = titleText.boundingRect(with: CGSize(width: self.frame.size.width + 1.0, height: self.frame.size.height), options:NSStringDrawingOptions.usesLineFragmentOrigin , attributes: [NSAttributedStringKey.font: self.label.font], context: nil).size.width
+            }
+            else {
+                self.labelWidth = 0.0
+            }
+            
+            self.resetContentFrame()
+        }
+    }
+    
+    // Segment Image
+    var onSelectionImage: UIImage? {
+        didSet {
+            if self.onSelectionImage != nil {
+                self.resetContentFrame()
+            }
+            if self.isSelected == true {
+                self.imageView.image = self.onSelectionImage
+                self.imageView.contentMode = .center
+            }
+        }
+    }
+    var offSelectionImage: UIImage? {
+        didSet {
+            if self.offSelectionImage != nil {
+                self.resetContentFrame()
+            }
+            if self.isSelected == false {
+                self.imageView.image = self.offSelectionImage
+                self.imageView.contentMode = .center
+            }
+        }
+    }
+    
+    // UI Elements
+    override var frame: CGRect {
+        didSet {
+            self.resetContentFrame()
+        }
+    }
+    fileprivate var imageView: UIImageView = UIImageView()
+    fileprivate var label: UILabel = UILabel()
+    fileprivate var labelWidth: CGFloat = 0.0
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    init(separatorWidth: CGFloat, verticalMargin: CGFloat, onSelectionColour: UIColor, offSelectionColour: UIColor, onSelectionTextColour: UIColor, offSelectionTextColour: UIColor, titleFont: UIFont) {
+        
+        self.separatorWidth = separatorWidth
+        self.verticalMargin = verticalMargin
+        self.onSelectionColour = onSelectionColour
+        self.offSelectionColour = offSelectionColour
+        self.onSelectionTextColour = onSelectionTextColour
+        self.offSelectionTextColour = offSelectionTextColour
+        self.titleFont = titleFont
+        
+        super.init(frame: CGRect.zero)
+        self.setupUIElements()
+    }
+    
+    func setupUIElements() {
+        
+        self.backgroundColor = self.offSelectionColour
+        
+        self.imageView.contentMode = UIViewContentMode.scaleAspectFit
+        self.addSubview(self.imageView)
+        
+        self.label.textAlignment = NSTextAlignment.center
+        self.label.font = self.titleFont
+        self.label.textColor = self.offSelectionTextColour
+        self.addSubview(self.label)
+    }
+    
+    // MARK: Selections
+    func setSelected(_ selected: Bool) {
+        if selected == true {
+            self.isSelected = true
+            self.backgroundColor = self.onSelectionColour
+            self.label.textColor = self.onSelectionTextColour
+            self.imageView.image = self.onSelectionImage
+        }
+        else {
+            self.isSelected = false
+            self.backgroundColor = self.offSelectionColour
+            self.label.textColor = self.offSelectionTextColour
+            self.imageView.image = self.offSelectionImage
+        }
+    }
+    
+    // MARK: Update Frame
+    fileprivate func resetContentFrame() {
+        
+        var distanceBetween: CGFloat = 0.0
+        var imageViewFrame = CGRect(x: 0.0, y: self.verticalMargin, width: 0.0, height: self.frame.size.height - self.verticalMargin*2)
+        
+        if self.onSelectionImage != nil || self.offSelectionImage != nil {
+            // Set imageView as a square
+            imageViewFrame.size.width = self.frame.size.height - self.verticalMargin*2
+            distanceBetween = 5.0
+        }
+        
+        // If there's no text, align imageView centred
+        // Else align text centred
+        if self.labelWidth == 0.0 {
+            imageViewFrame.origin.x = max((self.frame.size.width - imageViewFrame.size.width) / 2.0, 0.0)
+        }
+        else {
+            imageViewFrame.origin.x = max((self.frame.size.width - imageViewFrame.size.width - self.labelWidth) / 2.0 - distanceBetween, 0.0)
+        }
+        
+        self.imageView.frame = imageViewFrame
+        
+        self.label.frame = CGRect(x: imageViewFrame.origin.x + imageViewFrame.size.width + distanceBetween, y: self.verticalMargin, width: self.labelWidth, height: self.frame.size.height - self.verticalMargin * 2)
+    }
+    
+    // MARK: Handle touch
+    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
+        super.touchesBegan(touches, with: event)
+        
+        if self.isSelected == false {
+            self.shouldResponse = true
+            self.backgroundColor = self.willOnSelectionColour
+        }
+    }
+    
+    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
+        super.touchesEnded(touches, with: event)
+        
+        self.delegate?.selectSegment(self)
+    }
+}
diff --git a/iOS/Pods/FolioReaderKit/Vendor/SMSegmentView/SMSegmentView.swift b/iOS/Pods/FolioReaderKit/Vendor/SMSegmentView/SMSegmentView.swift
new file mode 100755 (executable)
index 0000000..18c5821
--- /dev/null
@@ -0,0 +1,299 @@
+//
+//  SMSegmentView.swift
+//
+//  Created by Si MA on 03/01/2015.
+//  Copyright (c) 2015 Si Ma. All rights reserved.
+//
+
+import UIKit
+
+/*
+  Keys for segment properties
+*/
+
+// This is mainly for the top/bottom margin of the imageView
+let keyContentVerticalMargin = "VerticalMargin"
+
+// The colour when the segment is under selected/unselected
+let keySegmentOnSelectionColour = "OnSelectionBackgroundColour"
+let keySegmentOffSelectionColour = "OffSelectionBackgroundColour"
+
+// The colour of the text in the segment for the segment is under selected/unselected
+let keySegmentOnSelectionTextColour = "OnSelectionTextColour"
+let keySegmentOffSelectionTextColour = "OffSelectionTextColour"
+
+// The font of the text in the segment
+let keySegmentTitleFont = "TitleFont"
+
+
+enum SegmentOrganiseMode: Int {
+    case segmentOrganiseHorizontal = 0
+    case segmentOrganiseVertical
+}
+
+
+protocol SMSegmentViewDelegate: class {
+    func segmentView(_ segmentView: SMSegmentView, didSelectSegmentAtIndex index: Int)
+}
+
+class SMSegmentView: UIView, SMSegmentDelegate {
+    weak var delegate: SMSegmentViewDelegate?
+    
+    var indexOfSelectedSegment = NSNotFound
+    var numberOfSegments = 0
+    
+    var organiseMode: SegmentOrganiseMode = SegmentOrganiseMode.segmentOrganiseHorizontal {
+        didSet {
+            self.setNeedsDisplay()
+        }
+    }
+    
+    var segmentVerticalMargin: CGFloat = 5.0 {
+        didSet {
+            for segment in self.segments {
+                segment.verticalMargin = self.segmentVerticalMargin
+            }
+        }
+    }
+    
+    // Segment Separator
+    var separatorColour: UIColor = UIColor.lightGray {
+        didSet {
+            self.setNeedsDisplay()
+        }
+    }
+    var separatorWidth: CGFloat = 1.0 {
+        didSet {
+            for segment in self.segments {
+                segment.separatorWidth = self.separatorWidth
+            }
+            self.updateFrameForSegments()
+        }
+    }
+    
+    // Segment Colour
+    var segmentOnSelectionColour: UIColor = UIColor.darkGray {
+        didSet {
+            for segment in self.segments {
+                segment.onSelectionColour = self.segmentOnSelectionColour
+            }
+        }
+    }
+    var segmentOffSelectionColour: UIColor = UIColor.white {
+        didSet {
+            for segment in self.segments {
+                segment.offSelectionColour = self.segmentOffSelectionColour
+            }
+        }
+    }
+    
+    // Segment Title Text Colour & Font
+    var segmentOnSelectionTextColour: UIColor = UIColor.white {
+        didSet {
+            for segment in self.segments {
+                segment.onSelectionTextColour = self.segmentOnSelectionTextColour
+            }
+        }
+    }
+    var segmentOffSelectionTextColour: UIColor = UIColor.darkGray {
+        didSet {
+            for segment in self.segments {
+                segment.offSelectionTextColour = self.segmentOffSelectionTextColour
+            }
+        }
+    }
+    var segmentTitleFont: UIFont = UIFont.systemFont(ofSize: 17.0) {
+        didSet {
+            for segment in self.segments {
+                segment.titleFont = self.segmentTitleFont
+            }
+        }
+    }
+    
+    override var frame: CGRect {
+        didSet {
+            self.updateFrameForSegments()
+        }
+    }
+    
+    var segments: Array<SMSegment> = Array()
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        self.backgroundColor = UIColor.clear
+        self.layer.masksToBounds = true
+    }
+    
+    init(frame: CGRect, separatorColour: UIColor, separatorWidth: CGFloat, segmentProperties: Dictionary<String, AnyObject>?) {
+        self.separatorColour = separatorColour
+        self.separatorWidth = separatorWidth
+        
+        if let margin = segmentProperties?[keyContentVerticalMargin] as? Float {
+            self.segmentVerticalMargin = CGFloat(margin)
+        }
+        
+        if let onSelectionColour = segmentProperties?[keySegmentOnSelectionColour] as? UIColor {
+            self.segmentOnSelectionColour = onSelectionColour
+        }
+        else {
+            self.segmentOnSelectionColour = UIColor.darkGray
+        }
+        
+        if let offSelectionColour = segmentProperties?[keySegmentOffSelectionColour] as? UIColor {
+            self.segmentOffSelectionColour = offSelectionColour
+        }
+        else {
+            self.segmentOffSelectionColour = UIColor.white
+        }
+        
+        if let onSelectionTextColour = segmentProperties?[keySegmentOnSelectionTextColour] as? UIColor {
+            self.segmentOnSelectionTextColour = onSelectionTextColour
+        }
+        else {
+            self.segmentOnSelectionTextColour = UIColor.white
+        }
+        
+        if let offSelectionTextColour = segmentProperties?[keySegmentOffSelectionTextColour] as? UIColor {
+            self.segmentOffSelectionTextColour = offSelectionTextColour
+        }
+        else {
+            self.segmentOffSelectionTextColour = UIColor.darkGray
+        }
+        
+        if let titleFont = segmentProperties?[keySegmentTitleFont] as? UIFont {
+            self.segmentTitleFont = titleFont
+        }
+        else {
+            self.segmentTitleFont = UIFont.systemFont(ofSize: 17.0)
+        }
+        
+        super.init(frame: frame)
+        self.backgroundColor = UIColor.clear
+        self.layer.masksToBounds = true
+    }
+    
+    func addSegmentWithTitle(_ title: String?, onSelectionImage: UIImage?, offSelectionImage: UIImage?) {
+        
+        let segment = SMSegment(
+            separatorWidth: self.separatorWidth,
+            verticalMargin: self.segmentVerticalMargin,
+            onSelectionColour: self.segmentOnSelectionColour,
+            offSelectionColour: self.segmentOffSelectionColour,
+            onSelectionTextColour: self.segmentOnSelectionTextColour,
+            offSelectionTextColour: self.segmentOffSelectionTextColour,
+            titleFont: self.segmentTitleFont
+        )
+        segment.index = self.segments.count
+        self.segments.append(segment)
+        self.updateFrameForSegments()
+        
+        segment.delegate = self
+        segment.title = title
+        segment.onSelectionImage = onSelectionImage
+        segment.offSelectionImage = offSelectionImage
+        
+        self.addSubview(segment)
+        
+        self.numberOfSegments = self.segments.count
+    }
+    
+    func updateFrameForSegments() {
+        if self.segments.count == 0 {
+            return
+        }
+        
+        let count = self.segments.count
+        if count > 1 {
+            if self.organiseMode == SegmentOrganiseMode.segmentOrganiseHorizontal {
+                let segmentWidth = (self.frame.size.width - self.separatorWidth*CGFloat(count-1)) / CGFloat(count)
+                var originX: CGFloat = 0.0
+                for segment in self.segments {
+                    segment.frame = CGRect(x: originX, y: 0.0, width: segmentWidth, height: self.frame.size.height)
+                    originX += segmentWidth + self.separatorWidth
+                }
+            }
+            else {
+                let segmentHeight = (self.frame.size.height - self.separatorWidth*CGFloat(count-1)) / CGFloat(count)
+                var originY: CGFloat = 0.0
+                for segment in self.segments {
+                    segment.frame = CGRect(x: 0.0, y: originY, width: self.frame.size.width, height: segmentHeight)
+                    originY += segmentHeight + self.separatorWidth
+                }
+            }
+        }
+        else {
+            self.segments[0].frame = CGRect(x: 0.0, y: 0.0, width: self.frame.size.width, height: self.frame.size.height)
+        }
+        
+        self.setNeedsDisplay()
+    }
+    
+    // MARK: SMSegment Delegate
+    func selectSegment(_ segment: SMSegment) {
+        if self.indexOfSelectedSegment != NSNotFound {
+            let previousSelectedSegment = self.segments[self.indexOfSelectedSegment]
+            previousSelectedSegment.setSelected(false)
+        }
+        self.indexOfSelectedSegment = segment.index
+        segment.setSelected(true)
+        self.delegate?.segmentView(self, didSelectSegmentAtIndex: segment.index)
+    }
+    
+    // MARK: Actions
+    func selectSegmentAtIndex(_ index: Int) {
+        assert(index >= 0 && index < self.segments.count, "Index at \(index) is out of bounds")
+        self.selectSegment(self.segments[index])
+    }
+    
+    func deselectSegment() {
+        if self.indexOfSelectedSegment != NSNotFound {
+            let segment = self.segments[self.indexOfSelectedSegment]
+            segment.setSelected(false)
+            self.indexOfSelectedSegment = NSNotFound
+        }
+    }
+    
+    // MARK: Drawing Segment Separators
+    override func draw(_ rect: CGRect) {
+        let context = UIGraphicsGetCurrentContext()
+        self.drawSeparatorWithContext(context!)
+    }
+    
+    func drawSeparatorWithContext(_ context: CGContext) {
+        context.saveGState()
+        
+        if self.segments.count > 1 {
+            let path = CGMutablePath()
+            
+            if self.organiseMode == SegmentOrganiseMode.segmentOrganiseHorizontal {
+                var originX: CGFloat = self.segments[0].frame.size.width + self.separatorWidth/2.0
+                for index in 1..<self.segments.count {
+                    path.move(to: CGPoint(x: originX, y: 0.0))
+                    path.addLine(to: CGPoint(x: originX, y: frame.size.height))
+                    
+                    originX += self.segments[index].frame.width + self.separatorWidth
+                }
+            }
+            else {
+                var originY: CGFloat = self.segments[0].frame.size.height + self.separatorWidth/2.0
+                for index in 1..<self.segments.count {
+                    path.move(to: CGPoint(x: 0.0, y: originY))
+                    path.addLine(to: CGPoint(x: frame.size.width, y: originY))
+                    
+                    originY += self.segments[index].frame.height + self.separatorWidth
+                }
+            }
+            
+            context.addPath(path)
+            context.setStrokeColor(self.separatorColour.cgColor)
+            context.setLineWidth(self.separatorWidth)
+            context.drawPath(using: CGPathDrawingMode.stroke)
+        }
+        
+        context.restoreGState()
+    }
+}
diff --git a/iOS/Pods/FontBlaster/LICENSE b/iOS/Pods/FontBlaster/LICENSE
new file mode 100755 (executable)
index 0000000..5b10da0
--- /dev/null
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Arthur Ariel Sabintsev
+
+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/iOS/Pods/FontBlaster/README.md b/iOS/Pods/FontBlaster/README.md
new file mode 100755 (executable)
index 0000000..127f057
--- /dev/null
@@ -0,0 +1,116 @@
+# FontBlaster
+
+### Programmatically load custom fonts into your iOS app.
+
+![Swift Support](https://img.shields.io/badge/Swift-3.1%2C%203.2%2C%204.0-orange.svg)
+
+[![CocoaPods](https://img.shields.io/cocoapods/v/FontBlaster.svg)](https://cocoapods.org/pods/FontBlaster)  [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![SwiftPM Compatible](https://img.shields.io/badge/SwiftPM-Compatible-brightgreen.svg)](https://swift.org/package-manager/) [![CocoaPods](https://img.shields.io/cocoapods/dt/FontBlaster.svg)](https://cocoapods.org/pods/FontBlaster) [![CocoaPods](https://img.shields.io/cocoapods/dm/FontBlaster.svg)](https://cocoapods.org/pods/FontBlaster)
+---
+## About
+
+Say goodbye to importing custom fonts via property lists as **FontBlaster** automatically imports and loads all fonts in your app's Bundles with one line of code.
+
+## Features
+- [x] CocoaPods Support
+- [x] Carthage Support
+- [x] Swift PM Support
+- [x] Automatically imports fonts from `Bundle.main`
+- [x] Able to import fonts from remote bundles
+- [x] Sample Project
+- [x] Documentation at http://sabintsev.com/FontBlaster/
+
+## Installation Instructions
+
+| Swift Version |  Branch Name  | Will Continue to Receive Updates?
+| ------------- | ------------- |  -------------
+| 4.0  | master   | **Yes**
+| 3.2  | swift3.2 | No
+| 3.1  | swift3.1  | No
+
+#### CocoaPods
+For Swift 4 support:
+```ruby
+pod 'FontBlaster'
+```
+
+For Swift 3.2 support:
+```ruby
+pod 'FontBlaster', :git => 'https://github.com/ArtSabintsev/FontBlaster.git', :branch => 'swift3.2'
+```
+
+For Swift 3.1 support:
+```ruby
+pod 'FontBlaster', :git => 'https://github.com/ArtSabintsev/FontBlaster.git', :branch => 'swift3.1'
+```
+
+### Carthage
+For Swift 4 support:
+
+```swift
+github "ArtSabintsev/FontBlaster"
+```
+
+For Swift 3.2 support:
+```swift
+github "ArtSabintsev/FontBlaster", "swift3.2"
+```
+
+For Swift 3.1 support:
+```swift
+github "ArtSabintsev/FontBlaster", "swift3.1"
+```
+
+### Swift Package Manager
+``` swift
+.Package(url: "https://github.com/ArtSabintsev/FontBlaster.git", majorVersion: 4)
+```
+
+### Manual
+
+1. [Download FontBlaster](//github.com/ArtSabintsev/FontBlaster/archive/master.zip).
+2. Copy `FontBlaster.swift` into your project.
+
+## Setup
+
+Typically, all fonts are automatically found in `Bundle.main`. Even if you have a custom bundle, it's usually lodged inside of the `mainBundle.` Therefore, to load all the fonts in your application, irrespective of the bundle it's in, simply call:
+
+```Swift
+FontBlaster.blast() // Defaults to Bundle.main if no arguments are passed
+```
+
+If you are loading from a bundle that isn't found inside your app's `mainBundle`, simply pass a reference to your `Bundle` in the `blast(_:)` method:
+
+```Swift
+FontBlaster.blast(bundle:) // Takes one argument of type Bundle, or as mentioned above, defaults to Bundle.main if no arguments are passed
+```
+
+If you need a list of all of the loaded fonts, an overloaded version of the `blast(_:)` method has a completion handler that returns just that. Just like the original method, this method takes either a custom `Bundle` or defaults to `Bundle.main` if no argument is passed.
+
+```Swift
+
+// Defaults to Bundle.main as no argument is passed
+FontBlaster.blast() { (fonts) in
+  print(fonts) // fonts is an array of Strings containing font names
+}
+
+// Custom bundle is passed as argument
+FontBlaster.blast(bundle:) { (fonts) in
+  print(fonts) // fonts is an array of Strings containing font names
+}
+```
+
+To turn on console debug statements, simply set `debugEnabled() = true` **before** calling either `blast()` method:
+
+```Swift
+FontBlaster.debugEnabled = true
+FontBlaster.blast()
+```
+
+## Sample Project
+A Sample iOS project is included in the repo. When you launch the app, all fonts are configured to load custom fonts, but don't actually display them *until* you push the button. After pushing the button, **FontBlaster** imports your fonts and redraws the view.
+
+## Inspiration
+This project builds upon an old solution that [Marco Arment](http://twitter.com/marcoarment) proposed and wrote about on his [blog](http://www.marco.org/2012/12/21/ios-dynamic-font-loading).
+
+## Created and maintained by
+[Arthur Ariel Sabintsev](http://www.sabintsev.com/)
diff --git a/iOS/Pods/FontBlaster/Sources/FontBlaster.swift b/iOS/Pods/FontBlaster/Sources/FontBlaster.swift
new file mode 100644 (file)
index 0000000..91d07b5
--- /dev/null
@@ -0,0 +1,182 @@
+//
+//  FontBlaster.swift
+//  FontBlaster
+//
+//  Created by Arthur Sabintsev on 5/5/15.
+//  Copyright (c) 2015 Arthur Ariel Sabintsev. All rights reserved.
+//
+
+import CoreGraphics
+import CoreText
+import Foundation
+import UIKit
+
+// MARK: - FontBlaster
+
+final public class FontBlaster {
+
+    fileprivate enum SupportedFontExtensions: String {
+        case TrueTypeFont = ".ttf"
+        case OpenTypeFont = ".otf"
+    }
+
+    fileprivate typealias FontPath = String
+    fileprivate typealias FontName = String
+    fileprivate typealias FontExtension = String
+    fileprivate typealias Font = (path: FontPath, name: FontName, ext: FontExtension)
+
+    /// Toggles debug print() statements
+    public static var debugEnabled = false
+
+    /// A list of the loaded fonts
+    public static var loadedFonts: [String] = []
+
+    /// Load all fonts found in a specific bundle. If no value is entered, it defaults to the main bundle.
+    public class func blast(bundle: Bundle = Bundle.main) {
+        blast(bundle: bundle, completion: nil)
+    }
+
+    /**
+     Load all fonts found in a specific bundle. If no value is entered, it defaults to the main bundle.
+
+     - returns: An array of strings constaining the names of the fonts that were loaded.
+     */
+    public class func blast(bundle: Bundle = Bundle.main, completion handler: (([String])->Void)?) {
+        let path = bundle.bundlePath
+        loadFontsForBundle(withPath: path)
+        loadFontsFromBundlesFoundInBundle(path: path)
+        handler?(loadedFonts)
+    }
+}
+
+// MARK: - Helpers (Font Loading)
+
+private extension FontBlaster {
+    /// Loads all fonts found in a bundle.
+    ///
+    /// - Parameter path: The absolute path to the bundle.
+    class func loadFontsForBundle(withPath path: String) {
+        do {
+            let contents = try FileManager.default.contentsOfDirectory(atPath: path) as [String]
+            let loadedFonts = fonts(fromPath: path, withContents: contents)
+            if !loadedFonts.isEmpty {
+                for font in loadedFonts {
+                    loadFont(font: font)
+                }
+            } else {
+                printDebugMessage(message: "No fonts were found in the bundle path: \(path).")
+            }
+        } catch let error as NSError {
+            printDebugMessage(message: "There was an error loading fonts from the bundle. \nPath: \(path).\nError: \(error)")
+        }
+    }
+
+    /// Loads all fonts found in a bundle that is loaded within another bundle.
+    ///
+    /// - Parameter path: The absolute path to the bundle.
+    class func loadFontsFromBundlesFoundInBundle(path: String) {
+        do {
+            let contents = try FileManager.default.contentsOfDirectory(atPath: path)
+            for item in contents {
+                if let url = URL(string: path),
+                    item.contains(".bundle") {
+                    let urlPathString = url.appendingPathComponent(item).absoluteString
+                    loadFontsForBundle(withPath: urlPathString)
+                }
+            }
+        } catch let error as NSError {
+            printDebugMessage(message: "There was an error accessing bundle with path. \nPath: \(path).\nError: \(error)")
+        }
+    }
+
+    /// Loads a specific font.
+    ///
+    /// - Parameter font: The font to load.
+    class func loadFont(font: Font) {
+        let fontPath: FontPath = font.path
+        let fontName: FontName = font.name
+        let fontExtension: FontExtension = font.ext
+        let fontFileURL = URL(fileURLWithPath: fontPath).appendingPathComponent(fontName).appendingPathExtension(fontExtension)
+
+        var fontError: Unmanaged<CFError>?
+        if let fontData = try? Data(contentsOf: fontFileURL) as CFData,
+            let dataProvider = CGDataProvider(data: fontData) {
+
+            /// Fixes deadlocking issue caused by `let fontRef = CGFont(dataProvider)`.
+            /// Temporary fix until rdar://18778790 is addressed.
+            /// Open Radar at http://www.openradar.me/18778790
+            /// Discussion at https://github.com/ArtSabintsev/FontBlaster/issues/19
+            _ = UIFont()
+
+            let fontRef = CGFont(dataProvider)
+
+            if CTFontManagerRegisterGraphicsFont(fontRef!, &fontError) {
+
+                if let postScriptName = fontRef?.postScriptName {
+                    printDebugMessage(message: "Successfully loaded font: '\(postScriptName)'.")
+                    loadedFonts.append(String(postScriptName))
+                }
+
+            } else if let fontError = fontError?.takeRetainedValue() {
+                let errorDescription = CFErrorCopyDescription(fontError)
+                printDebugMessage(message: "Failed to load font '\(fontName)': \(String(describing: errorDescription))")
+            }
+        } else {
+            guard let fontError = fontError?.takeRetainedValue() else {
+                printDebugMessage(message: "Failed to load font '\(fontName)'.")
+                return
+            }
+
+            let errorDescription = CFErrorCopyDescription(fontError)
+            printDebugMessage(message: "Failed to load font '\(fontName)': \(String(describing: errorDescription))")
+        }
+    }
+}
+
+// MARK: - Helpers (Miscellaneous)
+
+private extension FontBlaster {
+    /// Parses all of the fonts into their name and extension components.
+    ///
+    /// - Parameters:
+    ///     - path: The absolute path to the font file.
+    ///     - contents: The contents of an Bundle as an array of String objects.
+    /// 
+    /// - Returns: A an array of Font objects.
+    class func fonts(fromPath path: String, withContents contents: [String]) -> [Font] {
+        var fonts = [Font]()
+        for fontName in contents {
+            var parsedFont: (FontName, FontExtension)?
+
+            if fontName.contains(SupportedFontExtensions.TrueTypeFont.rawValue) || fontName.contains(FontBlaster.SupportedFontExtensions.OpenTypeFont.rawValue) {
+                parsedFont = font(fromName: fontName)
+            }
+
+            if let parsedFont = parsedFont {
+                let font: Font = (path, parsedFont.0, parsedFont.1)
+                fonts.append(font)
+            }
+        }
+
+        return fonts
+    }
+
+    /// Parses a font into its name and extension components.
+    ///
+    /// - Parameter name: The name of the font.
+    /// 
+    /// - Returns: A tuple with the font's name and extension.
+    class func font(fromName name: String) -> (FontName, FontExtension) {
+        let components = name.split{$0 == "."}.map { String($0) }
+        return (components[0], components[1])
+    }
+
+    /// Prints debug messages to the console if debugEnabled is set to true.
+    ///
+    /// - Parameter message: The status to print to the console.
+    class func printDebugMessage(message: String) {
+        if debugEnabled == true {
+            print("[FontBlaster]: \(message)")
+        }
+    }
+}
diff --git a/iOS/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement b/iOS/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement
new file mode 100755 (executable)
index 0000000..f8f94b1
Binary files /dev/null and b/iOS/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement differ
diff --git a/iOS/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/Modules/module.modulemap b/iOS/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/Modules/module.modulemap
new file mode 100755 (executable)
index 0000000..ea1e687
--- /dev/null
@@ -0,0 +1,9 @@
+framework module GoogleAppMeasurement {
+  export *
+  module * { export *}
+  link "sqlite3"
+  link "z"
+  link framework "Security"
+  link framework "StoreKit"
+  link framework "SystemConfiguration"
+  link framework "UIKit"}
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m
new file mode 100644 (file)
index 0000000..59f400d
--- /dev/null
@@ -0,0 +1,718 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "TargetConditionals.h"
+
+#if TARGET_OS_IOS
+
+#import <GoogleUtilities/GULAppEnvironmentUtil.h>
+#import <GoogleUtilities/GULLogger.h>
+#import <GoogleUtilities/GULMutableDictionary.h>
+#import "../Common/GULLoggerCodes.h"
+#import "Internal/GULAppDelegateSwizzler_Private.h"
+#import "Private/GULAppDelegateSwizzler.h"
+
+#import <UIKit/UIKit.h>
+#import <objc/runtime.h>
+
+// Implementations need to be typed before calling the implementation directly to cast the
+// arguments and the return types correctly. Otherwise, it will crash the app.
+typedef BOOL (*GULRealOpenURLSourceApplicationAnnotationIMP)(
+    id, SEL, UIApplication *, NSURL *, NSString *, id);
+
+typedef BOOL (*GULRealOpenURLOptionsIMP)(
+    id, SEL, UIApplication *, NSURL *, NSDictionary<NSString *, id> *);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wstrict-prototypes"
+typedef void (*GULRealHandleEventsForBackgroundURLSessionIMP)(
+    id, SEL, UIApplication *, NSString *, void (^)());
+#pragma clang diagnostic pop
+
+// This is needed to for the library to be warning free on iOS versions < 8.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+typedef BOOL (*GULRealContinueUserActivityIMP)(
+    id, SEL, UIApplication *, NSUserActivity *, void (^)(NSArray *restorableObjects));
+#pragma clang diagnostic pop
+
+typedef void (^GULAppDelegateInterceptorCallback)(id<UIApplicationDelegate>);
+
+// The strings below are the keys for associated objects.
+static char const *const kGULContinueUserActivityIMPKey = "GUL_continueUserActivityIMP";
+static char const *const kGULHandleBackgroundSessionIMPKey = "GUL_handleBackgroundSessionIMP";
+static char const *const kGULOpenURLOptionsIMPKey = "GUL_openURLOptionsIMP";
+static char const *const kGULOpenURLOptionsSourceAnnotationsIMPKey =
+    "GUL_openURLSourceApplicationAnnotationIMP";
+static char const *const kGULRealClassKey = "GUL_realClass";
+static NSString *const kGULAppDelegateKeyPath = @"delegate";
+
+static GULLoggerService kGULLoggerSwizzler = @"[GoogleUtilities/AppDelegateSwizzler]";
+
+// Since Firebase SDKs also use this for app delegate proxying, in order to not be a breaking change
+// we disable App Delegate proxying when either of these two flags are set to NO.
+
+/** Plist key that allows Firebase developers to disable App Delegate Proxying. */
+static NSString *const kGULFirebaseAppDelegateProxyEnabledPlistKey =
+    @"FirebaseAppDelegateProxyEnabled";
+
+/** Plist key that allows developers not using Firebase to disable App Delegate Proxying. */
+static NSString *const kGULGoogleUtilitiesAppDelegateProxyEnabledPlistKey =
+    @"GoogleUtilitiesAppDelegateProxyEnabled";
+
+/** The prefix of the App Delegate. */
+static NSString *const kGULAppDelegatePrefix = @"GUL_";
+
+/** The original instance of App Delegate. */
+static id<UIApplicationDelegate> gOriginalAppDelegate;
+
+/**
+ * This class is necessary to store the delegates in an NSArray without retaining them.
+ * [NSValue valueWithNonRetainedObject] also provides this functionality, but does not provide a
+ * zeroing pointer. This will cause EXC_BAD_ACCESS when trying to access the object after it is
+ * dealloced. Instead, this container stores a weak, zeroing reference to the object, which
+ * automatically is set to nil by the runtime when the object is dealloced.
+ */
+@interface GULZeroingWeakContainer : NSObject
+
+/** Stores a weak object. */
+@property(nonatomic, weak) id object;
+
+@end
+
+@implementation GULZeroingWeakContainer
+@end
+
+@interface GULAppDelegateObserver : NSObject
+@end
+
+@implementation GULAppDelegateObserver {
+  BOOL _isObserving;
+}
+
++ (GULAppDelegateObserver *)sharedInstance {
+  static GULAppDelegateObserver *instance;
+  static dispatch_once_t once;
+  dispatch_once(&once, ^{
+    instance = [[GULAppDelegateObserver alloc] init];
+  });
+  return instance;
+}
+
+- (void)observeUIApplication {
+  if (_isObserving) {
+    return;
+  }
+  [[GULAppDelegateSwizzler sharedApplication]
+      addObserver:self
+       forKeyPath:kGULAppDelegateKeyPath
+          options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
+          context:nil];
+  _isObserving = YES;
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath
+                      ofObject:(id)object
+                        change:(NSDictionary *)change
+                       context:(void *)context {
+  if ([keyPath isEqual:kGULAppDelegateKeyPath]) {
+    id newValue = change[NSKeyValueChangeNewKey];
+    id oldValue = change[NSKeyValueChangeOldKey];
+    if ([newValue isEqual:oldValue]) {
+      return;
+    }
+    // Free the stored app delegate instance because it has been changed to a different instance to
+    // avoid keeping it alive forever.
+    if ([oldValue isEqual:gOriginalAppDelegate]) {
+      gOriginalAppDelegate = nil;
+      // Remove the observer. Parse it to NSObject to avoid warning.
+      [[GULAppDelegateSwizzler sharedApplication] removeObserver:self
+                                                      forKeyPath:kGULAppDelegateKeyPath];
+      _isObserving = NO;
+    }
+  }
+}
+
+@end
+
+@implementation GULAppDelegateSwizzler
+
+static dispatch_once_t sProxyAppDelegateOnceToken;
+
+#pragma mark - Public methods
+
++ (BOOL)isAppDelegateProxyEnabled {
+  NSDictionary *infoDictionary = [NSBundle mainBundle].infoDictionary;
+
+  id isFirebaseProxyEnabledPlistValue = infoDictionary[kGULFirebaseAppDelegateProxyEnabledPlistKey];
+  id isGoogleProxyEnabledPlistValue =
+      infoDictionary[kGULGoogleUtilitiesAppDelegateProxyEnabledPlistKey];
+
+  // Enabled by default.
+  BOOL isFirebaseAppDelegateProxyEnabled = YES;
+  BOOL isGoogleUtilitiesAppDelegateProxyEnabled = YES;
+
+  if ([isFirebaseProxyEnabledPlistValue isKindOfClass:[NSNumber class]]) {
+    isFirebaseAppDelegateProxyEnabled = [isFirebaseProxyEnabledPlistValue boolValue];
+  }
+
+  if ([isGoogleProxyEnabledPlistValue isKindOfClass:[NSNumber class]]) {
+    isGoogleUtilitiesAppDelegateProxyEnabled = [isGoogleProxyEnabledPlistValue boolValue];
+  }
+
+  // Only deactivate the proxy if it is explicitly disabled by app developers using either one of
+  // the plist flags.
+  return isFirebaseAppDelegateProxyEnabled && isGoogleUtilitiesAppDelegateProxyEnabled;
+}
+
++ (GULAppDelegateInterceptorID)registerAppDelegateInterceptor:
+    (id<UIApplicationDelegate>)interceptor {
+  NSAssert(interceptor, @"AppDelegateProxy cannot add nil interceptor");
+  NSAssert([interceptor conformsToProtocol:@protocol(UIApplicationDelegate)],
+           @"AppDelegateProxy interceptor does not conform to UIApplicationDelegate");
+
+  if (!interceptor) {
+    GULLogError(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling000],
+                @"AppDelegateProxy cannot add nil interceptor.");
+    return nil;
+  }
+  if (![interceptor conformsToProtocol:@protocol(UIApplicationDelegate)]) {
+    GULLogError(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling001],
+                @"AppDelegateProxy interceptor does not conform to UIApplicationDelegate");
+    return nil;
+  }
+
+  // The ID should be the same given the same interceptor object.
+  NSString *interceptorID = [NSString stringWithFormat:@"%@%p", kGULAppDelegatePrefix, interceptor];
+  if (!interceptorID.length) {
+    GULLogError(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling002],
+                @"AppDelegateProxy cannot create Interceptor ID.");
+    return nil;
+  }
+  GULZeroingWeakContainer *weakObject = [[GULZeroingWeakContainer alloc] init];
+  weakObject.object = interceptor;
+  [GULAppDelegateSwizzler interceptors][interceptorID] = weakObject;
+  return interceptorID;
+}
+
++ (void)unregisterAppDelegateInterceptorWithID:(GULAppDelegateInterceptorID)interceptorID {
+  NSAssert(interceptorID, @"AppDelegateProxy cannot unregister nil interceptor ID.");
+  NSAssert(((NSString *)interceptorID).length != 0,
+           @"AppDelegateProxy cannot unregister empty interceptor ID.");
+
+  if (!interceptorID) {
+    GULLogError(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling003],
+                @"AppDelegateProxy cannot unregister empty interceptor ID.");
+    return;
+  }
+
+  GULZeroingWeakContainer *weakContainer = [GULAppDelegateSwizzler interceptors][interceptorID];
+  if (!weakContainer.object) {
+    GULLogError(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling004],
+                @"AppDelegateProxy cannot unregister interceptor that was not registered. "
+                 "Interceptor ID %@",
+                interceptorID);
+    return;
+  }
+
+  [[GULAppDelegateSwizzler interceptors] removeObjectForKey:interceptorID];
+}
+
++ (void)proxyOriginalDelegate {
+  dispatch_once(&sProxyAppDelegateOnceToken, ^{
+    id<UIApplicationDelegate> originalDelegate =
+        [GULAppDelegateSwizzler sharedApplication].delegate;
+    [GULAppDelegateSwizzler proxyAppDelegate:originalDelegate];
+  });
+}
+
+#pragma mark - Create proxy
+
++ (UIApplication *)sharedApplication {
+  if ([GULAppEnvironmentUtil isAppExtension]) {
+    return nil;
+  }
+  id sharedApplication = nil;
+  Class uiApplicationClass = NSClassFromString(@"UIApplication");
+  if (uiApplicationClass &&
+      [uiApplicationClass respondsToSelector:(NSSelectorFromString(@"sharedApplication"))]) {
+    sharedApplication = [uiApplicationClass sharedApplication];
+  }
+  return sharedApplication;
+}
+
+#pragma mark - Override default methods
+
+/** Creates a new subclass of the class of the given object and sets the isa value of the given
+ *  object to the new subclass. Additionally this copies methods to that new subclass that allow us
+ *  to intercept UIApplicationDelegate methods. This is better known as isa swizzling.
+ *
+ *  @param anObject The object to which you want to isa swizzle. This has to conform to the
+ *      UIApplicationDelegate subclass.
+ */
++ (void)createSubclassWithObject:(id<UIApplicationDelegate>)anObject {
+  Class realClass = [anObject class];
+
+  // Create GUL_<RealAppDelegate>_<timestampMs>
+  NSString *classNameWithPrefix =
+      [kGULAppDelegatePrefix stringByAppendingString:NSStringFromClass(realClass)];
+  NSTimeInterval timestamp = [NSDate date].timeIntervalSince1970;
+  NSString *newClassName =
+      [NSString stringWithFormat:@"%@-%0.0f", classNameWithPrefix, timestamp * 1000];
+
+  if (NSClassFromString(newClassName)) {
+    GULLogError(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling005],
+                @"Cannot create a proxy for App Delegate. Subclass already exists. Original Class: "
+                @"%@, subclass: %@",
+                NSStringFromClass(realClass), newClassName);
+    return;
+  }
+
+  // Register the new class as subclass of the real one. Do not allocate more than the real class
+  // size.
+  Class appDelegateSubClass = objc_allocateClassPair(realClass, newClassName.UTF8String, 0);
+  if (appDelegateSubClass == Nil) {
+    GULLogError(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling006],
+                @"Cannot create a proxy for App Delegate. Subclass already exists. Original Class: "
+                @"%@, subclass: Nil",
+                NSStringFromClass(realClass));
+    return;
+  }
+
+  // Add the following methods from GULAppDelegate class, and store the real implementation so it
+  // can forward to the real one.
+  // For application:openURL:options:
+  NSValue *openURLOptionsIMPPointer;
+  SEL applicationOpenURLOptionsSEL = @selector(application:openURL:options:);
+  if ([anObject respondsToSelector:applicationOpenURLOptionsSEL]) {
+    // Only add the application:openURL:options: method if the original AppDelegate implements it.
+    // This fixes a bug if an app only implements application:openURL:sourceApplication:annotation:
+    // (if we add the `options` method, iOS sees that one exists and does not call the
+    // `sourceApplication` method, which in this case is the only one the app implements).
+
+    [GULAppDelegateSwizzler addInstanceMethodWithSelector:applicationOpenURLOptionsSEL
+                                                fromClass:[GULAppDelegateSwizzler class]
+                                                  toClass:appDelegateSubClass];
+    GULRealOpenURLOptionsIMP openURLOptionsIMP = (GULRealOpenURLOptionsIMP)
+        [GULAppDelegateSwizzler implementationOfMethodSelector:applicationOpenURLOptionsSEL
+                                                     fromClass:realClass];
+    openURLOptionsIMPPointer = [NSValue valueWithPointer:openURLOptionsIMP];
+  }
+
+  // For application:continueUserActivity:restorationHandler:
+  SEL continueUserActivitySEL = @selector(application:continueUserActivity:restorationHandler:);
+  [GULAppDelegateSwizzler addInstanceMethodWithSelector:continueUserActivitySEL
+                                              fromClass:[GULAppDelegateSwizzler class]
+                                                toClass:appDelegateSubClass];
+  GULRealContinueUserActivityIMP continueUserActivityIMP = (GULRealContinueUserActivityIMP)
+      [GULAppDelegateSwizzler implementationOfMethodSelector:continueUserActivitySEL
+                                                   fromClass:realClass];
+  NSValue *continueUserActivityIMPPointer = [NSValue valueWithPointer:continueUserActivityIMP];
+
+  // For application:openURL:sourceApplication:annotation:
+  SEL openURLSourceApplicationAnnotationSEL =
+      @selector(application:openURL:sourceApplication:annotation:);
+  [GULAppDelegateSwizzler addInstanceMethodWithSelector:openURLSourceApplicationAnnotationSEL
+                                              fromClass:[GULAppDelegateSwizzler class]
+                                                toClass:appDelegateSubClass];
+  GULRealOpenURLSourceApplicationAnnotationIMP openURLSourceApplicationAnnotationIMP =
+      (GULRealOpenURLSourceApplicationAnnotationIMP)[GULAppDelegateSwizzler
+          implementationOfMethodSelector:openURLSourceApplicationAnnotationSEL
+                               fromClass:realClass];
+  NSValue *openURLSourceAppAnnotationIMPPointer =
+      [NSValue valueWithPointer:openURLSourceApplicationAnnotationIMP];
+
+  // For application:handleEventsForBackgroundURLSession:completionHandler:
+  SEL handleEventsForBackgroundURLSessionSEL =
+      @selector(application:handleEventsForBackgroundURLSession:completionHandler:);
+  [GULAppDelegateSwizzler addInstanceMethodWithSelector:handleEventsForBackgroundURLSessionSEL
+                                              fromClass:[GULAppDelegateSwizzler class]
+                                                toClass:appDelegateSubClass];
+  GULRealHandleEventsForBackgroundURLSessionIMP handleBackgroundSessionIMP =
+      (GULRealHandleEventsForBackgroundURLSessionIMP)[GULAppDelegateSwizzler
+          implementationOfMethodSelector:handleEventsForBackgroundURLSessionSEL
+                               fromClass:realClass];
+  NSValue *handleBackgroundSessionIMPPointer =
+      [NSValue valueWithPointer:handleBackgroundSessionIMP];
+
+  // Override the description too so the custom class name will not show up.
+  [GULAppDelegateSwizzler addInstanceMethodWithDestinationSelector:@selector(description)
+                              withImplementationFromSourceSelector:@selector(fakeDescription)
+                                                         fromClass:[self class]
+                                                           toClass:appDelegateSubClass];
+
+  // Create fake properties for the real app delegate object.
+  objc_setAssociatedObject(anObject, &kGULContinueUserActivityIMPKey,
+                           continueUserActivityIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+  objc_setAssociatedObject(anObject, &kGULHandleBackgroundSessionIMPKey,
+                           handleBackgroundSessionIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+  if (openURLOptionsIMPPointer) {
+    objc_setAssociatedObject(anObject, &kGULOpenURLOptionsIMPKey, openURLOptionsIMPPointer,
+                             OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+  }
+  objc_setAssociatedObject(anObject, &kGULOpenURLOptionsSourceAnnotationsIMPKey,
+                           openURLSourceAppAnnotationIMPPointer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+  objc_setAssociatedObject(anObject, &kGULRealClassKey, realClass,
+                           OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+  // The subclass size has to be exactly the same size with the original class size. The subclass
+  // cannot have more ivars/properties than its superclass since it will cause an offset in memory
+  // that can lead to overwriting the isa of an object in the next frame.
+  if (class_getInstanceSize(realClass) != class_getInstanceSize(appDelegateSubClass)) {
+    GULLogError(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling007],
+                @"Cannot create subclass of App Delegate, because the created subclass is not the "
+                @"same size. %@",
+                NSStringFromClass(realClass));
+    NSAssert(NO, @"Classes must be the same size to swizzle isa");
+    return;
+  }
+
+  // Make the newly created class to be the subclass of the real App Delegate class.
+  objc_registerClassPair(appDelegateSubClass);
+  if (object_setClass(anObject, appDelegateSubClass)) {
+    GULLogDebug(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling008],
+                @"Successfully created App Delegate Proxy automatically. To disable the "
+                @"proxy, set the flag %@ to NO (Boolean) in the Info.plist",
+                [GULAppDelegateSwizzler correctAppDelegateProxyKey]);
+  }
+
+  // We have to do this to invalidate the cache that caches the original respondsToSelector of
+  // openURL handlers. Without this, it won't call the default implementations because the system
+  // checks and caches them.
+  // Register KVO only once. Otherwise, the observing method will be called as many times as
+  // being registered.
+  id<UIApplicationDelegate> delegate = [GULAppDelegateSwizzler sharedApplication].delegate;
+  [GULAppDelegateSwizzler sharedApplication].delegate = nil;
+  [GULAppDelegateSwizzler sharedApplication].delegate = delegate;
+  gOriginalAppDelegate = delegate;
+  [[GULAppDelegateObserver sharedInstance] observeUIApplication];
+}
+
+#pragma mark - Helper methods
+
++ (GULMutableDictionary *)interceptors {
+  static dispatch_once_t onceToken;
+  static GULMutableDictionary *sInterceptors;
+  dispatch_once(&onceToken, ^{
+    sInterceptors = [[GULMutableDictionary alloc] init];
+  });
+  return sInterceptors;
+}
+
+/** Copies a method identified by the methodSelector from one class to the other. After this method
+ *  is called, performing [toClassInstance methodSelector] will be similar to calling
+ *  [fromClassInstance methodSelector]. This method does nothing if toClass already has a method
+ *  identified by methodSelector.
+ *
+ *  @param methodSelector The SEL that identifies both the method on the fromClass as well as the
+ *      one on the toClass.
+ *  @param fromClass The class from which a method is sourced.
+ *  @param toClass The class to which the method is added. If the class already has a method with
+ *      the same selector, this has no effect.
+ */
++ (void)addInstanceMethodWithSelector:(SEL)methodSelector
+                            fromClass:(Class)fromClass
+                              toClass:(Class)toClass {
+  [self addInstanceMethodWithDestinationSelector:methodSelector
+            withImplementationFromSourceSelector:methodSelector
+                                       fromClass:fromClass
+                                         toClass:toClass];
+}
+
+/** Copies a method identified by the sourceSelector from the fromClass as a method for the
+ *  destinationSelector on the toClass. After this method is called, performing
+ *  [toClassInstance destinationSelector] will be similar to calling
+ *  [fromClassInstance sourceSelector]. This method does nothing if toClass already has a method
+ *  identified by destinationSelector.
+ *
+ *  @param destinationSelector The SEL that identifies the method on the toClass.
+ *  @param sourceSelector The SEL that identifies the method on the fromClass.
+ *  @param fromClass The class from which a method is sourced.
+ *  @param toClass The class to which the method is added. If the class already has a method with
+ *      the same selector, this has no effect.
+ */
++ (void)addInstanceMethodWithDestinationSelector:(SEL)destinationSelector
+            withImplementationFromSourceSelector:(SEL)sourceSelector
+                                       fromClass:(Class)fromClass
+                                         toClass:(Class)toClass {
+  Method method = class_getInstanceMethod(fromClass, sourceSelector);
+  IMP methodIMP = method_getImplementation(method);
+  const char *types = method_getTypeEncoding(method);
+  if (!class_addMethod(toClass, destinationSelector, methodIMP, types)) {
+    GULLogWarning(kGULLoggerSwizzler, NO,
+                  [NSString stringWithFormat:@"I-SWZ%06ld",
+                                             (long)kGULSwizzlerMessageCodeAppDelegateSwizzling009],
+                  @"Cannot copy method to destination selector %@ as it already exists",
+                  NSStringFromSelector(destinationSelector));
+  }
+}
+
+/** Gets the IMP of the instance method on the class identified by the selector.
+ *
+ *  @param selector The selector of which the IMP is to be fetched.
+ *  @param aClass The class from which the IMP is to be fetched.
+ *  @return The IMP of the instance method identified by selector and aClass.
+ */
++ (IMP)implementationOfMethodSelector:(SEL)selector fromClass:(Class)aClass {
+  Method aMethod = class_getInstanceMethod(aClass, selector);
+  return method_getImplementation(aMethod);
+}
+
+/** Enumerates through all the interceptors and if they respond to a given selector, executes a
+ *  GULAppDelegateInterceptorCallback with the interceptor.
+ *
+ *  @param methodSelector The SEL to check if an interceptor responds to.
+ *  @param callback the GULAppDelegateInterceptorCallback.
+ */
++ (void)notifyInterceptorsWithMethodSelector:(SEL)methodSelector
+                                    callback:(GULAppDelegateInterceptorCallback)callback {
+  if (!callback) {
+    return;
+  }
+
+  NSDictionary *interceptors = [GULAppDelegateSwizzler interceptors].dictionary;
+  [interceptors enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
+    GULZeroingWeakContainer *interceptorContainer = obj;
+    id interceptor = interceptorContainer.object;
+    if (!interceptor) {
+      GULLogWarning(
+          kGULLoggerSwizzler, NO,
+          [NSString
+              stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeAppDelegateSwizzling010],
+          @"AppDelegateProxy cannot find interceptor with ID %@. Removing the interceptor.", key);
+      [[GULAppDelegateSwizzler interceptors] removeObjectForKey:key];
+      return;
+    }
+    if ([interceptor respondsToSelector:methodSelector]) {
+      callback(interceptor);
+    }
+  }];
+}
+
+// The methods below are donor methods which are added to the dynamic subclass of the App Delegate.
+// They are called within the scope of the real App Delegate so |self| does not refer to the
+// GULAppDelegateSwizzler instance but the real App Delegate instance.
+
+#pragma mark - [Donor Methods] Overridden instance description method
+
+- (NSString *)fakeDescription {
+  Class realClass = objc_getAssociatedObject(self, &kGULRealClassKey);
+  return [NSString stringWithFormat:@"<%@: %p>", realClass, self];
+}
+
+#pragma mark - [Donor Methods] URL overridden handler methods
+
+- (BOOL)application:(UIApplication *)application
+            openURL:(NSURL *)url
+            options:(NSDictionary<NSString *, id> *)options {
+  // Call the real implementation if the real App Delegate has any.
+  NSValue *openURLIMPPointer = objc_getAssociatedObject(self, &kGULOpenURLOptionsIMPKey);
+  GULRealOpenURLOptionsIMP openURLOptionsIMP = [openURLIMPPointer pointerValue];
+
+  __block BOOL returnedValue = NO;
+  SEL methodSelector = @selector(application:openURL:options:);
+
+// This is needed to for the library to be warning free on iOS versions < 9.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+  [GULAppDelegateSwizzler
+      notifyInterceptorsWithMethodSelector:methodSelector
+                                  callback:^(id<UIApplicationDelegate> interceptor) {
+                                    returnedValue |= [interceptor application:application
+                                                                      openURL:url
+                                                                      options:options];
+                                  }];
+#pragma clang diagnostic pop
+  if (openURLOptionsIMP) {
+    returnedValue |= openURLOptionsIMP(self, methodSelector, application, url, options);
+  }
+  return returnedValue;
+}
+
+- (BOOL)application:(UIApplication *)application
+              openURL:(NSURL *)url
+    sourceApplication:(NSString *)sourceApplication
+           annotation:(id)annotation {
+  // Call the real implementation if the real App Delegate has any.
+  NSValue *openURLSourceAppAnnotationIMPPointer =
+      objc_getAssociatedObject(self, &kGULOpenURLOptionsSourceAnnotationsIMPKey);
+  GULRealOpenURLSourceApplicationAnnotationIMP openURLSourceApplicationAnnotationIMP =
+      [openURLSourceAppAnnotationIMPPointer pointerValue];
+
+  __block BOOL returnedValue = NO;
+  SEL methodSelector = @selector(application:openURL:sourceApplication:annotation:);
+  [GULAppDelegateSwizzler
+      notifyInterceptorsWithMethodSelector:methodSelector
+                                  callback:^(id<UIApplicationDelegate> interceptor) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+                                    returnedValue |= [interceptor application:application
+                                                                      openURL:url
+                                                            sourceApplication:sourceApplication
+                                                                   annotation:annotation];
+#pragma clang diagnostic pop
+                                  }];
+  if (openURLSourceApplicationAnnotationIMP) {
+    returnedValue |= openURLSourceApplicationAnnotationIMP(self, methodSelector, application, url,
+                                                           sourceApplication, annotation);
+  }
+  return returnedValue;
+}
+
+#pragma mark - [Donor Methods] Network overridden handler methods
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wstrict-prototypes"
+- (void)application:(UIApplication *)application
+    handleEventsForBackgroundURLSession:(NSString *)identifier
+                      completionHandler:(void (^)())completionHandler API_AVAILABLE(ios(7.0)) {
+#pragma clang diagnostic pop
+  NSValue *handleBackgroundSessionPointer =
+      objc_getAssociatedObject(self, &kGULHandleBackgroundSessionIMPKey);
+  GULRealHandleEventsForBackgroundURLSessionIMP handleBackgroundSessionIMP =
+      [handleBackgroundSessionPointer pointerValue];
+
+  // Notify interceptors.
+  SEL methodSelector =
+      @selector(application:handleEventsForBackgroundURLSession:completionHandler:);
+  [GULAppDelegateSwizzler
+      notifyInterceptorsWithMethodSelector:methodSelector
+                                  callback:^(id<UIApplicationDelegate> interceptor) {
+                                    [interceptor application:application
+                                        handleEventsForBackgroundURLSession:identifier
+                                                          completionHandler:completionHandler];
+                                  }];
+  // Call the real implementation if the real App Delegate has any.
+  if (handleBackgroundSessionIMP) {
+    handleBackgroundSessionIMP(self, methodSelector, application, identifier, completionHandler);
+  }
+}
+
+#pragma mark - [Donor Methods] User Activities overridden handler methods
+
+// This is needed to for the library to be warning free on iOS versions < 8.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+- (BOOL)application:(UIApplication *)application
+    continueUserActivity:(NSUserActivity *)userActivity
+      restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
+  NSValue *continueUserActivityIMPPointer =
+      objc_getAssociatedObject(self, &kGULContinueUserActivityIMPKey);
+  GULRealContinueUserActivityIMP continueUserActivityIMP =
+      continueUserActivityIMPPointer.pointerValue;
+
+  __block BOOL returnedValue = NO;
+  SEL methodSelector = @selector(application:continueUserActivity:restorationHandler:);
+  [GULAppDelegateSwizzler
+      notifyInterceptorsWithMethodSelector:methodSelector
+                                  callback:^(id<UIApplicationDelegate> interceptor) {
+                                    returnedValue |= [interceptor application:application
+                                                         continueUserActivity:userActivity
+                                                           restorationHandler:restorationHandler];
+                                  }];
+  // Call the real implementation if the real App Delegate has any.
+  if (continueUserActivityIMP) {
+    returnedValue |= continueUserActivityIMP(self, methodSelector, application, userActivity,
+                                             restorationHandler);
+  }
+  return returnedValue;
+}
+#pragma clang diagnostic pop
+
++ (void)proxyAppDelegate:(id<UIApplicationDelegate>)appDelegate {
+  id<UIApplicationDelegate> originalDelegate = appDelegate;
+  // Do not create a subclass if it is not enabled.
+  if (![GULAppDelegateSwizzler isAppDelegateProxyEnabled]) {
+    GULLogNotice(kGULLoggerSwizzler, NO,
+                 [NSString stringWithFormat:@"I-SWZ%06ld",
+                                            (long)kGULSwizzlerMessageCodeAppDelegateSwizzling011],
+                 @"App Delegate Proxy is disabled. %@",
+                 [GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
+    return;
+  }
+  // Do not accept nil delegate.
+  if (!originalDelegate) {
+    GULLogError(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling012],
+                @"Cannot create App Delegate Proxy because App Delegate instance is nil. %@",
+                [GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
+    return;
+  }
+
+  @try {
+    [self createSubclassWithObject:originalDelegate];
+  } @catch (NSException *exception) {
+    GULLogError(kGULLoggerSwizzler, NO,
+                [NSString stringWithFormat:@"I-SWZ%06ld",
+                                           (long)kGULSwizzlerMessageCodeAppDelegateSwizzling013],
+                @"Cannot create App Delegate Proxy. %@",
+                [GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
+    return;
+  }
+}
+
+#pragma mark - Methods to print correct debug logs
+
++ (NSString *)correctAppDelegateProxyKey {
+  return NSClassFromString(@"FIRCore") ? kGULFirebaseAppDelegateProxyEnabledPlistKey
+                                       : kGULGoogleUtilitiesAppDelegateProxyEnabledPlistKey;
+}
+
++ (NSString *)correctAlternativeWhenAppDelegateProxyNotCreated {
+  return NSClassFromString(@"FIRCore")
+             ? @"To log deep link campaigns manually, call the methods in "
+               @"FIRAnalytics+AppDelegate.h."
+             : @"";
+}
+
+#pragma mark - Private Methods for Testing
+
+#ifdef GUL_APP_DELEGATE_TESTING
+
++ (void)clearInterceptors {
+  [[self interceptors] removeAllObjects];
+}
+
++ (void)resetProxyOriginalDelegateOnceToken {
+  sProxyAppDelegateOnceToken = 0;
+}
+
++ (id<UIApplicationDelegate>)originalDelegate {
+  return gOriginalAppDelegate;
+}
+
+#endif  // GUL_APP_DELEGATE_TESTING
+
+@end
+
+#endif  // TARGET_OS_IOS
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h
new file mode 100644 (file)
index 0000000..219b220
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <GoogleUtilities/GULAppDelegateSwizzler.h>
+#import <GoogleUtilities/GULMutableDictionary.h>
+
+@class UIApplication;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface GULAppDelegateSwizzler ()
+
+/** Returns the current sharedApplication.
+ *
+ *  @return the current UIApplication if in an app, or nil if in extension or if it doesn't exist.
+ */
++ (nullable UIApplication *)sharedApplication;
+
+/** ISA Swizzles the given appDelegate as the original app delegate would be.
+ *
+ *  @param appDelegate The object that needs to be isa swizzled. This should conform to the
+ *      UIApplicationDelegate protocol.
+ */
++ (void)proxyAppDelegate:(id<UIApplicationDelegate>)appDelegate;
+
+/** Returns a dictionary containing interceptor IDs mapped to a GULZeroingWeakContainer.
+ *
+ *  @return A dictionary of the form {NSString : GULZeroingWeakContainer}, where the NSString is
+ *      the interceptorID.
+ */
++ (GULMutableDictionary *)interceptors;
+
+#ifdef GUL_APP_DELEGATE_TESTING  // Methods only used in tests.
+
+/** Deletes all the registered interceptors. */
++ (void)clearInterceptors;
+
+/** Resets the token that prevents the app delegate proxy from being isa swizzled multiple times. */
++ (void)resetProxyOriginalDelegateOnceToken;
+
+/** Returns the original app delegate that was proxied.
+ *
+ *  @return The original app delegate instance that was proxied.
+ */
++ (id<UIApplicationDelegate>)originalDelegate;
+
+#endif  // GUL_APP_DELEGATE_TESTING
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h
new file mode 100644 (file)
index 0000000..31fc4b0
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@protocol UIApplicationDelegate;
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NSString *const GULAppDelegateInterceptorID;
+
+/** This class contains methods that isa swizzle the app delegate. */
+@interface GULAppDelegateSwizzler : NSProxy
+
+/** Registers an app delegate interceptor whose methods will be invoked as they're invoked on the
+ *  original app delegate.
+ *
+ *  @param interceptor An instance of a class that conforms to the UIApplicationDelegate protocol.
+ *      The interceptor is NOT retained.
+ *  @return A unique GULAppDelegateInterceptorID if interceptor was successfully registered; nil
+ *      if it fails.
+ */
++ (nullable GULAppDelegateInterceptorID)registerAppDelegateInterceptor:
+    (id<UIApplicationDelegate>)interceptor;
+
+/** Unregisters an interceptor with the given ID if it exists.
+ *
+ *  @param interceptorID The object that was generated when the interceptor was registered.
+ */
++ (void)unregisterAppDelegateInterceptorWithID:(GULAppDelegateInterceptorID)interceptorID;
+
+/** This method ensures that the original app delegate has been proxied. Call this before
+ *  registering your interceptor. This method is safe to call multiple times (but it only proxies
+ *  the app delegate once).
+ */
++ (void)proxyOriginalDelegate NS_EXTENSION_UNAVAILABLE(
+    "App delegate proxy doesn't support extensions.");
+
+/** Indicates whether app delegate proxy is explicitly disabled or enabled. Enabled by default.
+ *
+ *  @return YES if AppDelegateProxy is Enabled, NO otherwise.
+ */
++ (BOOL)isAppDelegateProxyEnabled;
+
+/** Do not initialize this class. */
+- (instancetype)init NS_UNAVAILABLE;
+
+NS_ASSUME_NONNULL_END
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Common/GULLoggerCodes.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Common/GULLoggerCodes.h
new file mode 100644 (file)
index 0000000..b71c037
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+typedef NS_ENUM(NSInteger, GULSwizzlerMessageCode) {
+  // App Delegate Swizzling.
+  kGULSwizzlerMessageCodeAppDelegateSwizzling000 = 1000,  // I-SWZ001000
+  kGULSwizzlerMessageCodeAppDelegateSwizzling001 = 1001,  // I-SWZ001001
+  kGULSwizzlerMessageCodeAppDelegateSwizzling002 = 1002,  // I-SWZ001002
+  kGULSwizzlerMessageCodeAppDelegateSwizzling003 = 1003,  // I-SWZ001003
+  kGULSwizzlerMessageCodeAppDelegateSwizzling004 = 1004,  // I-SWZ001004
+  kGULSwizzlerMessageCodeAppDelegateSwizzling005 = 1005,  // I-SWZ001005
+  kGULSwizzlerMessageCodeAppDelegateSwizzling006 = 1006,  // I-SWZ001006
+  kGULSwizzlerMessageCodeAppDelegateSwizzling007 = 1007,  // I-SWZ001007
+  kGULSwizzlerMessageCodeAppDelegateSwizzling008 = 1008,  // I-SWZ001008
+  kGULSwizzlerMessageCodeAppDelegateSwizzling009 = 1009,  // I-SWZ001009
+  kGULSwizzlerMessageCodeAppDelegateSwizzling010 = 1010,  // I-SWZ001010
+  kGULSwizzlerMessageCodeAppDelegateSwizzling011 = 1011,  // I-SWZ001011
+  kGULSwizzlerMessageCodeAppDelegateSwizzling012 = 1012,  // I-SWZ001012
+  kGULSwizzlerMessageCodeAppDelegateSwizzling013 = 1013,  // I-SWZ001013
+
+  // Method Swizzling.
+  kGULSwizzlerMessageCodeMethodSwizzling000 = 2000,  // I-SWZ002000
+};
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h
new file mode 100644 (file)
index 0000000..5b56271
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+@interface GULAppEnvironmentUtil : NSObject
+
+/// Indicates whether the app is from Apple Store or not. Returns NO if the app is on simulator,
+/// development environment or sideloaded.
++ (BOOL)isFromAppStore;
+
+/// Indicates whether the app is a Testflight app. Returns YES if the app has sandbox receipt.
+/// Returns NO otherwise.
++ (BOOL)isAppStoreReceiptSandbox;
+
+/// Indicates whether the app is on simulator or not at runtime depending on the device
+/// architecture.
++ (BOOL)isSimulator;
+
+/// The current device model. Returns an empty string if device model cannot be retrieved.
++ (NSString *)deviceModel;
+
+/// The current operating system version. Returns an empty string if the system version cannot be
+/// retrieved.
++ (NSString *)systemVersion;
+
+/// Indicates whether it is running inside an extension or an app.
++ (BOOL)isAppExtension;
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m
new file mode 100644 (file)
index 0000000..1fa767b
--- /dev/null
@@ -0,0 +1,250 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "GULAppEnvironmentUtil.h"
+
+#import <Foundation/Foundation.h>
+#import <dlfcn.h>
+#import <mach-o/dyld.h>
+#import <sys/utsname.h>
+
+#if TARGET_OS_IOS
+#import <UIKit/UIKit.h>
+#endif
+
+/// The encryption info struct and constants are missing from the iPhoneSimulator SDK, but not from
+/// the iPhoneOS or Mac OS X SDKs. Since one doesn't ever ship a Simulator binary, we'll just
+/// provide the definitions here.
+#if TARGET_OS_SIMULATOR && !defined(LC_ENCRYPTION_INFO)
+#define LC_ENCRYPTION_INFO 0x21
+struct encryption_info_command {
+  uint32_t cmd;
+  uint32_t cmdsize;
+  uint32_t cryptoff;
+  uint32_t cryptsize;
+  uint32_t cryptid;
+};
+#endif
+
+@implementation GULAppEnvironmentUtil
+
+/// A key for the Info.plist to enable or disable checking if the App Store is running in a sandbox.
+/// This will affect your data integrity when using Firebase Analytics, as it will disable some
+/// necessary checks.
+static NSString *const kFIRAppStoreReceiptURLCheckEnabledKey =
+    @"FirebaseAppStoreReceiptURLCheckEnabled";
+
+/// The file name of the sandbox receipt. This is available on iOS >= 8.0
+static NSString *const kFIRAIdentitySandboxReceiptFileName = @"sandboxReceipt";
+
+/// The following copyright from Landon J. Fuller applies to the isAppEncrypted function.
+///
+/// Copyright (c) 2017 Landon J. Fuller <landon@landonf.org>
+/// All rights reserved.
+///
+/// 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.
+///
+/// Comment from <a href="http://iphonedevwiki.net/index.php/Crack_prevention">iPhone Dev Wiki
+/// Crack Prevention</a>:
+/// App Store binaries are signed by both their developer and Apple. This encrypts the binary so
+/// that decryption keys are needed in order to make the binary readable. When iOS executes the
+/// binary, the decryption keys are used to decrypt the binary into a readable state where it is
+/// then loaded into memory and executed. iOS can tell the encryption status of a binary via the
+/// cryptid structure member of LC_ENCRYPTION_INFO MachO load command. If cryptid is a non-zero
+/// value then the binary is encrypted.
+///
+/// 'Cracking' works by letting the kernel decrypt the binary then siphoning the decrypted data into
+/// a new binary file, resigning, and repackaging. This will only work on jailbroken devices as
+/// codesignature validation has been removed. Resigning takes place because while the codesignature
+/// doesn't have to be valid thanks to the jailbreak, it does have to be in place unless you have
+/// AppSync or similar to disable codesignature checks.
+///
+/// More information at <a href="http://landonf.org/2009/02/index.html">Landon Fuller's blog</a>
+static BOOL IsAppEncrypted() {
+  const struct mach_header *executableHeader = NULL;
+  for (uint32_t i = 0; i < _dyld_image_count(); i++) {
+    const struct mach_header *header = _dyld_get_image_header(i);
+    if (header && header->filetype == MH_EXECUTE) {
+      executableHeader = header;
+      break;
+    }
+  }
+
+  if (!executableHeader) {
+    return NO;
+  }
+
+  BOOL is64bit = (executableHeader->magic == MH_MAGIC_64);
+  uintptr_t cursor = (uintptr_t)executableHeader +
+                     (is64bit ? sizeof(struct mach_header_64) : sizeof(struct mach_header));
+  const struct segment_command *segmentCommand = NULL;
+  uint32_t i = 0;
+
+  while (i++ < executableHeader->ncmds) {
+    segmentCommand = (struct segment_command *)cursor;
+
+    if (!segmentCommand) {
+      continue;
+    }
+
+    if ((!is64bit && segmentCommand->cmd == LC_ENCRYPTION_INFO) ||
+        (is64bit && segmentCommand->cmd == LC_ENCRYPTION_INFO_64)) {
+      if (is64bit) {
+        struct encryption_info_command_64 *cryptCmd =
+            (struct encryption_info_command_64 *)segmentCommand;
+        return cryptCmd && cryptCmd->cryptid != 0;
+      } else {
+        struct encryption_info_command *cryptCmd = (struct encryption_info_command *)segmentCommand;
+        return cryptCmd && cryptCmd->cryptid != 0;
+      }
+    }
+    cursor += segmentCommand->cmdsize;
+  }
+
+  return NO;
+}
+
+static BOOL HasSCInfoFolder() {
+#if TARGET_OS_IOS || TARGET_OS_TV
+  NSString *bundlePath = [NSBundle mainBundle].bundlePath;
+  NSString *scInfoPath = [bundlePath stringByAppendingPathComponent:@"SC_Info"];
+  return [[NSFileManager defaultManager] fileExistsAtPath:scInfoPath];
+#elif TARGET_OS_OSX
+  return NO;
+#endif
+}
+
+static BOOL HasEmbeddedMobileProvision() {
+#if TARGET_OS_IOS || TARGET_OS_TV
+  return [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"].length > 0;
+#elif TARGET_OS_OSX
+  return NO;
+#endif
+}
+
++ (BOOL)isFromAppStore {
+  static dispatch_once_t isEncryptedOnce;
+  static BOOL isEncrypted = NO;
+
+  dispatch_once(&isEncryptedOnce, ^{
+    isEncrypted = IsAppEncrypted();
+  });
+
+  if ([GULAppEnvironmentUtil isSimulator]) {
+    return NO;
+  }
+
+  // If an app contain the sandboxReceipt file, it means its coming from TestFlight
+  // This must be checked before the SCInfo Folder check below since TestFlight apps may
+  // also have an SCInfo folder.
+  if ([GULAppEnvironmentUtil isAppStoreReceiptSandbox]) {
+    return NO;
+  }
+
+  if (HasSCInfoFolder()) {
+    // When iTunes downloads a .ipa, it also gets a customized .sinf file which is added to the
+    // main SC_Info directory.
+    return YES;
+  }
+
+  // For iOS >= 8.0, iTunesMetadata.plist is moved outside of the sandbox. Any attempt to read
+  // the iTunesMetadata.plist outside of the sandbox will be rejected by Apple.
+  // If the app does not contain the embedded.mobileprovision which is stripped out by Apple when
+  // the app is submitted to store, then it is highly likely that it is from Apple Store.
+  return isEncrypted && !HasEmbeddedMobileProvision();
+}
+
++ (BOOL)isAppStoreReceiptSandbox {
+  // Since checking the App Store's receipt URL can be memory intensive, check the option in the
+  // Info.plist if developers opted out of this check.
+  id enableSandboxCheck =
+      [[NSBundle mainBundle] objectForInfoDictionaryKey:kFIRAppStoreReceiptURLCheckEnabledKey];
+  if (enableSandboxCheck && [enableSandboxCheck isKindOfClass:[NSNumber class]] &&
+      ![enableSandboxCheck boolValue]) {
+    return NO;
+  }
+// The #else is for pre Xcode 9 where @available is not yet implemented.
+#if __has_builtin(__builtin_available)
+  if (@available(iOS 7.0, *)) {
+#else
+  if ([[UIDevice currentDevice].systemVersion integerValue] >= 7) {
+#endif
+    NSURL *appStoreReceiptURL = [NSBundle mainBundle].appStoreReceiptURL;
+    NSString *appStoreReceiptFileName = appStoreReceiptURL.lastPathComponent;
+    return [appStoreReceiptFileName isEqualToString:kFIRAIdentitySandboxReceiptFileName];
+  }
+  return NO;
+}
+
++ (BOOL)isSimulator {
+#if TARGET_OS_IOS || TARGET_OS_TV
+  NSString *platform = [GULAppEnvironmentUtil deviceModel];
+  return [platform isEqual:@"x86_64"] || [platform isEqual:@"i386"];
+#elif TARGET_OS_OSX
+  return NO;
+#endif
+}
+
++ (NSString *)deviceModel {
+  static dispatch_once_t once;
+  static NSString *deviceModel;
+
+  dispatch_once(&once, ^{
+    struct utsname systemInfo;
+    if (uname(&systemInfo) == 0) {
+      deviceModel = [NSString stringWithUTF8String:systemInfo.machine];
+    }
+  });
+  return deviceModel;
+}
+
++ (NSString *)systemVersion {
+#if TARGET_OS_IOS
+  return [UIDevice currentDevice].systemVersion;
+#elif TARGET_OS_OSX || TARGET_OS_TV
+  // Assemble the systemVersion, excluding the patch version if it's 0.
+  NSOperatingSystemVersion osVersion = [NSProcessInfo processInfo].operatingSystemVersion;
+  NSMutableString *versionString = [[NSMutableString alloc]
+      initWithFormat:@"%ld.%ld", (long)osVersion.majorVersion, (long)osVersion.minorVersion];
+  if (osVersion.patchVersion != 0) {
+    [versionString appendFormat:@".%ld", (long)osVersion.patchVersion];
+  }
+  return versionString;
+#endif
+}
+
++ (BOOL)isAppExtension {
+#if TARGET_OS_IOS || TARGET_OS_TV
+  // Documented by <a href="https://goo.gl/RRB2Up">Apple</a>
+  BOOL appExtension = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"];
+  return appExtension;
+#elif TARGET_OS_OSX
+  return NO;
+#endif
+}
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m
new file mode 100644 (file)
index 0000000..038574a
--- /dev/null
@@ -0,0 +1,207 @@
+// Copyright 2018 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/GULLogger.h"
+
+#include <asl.h>
+
+#import <GoogleUtilities/GULAppEnvironmentUtil.h>
+#import "Public/GULLoggerLevel.h"
+
+/// ASL client facility name used by GULLogger.
+const char *kGULLoggerASLClientFacilityName = "com.google.utilities.logger";
+
+static dispatch_once_t sGULLoggerOnceToken;
+
+static aslclient sGULLoggerClient;
+
+static dispatch_queue_t sGULClientQueue;
+
+static BOOL sGULLoggerDebugMode;
+
+static GULLoggerLevel sGULLoggerMaximumLevel;
+
+// Allow clients to register a version to include in the log.
+static const char *sVersion = "";
+
+static GULLoggerService kGULLoggerLogger = @"[GULLogger]";
+
+#ifdef DEBUG
+/// The regex pattern for the message code.
+static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$";
+static NSRegularExpression *sMessageCodeRegex;
+#endif
+
+void GULLoggerInitializeASL(void) {
+  dispatch_once(&sGULLoggerOnceToken, ^{
+    NSInteger majorOSVersion = [[GULAppEnvironmentUtil systemVersion] integerValue];
+    uint32_t aslOptions = ASL_OPT_STDERR;
+#if TARGET_OS_SIMULATOR
+    // The iOS 11 simulator doesn't need the ASL_OPT_STDERR flag.
+    if (majorOSVersion >= 11) {
+      aslOptions = 0;
+    }
+#else
+    // Devices running iOS 10 or higher don't need the ASL_OPT_STDERR flag.
+    if (majorOSVersion >= 10) {
+      aslOptions = 0;
+    }
+#endif  // TARGET_OS_SIMULATOR
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"  // asl is deprecated
+    // Initialize the ASL client handle.
+    sGULLoggerClient = asl_open(NULL, kGULLoggerASLClientFacilityName, aslOptions);
+    sGULLoggerMaximumLevel = GULLoggerLevelNotice;
+
+    // Set the filter used by system/device log. Initialize in default mode.
+    asl_set_filter(sGULLoggerClient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE));
+
+    sGULClientQueue = dispatch_queue_create("GULLoggingClientQueue", DISPATCH_QUEUE_SERIAL);
+    dispatch_set_target_queue(sGULClientQueue,
+                              dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
+#ifdef DEBUG
+    sMessageCodeRegex =
+        [NSRegularExpression regularExpressionWithPattern:kMessageCodePattern options:0 error:NULL];
+#endif
+  });
+}
+
+void GULLoggerEnableSTDERR(void) {
+  asl_add_log_file(sGULLoggerClient, STDERR_FILENO);
+}
+
+void GULLoggerForceDebug(void) {
+  // We should enable debug mode if we're not running from App Store.
+  if (![GULAppEnvironmentUtil isFromAppStore]) {
+    sGULLoggerDebugMode = YES;
+    GULSetLoggerLevel(GULLoggerLevelDebug);
+  }
+}
+
+void GULSetLoggerLevel(GULLoggerLevel loggerLevel) {
+  if (loggerLevel < GULLoggerLevelMin || loggerLevel > GULLoggerLevelMax) {
+    GULLogError(kGULLoggerLogger, NO, @"I-COR000023", @"Invalid logger level, %ld",
+                (long)loggerLevel);
+    return;
+  }
+  GULLoggerInitializeASL();
+  // We should not raise the logger level if we are running from App Store.
+  if (loggerLevel >= GULLoggerLevelNotice && [GULAppEnvironmentUtil isFromAppStore]) {
+    return;
+  }
+
+  sGULLoggerMaximumLevel = loggerLevel;
+  dispatch_async(sGULClientQueue, ^{
+    asl_set_filter(sGULLoggerClient, ASL_FILTER_MASK_UPTO(loggerLevel));
+  });
+}
+
+/**
+ * Check if the level is high enough to be loggable.
+ */
+__attribute__((no_sanitize("thread"))) BOOL GULIsLoggableLevel(GULLoggerLevel loggerLevel) {
+  GULLoggerInitializeASL();
+  if (sGULLoggerDebugMode) {
+    return YES;
+  }
+  return (BOOL)(loggerLevel <= sGULLoggerMaximumLevel);
+}
+
+#ifdef DEBUG
+void GULResetLogger() {
+  sGULLoggerOnceToken = 0;
+}
+
+aslclient getGULLoggerClient() {
+  return sGULLoggerClient;
+}
+
+dispatch_queue_t getGULClientQueue() {
+  return sGULClientQueue;
+}
+
+BOOL getGULLoggerDebugMode() {
+  return sGULLoggerDebugMode;
+}
+#endif
+
+void GULLoggerRegisterVersion(const char *version) {
+  sVersion = version;
+}
+
+void GULLogBasic(GULLoggerLevel level,
+                 GULLoggerService service,
+                 BOOL forceLog,
+                 NSString *messageCode,
+                 NSString *message,
+                 va_list args_ptr) {
+  GULLoggerInitializeASL();
+  if (!(level <= sGULLoggerMaximumLevel || sGULLoggerDebugMode || forceLog)) {
+    return;
+  }
+
+#ifdef DEBUG
+  NSCAssert(messageCode.length == 11, @"Incorrect message code length.");
+  NSRange messageCodeRange = NSMakeRange(0, messageCode.length);
+  NSUInteger numberOfMatches =
+      [sMessageCodeRegex numberOfMatchesInString:messageCode options:0 range:messageCodeRange];
+  NSCAssert(numberOfMatches == 1, @"Incorrect message code format.");
+#endif
+  NSString *logMsg = [[NSString alloc] initWithFormat:message arguments:args_ptr];
+  logMsg = [NSString stringWithFormat:@"%s - %@[%@] %@", sVersion, service, messageCode, logMsg];
+  dispatch_async(sGULClientQueue, ^{
+    asl_log(sGULLoggerClient, NULL, level, "%s", logMsg.UTF8String);
+  });
+}
+#pragma clang diagnostic pop
+
+/**
+ * Generates the logging functions using macros.
+ *
+ * Calling GULLogError(kGULLoggerCore, @"I-COR000001", @"Configure %@ failed.", @"blah") shows:
+ * yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Error> [{service}][I-COR000001] Configure blah failed.
+ * Calling GULLogDebug(kGULLoggerCore, @"I-COR000001", @"Configure succeed.") shows:
+ * yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Debug> [{service}][I-COR000001] Configure succeed.
+ */
+#define GUL_LOGGING_FUNCTION(level)                                                     \
+  void GULLog##level(GULLoggerService service, BOOL force, NSString *messageCode,       \
+                     NSString *message, ...) {                                          \
+    va_list args_ptr;                                                                   \
+    va_start(args_ptr, message);                                                        \
+    GULLogBasic(GULLoggerLevel##level, service, force, messageCode, message, args_ptr); \
+    va_end(args_ptr);                                                                   \
+  }
+
+GUL_LOGGING_FUNCTION(Error)
+GUL_LOGGING_FUNCTION(Warning)
+GUL_LOGGING_FUNCTION(Notice)
+GUL_LOGGING_FUNCTION(Info)
+GUL_LOGGING_FUNCTION(Debug)
+
+#undef GUL_MAKE_LOGGER
+
+#pragma mark - GULLoggerWrapper
+
+@implementation GULLoggerWrapper
+
++ (void)logWithLevel:(GULLoggerLevel)level
+         withService:(GULLoggerService)service
+            withCode:(NSString *)messageCode
+         withMessage:(NSString *)message
+            withArgs:(va_list)args {
+  GULLogBasic(level, service, NO, messageCode, message, args);
+}
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Logger/Private/GULLogger.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Logger/Private/GULLogger.h
new file mode 100644 (file)
index 0000000..ff42576
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#import <GoogleUtilities/GULLoggerLevel.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * The services used in the logger.
+ */
+typedef NSString *const GULLoggerService;
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+/**
+ * Initialize GULLogger.
+ */
+extern void GULLoggerInitializeASL(void);
+
+/**
+ * Override log level to Debug.
+ */
+void GULLoggerForceDebug(void);
+
+/**
+ * Turn on logging to STDERR.
+ */
+extern void GULLoggerEnableSTDERR(void);
+
+/**
+ * Changes the default logging level of GULLoggerLevelNotice to a user-specified level.
+ * The default level cannot be set above GULLoggerLevelNotice if the app is running from App Store.
+ * (required) log level (one of the GULLoggerLevel enum values).
+ */
+extern void GULSetLoggerLevel(GULLoggerLevel loggerLevel);
+
+/**
+ * Checks if the specified logger level is loggable given the current settings.
+ * (required) log level (one of the GULLoggerLevel enum values).
+ */
+extern BOOL GULIsLoggableLevel(GULLoggerLevel loggerLevel);
+
+/**
+ * Register version to include in logs.
+ * (required) version
+ */
+extern void GULLoggerRegisterVersion(const char *version);
+
+/**
+ * Logs a message to the Xcode console and the device log. If running from AppStore, will
+ * not log any messages with a level higher than GULLoggerLevelNotice to avoid log spamming.
+ * (required) log level (one of the GULLoggerLevel enum values).
+ * (required) service name of type GULLoggerService.
+ * (required) message code starting with "I-" which means iOS, followed by a capitalized
+ *            three-character service identifier and a six digit integer message ID that is unique
+ *            within the service.
+ *            An example of the message code is @"I-COR000001".
+ * (required) message string which can be a format string.
+ * (optional) variable arguments list obtained from calling va_start, used when message is a format
+ *            string.
+ */
+extern void GULLogBasic(GULLoggerLevel level,
+                        GULLoggerService service,
+                        BOOL forceLog,
+                        NSString *messageCode,
+                        NSString *message,
+// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable
+// See: http://stackoverflow.com/q/29095469
+#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX
+                        va_list args_ptr
+#else
+                        va_list _Nullable args_ptr
+#endif
+);
+
+/**
+ * The following functions accept the following parameters in order:
+ * (required) service name of type GULLoggerService.
+ * (required) message code starting from "I-" which means iOS, followed by a capitalized
+ *            three-character service identifier and a six digit integer message ID that is unique
+ *            within the service.
+ *            An example of the message code is @"I-COR000001".
+ *            See go/firebase-log-proposal for details.
+ * (required) message string which can be a format string.
+ * (optional) the list of arguments to substitute into the format string.
+ * Example usage:
+ * GULLogError(kGULLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
+ */
+extern void GULLogError(GULLoggerService service,
+                        BOOL force,
+                        NSString *messageCode,
+                        NSString *message,
+                        ...) NS_FORMAT_FUNCTION(4, 5);
+extern void GULLogWarning(GULLoggerService service,
+                          BOOL force,
+                          NSString *messageCode,
+                          NSString *message,
+                          ...) NS_FORMAT_FUNCTION(4, 5);
+extern void GULLogNotice(GULLoggerService service,
+                         BOOL force,
+                         NSString *messageCode,
+                         NSString *message,
+                         ...) NS_FORMAT_FUNCTION(4, 5);
+extern void GULLogInfo(GULLoggerService service,
+                       BOOL force,
+                       NSString *messageCode,
+                       NSString *message,
+                       ...) NS_FORMAT_FUNCTION(4, 5);
+extern void GULLogDebug(GULLoggerService service,
+                        BOOL force,
+                        NSString *messageCode,
+                        NSString *message,
+                        ...) NS_FORMAT_FUNCTION(4, 5);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+@interface GULLoggerWrapper : NSObject
+
+/**
+ * Objective-C wrapper for GULLogBasic to allow weak linking to GULLogger
+ * (required) log level (one of the GULLoggerLevel enum values).
+ * (required) service name of type GULLoggerService.
+ * (required) message code starting with "I-" which means iOS, followed by a capitalized
+ *            three-character service identifier and a six digit integer message ID that is unique
+ *            within the service.
+ *            An example of the message code is @"I-COR000001".
+ * (required) message string which can be a format string.
+ * (optional) variable arguments list obtained from calling va_start, used when message is a format
+ *            string.
+ */
+
++ (void)logWithLevel:(GULLoggerLevel)level
+         withService:(GULLoggerService)service
+            withCode:(NSString *)messageCode
+         withMessage:(NSString *)message
+            withArgs:(va_list)args;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GULLoggerLevel.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GULLoggerLevel.h
new file mode 100644 (file)
index 0000000..81ff212
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The log levels used by internal logging.
+ */
+typedef NS_ENUM(NSInteger, GULLoggerLevel) {
+  /** Error level, matches ASL_LEVEL_ERR. */
+  GULLoggerLevelError = 3,
+  /** Warning level, matches ASL_LEVEL_WARNING. */
+  GULLoggerLevelWarning = 4,
+  /** Notice level, matches ASL_LEVEL_NOTICE. */
+  GULLoggerLevelNotice = 5,
+  /** Info level, matches ASL_LEVEL_INFO. */
+  GULLoggerLevelInfo = 6,
+  /** Debug level, matches ASL_LEVEL_DEBUG. */
+  GULLoggerLevelDebug = 7,
+  /** Minimum log level. */
+  GULLoggerLevelMin = GULLoggerLevelError,
+  /** Maximum log level. */
+  GULLoggerLevelMax = GULLoggerLevelDebug
+} NS_SWIFT_NAME(GoogleLoggerLevel);
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/GULSwizzler.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/GULSwizzler.m
new file mode 100644 (file)
index 0000000..9839124
--- /dev/null
@@ -0,0 +1,185 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/GULSwizzler.h"
+
+#import <objc/runtime.h>
+
+#import <GoogleUtilities/GULLogger.h>
+#import "../Common/GULLoggerCodes.h"
+
+#ifdef GUL_UNSWIZZLING_ENABLED
+#import <GoogleUtilities/GULSwizzlingCache.h>
+// We need a private method for an assert.
+#import <GoogleUtilities/GULSwizzlingCache_Private.h>
+#endif
+
+static GULLoggerService kGULLoggerSwizzler = @"[GoogleUtilites/MethodSwizzler]";
+
+dispatch_queue_t GetGULSwizzlingQueue() {
+  static dispatch_queue_t queue;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    queue = dispatch_queue_create("com.google.GULSwizzler", DISPATCH_QUEUE_SERIAL);
+  });
+  return queue;
+}
+
+@implementation GULSwizzler
+
++ (void)swizzleClass:(Class)aClass
+            selector:(SEL)selector
+     isClassSelector:(BOOL)isClassSelector
+           withBlock:(nullable id)block {
+  dispatch_sync(GetGULSwizzlingQueue(), ^{
+    NSAssert(selector, @"The selector cannot be NULL");
+    NSAssert(aClass, @"The class cannot be Nil");
+    Class resolvedClass = aClass;
+    Method method = nil;
+    if (isClassSelector) {
+      method = class_getClassMethod(aClass, selector);
+      resolvedClass = object_getClass(aClass);
+    } else {
+      method = class_getInstanceMethod(aClass, selector);
+    }
+    NSAssert(method, @"You're attempting to swizzle a method that doesn't exist. (%@, %@)",
+             NSStringFromClass(resolvedClass), NSStringFromSelector(selector));
+    IMP newImp = imp_implementationWithBlock(block);
+
+#ifdef GUL_UNSWIZZLING_ENABLED
+    IMP currentImp = class_getMethodImplementation(resolvedClass, selector);
+    [[GULSwizzlingCache sharedInstance] cacheCurrentIMP:currentImp
+                                              forNewIMP:newImp
+                                               forClass:resolvedClass
+                                           withSelector:selector];
+#endif
+
+    const char *typeEncoding = method_getTypeEncoding(method);
+    __unused IMP originalImpOfClass =
+        class_replaceMethod(resolvedClass, selector, newImp, typeEncoding);
+
+#ifdef GUL_UNSWIZZLING_ENABLED
+    // If !originalImpOfClass, then the IMP came from a superclass.
+    if (originalImpOfClass) {
+      if (originalImpOfClass !=
+          [[GULSwizzlingCache sharedInstance] originalIMPOfCurrentIMP:currentImp]) {
+        GULLogWarning(kGULLoggerSwizzler, NO,
+                      [NSString stringWithFormat:@"I-SWZ%06ld",
+                                                 (long)kGULSwizzlerMessageCodeMethodSwizzling000],
+                      @"Swizzling class: %@ SEL:%@ after it has been previously been swizzled.",
+                      NSStringFromClass(resolvedClass), NSStringFromSelector(selector));
+      }
+    }
+#endif
+  });
+}
+
++ (void)unswizzleClass:(Class)aClass selector:(SEL)selector isClassSelector:(BOOL)isClassSelector {
+#ifdef GUL_UNSWIZZLING_ENABLED
+  dispatch_sync(GetGULSwizzlingQueue(), ^{
+    NSAssert(aClass != nil && selector != nil, @"You cannot unswizzle a nil class or selector.");
+    Method method = nil;
+    Class resolvedClass = aClass;
+    if (isClassSelector) {
+      resolvedClass = object_getClass(aClass);
+      method = class_getClassMethod(aClass, selector);
+    } else {
+      method = class_getInstanceMethod(aClass, selector);
+    }
+    NSAssert(method, @"Couldn't find the method you're unswizzling in the runtime.");
+    IMP originalImp =
+        [[GULSwizzlingCache sharedInstance] cachedIMPForClass:resolvedClass withSelector:selector];
+    NSAssert(originalImp, @"This class/selector combination hasn't been swizzled");
+    IMP currentImp = method_setImplementation(method, originalImp);
+    BOOL didRemoveBlock = imp_removeBlock(currentImp);
+    NSAssert(didRemoveBlock, @"Wasn't able to remove the block of a swizzled IMP.");
+    [[GULSwizzlingCache sharedInstance] clearCacheForSwizzledIMP:currentImp
+                                                        selector:selector
+                                                          aClass:resolvedClass];
+  });
+#else
+  NSAssert(NO, @"Unswizzling is disabled.");
+#endif
+}
+
++ (nullable IMP)originalImplementationForClass:(Class)aClass
+                                      selector:(SEL)selector
+                               isClassSelector:(BOOL)isClassSelector {
+#ifdef GUL_UNSWIZZLING_ENABLED
+  __block IMP originalImp = nil;
+  dispatch_sync(GetGULSwizzlingQueue(), ^{
+    Class resolvedClass = isClassSelector ? object_getClass(aClass) : aClass;
+    originalImp =
+        [[GULSwizzlingCache sharedInstance] cachedIMPForClass:resolvedClass withSelector:selector];
+    NSAssert(originalImp, @"The IMP for this class/selector combo doesn't exist (%@, %@).",
+             NSStringFromClass(resolvedClass), NSStringFromSelector(selector));
+  });
+  return originalImp;
+#else
+  NSAssert(NO, @"Unswizzling is disabled and the original IMP is not cached.");
+  return nil;
+#endif
+}
+
++ (nullable IMP)currentImplementationForClass:(Class)aClass
+                                     selector:(SEL)selector
+                              isClassSelector:(BOOL)isClassSelector {
+  NSAssert(selector, @"The selector cannot be NULL");
+  NSAssert(aClass, @"The class cannot be Nil");
+  if (selector == NULL || aClass == nil) {
+    return nil;
+  }
+  __block IMP currentIMP = nil;
+  dispatch_sync(GetGULSwizzlingQueue(), ^{
+    Method method = nil;
+    if (isClassSelector) {
+      method = class_getClassMethod(aClass, selector);
+    } else {
+      method = class_getInstanceMethod(aClass, selector);
+    }
+    NSAssert(method, @"The Method for this class/selector combo doesn't exist (%@, %@).",
+             NSStringFromClass(aClass), NSStringFromSelector(selector));
+    if (method == nil) {
+      return;
+    }
+    currentIMP = method_getImplementation(method);
+    NSAssert(currentIMP, @"The IMP for this class/selector combo doesn't exist (%@, %@).",
+             NSStringFromClass(aClass), NSStringFromSelector(selector));
+  });
+  return currentIMP;
+}
+
++ (BOOL)selector:(SEL)selector existsInClass:(Class)aClass isClassSelector:(BOOL)isClassSelector {
+  Method method = isClassSelector ? class_getClassMethod(aClass, selector)
+                                  : class_getInstanceMethod(aClass, selector);
+  return method != nil;
+}
+
++ (NSArray<id> *)ivarObjectsForObject:(id)object {
+  NSMutableArray *array = [NSMutableArray array];
+  unsigned int count;
+  Ivar *vars = class_copyIvarList([object class], &count);
+  for (NSUInteger i = 0; i < count; i++) {
+    const char *typeEncoding = ivar_getTypeEncoding(vars[i]);
+    // Check to see if the ivar is an object.
+    if (strncmp(typeEncoding, "@", 1) == 0) {
+      id ivarObject = object_getIvar(object, vars[i]);
+      [array addObject:ivarObject];
+    }
+  }
+  free(vars);
+  return array;
+}
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/Private/GULOriginalIMPConvenienceMacros.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/Private/GULOriginalIMPConvenienceMacros.h
new file mode 100644 (file)
index 0000000..a33262a
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * GULOriginalIMPConvenienceMacros.h
+ *
+ * This header contains convenience macros for invoking the original IMP of a swizzled method.
+ */
+
+/**
+ *  Invokes original IMP when the original selector takes no arguments.
+ *
+ *  @param __receivingObject The object on which the IMP is invoked.
+ *  @param __swizzledSEL The selector used for swizzling.
+ *  @param __returnType  The return type of the original implementation.
+ *  @param __originalIMP The original IMP.
+ */
+#define GUL_INVOKE_ORIGINAL_IMP0(__receivingObject, __swizzledSEL, __returnType, __originalIMP) \
+  ((__returnType(*)(id, SEL))__originalIMP)(__receivingObject, __swizzledSEL)
+
+/**
+ *  Invokes original IMP when the original selector takes 1 argument.
+ *
+ *  @param __receivingObject The object on which the IMP is invoked.
+ *  @param __swizzledSEL The selector used for swizzling.
+ *  @param __returnType  The return type of the original implementation.
+ *  @param __originalIMP The original IMP.
+ *  @param __arg1 The first argument.
+ */
+#define GUL_INVOKE_ORIGINAL_IMP1(__receivingObject, __swizzledSEL, __returnType, __originalIMP,   \
+                                 __arg1)                                                          \
+  ((__returnType(*)(id, SEL, __typeof__(__arg1)))__originalIMP)(__receivingObject, __swizzledSEL, \
+                                                                __arg1)
+
+/**
+ *  Invokes original IMP when the original selector takes 2 arguments.
+ *
+ *  @param __receivingObject The object on which the IMP is invoked.
+ *  @param __swizzledSEL The selector used for swizzling.
+ *  @param __returnType  The return type of the original implementation.
+ *  @param __originalIMP The original IMP.
+ *  @param __arg1 The first argument.
+ *  @param __arg2 The second argument.
+ */
+#define GUL_INVOKE_ORIGINAL_IMP2(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
+                                 __arg1, __arg2)                                                \
+  ((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2)))__originalIMP)(            \
+      __receivingObject, __swizzledSEL, __arg1, __arg2)
+
+/**
+ *  Invokes original IMP when the original selector takes 3 arguments.
+ *
+ *  @param __receivingObject The object on which the IMP is invoked.
+ *  @param __swizzledSEL The selector used for swizzling.
+ *  @param __returnType  The return type of the original implementation.
+ *  @param __originalIMP The original IMP.
+ *  @param __arg1 The first argument.
+ *  @param __arg2 The second argument.
+ *  @param __arg3 The third argument.
+ */
+#define GUL_INVOKE_ORIGINAL_IMP3(__receivingObject, __swizzledSEL, __returnType, __originalIMP,  \
+                                 __arg1, __arg2, __arg3)                                         \
+  ((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2),                             \
+                    __typeof__(__arg3)))__originalIMP)(__receivingObject, __swizzledSEL, __arg1, \
+                                                       __arg2, __arg3)
+
+/**
+ *  Invokes original IMP when the original selector takes 4 arguments.
+ *
+ *  @param __receivingObject The object on which the IMP is invoked.
+ *  @param __swizzledSEL The selector used for swizzling.
+ *  @param __returnType  The return type of the original implementation.
+ *  @param __originalIMP The original IMP.
+ *  @param __arg1 The first argument.
+ *  @param __arg2 The second argument.
+ *  @param __arg3 The third argument.
+ *  @param __arg4 The fourth argument.
+ */
+#define GUL_INVOKE_ORIGINAL_IMP4(__receivingObject, __swizzledSEL, __returnType, __originalIMP,  \
+                                 __arg1, __arg2, __arg3, __arg4)                                 \
+  ((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3),         \
+                    __typeof__(__arg4)))__originalIMP)(__receivingObject, __swizzledSEL, __arg1, \
+                                                       __arg2, __arg3, __arg4)
+
+/**
+ *  Invokes original IMP when the original selector takes 5 arguments.
+ *
+ *  @param __receivingObject The object on which the IMP is invoked.
+ *  @param __swizzledSEL The selector used for swizzling.
+ *  @param __returnType  The return type of the original implementation.
+ *  @param __originalIMP The original IMP.
+ *  @param __arg1 The first argument.
+ *  @param __arg2 The second argument.
+ *  @param __arg3 The third argument.
+ *  @param __arg4 The fourth argument.
+ *  @param __arg5 The fifth argument.
+ */
+#define GUL_INVOKE_ORIGINAL_IMP5(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
+                                 __arg1, __arg2, __arg3, __arg4, __arg5)                        \
+  ((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3),        \
+                    __typeof__(__arg4), __typeof__(__arg5)))__originalIMP)(                     \
+      __receivingObject, __swizzledSEL, __arg1, __arg2, __arg3, __arg4, __arg5)
+
+/**
+ *  Invokes original IMP when the original selector takes 6 arguments.
+ *
+ *  @param __receivingObject The object on which the IMP is invoked.
+ *  @param __swizzledSEL The selector used for swizzling.
+ *  @param __returnType  The return type of the original implementation.
+ *  @param __originalIMP The original IMP.
+ *  @param __arg1 The first argument.
+ *  @param __arg2 The second argument.
+ *  @param __arg3 The third argument.
+ *  @param __arg4 The fourth argument.
+ *  @param __arg5 The fifth argument.
+ *  @param __arg6 The sixth argument.
+ */
+#define GUL_INVOKE_ORIGINAL_IMP6(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
+                                 __arg1, __arg2, __arg3, __arg4, __arg5, __arg6)                \
+  ((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3),        \
+                    __typeof__(__arg4), __typeof__(__arg5), __typeof__(__arg6)))__originalIMP)( \
+      __receivingObject, __swizzledSEL, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6)
+
+/**
+ *  Invokes original IMP when the original selector takes 7 arguments.
+ *
+ *  @param __receivingObject The object on which the IMP is invoked.
+ *  @param __swizzledSEL The selector used for swizzling.
+ *  @param __returnType  The return type of the original implementation.
+ *  @param __originalIMP The original IMP.
+ *  @param __arg1 The first argument.
+ *  @param __arg2 The second argument.
+ *  @param __arg3 The third argument.
+ *  @param __arg4 The fourth argument.
+ *  @param __arg5 The fifth argument.
+ *  @param __arg6 The sixth argument.
+ *  @param __arg7 The seventh argument.
+ */
+#define GUL_INVOKE_ORIGINAL_IMP7(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
+                                 __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7)        \
+  ((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3),        \
+                    __typeof__(__arg4), __typeof__(__arg5), __typeof__(__arg6),                 \
+                    __typeof__(__arg7)))__originalIMP)(                                         \
+      __receivingObject, __swizzledSEL, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7)
+
+/**
+ *  Invokes original IMP when the original selector takes 8 arguments.
+ *
+ *  @param __receivingObject The object on which the IMP is invoked.
+ *  @param __swizzledSEL The selector used for swizzling.
+ *  @param __returnType  The return type of the original implementation.
+ *  @param __originalIMP The original IMP.
+ *  @param __arg1 The first argument.
+ *  @param __arg2 The second argument.
+ *  @param __arg3 The third argument.
+ *  @param __arg4 The fourth argument.
+ *  @param __arg5 The fifth argument.
+ *  @param __arg6 The sixth argument.
+ *  @param __arg7 The seventh argument.
+ *  @param __arg8 The eighth argument.
+ */
+#define GUL_INVOKE_ORIGINAL_IMP8(__receivingObject, __swizzledSEL, __returnType, __originalIMP,  \
+                                 __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7, __arg8) \
+  ((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3),         \
+                    __typeof__(__arg4), __typeof__(__arg5), __typeof__(__arg6),                  \
+                    __typeof__(__arg7), __typeof__(__arg8)))__originalIMP)(                      \
+      __receivingObject, __swizzledSEL, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7,  \
+      __arg8)
+
+/**
+ *  Invokes original IMP when the original selector takes 9 arguments.
+ *
+ *  @param __receivingObject The object on which the IMP is invoked.
+ *  @param __swizzledSEL The selector used for swizzling.
+ *  @param __returnType  The return type of the original implementation.
+ *  @param __originalIMP The original IMP.
+ *  @param __arg1 The first argument.
+ *  @param __arg2 The second argument.
+ *  @param __arg3 The third argument.
+ *  @param __arg4 The fourth argument.
+ *  @param __arg5 The fifth argument.
+ *  @param __arg6 The sixth argument.
+ *  @param __arg7 The seventh argument.
+ *  @param __arg8 The eighth argument.
+ *  @param __arg9 The ninth argument.
+ */
+#define GUL_INVOKE_ORIGINAL_IMP9(__receivingObject, __swizzledSEL, __returnType, __originalIMP,  \
+                                 __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7, __arg8, \
+                                 __arg9)                                                         \
+  ((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3),         \
+                    __typeof__(__arg4), __typeof__(__arg5), __typeof__(__arg6),                  \
+                    __typeof__(__arg7), __typeof__(__arg8), __typeof__(__arg9)))__originalIMP)(  \
+      __receivingObject, __swizzledSEL, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7,  \
+      __arg8, __arg9)
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/Private/GULSwizzler.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/Private/GULSwizzler.h
new file mode 100644 (file)
index 0000000..ab008ca
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** This class handles the runtime manipulation necessary to instrument selectors. It stores the
+ *  classes and selectors that have been swizzled, and runs all operations on its own queue.
+ */
+@interface GULSwizzler : NSObject
+
+/** Manipulates the Objective-C runtime to replace the original IMP with the supplied block.
+ *
+ *  @param aClass The class to swizzle.
+ *  @param selector The selector of the class to swizzle.
+ *  @param isClassSelector A BOOL specifying whether the selector is a class or instance selector.
+ *  @param block The block that replaces the original IMP.
+ */
++ (void)swizzleClass:(Class)aClass
+            selector:(SEL)selector
+     isClassSelector:(BOOL)isClassSelector
+           withBlock:(nullable id)block;
+
+/** Restores the original implementation.
+ *
+ *  @param aClass The class to unswizzle.
+ *  @param selector The selector to restore the original implementation of.
+ *  @param isClassSelector A BOOL specifying whether the selector is a class or instance selector.
+ */
++ (void)unswizzleClass:(Class)aClass selector:(SEL)selector isClassSelector:(BOOL)isClassSelector;
+
+/** Returns the current IMP for the given class and selector.
+ *
+ *  @param aClass The class to use.
+ *  @param selector The selector to find the implementation of.
+ *  @param isClassSelector A BOOL specifying whether the selector is a class or instance selector.
+ *  @return The implementation of the selector in the runtime.
+ */
++ (nullable IMP)currentImplementationForClass:(Class)aClass
+                                     selector:(SEL)selector
+                              isClassSelector:(BOOL)isClassSelector;
+
+/** Returns the original IMP for the given class and selector.
+ *
+ *  @param aClass The class to use.
+ *  @param selector The selector to find the implementation of.
+ *  @param isClassSelector A BOOL specifying whether the selector is a class or instance selector.
+ *  @return The implementation of the selector in the runtime before any consumer or GULSwizzler
+ *          swizzled.
+ */
++ (nullable IMP)originalImplementationForClass:(Class)aClass
+                                      selector:(SEL)selector
+                               isClassSelector:(BOOL)isClassSelector;
+
+/** Checks the runtime to see if a selector exists on a class. If a property is declared as
+ *  @dynamic, we have a reverse swizzling situation, where the implementation of a method exists
+ *  only in concrete subclasses, and NOT in the superclass. We can detect that situation using
+ *  this helper method. Similarly, we can detect situations where a class doesn't implement a
+ *  protocol method.
+ *
+ *  @param selector The selector to check for.
+ *  @param aClass The class to check.
+ *  @param isClassSelector A BOOL specifying whether the selector is a class or instance selector.
+ *  @return YES if the method was found in this selector/class combination, NO otherwise.
+ */
++ (BOOL)selector:(SEL)selector existsInClass:(Class)aClass isClassSelector:(BOOL)isClassSelector;
+
+/** Returns a list of all Objective-C (and not primitive) ivars contained by the given object.
+ *
+ *  @param object The object whose ivars will be iterated.
+ *  @return The list of ivar objects.
+ */
++ (NSArray<id> *)ivarObjectsForObject:(id)object;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/NSData+zlib/GULNSData+zlib.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/NSData+zlib/GULNSData+zlib.h
new file mode 100644 (file)
index 0000000..36f94a7
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright 2018 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <Foundation/Foundation.h>
+
+/// This is a copy of Google Toolbox for Mac library to avoid creating an extra framework.
+
+// NOTE: For 64bit, none of these apis handle input sizes >32bits, they will return nil when given
+// such data. To handle data of that size you really should be streaming it rather then doing it all
+// in memory.
+
+@interface NSData (GULGzip)
+
+/// Returns an data as the result of decompressing the payload of |data|.The data to decompress must
+/// be a gzipped payloads.
++ (NSData *)gul_dataByInflatingGzippedData:(NSData *)data error:(NSError **)error;
+
+/// Returns an compressed data with the result of gzipping the payload of |data|. Uses the default
+/// compression level.
++ (NSData *)gul_dataByGzippingData:(NSData *)data error:(NSError **)error;
+
+FOUNDATION_EXPORT NSString *const GULNSDataZlibErrorDomain;
+FOUNDATION_EXPORT NSString *const GULNSDataZlibErrorKey;           // NSNumber
+FOUNDATION_EXPORT NSString *const GULNSDataZlibRemainingBytesKey;  // NSNumber
+
+typedef NS_ENUM(NSInteger, GULNSDataZlibError) {
+  GULNSDataZlibErrorGreaterThan32BitsToCompress = 1024,
+  // An internal zlib error.
+  // GULNSDataZlibErrorKey will contain the error value.
+  // NSLocalizedDescriptionKey may contain an error string from zlib.
+  // Look in zlib.h for list of errors.
+  GULNSDataZlibErrorInternal,
+  // There was left over data in the buffer that was not used.
+  // GULNSDataZlibRemainingBytesKey will contain number of remaining bytes.
+  GULNSDataZlibErrorDataRemaining
+};
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/NSData+zlib/GULNSData+zlib.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/NSData+zlib/GULNSData+zlib.m
new file mode 100644 (file)
index 0000000..cd3394a
--- /dev/null
@@ -0,0 +1,207 @@
+// Copyright 2018 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "GULNSData+zlib.h"
+
+#import <zlib.h>
+
+#define kChunkSize 1024
+#define Z_DEFAULT_COMPRESSION (-1)
+
+NSString *const GULNSDataZlibErrorDomain = @"com.google.GULNSDataZlibErrorDomain";
+NSString *const GULNSDataZlibErrorKey = @"GULNSDataZlibErrorKey";
+NSString *const GULNSDataZlibRemainingBytesKey = @"GULNSDataZlibRemainingBytesKey";
+
+@implementation NSData (GULGzip)
+
++ (NSData *)gul_dataByInflatingGzippedData:(NSData *)data error:(NSError **)error {
+  const void *bytes = [data bytes];
+  NSUInteger length = [data length];
+  if (!bytes || !length) {
+    return nil;
+  }
+
+#if defined(__LP64__) && __LP64__
+  // Don't support > 32bit length for 64 bit, see note in header.
+  if (length > UINT_MAX) {
+    return nil;
+  }
+#endif
+
+  z_stream strm;
+  bzero(&strm, sizeof(z_stream));
+
+  // Setup the input.
+  strm.avail_in = (unsigned int)length;
+  strm.next_in = (unsigned char *)bytes;
+
+  int windowBits = 15;  // 15 to enable any window size
+  windowBits += 32;     // and +32 to enable zlib or gzip header detection.
+
+  int retCode;
+  if ((retCode = inflateInit2(&strm, windowBits)) != Z_OK) {
+    if (error) {
+      NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
+                                                           forKey:GULNSDataZlibErrorKey];
+      *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
+                                   code:GULNSDataZlibErrorInternal
+                               userInfo:userInfo];
+    }
+    return nil;
+  }
+
+  // Hint the size at 4x the input size.
+  NSMutableData *result = [NSMutableData dataWithCapacity:(length * 4)];
+  unsigned char output[kChunkSize];
+
+  // Loop to collect the data.
+  do {
+    // Update what we're passing in.
+    strm.avail_out = kChunkSize;
+    strm.next_out = output;
+    retCode = inflate(&strm, Z_NO_FLUSH);
+    if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
+      if (error) {
+        NSMutableDictionary *userInfo =
+            [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
+                                               forKey:GULNSDataZlibErrorKey];
+        if (strm.msg) {
+          NSString *message = [NSString stringWithUTF8String:strm.msg];
+          if (message) {
+            [userInfo setObject:message forKey:NSLocalizedDescriptionKey];
+          }
+        }
+        *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
+                                     code:GULNSDataZlibErrorInternal
+                                 userInfo:userInfo];
+      }
+      inflateEnd(&strm);
+      return nil;
+    }
+    // Collect what we got.
+    unsigned gotBack = kChunkSize - strm.avail_out;
+    if (gotBack > 0) {
+      [result appendBytes:output length:gotBack];
+    }
+
+  } while (retCode == Z_OK);
+
+  // Make sure there wasn't more data tacked onto the end of a valid compressed stream.
+  if (strm.avail_in != 0) {
+    if (error) {
+      NSDictionary *userInfo =
+          [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:strm.avail_in]
+                                      forKey:GULNSDataZlibRemainingBytesKey];
+      *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
+                                   code:GULNSDataZlibErrorDataRemaining
+                               userInfo:userInfo];
+    }
+    result = nil;
+  }
+  // The only way out of the loop was by hitting the end of the stream.
+  NSAssert(retCode == Z_STREAM_END,
+           @"Thought we finished inflate w/o getting a result of stream end, code %d", retCode);
+
+  // Clean up.
+  inflateEnd(&strm);
+
+  return result;
+}
+
++ (NSData *)gul_dataByGzippingData:(NSData *)data error:(NSError **)error {
+  const void *bytes = [data bytes];
+  NSUInteger length = [data length];
+
+  int level = Z_DEFAULT_COMPRESSION;
+  if (!bytes || !length) {
+    return nil;
+  }
+
+#if defined(__LP64__) && __LP64__
+  // Don't support > 32bit length for 64 bit, see note in header.
+  if (length > UINT_MAX) {
+    if (error) {
+      *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
+                                   code:GULNSDataZlibErrorGreaterThan32BitsToCompress
+                               userInfo:nil];
+    }
+    return nil;
+  }
+#endif
+
+  z_stream strm;
+  bzero(&strm, sizeof(z_stream));
+
+  int memLevel = 8;          // Default.
+  int windowBits = 15 + 16;  // Enable gzip header instead of zlib header.
+
+  int retCode;
+  if ((retCode = deflateInit2(&strm, level, Z_DEFLATED, windowBits, memLevel,
+                              Z_DEFAULT_STRATEGY)) != Z_OK) {
+    if (error) {
+      NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
+                                                           forKey:GULNSDataZlibErrorKey];
+      *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
+                                   code:GULNSDataZlibErrorInternal
+                               userInfo:userInfo];
+    }
+    return nil;
+  }
+
+  // Hint the size at 1/4 the input size.
+  NSMutableData *result = [NSMutableData dataWithCapacity:(length / 4)];
+  unsigned char output[kChunkSize];
+
+  // Setup the input.
+  strm.avail_in = (unsigned int)length;
+  strm.next_in = (unsigned char *)bytes;
+
+  // Collect the data.
+  do {
+    // update what we're passing in
+    strm.avail_out = kChunkSize;
+    strm.next_out = output;
+    retCode = deflate(&strm, Z_FINISH);
+    if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
+      if (error) {
+        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
+                                                             forKey:GULNSDataZlibErrorKey];
+        *error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
+                                     code:GULNSDataZlibErrorInternal
+                                 userInfo:userInfo];
+      }
+      deflateEnd(&strm);
+      return nil;
+    }
+    // Collect what we got.
+    unsigned gotBack = kChunkSize - strm.avail_out;
+    if (gotBack > 0) {
+      [result appendBytes:output length:gotBack];
+    }
+
+  } while (retCode == Z_OK);
+
+  // If the loop exits, it used all input and the stream ended.
+  NSAssert(strm.avail_in == 0,
+           @"Should have finished deflating without using all input, %u bytes left", strm.avail_in);
+  NSAssert(retCode == Z_STREAM_END,
+           @"thought we finished deflate w/o getting a result of stream end, code %d", retCode);
+
+  // Clean up.
+  deflateEnd(&strm);
+
+  return result;
+}
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULMutableDictionary.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULMutableDictionary.m
new file mode 100644 (file)
index 0000000..d281eb4
--- /dev/null
@@ -0,0 +1,97 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/GULMutableDictionary.h"
+
+@implementation GULMutableDictionary {
+  /// The mutable dictionary.
+  NSMutableDictionary *_objects;
+
+  /// Serial synchronization queue. All reads should use dispatch_sync, while writes use
+  /// dispatch_async.
+  dispatch_queue_t _queue;
+}
+
+- (instancetype)init {
+  self = [super init];
+
+  if (self) {
+    _objects = [[NSMutableDictionary alloc] init];
+    _queue = dispatch_queue_create("GULMutableDictionary", DISPATCH_QUEUE_SERIAL);
+  }
+
+  return self;
+}
+
+- (NSString *)description {
+  __block NSString *description;
+  dispatch_sync(_queue, ^{
+    description = self->_objects.description;
+  });
+  return description;
+}
+
+- (id)objectForKey:(id)key {
+  __block id object;
+  dispatch_sync(_queue, ^{
+    object = self->_objects[key];
+  });
+  return object;
+}
+
+- (void)setObject:(id)object forKey:(id<NSCopying>)key {
+  dispatch_async(_queue, ^{
+    self->_objects[key] = object;
+  });
+}
+
+- (void)removeObjectForKey:(id)key {
+  dispatch_async(_queue, ^{
+    [self->_objects removeObjectForKey:key];
+  });
+}
+
+- (void)removeAllObjects {
+  dispatch_async(_queue, ^{
+    [self->_objects removeAllObjects];
+  });
+}
+
+- (NSUInteger)count {
+  __block NSUInteger count;
+  dispatch_sync(_queue, ^{
+    count = self->_objects.count;
+  });
+  return count;
+}
+
+- (id)objectForKeyedSubscript:(id<NSCopying>)key {
+  // The method this calls is already synchronized.
+  return [self objectForKey:key];
+}
+
+- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
+  // The method this calls is already synchronized.
+  [self setObject:obj forKey:key];
+}
+
+- (NSDictionary *)dictionary {
+  __block NSDictionary *dictionary;
+  dispatch_sync(_queue, ^{
+    dictionary = [self->_objects copy];
+  });
+  return dictionary;
+}
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULNetwork.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULNetwork.m
new file mode 100644 (file)
index 0000000..233500b
--- /dev/null
@@ -0,0 +1,388 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/GULNetwork.h"
+#import "Private/GULNetworkMessageCode.h"
+
+#import <GoogleUtilities/GULLogger.h>
+#import <GoogleUtilities/GULNSData+zlib.h>
+#import <GoogleUtilities/GULReachabilityChecker.h>
+#import "Private/GULMutableDictionary.h"
+#import "Private/GULNetworkConstants.h"
+
+/// Constant string for request header Content-Encoding.
+static NSString *const kGULNetworkContentCompressionKey = @"Content-Encoding";
+
+/// Constant string for request header Content-Encoding value.
+static NSString *const kGULNetworkContentCompressionValue = @"gzip";
+
+/// Constant string for request header Content-Length.
+static NSString *const kGULNetworkContentLengthKey = @"Content-Length";
+
+/// Constant string for request header Content-Type.
+static NSString *const kGULNetworkContentTypeKey = @"Content-Type";
+
+/// Constant string for request header Content-Type value.
+static NSString *const kGULNetworkContentTypeValue = @"application/x-www-form-urlencoded";
+
+/// Constant string for GET request method.
+static NSString *const kGULNetworkGETRequestMethod = @"GET";
+
+/// Constant string for POST request method.
+static NSString *const kGULNetworkPOSTRequestMethod = @"POST";
+
+/// Default constant string as a prefix for network logger.
+static NSString *const kGULNetworkLogTag = @"Google/Utilities/Network";
+
+@interface GULNetwork () <GULReachabilityDelegate, GULNetworkLoggerDelegate>
+@end
+
+@implementation GULNetwork {
+  /// Network reachability.
+  GULReachabilityChecker *_reachability;
+
+  /// The dictionary of requests by session IDs { NSString : id }.
+  GULMutableDictionary *_requests;
+}
+
+- (instancetype)init {
+  return [self initWithReachabilityHost:kGULNetworkReachabilityHost];
+}
+
+- (instancetype)initWithReachabilityHost:(NSString *)reachabilityHost {
+  self = [super init];
+  if (self) {
+    // Setup reachability.
+    _reachability = [[GULReachabilityChecker alloc] initWithReachabilityDelegate:self
+                                                                        withHost:reachabilityHost];
+    if (![_reachability start]) {
+      return nil;
+    }
+
+    _requests = [[GULMutableDictionary alloc] init];
+    _timeoutInterval = kGULNetworkTimeOutInterval;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  _reachability.reachabilityDelegate = nil;
+  [_reachability stop];
+}
+
+#pragma mark - External Methods
+
++ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
+                            completionHandler:(GULNetworkSystemCompletionHandler)completionHandler {
+  [GULNetworkURLSession handleEventsForBackgroundURLSessionID:sessionID
+                                            completionHandler:completionHandler];
+}
+
+- (NSString *)postURL:(NSURL *)url
+                   payload:(NSData *)payload
+                     queue:(dispatch_queue_t)queue
+    usingBackgroundSession:(BOOL)usingBackgroundSession
+         completionHandler:(GULNetworkCompletionHandler)handler {
+  if (!url.absoluteString.length) {
+    [self handleErrorWithCode:GULErrorCodeNetworkInvalidURL queue:queue withHandler:handler];
+    return nil;
+  }
+
+  NSTimeInterval timeOutInterval = _timeoutInterval ?: kGULNetworkTimeOutInterval;
+
+  NSMutableURLRequest *request =
+      [[NSMutableURLRequest alloc] initWithURL:url
+                                   cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
+                               timeoutInterval:timeOutInterval];
+
+  if (!request) {
+    [self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
+                        queue:queue
+                  withHandler:handler];
+    return nil;
+  }
+
+  NSError *compressError = nil;
+  NSData *compressedData = [NSData gul_dataByGzippingData:payload error:&compressError];
+  if (!compressedData || compressError) {
+    if (compressError || payload.length > 0) {
+      // If the payload is not empty but it fails to compress the payload, something has been wrong.
+      [self handleErrorWithCode:GULErrorCodeNetworkPayloadCompression
+                          queue:queue
+                    withHandler:handler];
+      return nil;
+    }
+    compressedData = [[NSData alloc] init];
+  }
+
+  NSString *postLength = @(compressedData.length).stringValue;
+
+  // Set up the request with the compressed data.
+  [request setValue:postLength forHTTPHeaderField:kGULNetworkContentLengthKey];
+  request.HTTPBody = compressedData;
+  request.HTTPMethod = kGULNetworkPOSTRequestMethod;
+  [request setValue:kGULNetworkContentTypeValue forHTTPHeaderField:kGULNetworkContentTypeKey];
+  [request setValue:kGULNetworkContentCompressionValue
+      forHTTPHeaderField:kGULNetworkContentCompressionKey];
+
+  GULNetworkURLSession *fetcher = [[GULNetworkURLSession alloc] initWithNetworkLoggerDelegate:self];
+  fetcher.backgroundNetworkEnabled = usingBackgroundSession;
+
+  __weak GULNetwork *weakSelf = self;
+  NSString *requestID = [fetcher
+      sessionIDFromAsyncPOSTRequest:request
+                  completionHandler:^(NSHTTPURLResponse *response, NSData *data,
+                                      NSString *sessionID, NSError *error) {
+                    GULNetwork *strongSelf = weakSelf;
+                    if (!strongSelf) {
+                      return;
+                    }
+                    dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
+                    dispatch_async(queueToDispatch, ^{
+                      if (sessionID.length) {
+                        [strongSelf->_requests removeObjectForKey:sessionID];
+                      }
+                      if (handler) {
+                        handler(response, data, error);
+                      }
+                    });
+                  }];
+  if (!requestID) {
+    [self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
+                        queue:queue
+                  withHandler:handler];
+    return nil;
+  }
+
+  [self GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
+                    messageCode:kGULNetworkMessageCodeNetwork000
+                        message:@"Uploading data. Host"
+                        context:url];
+  _requests[requestID] = fetcher;
+  return requestID;
+}
+
+- (NSString *)getURL:(NSURL *)url
+                   headers:(NSDictionary *)headers
+                     queue:(dispatch_queue_t)queue
+    usingBackgroundSession:(BOOL)usingBackgroundSession
+         completionHandler:(GULNetworkCompletionHandler)handler {
+  if (!url.absoluteString.length) {
+    [self handleErrorWithCode:GULErrorCodeNetworkInvalidURL queue:queue withHandler:handler];
+    return nil;
+  }
+
+  NSTimeInterval timeOutInterval = _timeoutInterval ?: kGULNetworkTimeOutInterval;
+  NSMutableURLRequest *request =
+      [[NSMutableURLRequest alloc] initWithURL:url
+                                   cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
+                               timeoutInterval:timeOutInterval];
+
+  if (!request) {
+    [self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
+                        queue:queue
+                  withHandler:handler];
+    return nil;
+  }
+
+  request.HTTPMethod = kGULNetworkGETRequestMethod;
+  request.allHTTPHeaderFields = headers;
+
+  GULNetworkURLSession *fetcher = [[GULNetworkURLSession alloc] initWithNetworkLoggerDelegate:self];
+  fetcher.backgroundNetworkEnabled = usingBackgroundSession;
+
+  __weak GULNetwork *weakSelf = self;
+  NSString *requestID = [fetcher
+      sessionIDFromAsyncGETRequest:request
+                 completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSString *sessionID,
+                                     NSError *error) {
+                   GULNetwork *strongSelf = weakSelf;
+                   if (!strongSelf) {
+                     return;
+                   }
+                   dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
+                   dispatch_async(queueToDispatch, ^{
+                     if (sessionID.length) {
+                       [strongSelf->_requests removeObjectForKey:sessionID];
+                     }
+                     if (handler) {
+                       handler(response, data, error);
+                     }
+                   });
+                 }];
+
+  if (!requestID) {
+    [self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
+                        queue:queue
+                  withHandler:handler];
+    return nil;
+  }
+
+  [self GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
+                    messageCode:kGULNetworkMessageCodeNetwork001
+                        message:@"Downloading data. Host"
+                        context:url];
+  _requests[requestID] = fetcher;
+  return requestID;
+}
+
+- (BOOL)hasUploadInProgress {
+  return _requests.count > 0;
+}
+
+#pragma mark - Network Reachability
+
+/// Tells reachability delegate to call reachabilityDidChangeToStatus: to notify the network
+/// reachability has changed.
+- (void)reachability:(GULReachabilityChecker *)reachability
+       statusChanged:(GULReachabilityStatus)status {
+  _networkConnected = (status == kGULReachabilityViaCellular || status == kGULReachabilityViaWifi);
+  [_reachabilityDelegate reachabilityDidChange];
+}
+
+#pragma mark - Network logger delegate
+
+- (void)setLoggerDelegate:(id<GULNetworkLoggerDelegate>)loggerDelegate {
+  // Explicitly check whether the delegate responds to the methods because conformsToProtocol does
+  // not work correctly even though the delegate does respond to the methods.
+  if (!loggerDelegate ||
+      ![loggerDelegate
+          respondsToSelector:@selector(GULNetwork_logWithLevel:messageCode:message:contexts:)] ||
+      ![loggerDelegate
+          respondsToSelector:@selector(GULNetwork_logWithLevel:messageCode:message:context:)] ||
+      !
+      [loggerDelegate respondsToSelector:@selector(GULNetwork_logWithLevel:messageCode:message:)]) {
+    GULLogError(kGULLoggerNetwork, NO,
+                [NSString stringWithFormat:@"I-NET%06ld", (long)kGULNetworkMessageCodeNetwork002],
+                @"Cannot set the network logger delegate: delegate does not conform to the network "
+                 "logger protocol.");
+    return;
+  }
+  _loggerDelegate = loggerDelegate;
+}
+
+#pragma mark - Private methods
+
+/// Handles network error and calls completion handler with the error.
+- (void)handleErrorWithCode:(NSInteger)code
+                      queue:(dispatch_queue_t)queue
+                withHandler:(GULNetworkCompletionHandler)handler {
+  NSDictionary *userInfo = @{kGULNetworkErrorContext : @"Failed to create network request"};
+  NSError *error =
+      [[NSError alloc] initWithDomain:kGULNetworkErrorDomain code:code userInfo:userInfo];
+  [self GULNetwork_logWithLevel:kGULNetworkLogLevelWarning
+                    messageCode:kGULNetworkMessageCodeNetwork002
+                        message:@"Failed to create network request. Code, error"
+                       contexts:@[ @(code), error ]];
+  if (handler) {
+    dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
+    dispatch_async(queueToDispatch, ^{
+      handler(nil, nil, error);
+    });
+  }
+}
+
+#pragma mark - Network logger
+
+- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
+                    messageCode:(GULNetworkMessageCode)messageCode
+                        message:(NSString *)message
+                       contexts:(NSArray *)contexts {
+  // Let the delegate log the message if there is a valid logger delegate. Otherwise, just log
+  // errors/warnings/info messages to the console log.
+  if (_loggerDelegate) {
+    [_loggerDelegate GULNetwork_logWithLevel:logLevel
+                                 messageCode:messageCode
+                                     message:message
+                                    contexts:contexts];
+    return;
+  }
+  if (_isDebugModeEnabled || logLevel == kGULNetworkLogLevelError ||
+      logLevel == kGULNetworkLogLevelWarning || logLevel == kGULNetworkLogLevelInfo) {
+    NSString *formattedMessage = GULStringWithLogMessage(message, logLevel, contexts);
+    NSLog(@"%@", formattedMessage);
+    GULLogBasic((GULLoggerLevel)logLevel, kGULLoggerNetwork, NO,
+                [NSString stringWithFormat:@"I-NET%06ld", (long)messageCode], formattedMessage,
+                NULL);
+  }
+}
+
+- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
+                    messageCode:(GULNetworkMessageCode)messageCode
+                        message:(NSString *)message
+                        context:(id)context {
+  if (_loggerDelegate) {
+    [_loggerDelegate GULNetwork_logWithLevel:logLevel
+                                 messageCode:messageCode
+                                     message:message
+                                     context:context];
+    return;
+  }
+  NSArray *contexts = context ? @[ context ] : @[];
+  [self GULNetwork_logWithLevel:logLevel messageCode:messageCode message:message contexts:contexts];
+}
+
+- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
+                    messageCode:(GULNetworkMessageCode)messageCode
+                        message:(NSString *)message {
+  if (_loggerDelegate) {
+    [_loggerDelegate GULNetwork_logWithLevel:logLevel messageCode:messageCode message:message];
+    return;
+  }
+  [self GULNetwork_logWithLevel:logLevel messageCode:messageCode message:message contexts:@[]];
+}
+
+/// Returns a string for the given log level (e.g. kGULNetworkLogLevelError -> @"ERROR").
+static NSString *GULLogLevelDescriptionFromLogLevel(GULNetworkLogLevel logLevel) {
+  static NSDictionary *levelNames = nil;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    levelNames = @{
+      @(kGULNetworkLogLevelError) : @"ERROR",
+      @(kGULNetworkLogLevelWarning) : @"WARNING",
+      @(kGULNetworkLogLevelInfo) : @"INFO",
+      @(kGULNetworkLogLevelDebug) : @"DEBUG"
+    };
+  });
+  return levelNames[@(logLevel)];
+}
+
+/// Returns a formatted string to be used for console logging.
+static NSString *GULStringWithLogMessage(NSString *message,
+                                         GULNetworkLogLevel logLevel,
+                                         NSArray *contexts) {
+  if (!message) {
+    message = @"(Message was nil)";
+  } else if (!message.length) {
+    message = @"(Message was empty)";
+  }
+  NSMutableString *result = [[NSMutableString alloc]
+      initWithFormat:@"<%@/%@> %@", kGULNetworkLogTag, GULLogLevelDescriptionFromLogLevel(logLevel),
+                     message];
+
+  if (!contexts.count) {
+    return result;
+  }
+
+  NSMutableArray *formattedContexts = [[NSMutableArray alloc] init];
+  for (id item in contexts) {
+    [formattedContexts addObject:(item != [NSNull null] ? item : @"(nil)")];
+  }
+
+  [result appendString:@": "];
+  [result appendString:[formattedContexts componentsJoinedByString:@", "]];
+  return result;
+}
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkConstants.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkConstants.m
new file mode 100644 (file)
index 0000000..90bd03d
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/GULNetworkConstants.h"
+
+#import <Foundation/Foundation.h>
+
+NSString *const kGULNetworkBackgroundSessionConfigIDPrefix = @"com.gul.network.background-upload";
+NSString *const kGULNetworkApplicationSupportSubdirectory = @"GUL/Network";
+NSString *const kGULNetworkTempDirectoryName = @"GULNetworkTemporaryDirectory";
+const NSTimeInterval kGULNetworkTempFolderExpireTime = 60 * 60;  // 1 hour
+const NSTimeInterval kGULNetworkTimeOutInterval = 60;            // 1 minute.
+NSString *const kGULNetworkReachabilityHost = @"app-measurement.com";
+NSString *const kGULNetworkErrorContext = @"Context";
+
+const int kGULNetworkHTTPStatusOK = 200;
+const int kGULNetworkHTTPStatusNoContent = 204;
+const int kGULNetworkHTTPStatusCodeMultipleChoices = 300;
+const int kGULNetworkHTTPStatusCodeMovedPermanently = 301;
+const int kGULNetworkHTTPStatusCodeFound = 302;
+const int kGULNetworkHTTPStatusCodeNotModified = 304;
+const int kGULNetworkHTTPStatusCodeMovedTemporarily = 307;
+const int kGULNetworkHTTPStatusCodeNotFound = 404;
+const int kGULNetworkHTTPStatusCodeCannotAcceptTraffic = 429;
+const int kGULNetworkHTTPStatusCodeUnavailable = 503;
+
+NSString *const kGULNetworkErrorDomain = @"com.gul.network.ErrorDomain";
+
+GULLoggerService kGULLoggerNetwork = @"[GULNetwork]";
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkURLSession.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkURLSession.m
new file mode 100644 (file)
index 0000000..92e91a8
--- /dev/null
@@ -0,0 +1,680 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <Foundation/Foundation.h>
+
+#import "Private/GULNetworkURLSession.h"
+
+#import <GoogleUtilities/GULLogger.h>
+#import "Private/GULMutableDictionary.h"
+#import "Private/GULNetworkConstants.h"
+#import "Private/GULNetworkMessageCode.h"
+
+@implementation GULNetworkURLSession {
+  /// The handler to be called when the request completes or error has occurs.
+  GULNetworkURLSessionCompletionHandler _completionHandler;
+
+  /// Session ID generated randomly with a fixed prefix.
+  NSString *_sessionID;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+  /// The session configuration. NSURLSessionConfiguration' is only available on iOS 7.0 or newer.
+  NSURLSessionConfiguration *_sessionConfig;
+#pragma pop
+
+  /// The path to the directory where all temporary files are stored before uploading.
+  NSURL *_networkDirectoryURL;
+
+  /// The downloaded data from fetching.
+  NSData *_downloadedData;
+
+  /// The path to the temporary file which stores the uploading data.
+  NSURL *_uploadingFileURL;
+
+  /// The current request.
+  NSURLRequest *_request;
+}
+
+#pragma mark - Init
+
+- (instancetype)initWithNetworkLoggerDelegate:(id<GULNetworkLoggerDelegate>)networkLoggerDelegate {
+  self = [super init];
+  if (self) {
+    // Create URL to the directory where all temporary files to upload have to be stored.
+    NSArray *paths =
+        NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
+    NSString *applicationSupportDirectory = paths.firstObject;
+    NSArray *tempPathComponents = @[
+      applicationSupportDirectory, kGULNetworkApplicationSupportSubdirectory,
+      kGULNetworkTempDirectoryName
+    ];
+    _networkDirectoryURL = [NSURL fileURLWithPathComponents:tempPathComponents];
+    _sessionID = [NSString stringWithFormat:@"%@-%@", kGULNetworkBackgroundSessionConfigIDPrefix,
+                                            [[NSUUID UUID] UUIDString]];
+    _loggerDelegate = networkLoggerDelegate;
+  }
+  return self;
+}
+
+#pragma mark - External Methods
+
+#pragma mark - To be called from AppDelegate
+
++ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
+                            completionHandler:
+                                (GULNetworkSystemCompletionHandler)systemCompletionHandler {
+  // The session may not be Analytics background. Ignore those that do not have the prefix.
+  if (![sessionID hasPrefix:kGULNetworkBackgroundSessionConfigIDPrefix]) {
+    return;
+  }
+  GULNetworkURLSession *fetcher = [self fetcherWithSessionIdentifier:sessionID];
+  if (fetcher != nil) {
+    [fetcher addSystemCompletionHandler:systemCompletionHandler forSession:sessionID];
+  } else {
+    GULLogError(kGULLoggerNetwork, NO,
+                [NSString stringWithFormat:@"I-NET%06ld", (long)kGULNetworkMessageCodeNetwork003],
+                @"Failed to retrieve background session with ID %@ after app is relaunched.",
+                sessionID);
+  }
+}
+
+#pragma mark - External Methods
+
+/// Sends an async POST request using NSURLSession for iOS >= 7.0, and returns an ID of the
+/// connection.
+- (NSString *)sessionIDFromAsyncPOSTRequest:(NSURLRequest *)request
+                          completionHandler:(GULNetworkURLSessionCompletionHandler)handler
+    API_AVAILABLE(ios(7.0)) {
+  // NSURLSessionUploadTask does not work with NSData in the background.
+  // To avoid this issue, write the data to a temporary file to upload it.
+  // Make a temporary file with the data subset.
+  _uploadingFileURL = [self temporaryFilePathWithSessionID:_sessionID];
+  NSError *writeError;
+  NSURLSessionUploadTask *postRequestTask;
+  NSURLSession *session;
+  BOOL didWriteFile = NO;
+
+  // Clean up the entire temp folder to avoid temp files that remain in case the previous session
+  // crashed and did not clean up.
+  [self maybeRemoveTempFilesAtURL:_networkDirectoryURL
+                     expiringTime:kGULNetworkTempFolderExpireTime];
+
+  // If there is no background network enabled, no need to write to file. This will allow default
+  // network session which runs on the foreground.
+  if (_backgroundNetworkEnabled && [self ensureTemporaryDirectoryExists]) {
+    didWriteFile = [request.HTTPBody writeToFile:_uploadingFileURL.path
+                                         options:NSDataWritingAtomic
+                                           error:&writeError];
+
+    if (writeError) {
+      [_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
+                                   messageCode:kGULNetworkMessageCodeURLSession000
+                                       message:@"Failed to write request data to file"
+                                       context:writeError];
+    }
+  }
+
+  if (didWriteFile) {
+    // Exclude this file from backing up to iTunes. There are conflicting reports that excluding
+    // directory from backing up does not excluding files of that directory from backing up.
+    [self excludeFromBackupForURL:_uploadingFileURL];
+
+    _sessionConfig = [self backgroundSessionConfigWithSessionID:_sessionID];
+    [self populateSessionConfig:_sessionConfig withRequest:request];
+    session = [NSURLSession sessionWithConfiguration:_sessionConfig
+                                            delegate:self
+                                       delegateQueue:[NSOperationQueue mainQueue]];
+    postRequestTask = [session uploadTaskWithRequest:request fromFile:_uploadingFileURL];
+  } else {
+    // If we cannot write to file, just send it in the foreground.
+    _sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
+    [self populateSessionConfig:_sessionConfig withRequest:request];
+    _sessionConfig.URLCache = nil;
+    session = [NSURLSession sessionWithConfiguration:_sessionConfig
+                                            delegate:self
+                                       delegateQueue:[NSOperationQueue mainQueue]];
+    postRequestTask = [session uploadTaskWithRequest:request fromData:request.HTTPBody];
+  }
+
+  if (!session || !postRequestTask) {
+    NSError *error = [[NSError alloc]
+        initWithDomain:kGULNetworkErrorDomain
+                  code:GULErrorCodeNetworkRequestCreation
+              userInfo:@{kGULNetworkErrorContext : @"Cannot create network session"}];
+    [self callCompletionHandler:handler withResponse:nil data:nil error:error];
+    return nil;
+  }
+
+  // Save the session into memory.
+  NSMapTable *sessionIdentifierToFetcherMap = [[self class] sessionIDToFetcherMap];
+  [sessionIdentifierToFetcherMap setObject:self forKey:_sessionID];
+
+  _request = [request copy];
+
+  // Store completion handler because background session does not accept handler block but custom
+  // delegate.
+  _completionHandler = [handler copy];
+  [postRequestTask resume];
+
+  return _sessionID;
+}
+
+/// Sends an async GET request using NSURLSession for iOS >= 7.0, and returns an ID of the session.
+- (NSString *)sessionIDFromAsyncGETRequest:(NSURLRequest *)request
+                         completionHandler:(GULNetworkURLSessionCompletionHandler)handler
+    API_AVAILABLE(ios(7.0)) {
+  if (_backgroundNetworkEnabled) {
+    _sessionConfig = [self backgroundSessionConfigWithSessionID:_sessionID];
+  } else {
+    _sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
+  }
+
+  [self populateSessionConfig:_sessionConfig withRequest:request];
+
+  // Do not cache the GET request.
+  _sessionConfig.URLCache = nil;
+
+  NSURLSession *session = [NSURLSession sessionWithConfiguration:_sessionConfig
+                                                        delegate:self
+                                                   delegateQueue:[NSOperationQueue mainQueue]];
+  NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
+
+  if (!session || !downloadTask) {
+    NSError *error = [[NSError alloc]
+        initWithDomain:kGULNetworkErrorDomain
+                  code:GULErrorCodeNetworkRequestCreation
+              userInfo:@{kGULNetworkErrorContext : @"Cannot create network session"}];
+    [self callCompletionHandler:handler withResponse:nil data:nil error:error];
+    return nil;
+  }
+
+  // Save the session into memory.
+  NSMapTable *sessionIdentifierToFetcherMap = [[self class] sessionIDToFetcherMap];
+  [sessionIdentifierToFetcherMap setObject:self forKey:_sessionID];
+
+  _request = [request copy];
+
+  _completionHandler = [handler copy];
+  [downloadTask resume];
+
+  return _sessionID;
+}
+
+#pragma mark - NSURLSessionTaskDelegate
+
+/// Called by the NSURLSession once the download task is completed. The file is saved in the
+/// provided URL so we need to read the data and store into _downloadedData. Once the session is
+/// completed, URLSession:task:didCompleteWithError will be called and the completion handler will
+/// be called with the downloaded data.
+- (void)URLSession:(NSURLSession *)session
+                 downloadTask:(NSURLSessionDownloadTask *)task
+    didFinishDownloadingToURL:(NSURL *)url API_AVAILABLE(ios(7.0)) {
+  if (!url.path) {
+    [_loggerDelegate
+        GULNetwork_logWithLevel:kGULNetworkLogLevelError
+                    messageCode:kGULNetworkMessageCodeURLSession001
+                        message:@"Unable to read downloaded data from empty temp path"];
+    _downloadedData = nil;
+    return;
+  }
+
+  NSError *error;
+  _downloadedData = [NSData dataWithContentsOfFile:url.path options:0 error:&error];
+
+  if (error) {
+    [_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
+                                 messageCode:kGULNetworkMessageCodeURLSession002
+                                     message:@"Cannot read the content of downloaded data"
+                                     context:error];
+    _downloadedData = nil;
+  }
+}
+
+#if TARGET_OS_IOS || TARGET_OS_TV
+- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
+    API_AVAILABLE(ios(7.0)) {
+  [_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
+                               messageCode:kGULNetworkMessageCodeURLSession003
+                                   message:@"Background session finished"
+                                   context:session.configuration.identifier];
+  [self callSystemCompletionHandler:session.configuration.identifier];
+}
+#endif
+
+- (void)URLSession:(NSURLSession *)session
+                    task:(NSURLSessionTask *)task
+    didCompleteWithError:(NSError *)error API_AVAILABLE(ios(7.0)) {
+  // Avoid any chance of recursive behavior leading to it being used repeatedly.
+  GULNetworkURLSessionCompletionHandler handler = _completionHandler;
+  _completionHandler = nil;
+
+  if (task.response) {
+    // The following assertion should always be true for HTTP requests, see https://goo.gl/gVLxT7.
+    NSAssert([task.response isKindOfClass:[NSHTTPURLResponse class]], @"URL response must be HTTP");
+
+    // The server responded so ignore the error created by the system.
+    error = nil;
+  } else if (!error) {
+    error = [[NSError alloc]
+        initWithDomain:kGULNetworkErrorDomain
+                  code:GULErrorCodeNetworkInvalidResponse
+              userInfo:@{kGULNetworkErrorContext : @"Network Error: Empty network response"}];
+  }
+
+  [self callCompletionHandler:handler
+                 withResponse:(NSHTTPURLResponse *)task.response
+                         data:_downloadedData
+                        error:error];
+
+  // Remove the temp file to avoid trashing devices with lots of temp files.
+  [self removeTempItemAtURL:_uploadingFileURL];
+
+  // Try to clean up stale files again.
+  [self maybeRemoveTempFilesAtURL:_networkDirectoryURL
+                     expiringTime:kGULNetworkTempFolderExpireTime];
+}
+
+- (void)URLSession:(NSURLSession *)session
+                   task:(NSURLSessionTask *)task
+    didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
+      completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
+                                  NSURLCredential *credential))completionHandler
+    API_AVAILABLE(ios(7.0)) {
+  // The handling is modeled after GTMSessionFetcher.
+  if ([challenge.protectionSpace.authenticationMethod
+          isEqualToString:NSURLAuthenticationMethodServerTrust]) {
+    SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
+    if (serverTrust == NULL) {
+      [_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
+                                   messageCode:kGULNetworkMessageCodeURLSession004
+                                       message:@"Received empty server trust for host. Host"
+                                       context:_request.URL];
+      completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
+      return;
+    }
+    NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
+    if (!credential) {
+      [_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelWarning
+                                   messageCode:kGULNetworkMessageCodeURLSession005
+                                       message:@"Unable to verify server identity. Host"
+                                       context:_request.URL];
+      completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
+      return;
+    }
+
+    [_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
+                                 messageCode:kGULNetworkMessageCodeURLSession006
+                                     message:@"Received SSL challenge for host. Host"
+                                     context:_request.URL];
+
+    void (^callback)(BOOL) = ^(BOOL allow) {
+      if (allow) {
+        completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
+      } else {
+        [self->_loggerDelegate
+            GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
+                        messageCode:kGULNetworkMessageCodeURLSession007
+                            message:@"Cancelling authentication challenge for host. Host"
+                            context:self->_request.URL];
+        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
+      }
+    };
+
+    // Retain the trust object to avoid a SecTrustEvaluate() crash on iOS 7.
+    CFRetain(serverTrust);
+
+    // Evaluate the certificate chain.
+    //
+    // The delegate queue may be the main thread. Trust evaluation could cause some
+    // blocking network activity, so we must evaluate async, as documented at
+    // https://developer.apple.com/library/ios/technotes/tn2232/
+    dispatch_queue_t evaluateBackgroundQueue =
+        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+
+    dispatch_async(evaluateBackgroundQueue, ^{
+      SecTrustResultType trustEval = kSecTrustResultInvalid;
+      BOOL shouldAllow;
+      OSStatus trustError;
+
+      @synchronized([GULNetworkURLSession class]) {
+        trustError = SecTrustEvaluate(serverTrust, &trustEval);
+      }
+
+      if (trustError != errSecSuccess) {
+        [self->_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
+                                           messageCode:kGULNetworkMessageCodeURLSession008
+                                               message:@"Cannot evaluate server trust. Error, host"
+                                              contexts:@[ @(trustError), self->_request.URL ]];
+        shouldAllow = NO;
+      } else {
+        // Having a trust level "unspecified" by the user is the usual result, described at
+        // https://developer.apple.com/library/mac/qa/qa1360
+        shouldAllow =
+            (trustEval == kSecTrustResultUnspecified || trustEval == kSecTrustResultProceed);
+      }
+
+      // Call the call back with the permission.
+      callback(shouldAllow);
+
+      CFRelease(serverTrust);
+    });
+    return;
+  }
+
+  // Default handling for other Auth Challenges.
+  completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
+}
+
+#pragma mark - Internal Methods
+
+/// Stores system completion handler with session ID as key.
+- (void)addSystemCompletionHandler:(GULNetworkSystemCompletionHandler)handler
+                        forSession:(NSString *)identifier {
+  if (!handler) {
+    [_loggerDelegate
+        GULNetwork_logWithLevel:kGULNetworkLogLevelError
+                    messageCode:kGULNetworkMessageCodeURLSession009
+                        message:@"Cannot store nil system completion handler in network"];
+    return;
+  }
+
+  if (!identifier.length) {
+    [_loggerDelegate
+        GULNetwork_logWithLevel:kGULNetworkLogLevelError
+                    messageCode:kGULNetworkMessageCodeURLSession010
+                        message:
+                            @"Cannot store system completion handler with empty network "
+                             "session identifier"];
+    return;
+  }
+
+  GULMutableDictionary *systemCompletionHandlers =
+      [[self class] sessionIDToSystemCompletionHandlerDictionary];
+  if (systemCompletionHandlers[identifier]) {
+    [_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelWarning
+                                 messageCode:kGULNetworkMessageCodeURLSession011
+                                     message:@"Got multiple system handlers for a single session ID"
+                                     context:identifier];
+  }
+
+  systemCompletionHandlers[identifier] = handler;
+}
+
+/// Calls the system provided completion handler with the session ID stored in the dictionary.
+/// The handler will be removed from the dictionary after being called.
+- (void)callSystemCompletionHandler:(NSString *)identifier {
+  GULMutableDictionary *systemCompletionHandlers =
+      [[self class] sessionIDToSystemCompletionHandlerDictionary];
+  GULNetworkSystemCompletionHandler handler = [systemCompletionHandlers objectForKey:identifier];
+
+  if (handler) {
+    [systemCompletionHandlers removeObjectForKey:identifier];
+
+    dispatch_async(dispatch_get_main_queue(), ^{
+      handler();
+    });
+  }
+}
+
+/// Sets or updates the session ID of this session.
+- (void)setSessionID:(NSString *)sessionID {
+  _sessionID = [sessionID copy];
+}
+
+/// Creates a background session configuration with the session ID using the supported method.
+- (NSURLSessionConfiguration *)backgroundSessionConfigWithSessionID:(NSString *)sessionID
+    API_AVAILABLE(ios(7.0)) {
+#if (TARGET_OS_OSX && defined(MAC_OS_X_VERSION_10_10) &&         \
+     MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) || \
+    TARGET_OS_TV ||                                              \
+    (TARGET_OS_IOS && defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0)
+
+  // iOS 8/10.10 builds require the new backgroundSessionConfiguration method name.
+  return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessionID];
+
+#elif (TARGET_OS_OSX && defined(MAC_OS_X_VERSION_10_10) &&        \
+       MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10) || \
+    (TARGET_OS_IOS && defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0)
+
+  // Do a runtime check to avoid a deprecation warning about using
+  // +backgroundSessionConfiguration: on iOS 8.
+  if ([NSURLSessionConfiguration
+          respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) {
+    // Running on iOS 8+/OS X 10.10+.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+    return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessionID];
+#pragma clang diagnostic pop
+  } else {
+    // Running on iOS 7/OS X 10.9.
+    return [NSURLSessionConfiguration backgroundSessionConfiguration:sessionID];
+  }
+
+#else
+  // Building with an SDK earlier than iOS 8/OS X 10.10.
+  return [NSURLSessionConfiguration backgroundSessionConfiguration:sessionID];
+#endif
+}
+
+- (void)maybeRemoveTempFilesAtURL:(NSURL *)folderURL expiringTime:(NSTimeInterval)staleTime {
+  if (!folderURL.absoluteString.length) {
+    return;
+  }
+
+  NSFileManager *fileManager = [NSFileManager defaultManager];
+  NSError *error = nil;
+
+  NSArray *properties = @[ NSURLCreationDateKey ];
+  NSArray *directoryContent =
+      [fileManager contentsOfDirectoryAtURL:folderURL
+                 includingPropertiesForKeys:properties
+                                    options:NSDirectoryEnumerationSkipsSubdirectoryDescendants
+                                      error:&error];
+  if (error && error.code != NSFileReadNoSuchFileError) {
+    [_loggerDelegate
+        GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
+                    messageCode:kGULNetworkMessageCodeURLSession012
+                        message:@"Cannot get files from the temporary network folder. Error"
+                        context:error];
+    return;
+  }
+
+  if (!directoryContent.count) {
+    return;
+  }
+
+  NSTimeInterval now = [NSDate date].timeIntervalSince1970;
+  for (NSURL *tempFile in directoryContent) {
+    NSDate *creationDate;
+    BOOL getCreationDate =
+        [tempFile getResourceValue:&creationDate forKey:NSURLCreationDateKey error:NULL];
+    if (!getCreationDate) {
+      continue;
+    }
+    NSTimeInterval creationTimeInterval = creationDate.timeIntervalSince1970;
+    if (fabs(now - creationTimeInterval) > staleTime) {
+      [self removeTempItemAtURL:tempFile];
+    }
+  }
+}
+
+/// Removes the temporary file written to disk for sending the request. It has to be cleaned up
+/// after the session is done.
+- (void)removeTempItemAtURL:(NSURL *)fileURL {
+  if (!fileURL.absoluteString.length) {
+    return;
+  }
+
+  NSFileManager *fileManager = [NSFileManager defaultManager];
+  NSError *error = nil;
+
+  if (![fileManager removeItemAtURL:fileURL error:&error] && error.code != NSFileNoSuchFileError) {
+    [_loggerDelegate
+        GULNetwork_logWithLevel:kGULNetworkLogLevelError
+                    messageCode:kGULNetworkMessageCodeURLSession013
+                        message:@"Failed to remove temporary uploading data file. Error"
+                        context:error.localizedDescription];
+  }
+}
+
+/// Gets the fetcher with the session ID.
++ (instancetype)fetcherWithSessionIdentifier:(NSString *)sessionIdentifier {
+  NSMapTable *sessionIdentifierToFetcherMap = [self sessionIDToFetcherMap];
+  GULNetworkURLSession *session = [sessionIdentifierToFetcherMap objectForKey:sessionIdentifier];
+  if (!session && [sessionIdentifier hasPrefix:kGULNetworkBackgroundSessionConfigIDPrefix]) {
+    session = [[GULNetworkURLSession alloc] initWithNetworkLoggerDelegate:nil];
+    [session setSessionID:sessionIdentifier];
+    [sessionIdentifierToFetcherMap setObject:session forKey:sessionIdentifier];
+  }
+  return session;
+}
+
+/// Returns a map of the fetcher by session ID. Creates a map if it is not created.
++ (NSMapTable *)sessionIDToFetcherMap {
+  static NSMapTable *sessionIDToFetcherMap;
+
+  static dispatch_once_t sessionMapOnceToken;
+  dispatch_once(&sessionMapOnceToken, ^{
+    sessionIDToFetcherMap = [NSMapTable strongToWeakObjectsMapTable];
+  });
+  return sessionIDToFetcherMap;
+}
+
+/// Returns a map of system provided completion handler by session ID. Creates a map if it is not
+/// created.
++ (GULMutableDictionary *)sessionIDToSystemCompletionHandlerDictionary {
+  static GULMutableDictionary *systemCompletionHandlers;
+
+  static dispatch_once_t systemCompletionHandlerOnceToken;
+  dispatch_once(&systemCompletionHandlerOnceToken, ^{
+    systemCompletionHandlers = [[GULMutableDictionary alloc] init];
+  });
+  return systemCompletionHandlers;
+}
+
+- (NSURL *)temporaryFilePathWithSessionID:(NSString *)sessionID {
+  NSString *tempName = [NSString stringWithFormat:@"GULUpload_temp_%@", sessionID];
+  return [_networkDirectoryURL URLByAppendingPathComponent:tempName];
+}
+
+/// Makes sure that the directory to store temp files exists. If not, tries to create it and returns
+/// YES. If there is anything wrong, returns NO.
+- (BOOL)ensureTemporaryDirectoryExists {
+  NSFileManager *fileManager = [NSFileManager defaultManager];
+  NSError *error = nil;
+
+  // Create a temporary directory if it does not exist or was deleted.
+  if ([_networkDirectoryURL checkResourceIsReachableAndReturnError:&error]) {
+    return YES;
+  }
+
+  if (error && error.code != NSFileReadNoSuchFileError) {
+    [_loggerDelegate
+        GULNetwork_logWithLevel:kGULNetworkLogLevelWarning
+                    messageCode:kGULNetworkMessageCodeURLSession014
+                        message:@"Error while trying to access Network temp folder. Error"
+                        context:error];
+  }
+
+  NSError *writeError = nil;
+
+  [fileManager createDirectoryAtURL:_networkDirectoryURL
+        withIntermediateDirectories:YES
+                         attributes:nil
+                              error:&writeError];
+  if (writeError) {
+    [_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
+                                 messageCode:kGULNetworkMessageCodeURLSession015
+                                     message:@"Cannot create temporary directory. Error"
+                                     context:writeError];
+    return NO;
+  }
+
+  // Set the iCloud exclusion attribute on the Documents URL.
+  [self excludeFromBackupForURL:_networkDirectoryURL];
+
+  return YES;
+}
+
+- (void)excludeFromBackupForURL:(NSURL *)url {
+  if (!url.path) {
+    return;
+  }
+
+  // Set the iCloud exclusion attribute on the Documents URL.
+  NSError *preventBackupError = nil;
+  [url setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:&preventBackupError];
+  if (preventBackupError) {
+    [_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
+                                 messageCode:kGULNetworkMessageCodeURLSession016
+                                     message:@"Cannot exclude temporary folder from iTunes backup"];
+  }
+}
+
+- (void)URLSession:(NSURLSession *)session
+                          task:(NSURLSessionTask *)task
+    willPerformHTTPRedirection:(NSHTTPURLResponse *)response
+                    newRequest:(NSURLRequest *)request
+             completionHandler:(void (^)(NSURLRequest *))completionHandler API_AVAILABLE(ios(7.0)) {
+  NSArray *nonAllowedRedirectionCodes = @[
+    @(kGULNetworkHTTPStatusCodeFound), @(kGULNetworkHTTPStatusCodeMovedPermanently),
+    @(kGULNetworkHTTPStatusCodeMovedTemporarily), @(kGULNetworkHTTPStatusCodeMultipleChoices)
+  ];
+
+  // Allow those not in the non allowed list to be followed.
+  if (![nonAllowedRedirectionCodes containsObject:@(response.statusCode)]) {
+    completionHandler(request);
+    return;
+  }
+
+  // Do not allow redirection if the response code is in the non-allowed list.
+  NSURLRequest *newRequest = request;
+
+  if (response) {
+    newRequest = nil;
+  }
+
+  completionHandler(newRequest);
+}
+
+#pragma mark - Helper Methods
+
+- (void)callCompletionHandler:(GULNetworkURLSessionCompletionHandler)handler
+                 withResponse:(NSHTTPURLResponse *)response
+                         data:(NSData *)data
+                        error:(NSError *)error {
+  if (error) {
+    [_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
+                                 messageCode:kGULNetworkMessageCodeURLSession017
+                                     message:@"Encounter network error. Code, error"
+                                    contexts:@[ @(error.code), error ]];
+  }
+
+  if (handler) {
+    dispatch_async(dispatch_get_main_queue(), ^{
+      handler(response, data, self->_sessionID, error);
+    });
+  }
+}
+
+- (void)populateSessionConfig:(NSURLSessionConfiguration *)sessionConfig
+                  withRequest:(NSURLRequest *)request API_AVAILABLE(ios(7.0)) {
+  sessionConfig.HTTPAdditionalHeaders = request.allHTTPHeaderFields;
+  sessionConfig.timeoutIntervalForRequest = request.timeoutInterval;
+  sessionConfig.timeoutIntervalForResource = request.timeoutInterval;
+  sessionConfig.requestCachePolicy = request.cachePolicy;
+}
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULMutableDictionary.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULMutableDictionary.h
new file mode 100644 (file)
index 0000000..a8cc45b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+/// A mutable dictionary that provides atomic accessor and mutators.
+@interface GULMutableDictionary : NSObject
+
+/// Returns an object given a key in the dictionary or nil if not found.
+- (id)objectForKey:(id)key;
+
+/// Updates the object given its key or adds it to the dictionary if it is not in the dictionary.
+- (void)setObject:(id)object forKey:(id<NSCopying>)key;
+
+/// Removes the object given its session ID from the dictionary.
+- (void)removeObjectForKey:(id)key;
+
+/// Removes all objects.
+- (void)removeAllObjects;
+
+/// Returns the number of current objects in the dictionary.
+- (NSUInteger)count;
+
+/// Returns an object given a key in the dictionary or nil if not found.
+- (id)objectForKeyedSubscript:(id<NSCopying>)key;
+
+/// Updates the object given its key or adds it to the dictionary if it is not in the dictionary.
+- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key;
+
+/// Returns the immutable dictionary.
+- (NSDictionary *)dictionary;
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetwork.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetwork.h
new file mode 100644 (file)
index 0000000..0e75ae5
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#import "GULNetworkConstants.h"
+#import "GULNetworkLoggerProtocol.h"
+#import "GULNetworkURLSession.h"
+
+/// Delegate protocol for GULNetwork events.
+@protocol GULNetworkReachabilityDelegate
+
+/// Tells the delegate to handle events when the network reachability changes to connected or not
+/// connected.
+- (void)reachabilityDidChange;
+
+@end
+
+/// The Network component that provides network status and handles network requests and responses.
+/// This is not thread safe.
+///
+/// NOTE:
+/// User must add FIRAnalytics handleEventsForBackgroundURLSessionID:completionHandler to the
+/// AppDelegate application:handleEventsForBackgroundURLSession:completionHandler:
+@interface GULNetwork : NSObject
+
+/// Indicates if network connectivity is available.
+@property(nonatomic, readonly, getter=isNetworkConnected) BOOL networkConnected;
+
+/// Indicates if there are any uploads in progress.
+@property(nonatomic, readonly, getter=hasUploadInProgress) BOOL uploadInProgress;
+
+/// An optional delegate that can be used in the event when network reachability changes.
+@property(nonatomic, weak) id<GULNetworkReachabilityDelegate> reachabilityDelegate;
+
+/// An optional delegate that can be used to log messages, warnings or errors that occur in the
+/// network operations.
+@property(nonatomic, weak) id<GULNetworkLoggerDelegate> loggerDelegate;
+
+/// Indicates whether the logger should display debug messages.
+@property(nonatomic, assign) BOOL isDebugModeEnabled;
+
+/// The time interval in seconds for the network request to timeout.
+@property(nonatomic, assign) NSTimeInterval timeoutInterval;
+
+/// Initializes with the default reachability host.
+- (instancetype)init;
+
+/// Initializes with a custom reachability host.
+- (instancetype)initWithReachabilityHost:(NSString *)reachabilityHost;
+
+/// Handles events when background session with the given ID has finished.
++ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
+                            completionHandler:(GULNetworkSystemCompletionHandler)completionHandler;
+
+/// Compresses and sends a POST request with the provided data to the URL. The session will be
+/// background session if usingBackgroundSession is YES. Otherwise, the POST session is default
+/// session. Returns a session ID or nil if an error occurs.
+- (NSString *)postURL:(NSURL *)url
+                   payload:(NSData *)payload
+                     queue:(dispatch_queue_t)queue
+    usingBackgroundSession:(BOOL)usingBackgroundSession
+         completionHandler:(GULNetworkCompletionHandler)handler;
+
+/// Sends a GET request with the provided data to the URL. The session will be background session
+/// if usingBackgroundSession is YES. Otherwise, the GET session is default session. Returns a
+/// session ID or nil if an error occurs.
+- (NSString *)getURL:(NSURL *)url
+                   headers:(NSDictionary *)headers
+                     queue:(dispatch_queue_t)queue
+    usingBackgroundSession:(BOOL)usingBackgroundSession
+         completionHandler:(GULNetworkCompletionHandler)handler;
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkConstants.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkConstants.h
new file mode 100644 (file)
index 0000000..44d440b
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+#import <GoogleUtilities/GULLogger.h>
+
+/// Error codes in Firebase Network error domain.
+/// Note: these error codes should never change. It would make it harder to decode the errors if
+/// we inadvertently altered any of these codes in a future SDK version.
+typedef NS_ENUM(NSInteger, GULNetworkErrorCode) {
+  /// Unknown error.
+  GULNetworkErrorCodeUnknown = 0,
+  /// Error occurs when the request URL is invalid.
+  GULErrorCodeNetworkInvalidURL = 1,
+  /// Error occurs when request cannot be constructed.
+  GULErrorCodeNetworkRequestCreation = 2,
+  /// Error occurs when payload cannot be compressed.
+  GULErrorCodeNetworkPayloadCompression = 3,
+  /// Error occurs when session task cannot be created.
+  GULErrorCodeNetworkSessionTaskCreation = 4,
+  /// Error occurs when there is no response.
+  GULErrorCodeNetworkInvalidResponse = 5
+};
+
+#pragma mark - Network constants
+
+/// The prefix of the ID of the background session.
+extern NSString *const kGULNetworkBackgroundSessionConfigIDPrefix;
+
+/// The sub directory to store the files of data that is being uploaded in the background.
+extern NSString *const kGULNetworkApplicationSupportSubdirectory;
+
+/// Name of the temporary directory that stores files for background uploading.
+extern NSString *const kGULNetworkTempDirectoryName;
+
+/// The period when the temporary uploading file can stay.
+extern const NSTimeInterval kGULNetworkTempFolderExpireTime;
+
+/// The default network request timeout interval.
+extern const NSTimeInterval kGULNetworkTimeOutInterval;
+
+/// The host to check the reachability of the network.
+extern NSString *const kGULNetworkReachabilityHost;
+
+/// The key to get the error context of the UserInfo.
+extern NSString *const kGULNetworkErrorContext;
+
+#pragma mark - Network Status Code
+
+extern const int kGULNetworkHTTPStatusOK;
+extern const int kGULNetworkHTTPStatusNoContent;
+extern const int kGULNetworkHTTPStatusCodeMultipleChoices;
+extern const int kGULNetworkHTTPStatusCodeMovedPermanently;
+extern const int kGULNetworkHTTPStatusCodeFound;
+extern const int kGULNetworkHTTPStatusCodeNotModified;
+extern const int kGULNetworkHTTPStatusCodeMovedTemporarily;
+extern const int kGULNetworkHTTPStatusCodeNotFound;
+extern const int kGULNetworkHTTPStatusCodeCannotAcceptTraffic;
+extern const int kGULNetworkHTTPStatusCodeUnavailable;
+
+#pragma mark - Error Domain
+
+extern NSString *const kGULNetworkErrorDomain;
+
+/// The logger service for GULNetwork.
+extern GULLoggerService kGULLoggerNetwork;
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkLoggerProtocol.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkLoggerProtocol.h
new file mode 100644 (file)
index 0000000..f1be590
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#import <GoogleUtilities/GULLoggerLevel.h>
+
+#import "GULNetworkMessageCode.h"
+
+/// The log levels used by GULNetworkLogger.
+typedef NS_ENUM(NSInteger, GULNetworkLogLevel) {
+  kGULNetworkLogLevelError = GULLoggerLevelError,
+  kGULNetworkLogLevelWarning = GULLoggerLevelWarning,
+  kGULNetworkLogLevelInfo = GULLoggerLevelInfo,
+  kGULNetworkLogLevelDebug = GULLoggerLevelDebug,
+};
+
+@protocol GULNetworkLoggerDelegate <NSObject>
+
+@required
+/// Tells the delegate to log a message with an array of contexts and the log level.
+- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
+                    messageCode:(GULNetworkMessageCode)messageCode
+                        message:(NSString *)message
+                       contexts:(NSArray *)contexts;
+
+/// Tells the delegate to log a message with a context and the log level.
+- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
+                    messageCode:(GULNetworkMessageCode)messageCode
+                        message:(NSString *)message
+                        context:(id)context;
+
+/// Tells the delegate to log a message with the log level.
+- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
+                    messageCode:(GULNetworkMessageCode)messageCode
+                        message:(NSString *)message;
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkMessageCode.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkMessageCode.h
new file mode 100644 (file)
index 0000000..ce78e60
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Make sure these codes do not overlap with any contained in the FIRAMessageCode enum.
+typedef NS_ENUM(NSInteger, GULNetworkMessageCode) {
+  // GULNetwork.m
+  kGULNetworkMessageCodeNetwork000 = 900000,  // I-NET900000
+  kGULNetworkMessageCodeNetwork001 = 900001,  // I-NET900001
+  kGULNetworkMessageCodeNetwork002 = 900002,  // I-NET900002
+  kGULNetworkMessageCodeNetwork003 = 900003,  // I-NET900003
+  // GULNetworkURLSession.m
+  kGULNetworkMessageCodeURLSession000 = 901000,  // I-NET901000
+  kGULNetworkMessageCodeURLSession001 = 901001,  // I-NET901001
+  kGULNetworkMessageCodeURLSession002 = 901002,  // I-NET901002
+  kGULNetworkMessageCodeURLSession003 = 901003,  // I-NET901003
+  kGULNetworkMessageCodeURLSession004 = 901004,  // I-NET901004
+  kGULNetworkMessageCodeURLSession005 = 901005,  // I-NET901005
+  kGULNetworkMessageCodeURLSession006 = 901006,  // I-NET901006
+  kGULNetworkMessageCodeURLSession007 = 901007,  // I-NET901007
+  kGULNetworkMessageCodeURLSession008 = 901008,  // I-NET901008
+  kGULNetworkMessageCodeURLSession009 = 901009,  // I-NET901009
+  kGULNetworkMessageCodeURLSession010 = 901010,  // I-NET901010
+  kGULNetworkMessageCodeURLSession011 = 901011,  // I-NET901011
+  kGULNetworkMessageCodeURLSession012 = 901012,  // I-NET901012
+  kGULNetworkMessageCodeURLSession013 = 901013,  // I-NET901013
+  kGULNetworkMessageCodeURLSession014 = 901014,  // I-NET901014
+  kGULNetworkMessageCodeURLSession015 = 901015,  // I-NET901015
+  kGULNetworkMessageCodeURLSession016 = 901016,  // I-NET901016
+  kGULNetworkMessageCodeURLSession017 = 901017,  // I-NET901017
+  kGULNetworkMessageCodeURLSession018 = 901018,  // I-NET901018
+};
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkURLSession.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkURLSession.h
new file mode 100644 (file)
index 0000000..81190c6
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+
+#import "GULNetworkLoggerProtocol.h"
+
+typedef void (^GULNetworkCompletionHandler)(NSHTTPURLResponse *response,
+                                            NSData *data,
+                                            NSError *error);
+typedef void (^GULNetworkURLSessionCompletionHandler)(NSHTTPURLResponse *response,
+                                                      NSData *data,
+                                                      NSString *sessionID,
+                                                      NSError *error);
+typedef void (^GULNetworkSystemCompletionHandler)(void);
+
+/// The protocol that uses NSURLSession for iOS >= 7.0 to handle requests and responses.
+@interface GULNetworkURLSession
+    : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate>
+
+/// Indicates whether the background network is enabled. Default value is NO.
+@property(nonatomic, getter=isBackgroundNetworkEnabled) BOOL backgroundNetworkEnabled;
+
+/// The logger delegate to log message, errors or warnings that occur during the network operations.
+@property(nonatomic, weak) id<GULNetworkLoggerDelegate> loggerDelegate;
+
+/// Calls the system provided completion handler after the background session is finished.
++ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
+                            completionHandler:(GULNetworkSystemCompletionHandler)completionHandler;
+
+/// Initializes with logger delegate.
+- (instancetype)initWithNetworkLoggerDelegate:(id<GULNetworkLoggerDelegate>)networkLoggerDelegate
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+/// Sends an asynchronous POST request and calls the provided completion handler when the request
+/// completes or when errors occur, and returns an ID of the session/connection.
+- (NSString *)sessionIDFromAsyncPOSTRequest:(NSURLRequest *)request
+                          completionHandler:(GULNetworkURLSessionCompletionHandler)handler;
+
+/// Sends an asynchronous GET request and calls the provided completion handler when the request
+/// completes or when errors occur, and returns an ID of the session.
+- (NSString *)sessionIDFromAsyncGETRequest:(NSURLRequest *)request
+                         completionHandler:(GULNetworkURLSessionCompletionHandler)handler;
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h
new file mode 100644 (file)
index 0000000..8883c4d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <GoogleUtilities/GULReachabilityChecker.h>
+
+typedef SCNetworkReachabilityRef (*GULReachabilityCreateWithNameFn)(CFAllocatorRef allocator,
+                                                                    const char *host);
+
+typedef Boolean (*GULReachabilitySetCallbackFn)(SCNetworkReachabilityRef target,
+                                                SCNetworkReachabilityCallBack callback,
+                                                SCNetworkReachabilityContext *context);
+typedef Boolean (*GULReachabilityScheduleWithRunLoopFn)(SCNetworkReachabilityRef target,
+                                                        CFRunLoopRef runLoop,
+                                                        CFStringRef runLoopMode);
+typedef Boolean (*GULReachabilityUnscheduleFromRunLoopFn)(SCNetworkReachabilityRef target,
+                                                          CFRunLoopRef runLoop,
+                                                          CFStringRef runLoopMode);
+
+typedef void (*GULReachabilityReleaseFn)(CFTypeRef cf);
+
+struct GULReachabilityApi {
+  GULReachabilityCreateWithNameFn createWithNameFn;
+  GULReachabilitySetCallbackFn setCallbackFn;
+  GULReachabilityScheduleWithRunLoopFn scheduleWithRunLoopFn;
+  GULReachabilityUnscheduleFromRunLoopFn unscheduleFromRunLoopFn;
+  GULReachabilityReleaseFn releaseFn;
+};
+
+@interface GULReachabilityChecker (Internal)
+
+- (const struct GULReachabilityApi *)reachabilityApi;
+- (void)setReachabilityApi:(const struct GULReachabilityApi *)reachabilityApi;
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityChecker.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityChecker.m
new file mode 100644 (file)
index 0000000..1ddacdf
--- /dev/null
@@ -0,0 +1,240 @@
+// Copyright 2017 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <Foundation/Foundation.h>
+
+#import "GULReachabilityChecker+Internal.h"
+#import "Private/GULReachabilityChecker.h"
+#import "Private/GULReachabilityMessageCode.h"
+
+#import <GoogleUtilities/GULLogger.h>
+#import <GoogleUtilities/GULReachabilityChecker.h>
+
+static GULLoggerService kGULLoggerReachability = @"[GULReachability]";
+
+static void ReachabilityCallback(SCNetworkReachabilityRef reachability,
+                                 SCNetworkReachabilityFlags flags,
+                                 void *info);
+
+static const struct GULReachabilityApi kGULDefaultReachabilityApi = {
+    SCNetworkReachabilityCreateWithName,
+    SCNetworkReachabilitySetCallback,
+    SCNetworkReachabilityScheduleWithRunLoop,
+    SCNetworkReachabilityUnscheduleFromRunLoop,
+    CFRelease,
+};
+
+static NSString *const kGULReachabilityUnknownStatus = @"Unknown";
+static NSString *const kGULReachabilityConnectedStatus = @"Connected";
+static NSString *const kGULReachabilityDisconnectedStatus = @"Disconnected";
+
+@interface GULReachabilityChecker ()
+
+@property(nonatomic, assign) const struct GULReachabilityApi *reachabilityApi;
+@property(nonatomic, assign) GULReachabilityStatus reachabilityStatus;
+@property(nonatomic, copy) NSString *host;
+@property(nonatomic, assign) SCNetworkReachabilityRef reachability;
+
+@end
+
+@implementation GULReachabilityChecker
+
+@synthesize reachabilityApi = reachabilityApi_;
+@synthesize reachability = reachability_;
+
+- (const struct GULReachabilityApi *)reachabilityApi {
+  return reachabilityApi_;
+}
+
+- (void)setReachabilityApi:(const struct GULReachabilityApi *)reachabilityApi {
+  if (reachability_) {
+    GULLogError(kGULLoggerReachability, NO,
+                [NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode000],
+                @"Cannot change reachability API while reachability is running. "
+                @"Call stop first.");
+    return;
+  }
+  reachabilityApi_ = reachabilityApi;
+}
+
+@synthesize reachabilityStatus = reachabilityStatus_;
+@synthesize host = host_;
+@synthesize reachabilityDelegate = reachabilityDelegate_;
+
+- (BOOL)isActive {
+  return reachability_ != nil;
+}
+
+- (void)setReachabilityDelegate:(id<GULReachabilityDelegate>)reachabilityDelegate {
+  if (reachabilityDelegate &&
+      (![(NSObject *)reachabilityDelegate conformsToProtocol:@protocol(GULReachabilityDelegate)])) {
+    GULLogError(kGULLoggerReachability, NO,
+                [NSString stringWithFormat:@"I-NET%06ld", (long)kGULReachabilityMessageCode005],
+                @"Reachability delegate doesn't conform to Reachability protocol.");
+    return;
+  }
+  reachabilityDelegate_ = reachabilityDelegate;
+}
+
+- (instancetype)initWithReachabilityDelegate:(id<GULReachabilityDelegate>)reachabilityDelegate
+                                    withHost:(NSString *)host {
+  self = [super init];
+
+  if (!host || !host.length) {
+    GULLogError(kGULLoggerReachability, NO,
+                [NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode001],
+                @"Invalid host specified");
+    return nil;
+  }
+  if (self) {
+    [self setReachabilityDelegate:reachabilityDelegate];
+    reachabilityApi_ = &kGULDefaultReachabilityApi;
+    reachabilityStatus_ = kGULReachabilityUnknown;
+    host_ = [host copy];
+    reachability_ = nil;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  reachabilityDelegate_ = nil;
+  [self stop];
+}
+
+- (BOOL)start {
+  if (!reachability_) {
+    reachability_ = reachabilityApi_->createWithNameFn(kCFAllocatorDefault, [host_ UTF8String]);
+    if (!reachability_) {
+      return NO;
+    }
+    SCNetworkReachabilityContext context = {
+        0,                       /* version */
+        (__bridge void *)(self), /* info (passed as last parameter to reachability callback) */
+        NULL,                    /* retain */
+        NULL,                    /* release */
+        NULL                     /* copyDescription */
+    };
+    if (!reachabilityApi_->setCallbackFn(reachability_, ReachabilityCallback, &context) ||
+        !reachabilityApi_->scheduleWithRunLoopFn(reachability_, CFRunLoopGetMain(),
+                                                 kCFRunLoopCommonModes)) {
+      reachabilityApi_->releaseFn(reachability_);
+      reachability_ = nil;
+
+      GULLogError(kGULLoggerReachability, NO,
+                  [NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode002],
+                  @"Failed to start reachability handle");
+      return NO;
+    }
+  }
+  GULLogDebug(kGULLoggerReachability, NO,
+              [NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode003],
+              @"Monitoring the network status");
+  return YES;
+}
+
+- (void)stop {
+  if (reachability_) {
+    reachabilityStatus_ = kGULReachabilityUnknown;
+    reachabilityApi_->unscheduleFromRunLoopFn(reachability_, CFRunLoopGetMain(),
+                                              kCFRunLoopCommonModes);
+    reachabilityApi_->releaseFn(reachability_);
+    reachability_ = nil;
+  }
+}
+
+- (GULReachabilityStatus)statusForFlags:(SCNetworkReachabilityFlags)flags {
+  GULReachabilityStatus status = kGULReachabilityNotReachable;
+  // If the Reachable flag is not set, we definitely don't have connectivity.
+  if (flags & kSCNetworkReachabilityFlagsReachable) {
+    // Reachable flag is set. Check further flags.
+    if (!(flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
+// Connection required flag is not set, so we have connectivity.
+#if TARGET_OS_IOS || TARGET_OS_TV
+      status = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kGULReachabilityViaCellular
+                                                           : kGULReachabilityViaWifi;
+#elif TARGET_OS_OSX
+      status = kGULReachabilityViaWifi;
+#endif
+    } else if ((flags & (kSCNetworkReachabilityFlagsConnectionOnDemand |
+                         kSCNetworkReachabilityFlagsConnectionOnTraffic)) &&
+               !(flags & kSCNetworkReachabilityFlagsInterventionRequired)) {
+// If the connection on demand or connection on traffic flag is set, and user intervention
+// is not required, we have connectivity.
+#if TARGET_OS_IOS || TARGET_OS_TV
+      status = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kGULReachabilityViaCellular
+                                                           : kGULReachabilityViaWifi;
+#elif TARGET_OS_OSX
+      status = kGULReachabilityViaWifi;
+#endif
+    }
+  }
+  return status;
+}
+
+- (void)reachabilityFlagsChanged:(SCNetworkReachabilityFlags)flags {
+  GULReachabilityStatus status = [self statusForFlags:flags];
+  if (reachabilityStatus_ != status) {
+    NSString *reachabilityStatusString;
+    if (status == kGULReachabilityUnknown) {
+      reachabilityStatusString = kGULReachabilityUnknownStatus;
+    } else {
+      reachabilityStatusString = (status == kGULReachabilityNotReachable)
+                                     ? kGULReachabilityDisconnectedStatus
+                                     : kGULReachabilityConnectedStatus;
+    }
+
+    GULLogDebug(kGULLoggerReachability, NO,
+                [NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode004],
+                @"Network status has changed. Code:%@, status:%@", @(status),
+                reachabilityStatusString);
+    reachabilityStatus_ = status;
+    [reachabilityDelegate_ reachability:self statusChanged:reachabilityStatus_];
+  }
+}
+
+@end
+
+static void ReachabilityCallback(SCNetworkReachabilityRef reachability,
+                                 SCNetworkReachabilityFlags flags,
+                                 void *info) {
+  GULReachabilityChecker *checker = (__bridge GULReachabilityChecker *)info;
+  [checker reachabilityFlagsChanged:flags];
+}
+
+// This function used to be at the top of the file, but it was moved here
+// as a workaround for a suspected compiler bug. When compiled in Release mode
+// and run on an iOS device with WiFi disabled, the reachability code crashed
+// when calling SCNetworkReachabilityScheduleWithRunLoop, or shortly thereafter.
+// After unsuccessfully trying to diagnose the cause of the crash, it was
+// discovered that moving this function to the end of the file magically fixed
+// the crash. If you are going to edit this file, exercise caution and make sure
+// to test thoroughly with an iOS device under various network conditions.
+const NSString *GULReachabilityStatusString(GULReachabilityStatus status) {
+  switch (status) {
+    case kGULReachabilityUnknown:
+      return @"Reachability Unknown";
+
+    case kGULReachabilityNotReachable:
+      return @"Not reachable";
+
+    case kGULReachabilityViaWifi:
+      return @"Reachable via Wifi";
+
+    case kGULReachabilityViaCellular:
+      return @"Reachable via Cellular Data";
+
+    default:
+      return [NSString stringWithFormat:@"Invalid reachability status %d", (int)status];
+  }
+}
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/Private/GULReachabilityChecker.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/Private/GULReachabilityChecker.h
new file mode 100644 (file)
index 0000000..b317a0b
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <Foundation/Foundation.h>
+#import <SystemConfiguration/SystemConfiguration.h>
+
+/// Reachability Status
+typedef enum {
+  kGULReachabilityUnknown,  ///< Have not yet checked or been notified whether host is reachable.
+  kGULReachabilityNotReachable,  ///< Host is not reachable.
+  kGULReachabilityViaWifi,       ///< Host is reachable via Wifi.
+  kGULReachabilityViaCellular,   ///< Host is reachable via cellular.
+} GULReachabilityStatus;
+
+const NSString *GULReachabilityStatusString(GULReachabilityStatus status);
+
+@class GULReachabilityChecker;
+
+/// Google Analytics iOS Reachability Checker.
+@protocol GULReachabilityDelegate
+@required
+/// Called when network status has changed.
+- (void)reachability:(GULReachabilityChecker *)reachability
+       statusChanged:(GULReachabilityStatus)status;
+@end
+
+/// Google Analytics iOS Network Status Checker.
+@interface GULReachabilityChecker : NSObject
+
+/// The last known reachability status, or GULReachabilityStatusUnknown if the
+/// checker is not active.
+@property(nonatomic, readonly) GULReachabilityStatus reachabilityStatus;
+/// The host to which reachability status is to be checked.
+@property(nonatomic, copy, readonly) NSString *host;
+/// The delegate to be notified of reachability status changes.
+@property(nonatomic, weak) id<GULReachabilityDelegate> reachabilityDelegate;
+/// `YES` if the reachability checker is active, `NO` otherwise.
+@property(nonatomic, readonly) BOOL isActive;
+
+/// Initialize the reachability checker. Note that you must call start to begin checking for and
+/// receiving notifications about network status changes.
+///
+/// @param reachabilityDelegate The delegate to be notified when reachability status to host
+/// changes.
+///
+/// @param host The name of the host.
+///
+- (instancetype)initWithReachabilityDelegate:(id<GULReachabilityDelegate>)reachabilityDelegate
+                                    withHost:(NSString *)host;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+/// Start checking for reachability to the specified host. This has no effect if the status
+/// checker is already checking for connectivity.
+///
+/// @return `YES` if initiating status checking was successful or the status checking has already
+/// been initiated, `NO` otherwise.
+- (BOOL)start;
+
+/// Stop checking for reachability to the specified host. This has no effect if the status
+/// checker is not checking for connectivity.
+- (void)stop;
+
+@end
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/Private/GULReachabilityMessageCode.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/Reachability/Private/GULReachabilityMessageCode.h
new file mode 100644 (file)
index 0000000..283cdd5
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Make sure these codes do not overlap with any contained in the FIRAMessageCode enum.
+typedef NS_ENUM(NSInteger, GULReachabilityMessageCode) {
+  // GULReachabilityChecker.m
+  kGULReachabilityMessageCode000 = 902000,  // I-NET902000
+  kGULReachabilityMessageCode001 = 902001,  // I-NET902001
+  kGULReachabilityMessageCode002 = 902002,  // I-NET902002
+  kGULReachabilityMessageCode003 = 902003,  // I-NET902003
+  kGULReachabilityMessageCode004 = 902004,  // I-NET902004
+  kGULReachabilityMessageCode005 = 902005,  // I-NET902005
+  kGULReachabilityMessageCode006 = 902006,  // I-NET902006
+};
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/UserDefaults/GULUserDefaults.m b/iOS/Pods/GoogleUtilities/GoogleUtilities/UserDefaults/GULUserDefaults.m
new file mode 100644 (file)
index 0000000..1b1bafb
--- /dev/null
@@ -0,0 +1,235 @@
+// Copyright 2018 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "Private/GULUserDefaults.h"
+
+#import <GoogleUtilities/GULLogger.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+static NSTimeInterval const kGULSynchronizeInterval = 1.0;
+
+static NSString *const kGULLogFormat = @"I-GUL%06ld";
+
+static GULLoggerService kGULLogUserDefaultsService = @"[GoogleUtilities/UserDefaults]";
+
+typedef NS_ENUM(NSInteger, GULUDMessageCode) {
+  GULUDMessageCodeInvalidKeyGet = 1,
+  GULUDMessageCodeInvalidKeySet = 2,
+  GULUDMessageCodeInvalidObjectSet = 3,
+  GULUDMessageCodeSynchronizeFailed = 4,
+};
+
+@interface GULUserDefaults ()
+
+/// Equivalent to the suite name for NSUserDefaults.
+@property(readonly) CFStringRef appNameRef;
+
+@property(atomic) BOOL isPreferenceFileExcluded;
+
+@end
+
+@implementation GULUserDefaults {
+  // The application name is the same with the suite name of the NSUserDefaults, and it is used for
+  // preferences.
+  CFStringRef _appNameRef;
+}
+
++ (GULUserDefaults *)standardUserDefaults {
+  static GULUserDefaults *standardUserDefaults;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    standardUserDefaults = [[GULUserDefaults alloc] init];
+  });
+  return standardUserDefaults;
+}
+
+- (instancetype)init {
+  return [self initWithSuiteName:nil];
+}
+
+- (instancetype)initWithSuiteName:(nullable NSString *)suiteName {
+  self = [super init];
+
+  NSString *name = [suiteName copy];
+
+  if (self) {
+    // `kCFPreferencesCurrentApplication` maps to the same defaults database as
+    // `[NSUserDefaults standardUserDefaults]`.
+    _appNameRef =
+        name.length ? (__bridge_retained CFStringRef)name : kCFPreferencesCurrentApplication;
+  }
+
+  return self;
+}
+
+- (void)dealloc {
+  // If we're using a custom `_appNameRef` it needs to be released. If it's a constant, it shouldn't
+  // need to be released since we don't own it.
+  if (CFStringCompare(_appNameRef, kCFPreferencesCurrentApplication, 0) != kCFCompareEqualTo) {
+    CFRelease(_appNameRef);
+  }
+
+  [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                           selector:@selector(synchronize)
+                                             object:nil];
+}
+
+- (nullable id)objectForKey:(NSString *)defaultName {
+  NSString *key = [defaultName copy];
+  if (![key isKindOfClass:[NSString class]] || !key.length) {
+    GULLogWarning(@"<GoogleUtilities>", NO,
+                  [NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidKeyGet],
+                  @"Cannot get object for invalid user default key.");
+    return nil;
+  }
+  return (__bridge_transfer id)CFPreferencesCopyAppValue((__bridge CFStringRef)key, _appNameRef);
+}
+
+- (void)setObject:(nullable id)value forKey:(NSString *)defaultName {
+  NSString *key = [defaultName copy];
+  if (![key isKindOfClass:[NSString class]] || !key.length) {
+    GULLogWarning(kGULLogUserDefaultsService, NO,
+                  [NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidKeySet],
+                  @"Cannot set object for invalid user default key.");
+    return;
+  }
+  if (!value) {
+    CFPreferencesSetAppValue((__bridge CFStringRef)key, NULL, _appNameRef);
+    [self scheduleSynchronize];
+    return;
+  }
+  BOOL isAcceptableValue =
+      [value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]] ||
+      [value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]] ||
+      [value isKindOfClass:[NSDate class]] || [value isKindOfClass:[NSData class]];
+  if (!isAcceptableValue) {
+    GULLogWarning(kGULLogUserDefaultsService, NO,
+                  [NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidObjectSet],
+                  @"Cannot set invalid object to user defaults. Must be a string, number, array, "
+                  @"dictionary, date, or data. Value: %@",
+                  value);
+    return;
+  }
+
+  CFPreferencesSetAppValue((__bridge CFStringRef)key, (__bridge CFStringRef)value, _appNameRef);
+  [self scheduleSynchronize];
+}
+
+- (void)removeObjectForKey:(NSString *)key {
+  [self setObject:nil forKey:key];
+}
+
+#pragma mark - Getters
+
+- (NSInteger)integerForKey:(NSString *)defaultName {
+  NSNumber *object = [self objectForKey:defaultName];
+  return object.integerValue;
+}
+
+- (float)floatForKey:(NSString *)defaultName {
+  NSNumber *object = [self objectForKey:defaultName];
+  return object.floatValue;
+}
+
+- (double)doubleForKey:(NSString *)defaultName {
+  NSNumber *object = [self objectForKey:defaultName];
+  return object.doubleValue;
+}
+
+- (BOOL)boolForKey:(NSString *)defaultName {
+  NSNumber *object = [self objectForKey:defaultName];
+  return object.boolValue;
+}
+
+- (nullable NSString *)stringForKey:(NSString *)defaultName {
+  return [self objectForKey:defaultName];
+}
+
+- (nullable NSArray *)arrayForKey:(NSString *)defaultName {
+  return [self objectForKey:defaultName];
+}
+
+- (nullable NSDictionary<NSString *, id> *)dictionaryForKey:(NSString *)defaultName {
+  return [self objectForKey:defaultName];
+}
+
+#pragma mark - Setters
+
+- (void)setInteger:(NSInteger)integer forKey:(NSString *)defaultName {
+  [self setObject:@(integer) forKey:defaultName];
+}
+
+- (void)setFloat:(float)value forKey:(NSString *)defaultName {
+  [self setObject:@(value) forKey:defaultName];
+}
+
+- (void)setDouble:(double)doubleNumber forKey:(NSString *)defaultName {
+  [self setObject:@(doubleNumber) forKey:defaultName];
+}
+
+- (void)setBool:(BOOL)boolValue forKey:(NSString *)defaultName {
+  [self setObject:@(boolValue) forKey:defaultName];
+}
+
+#pragma mark - Save data
+
+- (void)synchronize {
+  if (!CFPreferencesAppSynchronize(_appNameRef)) {
+    GULLogError(kGULLogUserDefaultsService, NO,
+                [NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeSynchronizeFailed],
+                @"Cannot synchronize user defaults to disk");
+  }
+}
+
+#pragma mark - Private methods
+
+/// Removes all values from the search list entry specified by 'domainName', the current user, and
+/// any host. The change is persistent. Equivalent to -removePersistentDomainForName: of
+/// NSUserDefaults.
+- (void)clearAllData {
+  // On macOS, using `kCFPreferencesCurrentHost` will not set all the keys necessary to match
+  // `NSUserDefaults`.
+#if TARGET_OS_MAC
+  CFStringRef host = kCFPreferencesAnyHost;
+#else
+  CFStringRef host = kCFPreferencesCurrentHost;
+#endif  // TARGET_OS_OSX
+
+  CFArrayRef keyList = CFPreferencesCopyKeyList(_appNameRef, kCFPreferencesCurrentUser, host);
+  if (!keyList) {
+    return;
+  }
+
+  CFPreferencesSetMultiple(NULL, keyList, _appNameRef, kCFPreferencesCurrentUser, host);
+  CFRelease(keyList);
+  [self scheduleSynchronize];
+}
+
+- (void)scheduleSynchronize {
+  // Synchronize data using a timer so that multiple set... calls can be coalesced under one
+  // synchronize.
+  [NSObject cancelPreviousPerformRequestsWithTarget:self
+                                           selector:@selector(synchronize)
+                                             object:nil];
+  // This method may be called on multiple queues (due to set... methods can be called on any queue)
+  // synchronize can be scheduled on different queues, so make sure that it does not crash. If this
+  // instance goes away, self will be released also, no one will retain it and the schedule won't be
+  // called.
+  [self performSelector:@selector(synchronize) withObject:nil afterDelay:kGULSynchronizeInterval];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/GoogleUtilities/GoogleUtilities/UserDefaults/Private/GULUserDefaults.h b/iOS/Pods/GoogleUtilities/GoogleUtilities/UserDefaults/Private/GULUserDefaults.h
new file mode 100644 (file)
index 0000000..0d04781
--- /dev/null
@@ -0,0 +1,110 @@
+// Copyright 2018 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// A thread-safe user defaults that uses C functions from CFPreferences.h instead of
+/// `NSUserDefaults`. This is to avoid sending an `NSNotification` when it's changed from a
+/// background thread to avoid crashing. // TODO: Insert radar number here.
+@interface GULUserDefaults : NSObject
+
+/// A shared user defaults similar to +[NSUserDefaults standardUserDefaults] and accesses the same
+/// data of the standardUserDefaults.
++ (GULUserDefaults *)standardUserDefaults;
+
+/// Initializes preferences with a suite name that is the same with the NSUserDefaults' suite name.
+/// Both of CFPreferences and NSUserDefaults share the same plist file so their data will exactly
+/// the same.
+///
+/// @param suiteName The name of the suite of the user defaults.
+- (instancetype)initWithSuiteName:(nullable NSString *)suiteName;
+
+#pragma mark - Getters
+
+/// Searches the receiver's search list for a default with the key 'defaultName' and return it. If
+/// another process has changed defaults in the search list, NSUserDefaults will automatically
+/// update to the latest values. If the key in question has been marked as ubiquitous via a Defaults
+/// Configuration File, the latest value may not be immediately available, and the registered value
+/// will be returned instead.
+- (nullable id)objectForKey:(NSString *)defaultName;
+
+/// Equivalent to -objectForKey:, except that it will return nil if the value is not an NSArray.
+- (nullable NSArray *)arrayForKey:(NSString *)defaultName;
+
+/// Equivalent to -objectForKey:, except that it will return nil if the value
+/// is not an NSDictionary.
+- (nullable NSDictionary<NSString *, id> *)dictionaryForKey:(NSString *)defaultName;
+
+/// Equivalent to -objectForKey:, except that it will convert NSNumber values to their NSString
+/// representation. If a non-string non-number value is found, nil will be returned.
+- (nullable NSString *)stringForKey:(NSString *)defaultName;
+
+/// Equivalent to -objectForKey:, except that it converts the returned value to an NSInteger. If the
+/// value is an NSNumber, the result of -integerValue will be returned. If the value is an NSString,
+/// it will be converted to NSInteger if possible. If the value is a boolean, it will be converted
+/// to either 1 for YES or 0 for NO. If the value is absent or can't be converted to an integer, 0
+/// will be returned.
+- (NSInteger)integerForKey:(NSString *)defaultName;
+
+/// Similar to -integerForKey:, except that it returns a float, and boolean values will not be
+/// converted.
+- (float)floatForKey:(NSString *)defaultName;
+
+/// Similar to -integerForKey:, except that it returns a double, and boolean values will not be
+/// converted.
+- (double)doubleForKey:(NSString *)defaultName;
+
+/// Equivalent to -objectForKey:, except that it converts the returned value to a BOOL. If the value
+/// is an NSNumber, NO will be returned if the value is 0, YES otherwise. If the value is an
+/// NSString, values of "YES" or "1" will return YES, and values of "NO", "0", or any other string
+/// will return NO. If the value is absent or can't be converted to a BOOL, NO will be returned.
+- (BOOL)boolForKey:(NSString *)defaultName;
+
+#pragma mark - Setters
+
+/// Immediately stores a value (or removes the value if `nil` is passed as the value) for the
+/// provided key in the search list entry for the receiver's suite name in the current user and any
+/// host, then asynchronously stores the value persistently, where it is made available to other
+/// processes.
+- (void)setObject:(nullable id)value forKey:(NSString *)defaultName;
+
+/// Equivalent to -setObject:forKey: except that the value is converted from a float to an NSNumber.
+- (void)setFloat:(float)value forKey:(NSString *)defaultName;
+
+/// Equivalent to -setObject:forKey: except that the value is converted from a double to an
+/// NSNumber.
+- (void)setDouble:(double)value forKey:(NSString *)defaultName;
+
+/// Equivalent to -setObject:forKey: except that the value is converted from an NSInteger to an
+/// NSNumber.
+- (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName;
+
+/// Equivalent to -setObject:forKey: except that the value is converted from a BOOL to an NSNumber.
+- (void)setBool:(BOOL)value forKey:(NSString *)defaultName;
+
+#pragma mark - Removing Defaults
+
+/// Equivalent to -[... setObject:nil forKey:defaultName]
+- (void)removeObjectForKey:(NSString *)defaultName;
+
+#pragma mark - Save data
+
+/// Blocks the calling thread until all in-progress set operations have completed.
+- (void)synchronize;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/GoogleUtilities/LICENSE b/iOS/Pods/GoogleUtilities/LICENSE
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -0,0 +1,202 @@
+
+                                 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/iOS/Pods/GoogleUtilities/README.md b/iOS/Pods/GoogleUtilities/README.md
new file mode 100644 (file)
index 0000000..eb6ea33
--- /dev/null
@@ -0,0 +1,180 @@
+# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
+
+This repository contains a subset of the Firebase iOS SDK source. It currently
+includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
+FirebaseFunctions, FirebaseInAppMessagingDisplay, FirebaseMessaging and
+FirebaseStorage.
+
+The repository also includes GoogleUtilities source. The
+[GoogleUtilities](GoogleUtilities/README.md) pod is
+a set of utilities used by Firebase and other Google products.
+
+Firebase is an app development platform with tools to help you build, grow and
+monetize your app. More information about Firebase can be found at
+[https://firebase.google.com](https://firebase.google.com).
+
+## Installation
+
+See the three subsections for details about three different installation methods.
+1. [Standard pod install](README.md#standard-pod-install)
+1. [Installing from the GitHub repo](README.md#installing-from-github)
+1. [Experimental Carthage](README.md#carthage-ios-only)
+
+### Standard pod install
+
+Go to
+[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
+
+### Installing from GitHub
+
+For releases starting with 5.0.0, the source for each release is also deployed
+to CocoaPods master and available via standard
+[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
+
+These instructions can be used to access the Firebase repo at other branches,
+tags, or commits.
+
+#### Background
+
+See
+[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
+for instructions and options about overriding pod source locations.
+
+#### Accessing Firebase Source Snapshots
+
+All of the official releases are tagged in this repo and available via CocoaPods. To access a local
+source snapshot or unreleased branch, use Podfile directives like the following:
+
+To access FirebaseFirestore via a branch:
+```
+pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
+pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
+```
+
+To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
+
+```
+pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
+pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
+```
+
+### Carthage (iOS only)
+
+An experimental Carthage distribution is now available. See
+[Carthage](Carthage.md).
+
+## Development
+
+Follow the subsequent instructions to develop, debug, unit test, run integration
+tests, and try out reference samples:
+
+```
+$ git clone git@github.com:firebase/firebase-ios-sdk.git
+$ cd firebase-ios-sdk/Example
+$ pod update
+$ open Firebase.xcworkspace
+```
+
+Firestore and Functions have self contained Xcode projects. See
+[Firestore/README.md](Firestore/README.md) and
+[Functions/README.md](Functions/README.md).
+
+### Running Unit Tests
+
+Select a scheme and press Command-u to build a component and run its unit tests.
+
+### Running Sample Apps
+In order to run the sample apps and integration tests, you'll need valid
+`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
+files without real values, but can be replaced with real plist files. To get your own
+`GoogleService-Info.plist` files:
+
+1. Go to the [Firebase Console](https://console.firebase.google.com/)
+2. Create a new Firebase project, if you don't already have one
+3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
+identifier (e.g. `com.google.Database-Example`)
+4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
+(e.g. in [Example/Database/App/](Example/Database/App/));
+
+Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
+special Apple capabilities, and you will have to change the sample app to use a unique bundle
+identifier that you can control in your own Apple Developer account.
+
+## Specific Component Instructions
+See the sections below for any special instructions for those components.
+
+### Firebase Auth
+
+If you're doing specific Firebase Auth development, see
+[the Auth Sample README](Example/Auth/README.md) for instructions about
+building and running the FirebaseAuth pod along with various samples and tests.
+
+### Firebase Database
+
+To run the Database Integration tests, make your database authentication rules
+[public](https://firebase.google.com/docs/database/security/quickstart).
+
+### Firebase Storage
+
+To run the Storage Integration tests, follow the instructions in
+[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
+
+#### Push Notifications
+
+Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
+In order to actually test receiving push notifications, you will need to:
+
+1. Change the bundle identifier of the sample app to something you own in your Apple Developer
+account, and enable that App ID for push notifications.
+2. You'll also need to
+[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
+at **Project Settings > Cloud Messaging > [Your Firebase App]**.
+3. Ensure your iOS device is added to your Apple Developer portal as a test device.
+
+#### iOS Simulator
+
+The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
+In order to receive push notifications, you'll have to follow the steps above and run the app on a
+physical device.
+
+## Community Supported Efforts
+
+We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
+very grateful!  We'd like to empower as many developers as we can to be able to use Firebase and
+participate in the Firebase community.
+
+### macOS and tvOS
+FirebaseAuth, FirebaseCore, FirebaseDatabase and FirebaseStorage now compile, run unit tests, and
+work on macOS and tvOS, thanks to contributions from the community. There are a few tweaks needed,
+like ensuring iOS-only, macOS-only, or tvOS-only code is correctly guarded with checks for
+`TARGET_OS_IOS`, `TARGET_OS_OSX` and `TARGET_OS_TV`.
+
+For tvOS, checkout the [Sample](Example/tvOSSample).
+
+Keep in mind that macOS and tvOS are not officially supported by Firebase, and this repository is
+actively developed primarily for iOS. While we can catch basic unit test issues with Travis, there
+may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter
+this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
+
+For installation instructions, see [above](README.md#accessing-firebase-source-snapshots).
+
+Note that the Firebase pod is not available for macOS and tvOS. Install a selection of the
+`FirebaseAuth`, `FirebaseCore`, `FirebaseDatabase` and `FirebaseStorage` CocoaPods.
+
+## Roadmap
+
+See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
+plans and directions.
+
+## Contributing
+
+See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
+iOS SDK.
+
+## License
+
+The contents of this repository is licensed under the
+[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
+
+Your use of Firebase is governed by the
+[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
diff --git a/iOS/Pods/Headers/Private/Firebase/Firebase.h b/iOS/Pods/Headers/Private/Firebase/Firebase.h
new file mode 120000 (symlink)
index 0000000..07ac6eb
--- /dev/null
@@ -0,0 +1 @@
+../../../Firebase/CoreOnly/Sources/Firebase.h
\ No newline at end of file
diff --git a/iOS/Pods/Headers/Public/Firebase/Firebase.h b/iOS/Pods/Headers/Public/Firebase/Firebase.h
new file mode 120000 (symlink)
index 0000000..07ac6eb
--- /dev/null
@@ -0,0 +1 @@
+../../../Firebase/CoreOnly/Sources/Firebase.h
\ No newline at end of file
diff --git a/iOS/Pods/JSQWebViewController/LICENSE b/iOS/Pods/JSQWebViewController/LICENSE
new file mode 100644 (file)
index 0000000..3af0efe
--- /dev/null
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Jesse Squires
+
+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/iOS/Pods/JSQWebViewController/README.md b/iOS/Pods/JSQWebViewController/README.md
new file mode 100644 (file)
index 0000000..ec26fbf
--- /dev/null
@@ -0,0 +1,89 @@
+
+[![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/)
+
+> **NOTE:** As of iOS 9, this library is no longer necessary.
+>
+> You should use [`SFSafariViewController`](https://developer.apple.com/library/prerelease/ios/documentation/SafariServices/Reference/SFSafariViewController_Ref/index.html) instead.
+
+# ⚠ Deprecated ⚠
+
+# JSQWebViewController
+
+[![Build Status](https://secure.travis-ci.org/jessesquires/JSQWebViewController.svg)](http://travis-ci.org/jessesquires/JSQWebViewController) [![Version Status](https://img.shields.io/cocoapods/v/JSQWebViewController.svg)][podLink] [![license MIT](https://img.shields.io/cocoapods/l/JSQWebViewController.svg)][mitLink] [![codecov](https://codecov.io/gh/jessesquires/JSQWebViewController/branch/develop/graph/badge.svg)](https://codecov.io/gh/jessesquires/JSQWebViewController) [![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/JSQWebViewController.svg)][docsLink]
+
+*A lightweight Swift WebKit view controller for iOS*
+
+![screenshot](https://raw.githubusercontent.com/jessesquires/JSQWebViewController/develop/Screenshots/screenshot_0.png)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+![screenshot](https://raw.githubusercontent.com/jessesquires/JSQWebViewController/develop/Screenshots/screenshot_1.png)
+
+## Requirements
+
+* Swift 3.2+
+* Xcode 9+
+* iOS 8+
+
+## Installation
+
+#### [CocoaPods](http://cocoapods.org) (recommended)
+
+````ruby
+use_frameworks!
+
+# For latest release in cocoapods
+pod 'JSQWebViewController'
+````
+
+#### [Carthage](https://github.com/Carthage/Carthage)
+
+````bash
+github "jessesquires/JSQWebViewController"
+````
+
+## Documentation
+
+Read the [docs][docsLink]. Generated with [jazzy](https://github.com/realm/jazzy). Hosted by [GitHub Pages](https://pages.github.com).
+
+#### Generate
+
+````bash
+$ ./build_docs.sh
+````
+
+#### Preview
+
+````bash
+$ open index.html -a Safari
+````
+
+## Getting Started
+
+````swift
+import JSQWebViewController
+
+let controller = WebViewController(url: URL(string: "http://jessesquires.com")!)
+let nav = UINavigationController(rootViewController: controller)
+present(nav, animated: true, completion: nil)
+````
+
+See the included example app, open `Example/Example.xcodeproj`.
+
+## Contribute
+
+Please follow these sweet [contribution guidelines](https://github.com/jessesquires/HowToContribute).
+
+## Credits
+
+Created and maintained by [**@jesse_squires**](https://twitter.com/jesse_squires).
+
+## License
+
+`JSQWebViewController` is released under an [MIT License][mitLink]. See `LICENSE` for details.
+
+>**Copyright &copy; 2015 Jesse Squires.**
+
+*Please provide attribution, it is greatly appreciated.*
+
+[mitLink]:http://opensource.org/licenses/MIT
+[docsLink]:http://jessesquires.github.io/JSQWebViewController
+[podLink]:https://cocoapods.org/pods/JSQWebViewController
diff --git a/iOS/Pods/JSQWebViewController/Source/WebViewController.swift b/iOS/Pods/JSQWebViewController/Source/WebViewController.swift
new file mode 100644 (file)
index 0000000..8c82eb0
--- /dev/null
@@ -0,0 +1,222 @@
+//
+//  Created by Jesse Squires
+//  http://www.jessesquires.com
+//
+//
+//  Documentation
+//  http://jessesquires.github.io/JSQWebViewController
+//
+//
+//  GitHub
+//  https://github.com/jessesquires/JSQWebViewController
+//
+//
+//  License
+//  Copyright (c) 2015 Jesse Squires
+//  Released under an MIT license: http://opensource.org/licenses/MIT
+//
+
+import UIKit
+import WebKit
+
+
+private let titleKeyPath = "title"
+private let estimatedProgressKeyPath = "estimatedProgress"
+
+
+/// An instance of `WebViewController` displays interactive web content.
+open class WebViewController: UIViewController {
+
+    // MARK: Properties
+
+    /// Returns the web view for the controller.
+    public final var webView: WKWebView {
+        get {
+            return _webView
+        }
+    }
+
+    /// Returns the progress view for the controller.
+    public final var progressBar: UIProgressView {
+        get {
+            return _progressBar
+        }
+    }
+
+    /// The URL request for the web view. Upon setting this property, the web view immediately begins loading the request.
+    public final var urlRequest: URLRequest {
+        didSet {
+            webView.load(urlRequest)
+        }
+    }
+
+    /**
+     Specifies whether or not to display the web view title as the navigation bar title.
+     The default is `false`, which sets the navigation bar title to the URL host name of the URL request.
+     */
+    public final var displaysWebViewTitle: Bool = false
+
+    // MARK: Private properties
+
+    private final let configuration: WKWebViewConfiguration
+    private final let activities: [UIActivity]?
+
+    private lazy final var _webView: WKWebView = {
+        let webView = WKWebView(frame: CGRect.zero, configuration: configuration)
+        webView.addObserver(self, forKeyPath: titleKeyPath, options: .new, context: nil)
+        webView.addObserver(self, forKeyPath: estimatedProgressKeyPath, options: .new, context: nil)
+        webView.allowsBackForwardNavigationGestures = true
+        if #available(iOS 9.0, *) {
+            webView.allowsLinkPreview = true
+        }
+        return webView
+    }()
+
+    private lazy final var _progressBar: UIProgressView = {
+        let progressBar = UIProgressView(progressViewStyle: .bar)
+        progressBar.backgroundColor = .clear
+        progressBar.trackTintColor = .clear
+        return progressBar
+    }()
+
+    // MARK: Initialization
+
+    /**
+     Constructs a new `WebViewController`.
+
+     - parameter urlRequest:    The URL request for the web view to load.
+     - parameter configuration: The configuration for the web view.
+     - parameter activities:    The custom activities to display in the `UIActivityViewController` that is presented when the action button is tapped.
+
+     - returns: A new `WebViewController` instance.
+     */
+    public init(urlRequest: URLRequest, configuration: WKWebViewConfiguration = WKWebViewConfiguration(), activities: [UIActivity]? = nil) {
+        self.configuration = configuration
+        self.urlRequest = urlRequest
+        self.activities = activities
+        super.init(nibName: nil, bundle: nil)
+    }
+
+    /**
+     Constructs a new `WebViewController`.
+
+     - parameter url: The URL to display in the web view.
+
+     - returns: A new `WebViewController` instance.
+     */
+    public convenience init(url: URL) {
+        self.init(urlRequest: URLRequest(url: url))
+    }
+
+    /// :nodoc:
+    public required init?(coder aDecoder: NSCoder) {
+        self.configuration = WKWebViewConfiguration()
+        self.urlRequest = URLRequest(url: URL(string: "http://")!)
+        self.activities = nil
+        super.init(coder: aDecoder)
+    }
+
+    deinit {
+        webView.removeObserver(self, forKeyPath: titleKeyPath, context: nil)
+        webView.removeObserver(self, forKeyPath: estimatedProgressKeyPath, context: nil)
+    }
+
+
+    // MARK: View lifecycle
+
+    /// :nodoc:
+    open override func viewDidLoad() {
+        super.viewDidLoad()
+        title = urlRequest.url?.host
+        view.addSubview(_webView)
+        view.addSubview(_progressBar)
+
+        if presentingViewController?.presentedViewController != nil {
+            navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done,
+                                                               target: self,
+                                                               action: #selector(didTapDoneButton(_:)))
+        }
+
+        navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action,
+                                                            target: self,
+                                                            action: #selector(didTapActionButton(_:)))
+
+        webView.load(urlRequest)
+    }
+
+    /// :nodoc:
+    open override func viewWillAppear(_ animated: Bool) {
+        assert(navigationController != nil, "\(WebViewController.self) must be presented in a \(UINavigationController.self)")
+        super.viewWillAppear(animated)
+    }
+
+    /// :nodoc:
+    open override func viewDidDisappear(_ animated: Bool) {
+        super.viewDidDisappear(animated)
+        webView.stopLoading()
+    }
+
+    /// :nodoc:
+    open override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        webView.frame = view.bounds
+
+        let isIOS11 = ProcessInfo.processInfo.isOperatingSystemAtLeast(
+            OperatingSystemVersion(majorVersion: 11, minorVersion: 0, patchVersion: 0))
+        let top = isIOS11 ? CGFloat(0.0) : topLayoutGuide.length
+        let insets = UIEdgeInsets(top: top, left: 0, bottom: 0, right: 0)
+        webView.scrollView.contentInset = insets
+        webView.scrollView.scrollIndicatorInsets = insets
+
+        view.bringSubview(toFront: progressBar)
+        progressBar.frame = CGRect(x: view.frame.minX,
+                                   y: topLayoutGuide.length,
+                                   width: view.frame.size.width,
+                                   height: 2)
+    }
+
+
+    // MARK: Actions
+
+    @objc private func didTapDoneButton(_ sender: UIBarButtonItem) {
+        dismiss(animated: true, completion: nil)
+    }
+
+    @objc private func didTapActionButton(_ sender: UIBarButtonItem) {
+        if let url = urlRequest.url {
+            let activityVC = UIActivityViewController(activityItems: [url], applicationActivities: activities)
+            activityVC.popoverPresentationController?.barButtonItem = sender
+            present(activityVC, animated: true, completion: nil)
+        }
+    }
+
+
+    // MARK: KVO
+
+    /// :nodoc:
+    open override func observeValue(forKeyPath keyPath: String?,
+                                    of object: Any?,
+                                    change: [NSKeyValueChangeKey : Any]?,
+                                    context: UnsafeMutableRawPointer?) {
+        guard let theKeyPath = keyPath , object as? WKWebView == webView else {
+            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
+            return
+        }
+
+        if displaysWebViewTitle && theKeyPath == titleKeyPath {
+            title = webView.title
+        }
+        
+        if theKeyPath == estimatedProgressKeyPath {
+            updateProgress()
+        }
+    }
+    
+    // MARK: Private
+    
+    private final func updateProgress() {
+        let completed = webView.estimatedProgress == 1.0
+        progressBar.setProgress(completed ? 0.0 : Float(webView.estimatedProgress), animated: !completed)
+        UIApplication.shared.isNetworkActivityIndicatorVisible = !completed
+    }
+}
diff --git a/iOS/Pods/Kingfisher/LICENSE b/iOS/Pods/Kingfisher/LICENSE
new file mode 100644 (file)
index 0000000..5023261
--- /dev/null
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2018 Wei Wang
+
+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/iOS/Pods/Kingfisher/README.md b/iOS/Pods/Kingfisher/README.md
new file mode 100644 (file)
index 0000000..fa38fc1
--- /dev/null
@@ -0,0 +1,116 @@
+<p align="center">
+
+<img src="https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/logo.png" alt="Kingfisher" title="Kingfisher" width="557"/>
+
+</p>
+
+<p align="center">
+<a href="https://travis-ci.org/onevcat/Kingfisher"><img src="https://img.shields.io/travis/onevcat/Kingfisher/master.svg"></a>
+<a href="https://github.com/Carthage/Carthage/"><img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat"></a>
+<a href="http://onevcat.github.io/Kingfisher/"><img src="https://img.shields.io/cocoapods/v/Kingfisher.svg?style=flat"></a>
+<a href="https://raw.githubusercontent.com/onevcat/Kingfisher/master/LICENSE"><img src="https://img.shields.io/cocoapods/l/Kingfisher.svg?style=flat"></a>
+<a href="http://onevcat.github.io/Kingfisher/"><img src="https://img.shields.io/cocoapods/p/Kingfisher.svg?style=flat"></a>
+<a href="https://codebeat.co/projects/github-com-onevcat-kingfisher"><img alt="codebeat badge" src="https://codebeat.co/assets/svg/badges/A-398b39-669406e9e1b136187b91af587d4092b0160370f271f66a651f444b990c2730e9.svg" /></a>
+<a href="#backers" alt="sponsors on Open Collective"><img src="https://opencollective.com/Kingfisher/backers/badge.svg" /></a>
+<a href="#sponsors" alt="Sponsors on Open Collective"><img src="https://opencollective.com/Kingfisher/sponsors/badge.svg" /></a>
+</p>
+
+Kingfisher is a lightweight, pure-Swift library for downloading and caching images from the web. This project is heavily inspired by the popular [SDWebImage](https://github.com/rs/SDWebImage). It provides you a chance to use a pure-Swift alternative in your next app.
+
+## Features
+
+- [x] Asynchronous image downloading and caching.
+- [x] `URLSession`-based networking. Basic image processors and filters supplied.
+- [x] Multiple-layer cache for both memory and disk.
+- [x] Cancelable downloading and processing tasks to improve performance.
+- [x] Independent components. Use the downloader or caching system separately as you need.
+- [x] Prefetching images and showing them from cache later when necessary.
+- [x] Extensions for `UIImageView`, `NSImage` and `UIButton` to directly set an image from a URL.
+- [x] Built-in transition animation when setting images.
+- [x] Customizable placeholder while loading images.
+- [x] Extensible image processing and image format support.
+
+The simplest use-case is setting an image to an image view with the `UIImageView` extension:
+
+```swift
+let url = URL(string: "url_of_your_image")
+imageView.kf.setImage(with: url)
+```
+
+Kingfisher will download the image from `url`, send it to both the memory cache and the disk cache, and display it in `imageView`. When you use the same code later, the image will be retrieved from cache and shown immediately.
+
+For more examples of using Kingfisher, take a look at the [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet).
+
+## Requirements
+
+- iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
+- Swift 4 (Kingfisher 4.x), Swift 3 (Kingfisher 3.x)
+
+Main development of Kingfisher is based on Swift 4. Only critical bug fixes will be applied to Kingfisher 3.x.
+
+- Kingfisher 4.0 Migration - Kingfisher 3.x should be source compatible to Kingfisher 4. The reason for a major update is that we need to specify the Swift version explicitly for Xcode. All deprecated methods in Kingfisher 3 has been removed, so please ensure you have no warning left before you migrate from Kingfisher 3 to Kingfisher 4. If you have any trouble in migrating, please open an issue to discuss.
+- [Kingfisher 3.0 Migration Guide](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-3.0-Migration-Guide) - If you are upgrading to Kingfisher 3.x from an earlier version, please read this for more information.
+
+## Next Steps
+
+We prepared a [wiki page](https://github.com/onevcat/Kingfisher/wiki). You can find tons of useful things there.
+
+* [Installation Guide](https://github.com/onevcat/Kingfisher/wiki/Installation-Guide) - Follow it to integrate Kingfisher into your project.
+* [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet)- Curious about what Kingfisher could do and how would it look like when used in your project? See this page for useful code snippets. If you are already familiar with Kingfisher, you could also learn new tricks to improve the way you use Kingfisher! 
+* [API Reference](http://onevcat.github.io/Kingfisher/) - Lastly, please remember to read the full whenever you may need a more detailed reference.
+
+## Other
+
+### Future of Kingfisher
+
+I want to keep Kingfisher lightweight. This framework will focus on providing a simple solution for downloading and caching images. This doesn’t mean the framework can’t be improved. Kingfisher is far from perfect, so necessary and useful updates will be made to make it better.
+
+### Developments and Tests
+
+Any contributing and pull requests are warmly welcome. However, before you plan to implement some features or try to fix an uncertain issue, it is recommended to open a discussion first. 
+
+The test images are contained in another project to keep this project repo fast and slim. You could run `./setup.sh` in the root folder of Kingfisher to clone the test images when you need to run the tests target. It would be appreciated if your pull requests could build and with all tests green. :)
+
+### About the logo
+
+The logo of Kingfisher is inspired by [Tangram (七巧板)](http://en.wikipedia.org/wiki/Tangram), a dissection puzzle consisting of seven flat shapes from China. I believe she's a kingfisher bird instead of a swift, but someone insists that she is a pigeon. I guess I should give her a name. Hi, guys, do you have any suggestions?
+
+### Contact
+
+Follow and contact me on [Twitter](http://twitter.com/onevcat) or [Sina Weibo](http://weibo.com/onevcat). If you find an issue, just [open a ticket](https://github.com/onevcat/Kingfisher/issues/new). Pull requests are warmly welcome as well.
+
+## Contributors
+
+This project exists thanks to all the people who contribute. [[Contribute]](https://github.com/onevcat/Kingfisher/blob/master/CONTRIBUTING.md).
+<a href="https://github.com/onevcat/Kingfisher/graphs/contributors"><img src="https://opencollective.com/kingfisher/contributors.svg?width=890" /></a>
+
+
+## Backers
+
+Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/kingfisher#backer)]
+
+<a href="https://opencollective.com/kingfisher#backers" target="_blank"><img src="https://opencollective.com/kingfisher/backers.svg?width=890"></a>
+
+
+## Sponsors
+
+Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/kingfisher#sponsor)]
+
+<a href="https://opencollective.com/kingfisher/sponsor/0/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/0/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/1/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/1/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/2/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/2/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/3/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/3/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/4/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/4/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/5/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/5/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/6/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/6/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/7/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/7/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/8/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/8/avatar.svg"></a>
+<a href="https://opencollective.com/kingfisher/sponsor/9/website" target="_blank"><img src="https://opencollective.com/kingfisher/sponsor/9/avatar.svg"></a>
+
+
+
+### License
+
+Kingfisher is released under the MIT license. See LICENSE for details.
+
+
diff --git a/iOS/Pods/Kingfisher/Sources/AnimatedImageView.swift b/iOS/Pods/Kingfisher/Sources/AnimatedImageView.swift
new file mode 100755 (executable)
index 0000000..58cfa15
--- /dev/null
@@ -0,0 +1,485 @@
+//
+//  AnimatableImageView.swift
+//  Kingfisher
+//
+//  Created by bl4ckra1sond3tre on 4/22/16.
+//
+//  The AnimatableImageView, AnimatedFrame and Animator is a modified version of 
+//  some classes from kaishin's Gifu project (https://github.com/kaishin/Gifu)
+//
+//  The MIT License (MIT)
+//
+//  Copyright (c) 2018 Reda Lemeden.
+//
+//  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 name and characters used in the demo of this software are property of their
+//  respective owners.
+
+import UIKit
+import ImageIO
+
+/// Protocol of `AnimatedImageView`.
+public protocol AnimatedImageViewDelegate: class {
+    /**
+     Called after the animatedImageView has finished each animation loop.
+
+     - parameter imageView: The animatedImageView that is being animated.
+     - parameter count: The looped count.
+     */
+    func animatedImageView(_ imageView: AnimatedImageView, didPlayAnimationLoops count: UInt)
+
+    /**
+     Called after the animatedImageView has reached the max repeat count.
+
+     - parameter imageView: The animatedImageView that is being animated.
+     */
+    func animatedImageViewDidFinishAnimating(_ imageView: AnimatedImageView)
+}
+
+extension AnimatedImageViewDelegate {
+    public func animatedImageView(_ imageView: AnimatedImageView, didPlayAnimationLoops count: UInt) {}
+    public func animatedImageViewDidFinishAnimating(_ imageView: AnimatedImageView) {}
+}
+
+/// `AnimatedImageView` is a subclass of `UIImageView` for displaying animated image.
+open class AnimatedImageView: UIImageView {
+    
+    /// Proxy object for prevending a reference cycle between the CADDisplayLink and AnimatedImageView.
+    class TargetProxy {
+        private weak var target: AnimatedImageView?
+        
+        init(target: AnimatedImageView) {
+            self.target = target
+        }
+        
+        @objc func onScreenUpdate() {
+            target?.updateFrame()
+        }
+    }
+
+    /// Enumeration that specifies repeat count of GIF
+    public enum RepeatCount: Equatable {
+        case once
+        case finite(count: UInt)
+        case infinite
+
+        public static func ==(lhs: RepeatCount, rhs: RepeatCount) -> Bool {
+            switch (lhs, rhs) {
+            case let (.finite(l), .finite(r)):
+                return l == r
+            case (.once, .once),
+                 (.infinite, .infinite):
+                return true
+            case (.once, _),
+                 (.infinite, _),
+                 (.finite, _):
+                return false
+            }
+        }
+    }
+    
+    // MARK: - Public property
+    /// Whether automatically play the animation when the view become visible. Default is true.
+    public var autoPlayAnimatedImage = true
+    
+    /// The size of the frame cache.
+    public var framePreloadCount = 10
+    
+    /// Specifies whether the GIF frames should be pre-scaled to save memory. Default is true.
+    public var needsPrescaling = true
+    
+    /// The animation timer's run loop mode. Default is `NSRunLoopCommonModes`. Set this property to `NSDefaultRunLoopMode` will make the animation pause during UIScrollView scrolling.
+    public var runLoopMode = RunLoopMode.commonModes {
+        willSet {
+            if runLoopMode == newValue {
+                return
+            } else {
+                stopAnimating()
+                displayLink.remove(from: .main, forMode: runLoopMode)
+                displayLink.add(to: .main, forMode: newValue)
+                startAnimating()
+            }
+        }
+    }
+
+    /// The repeat count.
+    public var repeatCount = RepeatCount.infinite {
+        didSet {
+            if oldValue != repeatCount {
+                reset()
+                setNeedsDisplay()
+                layer.setNeedsDisplay()
+            }
+        }
+    }
+
+    /// Delegate of this `AnimatedImageView` object. See `AnimatedImageViewDelegate` protocol for more.
+    public weak var delegate: AnimatedImageViewDelegate?
+    
+    // MARK: - Private property
+    /// `Animator` instance that holds the frames of a specific image in memory.
+    private var animator: Animator?
+    
+    /// A flag to avoid invalidating the displayLink on deinit if it was never created, because displayLink is so lazy. :D
+    private var isDisplayLinkInitialized: Bool = false
+    
+    /// A display link that keeps calling the `updateFrame` method on every screen refresh.
+    private lazy var displayLink: CADisplayLink = {
+        self.isDisplayLinkInitialized = true
+        let displayLink = CADisplayLink(target: TargetProxy(target: self), selector: #selector(TargetProxy.onScreenUpdate))
+        displayLink.add(to: .main, forMode: self.runLoopMode)
+        displayLink.isPaused = true
+        return displayLink
+    }()
+    
+    // MARK: - Override
+    override open var image: Image? {
+        didSet {
+            if image != oldValue {
+                reset()
+            }
+            setNeedsDisplay()
+            layer.setNeedsDisplay()
+        }
+    }
+    
+    deinit {
+        if isDisplayLinkInitialized {
+            displayLink.invalidate()
+        }
+    }
+    
+    override open var isAnimating: Bool {
+        if isDisplayLinkInitialized {
+            return !displayLink.isPaused
+        } else {
+            return super.isAnimating
+        }
+    }
+    
+    /// Starts the animation.
+    override open func startAnimating() {
+        if self.isAnimating {
+            return
+        } else {
+            if animator?.isReachMaxRepeatCount ?? false {
+                return
+            }
+
+            displayLink.isPaused = false
+        }
+    }
+    
+    /// Stops the animation.
+    override open func stopAnimating() {
+        super.stopAnimating()
+        if isDisplayLinkInitialized {
+            displayLink.isPaused = true
+        }
+    }
+    
+    override open func display(_ layer: CALayer) {
+        if let currentFrame = animator?.currentFrame {
+            layer.contents = currentFrame.cgImage
+        } else {
+            layer.contents = image?.cgImage
+        }
+    }
+    
+    override open func didMoveToWindow() {
+        super.didMoveToWindow()
+        didMove()
+    }
+    
+    override open func didMoveToSuperview() {
+        super.didMoveToSuperview()
+        didMove()
+    }
+
+    // This is for back compatibility that using regular UIImageView to show animated image.
+    override func shouldPreloadAllAnimation() -> Bool {
+        return false
+    }
+
+    // MARK: - Private method
+    /// Reset the animator.
+    private func reset() {
+        animator = nil
+        if let imageSource = image?.kf.imageSource?.imageRef {
+            animator = Animator(imageSource: imageSource,
+                                contentMode: contentMode,
+                                size: bounds.size,
+                                framePreloadCount: framePreloadCount,
+                                repeatCount: repeatCount)
+            animator?.delegate = self
+            animator?.needsPrescaling = needsPrescaling
+            animator?.prepareFramesAsynchronously()
+        }
+        didMove()
+    }
+    
+    private func didMove() {
+        if autoPlayAnimatedImage && animator != nil {
+            if let _ = superview, let _ = window {
+                startAnimating()
+            } else {
+                stopAnimating()
+            }
+        }
+    }
+    
+    /// Update the current frame with the displayLink duration.
+    private func updateFrame() {
+        let duration: CFTimeInterval
+
+        // CA based display link is opt-out from ProMotion by default.
+        // So the duration and its FPS might not match. 
+        // See [#718](https://github.com/onevcat/Kingfisher/issues/718)
+        if #available(iOS 10.0, tvOS 10.0, *) {
+            // By setting CADisableMinimumFrameDuration to YES in Info.plist may 
+            // cause the preferredFramesPerSecond being 0
+            if displayLink.preferredFramesPerSecond == 0 {
+                duration = displayLink.duration
+            } else {
+                // Some devices (like iPad Pro 10.5) will have a different FPS.
+                duration = 1.0 / Double(displayLink.preferredFramesPerSecond)
+            }
+        } else {
+            duration = displayLink.duration
+        }
+    
+        if animator?.updateCurrentFrame(duration: duration) ?? false {
+            layer.setNeedsDisplay()
+
+            if animator?.isReachMaxRepeatCount ?? false {
+                stopAnimating()
+                delegate?.animatedImageViewDidFinishAnimating(self)
+            }
+        }
+    }
+}
+
+extension AnimatedImageView: AnimatorDelegate {
+    func animator(_ animator: Animator, didPlayAnimationLoops count: UInt) {
+        delegate?.animatedImageView(self, didPlayAnimationLoops: count)
+    }
+}
+
+/// Keeps a reference to an `Image` instance and its duration as a GIF frame.
+struct AnimatedFrame {
+    var image: Image?
+    let duration: TimeInterval
+    
+    static let null: AnimatedFrame = AnimatedFrame(image: .none, duration: 0.0)
+}
+
+protocol AnimatorDelegate: class {
+    func animator(_ animator: Animator, didPlayAnimationLoops count: UInt)
+}
+
+// MARK: - Animator
+class Animator {
+    // MARK: Private property
+    fileprivate let size: CGSize
+    fileprivate let maxFrameCount: Int
+    fileprivate let imageSource: CGImageSource
+    fileprivate let maxRepeatCount: AnimatedImageView.RepeatCount
+    
+    fileprivate var animatedFrames = [AnimatedFrame]()
+    fileprivate let maxTimeStep: TimeInterval = 1.0
+    fileprivate var frameCount = 0
+    fileprivate var currentFrameIndex = 0
+    fileprivate var currentFrameIndexInBuffer = 0
+    fileprivate var currentPreloadIndex = 0
+    fileprivate var timeSinceLastFrameChange: TimeInterval = 0.0
+    fileprivate var needsPrescaling = true
+    fileprivate var currentRepeatCount: UInt = 0
+    fileprivate weak var delegate: AnimatorDelegate?
+    
+    /// Loop count of animated image.
+    private var loopCount = 0
+    
+    var currentFrame: UIImage? {
+        return frame(at: currentFrameIndexInBuffer)
+    }
+
+    var isReachMaxRepeatCount: Bool {
+        switch maxRepeatCount {
+        case .once:
+            return currentRepeatCount >= 1
+        case .finite(let maxCount):
+            return currentRepeatCount >= maxCount
+        case .infinite:
+            return false
+        }
+    }
+    
+    var contentMode = UIViewContentMode.scaleToFill
+    
+    private lazy var preloadQueue: DispatchQueue = {
+        return DispatchQueue(label: "com.onevcat.Kingfisher.Animator.preloadQueue")
+    }()
+    
+    /**
+     Init an animator with image source reference.
+     
+     - parameter imageSource: The reference of animated image.
+     - parameter contentMode: Content mode of AnimatedImageView.
+     - parameter size: Size of AnimatedImageView.
+     - parameter framePreloadCount: Frame cache size.
+     
+     - returns: The animator object.
+     */
+    init(imageSource source: CGImageSource,
+         contentMode mode: UIViewContentMode,
+         size: CGSize,
+         framePreloadCount count: Int,
+         repeatCount: AnimatedImageView.RepeatCount) {
+        self.imageSource = source
+        self.contentMode = mode
+        self.size = size
+        self.maxFrameCount = count
+        self.maxRepeatCount = repeatCount
+    }
+    
+    func frame(at index: Int) -> Image? {
+        return animatedFrames[safe: index]?.image
+    }
+    
+    func prepareFramesAsynchronously() {
+        preloadQueue.async { [weak self] in
+            self?.prepareFrames()
+        }
+    }
+    
+    private func prepareFrames() {
+        frameCount = CGImageSourceGetCount(imageSource)
+        
+        if let properties = CGImageSourceCopyProperties(imageSource, nil),
+            let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary,
+            let loopCount = gifInfo[kCGImagePropertyGIFLoopCount as String] as? Int
+        {
+            self.loopCount = loopCount
+        }
+        
+        let frameToProcess = min(frameCount, maxFrameCount)
+        animatedFrames.reserveCapacity(frameToProcess)
+        animatedFrames = (0..<frameToProcess).reduce([]) { $0 + pure(prepareFrame(at: $1))}
+        currentPreloadIndex = (frameToProcess + 1) % frameCount - 1
+    }
+    
+    private func prepareFrame(at index: Int) -> AnimatedFrame {
+        
+        guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else {
+            return AnimatedFrame.null
+        }
+        
+        let defaultGIFFrameDuration = 0.100
+        let frameDuration = imageSource.kf.gifProperties(at: index).map {
+            gifInfo -> Double in
+            
+            let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double?
+            let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as Double?
+            let duration = unclampedDelayTime ?? delayTime ?? 0.0
+            
+            /**
+             http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp
+             Many annoying ads specify a 0 duration to make an image flash as quickly as
+             possible. We follow Safari and Firefox's behavior and use a duration of 100 ms
+             for any frames that specify a duration of <= 10 ms.
+             See <rdar://problem/7689300> and <http://webkit.org/b/36082> for more information.
+             
+             See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser.
+             */
+            return duration > 0.011 ? duration : defaultGIFFrameDuration
+        } ?? defaultGIFFrameDuration
+        
+        let image = Image(cgImage: imageRef)
+        let scaledImage: Image?
+        
+        if needsPrescaling {
+            scaledImage = image.kf.resize(to: size, for: contentMode)
+        } else {
+            scaledImage = image
+        }
+        
+        return AnimatedFrame(image: scaledImage, duration: frameDuration)
+    }
+    
+    /**
+     Updates the current frame if necessary using the frame timer and the duration of each frame in `animatedFrames`.
+     */
+    func updateCurrentFrame(duration: CFTimeInterval) -> Bool {
+        timeSinceLastFrameChange += min(maxTimeStep, duration)
+        guard let frameDuration = animatedFrames[safe: currentFrameIndexInBuffer]?.duration, frameDuration <= timeSinceLastFrameChange else {
+            return false
+        }
+        
+        timeSinceLastFrameChange -= frameDuration
+        
+        let lastFrameIndex = currentFrameIndexInBuffer
+        currentFrameIndexInBuffer += 1
+        currentFrameIndexInBuffer = currentFrameIndexInBuffer % animatedFrames.count
+        
+        if animatedFrames.count < frameCount {
+            preloadFrameAsynchronously(at: lastFrameIndex)
+        }
+        
+        currentFrameIndex += 1
+        
+        if currentFrameIndex == frameCount {
+            currentFrameIndex = 0
+            currentRepeatCount += 1
+
+            delegate?.animator(self, didPlayAnimationLoops: currentRepeatCount)
+        }
+
+        return true
+    }
+    
+    private func preloadFrameAsynchronously(at index: Int) {
+        preloadQueue.async { [weak self] in
+            self?.preloadFrame(at: index)
+        }
+    }
+    
+    private func preloadFrame(at index: Int) {
+        animatedFrames[index] = prepareFrame(at: currentPreloadIndex)
+        currentPreloadIndex += 1
+        currentPreloadIndex = currentPreloadIndex % frameCount
+    }
+}
+
+extension CGImageSource: KingfisherCompatible { }
+extension Kingfisher where Base: CGImageSource {
+    func gifProperties(at index: Int) -> [String: Double]? {
+        let properties = CGImageSourceCopyPropertiesAtIndex(base, index, nil) as Dictionary?
+        return properties?[kCGImagePropertyGIFDictionary] as? [String: Double]
+    }
+}
+
+extension Array {
+    fileprivate subscript(safe index: Int) -> Element? {
+        return indices ~= index ? self[index] : nil
+    }
+}
+
+private func pure<T>(_ value: T) -> [T] {
+    return [value]
+}
diff --git a/iOS/Pods/Kingfisher/Sources/Box.swift b/iOS/Pods/Kingfisher/Sources/Box.swift
new file mode 100644 (file)
index 0000000..7714a68
--- /dev/null
@@ -0,0 +1,34 @@
+//
+//  Box.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2018/3/17.
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+class Box<T> {
+    let value: T
+    
+    init(_ value: T) {
+        self.value = value
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/CacheSerializer.swift b/iOS/Pods/Kingfisher/Sources/CacheSerializer.swift
new file mode 100644 (file)
index 0000000..c3b6a27
--- /dev/null
@@ -0,0 +1,87 @@
+//
+//  CacheSerializer.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2016/09/02.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+/// An `CacheSerializer` would be used to convert some data to an image object for 
+/// retrieving from disk cache and vice versa for storing to disk cache.
+public protocol CacheSerializer {
+    
+    /// Get the serialized data from a provided image
+    /// and optional original data for caching to disk.
+    ///
+    ///
+    /// - parameter image:    The image needed to be serialized.
+    /// - parameter original: The original data which is just downloaded. 
+    ///                       If the image is retrieved from cache instead of
+    ///                       downloaded, it will be `nil`.
+    ///
+    /// - returns: A data which will be stored to cache, or `nil` when no valid
+    ///            data could be serialized.
+    func data(with image: Image, original: Data?) -> Data?
+    
+    /// Get an image deserialized from provided data.
+    ///
+    /// - parameter data:    The data from which an image should be deserialized.
+    /// - parameter options: Options for deserialization.
+    ///
+    /// - returns: An image deserialized or `nil` when no valid image 
+    ///            could be deserialized.
+    func image(with data: Data, options: KingfisherOptionsInfo?) -> Image?
+}
+
+
+/// `DefaultCacheSerializer` is a basic `CacheSerializer` used in default cache of
+/// Kingfisher. It could serialize and deserialize PNG, JEPG and GIF images. For 
+/// image other than these formats, a normalized `pngRepresentation` will be used.
+public struct DefaultCacheSerializer: CacheSerializer {
+    
+    public static let `default` = DefaultCacheSerializer()
+    private init() {}
+    
+    public func data(with image: Image, original: Data?) -> Data? {
+        let imageFormat = original?.kf.imageFormat ?? .unknown
+
+        let data: Data?
+        switch imageFormat {
+        case .PNG: data = image.kf.pngRepresentation()
+        case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0)
+        case .GIF: data = image.kf.gifRepresentation()
+        case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation()
+        }
+
+        return data
+    }
+    
+    public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? {
+        let options = options ?? KingfisherEmptyOptionsInfo
+        return Kingfisher<Image>.image(
+            data: data,
+            scale: options.scaleFactor,
+            preloadAllAnimationData: options.preloadAllAnimationData,
+            onlyFirstFrame: options.onlyLoadFirstFrame)
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/Filter.swift b/iOS/Pods/Kingfisher/Sources/Filter.swift
new file mode 100644 (file)
index 0000000..22a85b9
--- /dev/null
@@ -0,0 +1,137 @@
+//
+//  Filter.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2016/08/31.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+
+
+import CoreImage
+import Accelerate
+
+// Reuse the same CI Context for all CI drawing.
+private let ciContext = CIContext(options: nil)
+
+/// Transformer method which will be used in to provide a `Filter`.
+public typealias Transformer = (CIImage) -> CIImage?
+
+/// Supply a filter to create an `ImageProcessor`.
+public protocol CIImageProcessor: ImageProcessor {
+    var filter: Filter { get }
+}
+
+extension CIImageProcessor {
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.apply(filter)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Wrapper for a `Transformer` of CIImage filters.
+public struct Filter {
+    
+    let transform: Transformer
+
+    public init(tranform: @escaping Transformer) {
+        self.transform = tranform
+    }
+    
+    /// Tint filter which will apply a tint color to images.
+    public static var tint: (Color) -> Filter = {
+        color in
+        Filter { input in
+            let colorFilter = CIFilter(name: "CIConstantColorGenerator")!
+            colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey)
+            
+            let colorImage = colorFilter.outputImage
+            let filter = CIFilter(name: "CISourceOverCompositing")!
+            filter.setValue(colorImage, forKey: kCIInputImageKey)
+            filter.setValue(input, forKey: kCIInputBackgroundImageKey)
+            #if swift(>=4.0)
+            return filter.outputImage?.cropped(to: input.extent)
+            #else
+            return filter.outputImage?.cropping(to: input.extent)
+            #endif
+        }
+    }
+    
+    public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat)
+    
+    /// Color control filter which will apply color control change to images.
+    public static var colorControl: (ColorElement) -> Filter = { arg -> Filter in
+        let (brightness, contrast, saturation, inputEV) = arg
+        return Filter { input in
+            let paramsColor = [kCIInputBrightnessKey: brightness,
+                               kCIInputContrastKey: contrast,
+                               kCIInputSaturationKey: saturation]
+            
+            let paramsExposure = [kCIInputEVKey: inputEV]
+            #if swift(>=4.0)
+            let blackAndWhite = input.applyingFilter("CIColorControls", parameters: paramsColor)
+            return blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure)
+            #else
+            let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor)
+            return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure)
+            #endif
+        }
+        
+    }
+}
+
+extension Kingfisher where Base: Image {
+    /// Apply a `Filter` containing `CIImage` transformer to `self`.
+    ///
+    /// - parameter filter: The filter used to transform `self`.
+    ///
+    /// - returns: A transformed image by input `Filter`.
+    ///
+    /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned.
+    public func apply(_ filter: Filter) -> Image {
+        
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Tint image only works for CG-based image.")
+            return base
+        }
+        
+        let inputImage = CIImage(cgImage: cgImage)
+        guard let outputImage = filter.transform(inputImage) else {
+            return base
+        }
+        
+        guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else {
+            assertionFailure("[Kingfisher] Can not make an tint image within context.")
+            return base
+        }
+        
+        #if os(macOS)
+            return fixedForRetinaPixel(cgImage: result, to: size)
+        #else
+            return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation)
+        #endif
+    }
+
+}
diff --git a/iOS/Pods/Kingfisher/Sources/FormatIndicatedCacheSerializer.swift b/iOS/Pods/Kingfisher/Sources/FormatIndicatedCacheSerializer.swift
new file mode 100644 (file)
index 0000000..71f5856
--- /dev/null
@@ -0,0 +1,96 @@
+//
+//  RequestModifier.swift
+//  Kingfisher
+//
+//  Created by Junyu Kuang on 5/28/17.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+/// `FormatIndicatedCacheSerializer` let you indicate an image format for serialized caches.
+///
+/// It could serialize and deserialize PNG, JEPG and GIF images. For
+/// image other than these formats, a normalized `pngRepresentation` will be used.
+///
+/// Example:
+/// ````
+/// private let profileImageSize = CGSize(width: 44, height: 44)
+///
+/// private let imageProcessor = RoundCornerImageProcessor(
+///     cornerRadius: profileImageSize.width / 2, targetSize: profileImageSize)
+///
+/// private let optionsInfo: KingfisherOptionsInfo = [
+///     .cacheSerializer(FormatIndicatedCacheSerializer.png), 
+///     .backgroundDecode, .processor(imageProcessor), .scaleFactor(UIScreen.main.scale)]
+///
+/// extension UIImageView {
+///    func setProfileImage(with url: URL) {
+///        // Image will always cached as PNG format to preserve alpha channel for round rect.
+///        _ = kf.setImage(with: url, options: optionsInfo)
+///    }
+///}
+/// ````
+public struct FormatIndicatedCacheSerializer: CacheSerializer {
+    
+    public static let png = FormatIndicatedCacheSerializer(imageFormat: .PNG)
+    public static let jpeg = FormatIndicatedCacheSerializer(imageFormat: .JPEG)
+    public static let gif = FormatIndicatedCacheSerializer(imageFormat: .GIF)
+    
+    /// The indicated image format.
+    private let imageFormat: ImageFormat
+    
+    public func data(with image: Image, original: Data?) -> Data? {
+        
+        func imageData(withFormat imageFormat: ImageFormat) -> Data? {
+            switch imageFormat {
+            case .PNG: return image.kf.pngRepresentation()
+            case .JPEG: return image.kf.jpegRepresentation(compressionQuality: 1.0)
+            case .GIF: return image.kf.gifRepresentation()
+            case .unknown: return nil
+            }
+        }
+        
+        // generate data with indicated image format
+        if let data = imageData(withFormat: imageFormat) {
+            return data
+        }
+        
+        let originalFormat = original?.kf.imageFormat ?? .unknown
+        
+        // generate data with original image's format
+        if originalFormat != imageFormat, let data = imageData(withFormat: originalFormat) {
+            return data
+        }
+        
+        return original ?? image.kf.normalized.kf.pngRepresentation()
+    }
+    
+    /// Same implementation as `DefaultCacheSerializer`.
+    public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? {
+        let options = options ?? KingfisherEmptyOptionsInfo
+        return Kingfisher<Image>.image(
+            data: data,
+            scale: options.scaleFactor,
+            preloadAllAnimationData: options.preloadAllAnimationData,
+            onlyFirstFrame: options.onlyLoadFirstFrame)
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/Image.swift b/iOS/Pods/Kingfisher/Sources/Image.swift
new file mode 100755 (executable)
index 0000000..c6286cb
--- /dev/null
@@ -0,0 +1,1021 @@
+//
+//  Image.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 16/1/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+
+#if os(macOS)
+import AppKit
+private var imagesKey: Void?
+private var durationKey: Void?
+#else
+import UIKit
+import MobileCoreServices
+private var imageSourceKey: Void?
+#endif
+private var animatedImageDataKey: Void?
+
+import ImageIO
+import CoreGraphics
+
+#if !os(watchOS)
+import Accelerate
+import CoreImage
+#endif
+
+// MARK: - Image Properties
+extension Kingfisher where Base: Image {
+    fileprivate(set) var animatedImageData: Data? {
+        get {
+            return objc_getAssociatedObject(base, &animatedImageDataKey) as? Data
+        }
+        set {
+            objc_setAssociatedObject(base, &animatedImageDataKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    #if os(macOS)
+    var cgImage: CGImage? {
+        return base.cgImage(forProposedRect: nil, context: nil, hints: nil)
+    }
+    
+    var scale: CGFloat {
+        return 1.0
+    }
+    
+    fileprivate(set) var images: [Image]? {
+        get {
+            return objc_getAssociatedObject(base, &imagesKey) as? [Image]
+        }
+        set {
+            objc_setAssociatedObject(base, &imagesKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    fileprivate(set) var duration: TimeInterval {
+        get {
+            return objc_getAssociatedObject(base, &durationKey) as? TimeInterval ?? 0.0
+        }
+        set {
+            objc_setAssociatedObject(base, &durationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    var size: CGSize {
+        return base.representations.reduce(CGSize.zero, { size, rep in
+            return CGSize(width: max(size.width, CGFloat(rep.pixelsWide)), height: max(size.height, CGFloat(rep.pixelsHigh)))
+        })
+    }
+    
+    #else
+    var cgImage: CGImage? {
+        return base.cgImage
+    }
+    
+    var scale: CGFloat {
+        return base.scale
+    }
+    
+    var images: [Image]? {
+        return base.images
+    }
+    
+    var duration: TimeInterval {
+        return base.duration
+    }
+    
+    fileprivate(set) var imageSource: ImageSource? {
+        get {
+            return objc_getAssociatedObject(base, &imageSourceKey) as? ImageSource
+        }
+        set {
+            objc_setAssociatedObject(base, &imageSourceKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    var size: CGSize {
+        return base.size
+    }
+    #endif
+}
+
+// MARK: - Image Conversion
+extension Kingfisher where Base: Image {
+    #if os(macOS)
+    static func image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image {
+        return Image(cgImage: cgImage, size: CGSize.zero)
+    }
+    
+    /**
+     Normalize the image. This method does nothing in OS X.
+     
+     - returns: The image itself.
+     */
+    public var normalized: Image {
+        return base
+    }
+    
+    static func animated(with images: [Image], forDuration forDurationduration: TimeInterval) -> Image? {
+        return nil
+    }
+    #else
+    static func image(cgImage: CGImage, scale: CGFloat, refImage: Image?) -> Image {
+        if let refImage = refImage {
+            return Image(cgImage: cgImage, scale: scale, orientation: refImage.imageOrientation)
+        } else {
+            return Image(cgImage: cgImage, scale: scale, orientation: .up)
+        }
+    }
+    
+    /**
+     Normalize the image. This method will try to redraw an image with orientation and scale considered.
+     
+     - returns: The normalized image with orientation set to up and correct scale.
+     */
+    public var normalized: Image {
+        // prevent animated image (GIF) lose it's images
+        guard images == nil else { return base }
+        // No need to do anything if already up
+        guard base.imageOrientation != .up else { return base }
+    
+        return draw(cgImage: nil, to: size) {
+            base.draw(in: CGRect(origin: CGPoint.zero, size: size))
+        }
+    }
+    
+    static func animated(with images: [Image], forDuration duration: TimeInterval) -> Image? {
+        return .animatedImage(with: images, duration: duration)
+    }
+    #endif
+}
+
+// MARK: - Image Representation
+extension Kingfisher where Base: Image {
+    // MARK: - PNG
+    public func pngRepresentation() -> Data? {
+        #if os(macOS)
+            guard let cgimage = cgImage else {
+                return nil
+            }
+            let rep = NSBitmapImageRep(cgImage: cgimage)
+            return rep.representation(using: .png, properties: [:])
+        #else
+            return UIImagePNGRepresentation(base)
+        #endif
+    }
+    
+    // MARK: - JPEG
+    public func jpegRepresentation(compressionQuality: CGFloat) -> Data? {
+        #if os(macOS)
+            guard let cgImage = cgImage else {
+                return nil
+            }
+            let rep = NSBitmapImageRep(cgImage: cgImage)
+            return rep.representation(using:.jpeg, properties: [.compressionFactor: compressionQuality])
+        #else
+            return UIImageJPEGRepresentation(base, compressionQuality)
+        #endif
+    }
+    
+    // MARK: - GIF
+    public func gifRepresentation() -> Data? {
+        return animatedImageData
+    }
+}
+
+// MARK: - Create images from data
+extension Kingfisher where Base: Image {
+    static func animated(with data: Data, scale: CGFloat = 1.0, duration: TimeInterval = 0.0, preloadAll: Bool, onlyFirstFrame: Bool = false) -> Image? {
+        
+        func decode(from imageSource: CGImageSource, for options: NSDictionary) -> ([Image], TimeInterval)? {
+            
+            //Calculates frame duration for a gif frame out of the kCGImagePropertyGIFDictionary dictionary
+            func frameDuration(from gifInfo: NSDictionary?) -> Double {
+                let gifDefaultFrameDuration = 0.100
+                
+                guard let gifInfo = gifInfo else {
+                    return gifDefaultFrameDuration
+                }
+                
+                let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as? NSNumber
+                let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber
+                let duration = unclampedDelayTime ?? delayTime
+                
+                guard let frameDuration = duration else { return gifDefaultFrameDuration }
+                
+                return frameDuration.doubleValue > 0.011 ? frameDuration.doubleValue : gifDefaultFrameDuration
+            }
+            
+            let frameCount = CGImageSourceGetCount(imageSource)
+            var images = [Image]()
+            var gifDuration = 0.0
+            for i in 0 ..< frameCount {
+                
+                guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, options) else {
+                    return nil
+                }
+
+                if frameCount == 1 {
+                    // Single frame
+                    gifDuration = Double.infinity
+                } else {
+                    
+                    // Animated GIF
+                    guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil) else {
+                        return nil
+                    }
+
+                    let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary
+                    gifDuration += frameDuration(from: gifInfo)
+                }
+                
+                images.append(Kingfisher<Image>.image(cgImage: imageRef, scale: scale, refImage: nil))
+                
+                if onlyFirstFrame { break }
+            }
+            
+            return (images, gifDuration)
+        }
+        
+        // Start of kf.animatedImageWithGIFData
+        let options: NSDictionary = [kCGImageSourceShouldCache as String: true, kCGImageSourceTypeIdentifierHint as String: kUTTypeGIF]
+        guard let imageSource = CGImageSourceCreateWithData(data as CFData, options) else {
+            return nil
+        }
+        
+        #if os(macOS)
+            guard let (images, gifDuration) = decode(from: imageSource, for: options) else {
+                return nil
+            }
+            let image: Image?
+            if onlyFirstFrame {
+                image = images.first
+            } else {
+                image = Image(data: data)
+                image?.kf.images = images
+                image?.kf.duration = gifDuration
+            }
+            image?.kf.animatedImageData = data
+            return image
+        #else
+            
+            let image: Image?
+            if preloadAll || onlyFirstFrame {
+                guard let (images, gifDuration) = decode(from: imageSource, for: options) else { return nil }
+                image = onlyFirstFrame ? images.first : Kingfisher<Image>.animated(with: images, forDuration: duration <= 0.0 ? gifDuration : duration)
+            } else {
+                image = Image(data: data)
+                image?.kf.imageSource = ImageSource(ref: imageSource)
+            }
+            image?.kf.animatedImageData = data
+            return image
+        #endif
+    }
+
+    static func image(data: Data, scale: CGFloat, preloadAllAnimationData: Bool, onlyFirstFrame: Bool) -> Image? {
+        var image: Image?
+
+        #if os(macOS)
+            switch data.kf.imageFormat {
+            case .JPEG:
+                image = Image(data: data)
+            case .PNG:
+                image = Image(data: data)
+            case .GIF:
+                image = Kingfisher<Image>.animated(
+                    with: data,
+                    scale: scale,
+                    duration: 0.0,
+                    preloadAll: preloadAllAnimationData,
+                    onlyFirstFrame: onlyFirstFrame)
+            case .unknown:
+                image = Image(data: data)
+            }
+        #else
+            switch data.kf.imageFormat {
+            case .JPEG:
+                image = Image(data: data, scale: scale)
+            case .PNG:
+                image = Image(data: data, scale: scale)
+            case .GIF:
+                image = Kingfisher<Image>.animated(
+                    with: data,
+                    scale: scale,
+                    duration: 0.0,
+                    preloadAll: preloadAllAnimationData,
+                    onlyFirstFrame: onlyFirstFrame)
+            case .unknown:
+                image = Image(data: data, scale: scale)
+            }
+        #endif
+
+        return image
+    }
+}
+
+// MARK: - Image Transforming
+extension Kingfisher where Base: Image {
+    // MARK: - Blend Mode
+    /// Create image based on `self` and apply blend mode.
+    ///
+    /// - parameter blendMode:       The blend mode of creating image.
+    /// - parameter alpha:           The alpha should be used for image.
+    /// - parameter backgroundColor: The background color for the output image.
+    ///
+    /// - returns: An image with blend mode applied.
+    ///
+    /// - Note: This method only works for CG-based image.
+    #if !os(macOS)
+    public func image(withBlendMode blendMode: CGBlendMode,
+                      alpha: CGFloat = 1.0,
+                      backgroundColor: Color? = nil) -> Image
+    {
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Blend mode image only works for CG-based image.")
+            return base
+        }
+
+        let rect = CGRect(origin: .zero, size: size)
+        return draw(cgImage: cgImage, to: rect.size) {
+            if let backgroundColor = backgroundColor {
+                backgroundColor.setFill()
+                UIRectFill(rect)
+            }
+
+            base.draw(in: rect, blendMode: blendMode, alpha: alpha)
+        }
+    }
+    #endif
+
+    // MARK: - Compositing Operation
+    /// Create image based on `self` and apply compositing operation.
+    ///
+    /// - parameter compositingOperation: The compositing operation of creating image.
+    /// - parameter alpha:                The alpha should be used for image.
+    /// - parameter backgroundColor:      The background color for the output image.
+    ///
+    /// - returns: An image with compositing operation applied.
+    ///
+    /// - Note: This method only works for CG-based image.
+    #if os(macOS)
+    public func image(withCompositingOperation compositingOperation: NSCompositingOperation,
+                      alpha: CGFloat = 1.0,
+                      backgroundColor: Color? = nil) -> Image
+    {
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Compositing Operation image only works for CG-based image.")
+            return base
+        }
+
+        let rect = CGRect(origin: .zero, size: size)
+        return draw(cgImage: cgImage, to: rect.size) {
+            if let backgroundColor = backgroundColor {
+                backgroundColor.setFill()
+                rect.fill()
+            }
+
+            base.draw(in: rect, from: NSRect.zero, operation: compositingOperation, fraction: alpha)
+        }
+    }
+    #endif
+
+    // MARK: - Round Corner
+    /// Create a round corner image based on `self`.
+    ///
+    /// - parameter radius:          The round corner radius of creating image.
+    /// - parameter size:            The target size of creating image.
+    /// - parameter corners:         The target corners which will be applied rounding.
+    /// - parameter backgroundColor: The background color for the output image
+    ///
+    /// - returns: An image with round corner of `self`.
+    ///
+    /// - Note: This method only works for CG-based image.
+    public func image(withRoundRadius radius: CGFloat,
+                      fit size: CGSize,
+                      roundingCorners corners: RectCorner = .all,
+                      backgroundColor: Color? = nil) -> Image
+    {   
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Round corner image only works for CG-based image.")
+            return base
+        }
+        
+        let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
+        return draw(cgImage: cgImage, to: size) {
+            #if os(macOS)
+                if let backgroundColor = backgroundColor {
+                    let rectPath = NSBezierPath(rect: rect)
+                    backgroundColor.setFill()
+                    rectPath.fill()
+                }
+
+                let path = NSBezierPath(roundedRect: rect, byRoundingCorners: corners, radius: radius)
+                path.windingRule = .evenOddWindingRule
+                path.addClip()
+                base.draw(in: rect)
+            #else
+                guard let context = UIGraphicsGetCurrentContext() else {
+                    assertionFailure("[Kingfisher] Failed to create CG context for image.")
+                    return
+                }
+
+                if let backgroundColor = backgroundColor {
+                    let rectPath = UIBezierPath(rect: rect)
+                    backgroundColor.setFill()
+                    rectPath.fill()
+                }
+
+                let path = UIBezierPath(roundedRect: rect,
+                                        byRoundingCorners: corners.uiRectCorner,
+                                        cornerRadii: CGSize(width: radius, height: radius)).cgPath
+                context.addPath(path)
+                context.clip()
+                base.draw(in: rect)
+            #endif
+        }
+    }
+    
+    #if os(iOS) || os(tvOS)
+    func resize(to size: CGSize, for contentMode: UIViewContentMode) -> Image {
+        switch contentMode {
+        case .scaleAspectFit:
+            return resize(to: size, for: .aspectFit)
+        case .scaleAspectFill:
+            return resize(to: size, for: .aspectFill)
+        default:
+            return resize(to: size)
+        }
+    }
+    #endif
+    
+    // MARK: - Resize
+    /// Resize `self` to an image of new size.
+    ///
+    /// - parameter size: The target size.
+    ///
+    /// - returns: An image with new size.
+    ///
+    /// - Note: This method only works for CG-based image.
+    public func resize(to size: CGSize) -> Image {
+        
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Resize only works for CG-based image.")
+            return base
+        }
+        
+        let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
+        return draw(cgImage: cgImage, to: size) {
+            #if os(macOS)
+                base.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0)
+            #else
+                base.draw(in: rect)
+            #endif
+        }
+    }
+    
+    /// Resize `self` to an image of new size, respecting the content mode.
+    ///
+    /// - Parameters:
+    ///   - size: The target size.
+    ///   - contentMode: Content mode of output image should be.
+    /// - Returns: An image with new size.
+    public func resize(to size: CGSize, for contentMode: ContentMode) -> Image {
+        switch contentMode {
+        case .aspectFit:
+            let newSize = self.size.kf.constrained(size)
+            return resize(to: newSize)
+        case .aspectFill:
+            let newSize = self.size.kf.filling(size)
+            return resize(to: newSize)
+        default:
+            return resize(to: size)
+        }
+    }
+    
+    public func crop(to size: CGSize, anchorOn anchor: CGPoint) -> Image {
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Crop only works for CG-based image.")
+            return base
+        }
+        
+        let rect = self.size.kf.constrainedRect(for: size, anchor: anchor)
+        guard let image = cgImage.cropping(to: rect.scaled(scale)) else {
+            assertionFailure("[Kingfisher] Cropping image failed.")
+            return base
+        }
+        
+        return Kingfisher.image(cgImage: image, scale: scale, refImage: base)
+    }
+    
+    // MARK: - Blur
+    
+    /// Create an image with blur effect based on `self`.
+    ///
+    /// - parameter radius: The blur radius should be used when creating blur effect.
+    ///
+    /// - returns: An image with blur effect applied.
+    ///
+    /// - Note: This method only works for CG-based image.
+    public func blurred(withRadius radius: CGFloat) -> Image {
+        #if os(watchOS)
+            return base
+        #else
+            guard let cgImage = cgImage else {
+                assertionFailure("[Kingfisher] Blur only works for CG-based image.")
+                return base
+            }
+            
+            // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
+            // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
+            // if d is odd, use three box-blurs of size 'd', centered on the output pixel.
+            let s = Float(max(radius, 2.0))
+            // We will do blur on a resized image (*0.5), so the blur radius could be half as well.
+            
+            // Fix the slow compiling time for Swift 3. 
+            // See https://github.com/onevcat/Kingfisher/issues/611
+            let pi2 = 2 * Float.pi
+            let sqrtPi2 = sqrt(pi2)
+            var targetRadius = floor(s * 3.0 * sqrtPi2 / 4.0 + 0.5)
+            
+            if targetRadius.isEven {
+                targetRadius += 1
+            }
+            
+            let iterations: Int
+            if radius < 0.5 {
+                iterations = 1
+            } else if radius < 1.5 {
+                iterations = 2
+            } else {
+                iterations = 3
+            }
+            
+            let w = Int(size.width)
+            let h = Int(size.height)
+            let rowBytes = Int(CGFloat(cgImage.bytesPerRow))
+            
+            func createEffectBuffer(_ context: CGContext) -> vImage_Buffer {
+                let data = context.data
+                let width = vImagePixelCount(context.width)
+                let height = vImagePixelCount(context.height)
+                let rowBytes = context.bytesPerRow
+                
+                return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
+            }
+
+            guard let context = beginContext(size: size, scale: scale) else {
+                assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
+                return base
+            }
+            defer { endContext() }
+
+            context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h))
+            
+            var inBuffer = createEffectBuffer(context)
+            
+            guard let outContext = beginContext(size: size, scale: scale) else {
+                assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
+                return base
+            }
+            defer { endContext() }
+            var outBuffer = createEffectBuffer(outContext)
+            
+            for _ in 0 ..< iterations {
+                vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, nil, 0, 0, UInt32(targetRadius), UInt32(targetRadius), nil, vImage_Flags(kvImageEdgeExtend))
+                (inBuffer, outBuffer) = (outBuffer, inBuffer)
+            }
+            
+            #if os(macOS)
+                let result = outContext.makeImage().flatMap { fixedForRetinaPixel(cgImage: $0, to: size) }
+            #else
+                let result = outContext.makeImage().flatMap { Image(cgImage: $0, scale: base.scale, orientation: base.imageOrientation) }
+            #endif
+            guard let blurredImage = result else {
+                assertionFailure("[Kingfisher] Can not make an blurred image within this context.")
+                return base
+            }
+            
+            return blurredImage
+        #endif
+    }
+    
+    // MARK: - Overlay
+    
+    /// Create an image from `self` with a color overlay layer.
+    ///
+    /// - parameter color:    The color should be use to overlay.
+    /// - parameter fraction: Fraction of input color. From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay.
+    ///
+    /// - returns: An image with a color overlay applied.
+    ///
+    /// - Note: This method only works for CG-based image.
+    public func overlaying(with color: Color, fraction: CGFloat) -> Image {
+        
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Overlaying only works for CG-based image.")
+            return base
+        }
+        
+        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
+        return draw(cgImage: cgImage, to: rect.size) {
+            #if os(macOS)
+                base.draw(in: rect)
+                if fraction > 0 {
+                    color.withAlphaComponent(1 - fraction).set()
+                    rect.fill(using: .sourceAtop)
+                }
+            #else
+                color.set()
+                UIRectFill(rect)
+                base.draw(in: rect, blendMode: .destinationIn, alpha: 1.0)
+                
+                if fraction > 0 {
+                    base.draw(in: rect, blendMode: .sourceAtop, alpha: fraction)
+                }
+            #endif
+        }
+    }
+    
+    // MARK: - Tint
+    
+    /// Create an image from `self` with a color tint.
+    ///
+    /// - parameter color: The color should be used to tint `self`
+    ///
+    /// - returns: An image with a color tint applied.
+    public func tinted(with color: Color) -> Image {
+        #if os(watchOS)
+            return base
+        #else
+            return apply(.tint(color))
+        #endif
+    }
+    
+    // MARK: - Color Control
+    
+    /// Create an image from `self` with color control.
+    ///
+    /// - parameter brightness: Brightness changing to image.
+    /// - parameter contrast:   Contrast changing to image.
+    /// - parameter saturation: Saturation changing to image.
+    /// - parameter inputEV:    InputEV changing to image.
+    ///
+    /// - returns: An image with color control applied.
+    public func adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image {
+        #if os(watchOS)
+            return base
+        #else
+            return apply(.colorControl((brightness, contrast, saturation, inputEV)))
+        #endif
+    }
+
+    /// Return an image with given scale.
+    ///
+    /// - Parameter scale: Target scale factor the new image should have.
+    /// - Returns: The image with target scale. If the base image is already in the scale, `base` will be returned.
+    public func scaled(to scale: CGFloat) -> Image {
+        guard scale != self.scale else {
+            return base
+        }
+        guard let cgImage = cgImage else {
+            assertionFailure("[Kingfisher] Scaling only works for CG-based image.")
+            return base
+        }
+        return Kingfisher.image(cgImage: cgImage, scale: scale, refImage: base)
+    }
+}
+
+// MARK: - Decode
+extension Kingfisher where Base: Image {
+    var decoded: Image {
+        return decoded(scale: scale)
+    }
+    
+    func decoded(scale: CGFloat) -> Image {
+        // prevent animated image (GIF) lose it's images
+        #if os(iOS)
+            if imageSource != nil { return base }
+        #else
+            if images != nil { return base }
+        #endif
+        
+        guard let imageRef = self.cgImage else {
+            assertionFailure("[Kingfisher] Decoding only works for CG-based image.")
+            return base
+        }
+        
+        // Draw CGImage in a plain context with scale of 1.0.
+        guard let context = beginContext(size: CGSize(width: imageRef.width, height: imageRef.height), scale: 1.0) else {
+            assertionFailure("[Kingfisher] Decoding fails to create a valid context.")
+            return base
+        }
+        
+        defer { endContext() }
+        
+        let rect = CGRect(x: 0, y: 0, width: CGFloat(imageRef.width), height: CGFloat(imageRef.height))
+        context.draw(imageRef, in: rect)
+        let decompressedImageRef = context.makeImage()
+        return Kingfisher<Image>.image(cgImage: decompressedImageRef!, scale: scale, refImage: base)
+    }
+}
+
+/// Reference the source image reference
+final class ImageSource {
+    var imageRef: CGImageSource?
+    init(ref: CGImageSource) {
+        self.imageRef = ref
+    }
+}
+
+// MARK: - Image format
+private struct ImageHeaderData {
+    static var PNG: [UInt8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
+    static var JPEG_SOI: [UInt8] = [0xFF, 0xD8]
+    static var JPEG_IF: [UInt8] = [0xFF]
+    static var GIF: [UInt8] = [0x47, 0x49, 0x46]
+}
+
+enum ImageFormat {
+    case unknown, PNG, JPEG, GIF
+}
+
+
+// MARK: - Misc Helpers
+public struct DataProxy {
+    fileprivate let base: Data
+    init(proxy: Data) {
+        base = proxy
+    }
+}
+
+extension Data: KingfisherCompatible {
+    public typealias CompatibleType = DataProxy
+    public var kf: DataProxy {
+        return DataProxy(proxy: self)
+    }
+}
+
+extension DataProxy {
+    var imageFormat: ImageFormat {
+        var buffer = [UInt8](repeating: 0, count: 8)
+        (base as NSData).getBytes(&buffer, length: 8)
+        if buffer == ImageHeaderData.PNG {
+            return .PNG
+        } else if buffer[0] == ImageHeaderData.JPEG_SOI[0] &&
+            buffer[1] == ImageHeaderData.JPEG_SOI[1] &&
+            buffer[2] == ImageHeaderData.JPEG_IF[0]
+        {
+            return .JPEG
+        } else if buffer[0] == ImageHeaderData.GIF[0] &&
+            buffer[1] == ImageHeaderData.GIF[1] &&
+            buffer[2] == ImageHeaderData.GIF[2]
+        {
+            return .GIF
+        }
+
+        return .unknown
+    }
+}
+
+public struct CGSizeProxy {
+    fileprivate let base: CGSize
+    init(proxy: CGSize) {
+        base = proxy
+    }
+}
+
+extension CGSize: KingfisherCompatible {
+    public typealias CompatibleType = CGSizeProxy
+    public var kf: CGSizeProxy {
+        return CGSizeProxy(proxy: self)
+    }
+}
+
+extension CGSizeProxy {
+    func constrained(_ size: CGSize) -> CGSize {
+        let aspectWidth = round(aspectRatio * size.height)
+        let aspectHeight = round(size.width / aspectRatio)
+
+        return aspectWidth > size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height)
+    }
+
+    func filling(_ size: CGSize) -> CGSize {
+        let aspectWidth = round(aspectRatio * size.height)
+        let aspectHeight = round(size.width / aspectRatio)
+
+        return aspectWidth < size.width ? CGSize(width: size.width, height: aspectHeight) : CGSize(width: aspectWidth, height: size.height)
+    }
+
+    private var aspectRatio: CGFloat {
+        return base.height == 0.0 ? 1.0 : base.width / base.height
+    }
+    
+    
+    func constrainedRect(for size: CGSize, anchor: CGPoint) -> CGRect {
+        
+        let unifiedAnchor = CGPoint(x: anchor.x.clamped(to: 0.0...1.0),
+                                    y: anchor.y.clamped(to: 0.0...1.0))
+        
+        let x = unifiedAnchor.x * base.width - unifiedAnchor.x * size.width
+        let y = unifiedAnchor.y * base.height - unifiedAnchor.y * size.height
+        let r = CGRect(x: x, y: y, width: size.width, height: size.height)
+        
+        let ori = CGRect(origin: CGPoint.zero, size: base)
+        return ori.intersection(r)
+    }
+}
+
+extension CGRect {
+    func scaled(_ scale: CGFloat) -> CGRect {
+        return CGRect(x: origin.x * scale, y: origin.y * scale,
+                      width: size.width * scale, height: size.height * scale)
+    }
+}
+
+extension Comparable {
+    func clamped(to limits: ClosedRange<Self>) -> Self {
+        return min(max(self, limits.lowerBound), limits.upperBound)
+    }
+}
+
+extension Kingfisher where Base: Image {
+    
+    func beginContext(size: CGSize, scale: CGFloat) -> CGContext? {
+        #if os(macOS)
+            guard let rep = NSBitmapImageRep(
+                bitmapDataPlanes: nil,
+                pixelsWide: Int(size.width),
+                pixelsHigh: Int(size.height),
+                bitsPerSample: cgImage?.bitsPerComponent ?? 8,
+                samplesPerPixel: 4,
+                hasAlpha: true,
+                isPlanar: false,
+                colorSpaceName: .calibratedRGB,
+                bytesPerRow: 0,
+                bitsPerPixel: 0) else
+            {
+                assertionFailure("[Kingfisher] Image representation cannot be created.")
+                return nil
+            }
+            rep.size = size
+            NSGraphicsContext.saveGraphicsState()
+            guard let context = NSGraphicsContext(bitmapImageRep: rep) else {
+                assertionFailure("[Kingfisher] Image contenxt cannot be created.")
+                return nil
+            }
+            
+            NSGraphicsContext.current = context
+            return context.cgContext
+        #else
+            UIGraphicsBeginImageContextWithOptions(size, false, scale)
+            let context = UIGraphicsGetCurrentContext()
+            context?.scaleBy(x: 1.0, y: -1.0)
+            context?.translateBy(x: 0, y: -size.height)
+            return context
+        #endif
+    }
+    
+    func endContext() {
+        #if os(macOS)
+            NSGraphicsContext.restoreGraphicsState()
+        #else
+            UIGraphicsEndImageContext()
+        #endif
+    }
+    
+    func draw(cgImage: CGImage?, to size: CGSize, draw: ()->()) -> Image {
+        #if os(macOS)
+        guard let rep = NSBitmapImageRep(
+            bitmapDataPlanes: nil,
+            pixelsWide: Int(size.width),
+            pixelsHigh: Int(size.height),
+            bitsPerSample: cgImage?.bitsPerComponent ?? 8,
+            samplesPerPixel: 4,
+            hasAlpha: true,
+            isPlanar: false,
+            colorSpaceName: .calibratedRGB,
+            bytesPerRow: 0,
+            bitsPerPixel: 0) else
+        {
+            assertionFailure("[Kingfisher] Image representation cannot be created.")
+            return base
+        }
+        rep.size = size
+        
+        NSGraphicsContext.saveGraphicsState()
+        
+        let context = NSGraphicsContext(bitmapImageRep: rep)
+        NSGraphicsContext.current = context
+        draw()
+        NSGraphicsContext.restoreGraphicsState()
+        
+        let outputImage = Image(size: size)
+        outputImage.addRepresentation(rep)
+        return outputImage
+        #else
+            
+        UIGraphicsBeginImageContextWithOptions(size, false, scale)
+        defer { UIGraphicsEndImageContext() }
+        draw()
+        return UIGraphicsGetImageFromCurrentImageContext() ?? base
+        
+        #endif
+    }
+    
+    #if os(macOS)
+    func fixedForRetinaPixel(cgImage: CGImage, to size: CGSize) -> Image {
+        
+        let image = Image(cgImage: cgImage, size: base.size)
+        let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
+        
+        return draw(cgImage: cgImage, to: self.size) {
+            image.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0)
+        }
+    }
+    #endif
+}
+
+extension Float {
+    var isEven: Bool {
+        return truncatingRemainder(dividingBy: 2.0) == 0
+    }
+}
+
+#if os(macOS)
+extension NSBezierPath {
+    convenience init(roundedRect rect: NSRect, topLeftRadius: CGFloat, topRightRadius: CGFloat,
+         bottomLeftRadius: CGFloat, bottomRightRadius: CGFloat)
+    {
+        self.init()
+        
+        let maxCorner = min(rect.width, rect.height) / 2
+        
+        let radiusTopLeft = min(maxCorner, max(0, topLeftRadius))
+        let radiustopRight = min(maxCorner, max(0, topRightRadius))
+        let radiusbottomLeft = min(maxCorner, max(0, bottomLeftRadius))
+        let radiusbottomRight = min(maxCorner, max(0, bottomRightRadius))
+        
+        guard !NSIsEmptyRect(rect) else {
+            return
+        }
+        
+        let topLeft = NSMakePoint(NSMinX(rect), NSMaxY(rect));
+        let topRight = NSMakePoint(NSMaxX(rect), NSMaxY(rect));
+        let bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect));
+        
+        move(to: NSMakePoint(NSMidX(rect), NSMaxY(rect)))
+        appendArc(from: topLeft, to: rect.origin, radius: radiusTopLeft)
+        appendArc(from: rect.origin, to: bottomRight, radius: radiusbottomLeft)
+        appendArc(from: bottomRight, to: topRight, radius: radiusbottomRight)
+        appendArc(from: topRight, to: topLeft, radius: radiustopRight)
+        close()
+    }
+    
+    convenience init(roundedRect rect: NSRect, byRoundingCorners corners: RectCorner, radius: CGFloat) {
+        let radiusTopLeft = corners.contains(.topLeft) ? radius : 0
+        let radiusTopRight = corners.contains(.topRight) ? radius : 0
+        let radiusBottomLeft = corners.contains(.bottomLeft) ? radius : 0
+        let radiusBottomRight = corners.contains(.bottomRight) ? radius : 0
+        
+        self.init(roundedRect: rect, topLeftRadius: radiusTopLeft, topRightRadius: radiusTopRight,
+                  bottomLeftRadius: radiusBottomLeft, bottomRightRadius: radiusBottomRight)
+    }
+}
+    
+#else
+extension RectCorner {
+    var uiRectCorner: UIRectCorner {
+        
+        var result: UIRectCorner = []
+        
+        if self.contains(.topLeft) { result.insert(.topLeft) }
+        if self.contains(.topRight) { result.insert(.topRight) }
+        if self.contains(.bottomLeft) { result.insert(.bottomLeft) }
+        if self.contains(.bottomRight) { result.insert(.bottomRight) }
+        
+        return result
+    }
+}
+#endif
+
diff --git a/iOS/Pods/Kingfisher/Sources/ImageCache.swift b/iOS/Pods/Kingfisher/Sources/ImageCache.swift
new file mode 100755 (executable)
index 0000000..a29fe82
--- /dev/null
@@ -0,0 +1,729 @@
+//
+//  ImageCache.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+
+public extension Notification.Name {
+    /**
+     This notification will be sent when the disk cache got cleaned either there are cached files expired or the total size exceeding the max allowed size. The manually invoking of `clearDiskCache` method will not trigger this notification.
+     
+     The `object` of this notification is the `ImageCache` object which sends the notification.
+     
+     A list of removed hashes (files) could be retrieved by accessing the array under `KingfisherDiskCacheCleanedHashKey` key in `userInfo` of the notification object you received. By checking the array, you could know the hash codes of files are removed.
+     
+     The main purpose of this notification is supplying a chance to maintain some necessary information on the cached files. See [this wiki](https://github.com/onevcat/Kingfisher/wiki/How-to-implement-ETag-based-304-(Not-Modified)-handling-in-Kingfisher) for a use case on it.
+     */
+    public static var KingfisherDidCleanDiskCache = Notification.Name.init("com.onevcat.Kingfisher.KingfisherDidCleanDiskCache")
+}
+
+/**
+Key for array of cleaned hashes in `userInfo` of `KingfisherDidCleanDiskCacheNotification`.
+*/
+public let KingfisherDiskCacheCleanedHashKey = "com.onevcat.Kingfisher.cleanedHash"
+
+/// It represents a task of retrieving image. You can call `cancel` on it to stop the process.
+public typealias RetrieveImageDiskTask = DispatchWorkItem
+
+/**
+Cache type of a cached image.
+
+- None:   The image is not cached yet when retrieving it.
+- Memory: The image is cached in memory.
+- Disk:   The image is cached in disk.
+*/
+public enum CacheType {
+    case none, memory, disk
+    
+    public var cached: Bool {
+        switch self {
+        case .memory, .disk: return true
+        case .none: return false
+        }
+    }
+}
+
+/// `ImageCache` represents both the memory and disk cache system of Kingfisher. 
+/// While a default image cache object will be used if you prefer the extension methods of Kingfisher, 
+/// you can create your own cache object and configure it as your need. You could use an `ImageCache`
+/// object to manipulate memory and disk cache for Kingfisher.
+open class ImageCache {
+
+    //Memory
+    fileprivate let memoryCache = NSCache<NSString, AnyObject>()
+    
+    /// The largest cache cost of memory cache. The total cost is pixel count of 
+    /// all cached images in memory.
+    /// Default is unlimited. Memory cache will be purged automatically when a 
+    /// memory warning notification is received.
+    open var maxMemoryCost: UInt = 0 {
+        didSet {
+            self.memoryCache.totalCostLimit = Int(maxMemoryCost)
+        }
+    }
+    
+    //Disk
+    fileprivate let ioQueue: DispatchQueue
+    fileprivate var fileManager: FileManager!
+    
+    ///The disk cache location.
+    open let diskCachePath: String
+  
+    /// The default file extension appended to cached files.
+    open var pathExtension: String?
+    
+    /// The longest time duration in second of the cache being stored in disk. 
+    /// Default is 1 week (60 * 60 * 24 * 7 seconds).
+    /// Setting this to a negative value will make the disk cache never expiring.
+    open var maxCachePeriodInSecond: TimeInterval = 60 * 60 * 24 * 7 //Cache exists for 1 week
+    
+    /// The largest disk size can be taken for the cache. It is the total 
+    /// allocated size of cached files in bytes.
+    /// Default is no limit.
+    open var maxDiskCacheSize: UInt = 0
+    
+    fileprivate let processQueue: DispatchQueue
+    
+    /// The default cache.
+    public static let `default` = ImageCache(name: "default")
+    
+    /// Closure that defines the disk cache path from a given path and cacheName.
+    public typealias DiskCachePathClosure = (String?, String) -> String
+    
+    /// The default DiskCachePathClosure
+    public final class func defaultDiskCachePathClosure(path: String?, cacheName: String) -> String {
+        let dstPath = path ?? NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!
+        return (dstPath as NSString).appendingPathComponent(cacheName)
+    }
+    
+    /**
+    Init method. Passing a name for the cache. It represents a cache folder in the memory and disk.
+    
+    - parameter name: Name of the cache. It will be used as the memory cache name and the disk cache folder name 
+                      appending to the cache path. This value should not be an empty string.
+    - parameter path: Optional - Location of cache path on disk. If `nil` is passed in (the default value),
+                      the `.cachesDirectory` in of your app will be used.
+    - parameter diskCachePathClosure: Closure that takes in an optional initial path string and generates
+                      the final disk cache path. You could use it to fully customize your cache path.
+    
+    - returns: The cache object.
+    */
+    public init(name: String,
+                path: String? = nil,
+                diskCachePathClosure: DiskCachePathClosure = ImageCache.defaultDiskCachePathClosure)
+    {
+        
+        if name.isEmpty {
+            fatalError("[Kingfisher] You should specify a name for the cache. A cache with empty name is not permitted.")
+        }
+        
+        let cacheName = "com.onevcat.Kingfisher.ImageCache.\(name)"
+        memoryCache.name = cacheName
+        
+        diskCachePath = diskCachePathClosure(path, cacheName)
+        
+        let ioQueueName = "com.onevcat.Kingfisher.ImageCache.ioQueue.\(name)"
+        ioQueue = DispatchQueue(label: ioQueueName)
+        
+        let processQueueName = "com.onevcat.Kingfisher.ImageCache.processQueue.\(name)"
+        processQueue = DispatchQueue(label: processQueueName, attributes: .concurrent)
+        
+        ioQueue.sync { fileManager = FileManager() }
+        
+#if !os(macOS) && !os(watchOS)
+        NotificationCenter.default.addObserver(
+            self, selector: #selector(clearMemoryCache), name: .UIApplicationDidReceiveMemoryWarning, object: nil)
+        NotificationCenter.default.addObserver(
+            self, selector: #selector(cleanExpiredDiskCache), name: .UIApplicationWillTerminate, object: nil)
+        NotificationCenter.default.addObserver(
+            self, selector: #selector(backgroundCleanExpiredDiskCache), name: .UIApplicationDidEnterBackground, object: nil)
+#endif
+    }
+    
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
+
+
+    // MARK: - Store & Remove
+
+    /**
+    Store an image to cache. It will be saved to both memory and disk. It is an async operation.
+    
+    - parameter image:             The image to be stored.
+    - parameter original:          The original data of the image.
+                                   Kingfisher will use it to check the format of the image and optimize cache size on disk.
+                                   If `nil` is supplied, the image data will be saved as a normalized PNG file.
+                                   It is strongly suggested to supply it whenever possible, to get a better performance and disk usage.
+    - parameter key:               Key for the image.
+    - parameter identifier:        The identifier of processor used. If you are using a processor for the image, pass the identifier of
+                                   processor to it.
+                                   This identifier will be used to generate a corresponding key for the combination of `key` and processor.
+    - parameter toDisk:            Whether this image should be cached to disk or not. If false, the image will be only cached in memory.
+    - parameter completionHandler: Called when store operation completes.
+    */
+    open func store(_ image: Image,
+                      original: Data? = nil,
+                      forKey key: String,
+                      processorIdentifier identifier: String = "",
+                      cacheSerializer serializer: CacheSerializer = DefaultCacheSerializer.default,
+                      toDisk: Bool = true,
+                      completionHandler: (() -> Void)? = nil)
+    {
+        
+        let computedKey = key.computedKey(with: identifier)
+        memoryCache.setObject(image, forKey: computedKey as NSString, cost: image.kf.imageCost)
+
+        func callHandlerInMainQueue() {
+            if let handler = completionHandler {
+                DispatchQueue.main.async {
+                    handler()
+                }
+            }
+        }
+        
+        if toDisk {
+            ioQueue.async {
+                
+                if let data = serializer.data(with: image, original: original) {
+                    if !self.fileManager.fileExists(atPath: self.diskCachePath) {
+                        do {
+                            try self.fileManager.createDirectory(atPath: self.diskCachePath, withIntermediateDirectories: true, attributes: nil)
+                        } catch _ {}
+                    }
+                    
+                    self.fileManager.createFile(atPath: self.cachePath(forComputedKey: computedKey), contents: data, attributes: nil)
+                }
+                callHandlerInMainQueue()
+            }
+        } else {
+            callHandlerInMainQueue()
+        }
+    }
+    
+    /**
+    Remove the image for key for the cache. It will be opted out from both memory and disk. 
+    It is an async operation.
+    
+    - parameter key:               Key for the image.
+    - parameter identifier:        The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it.
+                                   This identifier will be used to generate a corresponding key for the combination of `key` and processor.
+    - parameter fromMemory:        Whether this image should be removed from memory or not. If false, the image won't be removed from memory.
+    - parameter fromDisk:          Whether this image should be removed from disk or not. If false, the image won't be removed from disk.
+    - parameter completionHandler: Called when removal operation completes.
+    */
+    open func removeImage(forKey key: String,
+                          processorIdentifier identifier: String = "",
+                          fromMemory: Bool = true,
+                          fromDisk: Bool = true,
+                          completionHandler: (() -> Void)? = nil)
+    {
+        let computedKey = key.computedKey(with: identifier)
+
+        if fromMemory {
+            memoryCache.removeObject(forKey: computedKey as NSString)
+        }
+        
+        func callHandlerInMainQueue() {
+            if let handler = completionHandler {
+                DispatchQueue.main.async {
+                    handler()
+                }
+            }
+        }
+        
+        if fromDisk {
+            ioQueue.async{
+                do {
+                    try self.fileManager.removeItem(atPath: self.cachePath(forComputedKey: computedKey))
+                } catch _ {}
+                callHandlerInMainQueue()
+            }
+        } else {
+            callHandlerInMainQueue()
+        }
+    }
+
+    // MARK: - Get data from cache
+
+    /**
+    Get an image for a key from memory or disk.
+    
+    - parameter key:               Key for the image.
+    - parameter options:           Options of retrieving image. If you need to retrieve an image which was 
+                                   stored with a specified `ImageProcessor`, pass the processor in the option too.
+    - parameter completionHandler: Called when getting operation completes with image result and cached type of 
+                                   this image. If there is no such key cached, the image will be `nil`.
+    
+    - returns: The retrieving task.
+    */
+    @discardableResult
+    open func retrieveImage(forKey key: String,
+                               options: KingfisherOptionsInfo?,
+                     completionHandler: ((Image?, CacheType) -> Void)?) -> RetrieveImageDiskTask?
+    {
+        // No completion handler. Not start working and early return.
+        guard let completionHandler = completionHandler else {
+            return nil
+        }
+        
+        var block: RetrieveImageDiskTask?
+        let options = options ?? KingfisherEmptyOptionsInfo
+        let imageModifier = options.imageModifier
+
+        if let image = self.retrieveImageInMemoryCache(forKey: key, options: options) {
+            options.callbackDispatchQueue.safeAsync {
+                completionHandler(imageModifier.modify(image), .memory)
+            }
+        } else if options.fromMemoryCacheOrRefresh { // Only allows to get images from memory cache.
+            options.callbackDispatchQueue.safeAsync {
+                completionHandler(nil, .none)
+            }
+        } else {
+            var sSelf: ImageCache! = self
+            block = DispatchWorkItem(block: {
+                // Begin to load image from disk
+                if let image = sSelf.retrieveImageInDiskCache(forKey: key, options: options) {
+                    if options.backgroundDecode {
+                        sSelf.processQueue.async {
+
+                            let result = image.kf.decoded
+                            
+                            sSelf.store(result,
+                                        forKey: key,
+                                        processorIdentifier: options.processor.identifier,
+                                        cacheSerializer: options.cacheSerializer,
+                                        toDisk: false,
+                                        completionHandler: nil)
+                            options.callbackDispatchQueue.safeAsync {
+                                completionHandler(imageModifier.modify(result), .memory)
+                                sSelf = nil
+                            }
+                        }
+                    } else {
+                        sSelf.store(image,
+                                    forKey: key,
+                                    processorIdentifier: options.processor.identifier,
+                                    cacheSerializer: options.cacheSerializer,
+                                    toDisk: false,
+                                    completionHandler: nil
+                        )
+                        options.callbackDispatchQueue.safeAsync {
+                            completionHandler(imageModifier.modify(image), .disk)
+                            sSelf = nil
+                        }
+                    }
+                } else {
+                    // No image found from either memory or disk
+                    options.callbackDispatchQueue.safeAsync {
+                        completionHandler(nil, .none)
+                        sSelf = nil
+                    }
+                }
+            })
+            
+            sSelf.ioQueue.async(execute: block!)
+        }
+    
+        return block
+    }
+    
+    /**
+    Get an image for a key from memory.
+    
+    - parameter key:     Key for the image.
+    - parameter options: Options of retrieving image. If you need to retrieve an image which was 
+                         stored with a specified `ImageProcessor`, pass the processor in the option too.
+    - returns: The image object if it is cached, or `nil` if there is no such key in the cache.
+    */
+    open func retrieveImageInMemoryCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? {
+        
+        let options = options ?? KingfisherEmptyOptionsInfo
+        let computedKey = key.computedKey(with: options.processor.identifier)
+        
+        return memoryCache.object(forKey: computedKey as NSString) as? Image
+    }
+    
+    /**
+    Get an image for a key from disk.
+    
+    - parameter key:     Key for the image.
+    - parameter options: Options of retrieving image. If you need to retrieve an image which was
+                         stored with a specified `ImageProcessor`, pass the processor in the option too.
+
+    - returns: The image object if it is cached, or `nil` if there is no such key in the cache.
+    */
+    open func retrieveImageInDiskCache(forKey key: String, options: KingfisherOptionsInfo? = nil) -> Image? {
+        
+        let options = options ?? KingfisherEmptyOptionsInfo
+        let computedKey = key.computedKey(with: options.processor.identifier)
+        
+        return diskImage(forComputedKey: computedKey, serializer: options.cacheSerializer, options: options)
+    }
+
+
+    // MARK: - Clear & Clean
+
+    /**
+    Clear memory cache.
+    */
+    @objc public func clearMemoryCache() {
+        memoryCache.removeAllObjects()
+    }
+    
+    /**
+    Clear disk cache. This is an async operation.
+    
+    - parameter completionHander: Called after the operation completes.
+    */
+    open func clearDiskCache(completion handler: (()->())? = nil) {
+        ioQueue.async {
+            do {
+                try self.fileManager.removeItem(atPath: self.diskCachePath)
+                try self.fileManager.createDirectory(atPath: self.diskCachePath, withIntermediateDirectories: true, attributes: nil)
+            } catch _ { }
+            
+            if let handler = handler {
+                DispatchQueue.main.async {
+                    handler()
+                }
+            }
+        }
+    }
+    
+    /**
+    Clean expired disk cache. This is an async operation.
+    */
+    @objc fileprivate func cleanExpiredDiskCache() {
+        cleanExpiredDiskCache(completion: nil)
+    }
+    
+    /**
+    Clean expired disk cache. This is an async operation.
+    
+    - parameter completionHandler: Called after the operation completes.
+    */
+    open func cleanExpiredDiskCache(completion handler: (()->())? = nil) {
+        
+        // Do things in cocurrent io queue
+        ioQueue.async {
+            
+            var (URLsToDelete, diskCacheSize, cachedFiles) = self.travelCachedFiles(onlyForCacheSize: false)
+            
+            for fileURL in URLsToDelete {
+                do {
+                    try self.fileManager.removeItem(at: fileURL)
+                } catch _ { }
+            }
+                
+            if self.maxDiskCacheSize > 0 && diskCacheSize > self.maxDiskCacheSize {
+                let targetSize = self.maxDiskCacheSize / 2
+                    
+                // Sort files by last modify date. We want to clean from the oldest files.
+                let sortedFiles = cachedFiles.keysSortedByValue {
+                    resourceValue1, resourceValue2 -> Bool in
+                    
+                    if let date1 = resourceValue1.contentAccessDate,
+                       let date2 = resourceValue2.contentAccessDate
+                    {
+                        return date1.compare(date2) == .orderedAscending
+                    }
+                    
+                    // Not valid date information. This should not happen. Just in case.
+                    return true
+                }
+                
+                for fileURL in sortedFiles {
+                    
+                    do {
+                        try self.fileManager.removeItem(at: fileURL)
+                    } catch { }
+                        
+                    URLsToDelete.append(fileURL)
+                    
+                    if let fileSize = cachedFiles[fileURL]?.totalFileAllocatedSize {
+                        diskCacheSize -= UInt(fileSize)
+                    }
+                    
+                    if diskCacheSize < targetSize {
+                        break
+                    }
+                }
+            }
+                
+            DispatchQueue.main.async {
+                
+                if URLsToDelete.count != 0 {
+                    let cleanedHashes = URLsToDelete.map { $0.lastPathComponent }
+                    NotificationCenter.default.post(name: .KingfisherDidCleanDiskCache, object: self, userInfo: [KingfisherDiskCacheCleanedHashKey: cleanedHashes])
+                }
+                
+                handler?()
+            }
+        }
+    }
+    
+    fileprivate func travelCachedFiles(onlyForCacheSize: Bool) -> (urlsToDelete: [URL], diskCacheSize: UInt, cachedFiles: [URL: URLResourceValues]) {
+        
+        let diskCacheURL = URL(fileURLWithPath: diskCachePath)
+        let resourceKeys: Set<URLResourceKey> = [.isDirectoryKey, .contentAccessDateKey, .totalFileAllocatedSizeKey]
+        let expiredDate: Date? = (maxCachePeriodInSecond < 0) ? nil : Date(timeIntervalSinceNow: -maxCachePeriodInSecond)
+        
+        var cachedFiles = [URL: URLResourceValues]()
+        var urlsToDelete = [URL]()
+        var diskCacheSize: UInt = 0
+
+        for fileUrl in (try? fileManager.contentsOfDirectory(at: diskCacheURL, includingPropertiesForKeys: Array(resourceKeys), options: .skipsHiddenFiles)) ?? [] {
+
+            do {
+                let resourceValues = try fileUrl.resourceValues(forKeys: resourceKeys)
+                // If it is a Directory. Continue to next file URL.
+                if resourceValues.isDirectory == true {
+                    continue
+                }
+
+                // If this file is expired, add it to URLsToDelete
+                if !onlyForCacheSize,
+                    let expiredDate = expiredDate,
+                    let lastAccessData = resourceValues.contentAccessDate,
+                    (lastAccessData as NSDate).laterDate(expiredDate) == expiredDate
+                {
+                    urlsToDelete.append(fileUrl)
+                    continue
+                }
+
+                if let fileSize = resourceValues.totalFileAllocatedSize {
+                    diskCacheSize += UInt(fileSize)
+                    if !onlyForCacheSize {
+                        cachedFiles[fileUrl] = resourceValues
+                    }
+                }
+            } catch _ { }
+        }
+
+        return (urlsToDelete, diskCacheSize, cachedFiles)
+    }
+
+#if !os(macOS) && !os(watchOS)
+    /**
+    Clean expired disk cache when app in background. This is an async operation.
+    In most cases, you should not call this method explicitly. 
+    It will be called automatically when `UIApplicationDidEnterBackgroundNotification` received.
+    */
+    @objc public func backgroundCleanExpiredDiskCache() {
+        // if 'sharedApplication()' is unavailable, then return
+        guard let sharedApplication = Kingfisher<UIApplication>.shared else { return }
+
+        func endBackgroundTask(_ task: inout UIBackgroundTaskIdentifier) {
+            sharedApplication.endBackgroundTask(task)
+            task = UIBackgroundTaskInvalid
+        }
+        
+        var backgroundTask: UIBackgroundTaskIdentifier!
+        backgroundTask = sharedApplication.beginBackgroundTask {
+            endBackgroundTask(&backgroundTask!)
+        }
+        
+        cleanExpiredDiskCache {
+            endBackgroundTask(&backgroundTask!)
+        }
+    }
+#endif
+
+
+    // MARK: - Check cache status
+    
+    /// Cache type for checking whether an image is cached for a key in current cache.
+    ///
+    /// - Parameters:
+    ///   - key: Key for the image.
+    ///   - identifier: Processor identifier which used for this image. Default is empty string.
+    /// - Returns: A `CacheType` instance which indicates the cache status. `.none` means the image is not in cache yet.
+    open func imageCachedType(forKey key: String, processorIdentifier identifier: String = "") -> CacheType {
+        let computedKey = key.computedKey(with: identifier)
+        
+        if memoryCache.object(forKey: computedKey as NSString) != nil {
+            return .memory
+        }
+        
+        let filePath = cachePath(forComputedKey: computedKey)
+        
+        var diskCached = false
+        ioQueue.sync {
+            diskCached = fileManager.fileExists(atPath: filePath)
+        }
+        
+        if diskCached {
+            return .disk
+        }
+        
+        return .none
+    }
+    
+    /**
+    Get the hash for the key. This could be used for matching files.
+    
+    - parameter key:        The key which is used for caching.
+    - parameter identifier: The identifier of processor used. If you are using a processor for the image, pass the identifier of processor to it.
+    
+     - returns: Corresponding hash.
+    */
+    open func hash(forKey key: String, processorIdentifier identifier: String = "") -> String {
+        let computedKey = key.computedKey(with: identifier)
+        return cacheFileName(forComputedKey: computedKey)
+    }
+    
+    /**
+    Calculate the disk size taken by cache. 
+    It is the total allocated size of the cached files in bytes.
+    
+    - parameter completionHandler: Called with the calculated size when finishes.
+    */
+    open func calculateDiskCacheSize(completion handler: @escaping ((_ size: UInt) -> Void)) {
+        ioQueue.async {
+            let (_, diskCacheSize, _) = self.travelCachedFiles(onlyForCacheSize: true)
+            DispatchQueue.main.async {
+                handler(diskCacheSize)
+            }
+        }
+    }
+    
+    /**
+    Get the cache path for the key.
+    It is useful for projects with UIWebView or anyone that needs access to the local file path.
+    
+    i.e. Replace the `<img src='path_for_key'>` tag in your HTML.
+     
+    - Note: This method does not guarantee there is an image already cached in the path. It just returns the path
+      that the image should be.
+      You could use `isImageCached(forKey:)` method to check whether the image is cached under that key.
+    */
+    open func cachePath(forKey key: String, processorIdentifier identifier: String = "") -> String {
+        let computedKey = key.computedKey(with: identifier)
+        return cachePath(forComputedKey: computedKey)
+    }
+
+    open func cachePath(forComputedKey key: String) -> String {
+        let fileName = cacheFileName(forComputedKey: key)
+        return (diskCachePath as NSString).appendingPathComponent(fileName)
+    }
+}
+
+// MARK: - Internal Helper
+extension ImageCache {
+  
+    func diskImage(forComputedKey key: String, serializer: CacheSerializer, options: KingfisherOptionsInfo) -> Image? {
+        if let data = diskImageData(forComputedKey: key) {
+            return serializer.image(with: data, options: options)
+        } else {
+            return nil
+        }
+    }
+    
+    func diskImageData(forComputedKey key: String) -> Data? {
+        let filePath = cachePath(forComputedKey: key)
+        return (try? Data(contentsOf: URL(fileURLWithPath: filePath)))
+    }
+    
+    func cacheFileName(forComputedKey key: String) -> String {
+        if let ext = self.pathExtension {
+          return (key.kf.md5 as NSString).appendingPathExtension(ext)!
+        }
+        return key.kf.md5
+    }
+}
+
+// MARK: - Deprecated
+extension ImageCache {
+    /**
+     *  Cache result for checking whether an image is cached for a key.
+     */
+    @available(*, deprecated,
+    message: "CacheCheckResult is deprecated. Use imageCachedType(forKey:processorIdentifier:) API instead.")
+    public struct CacheCheckResult {
+        public let cached: Bool
+        public let cacheType: CacheType?
+    }
+    
+    /**
+     Check whether an image is cached for a key.
+     
+     - parameter key: Key for the image.
+     
+     - returns: The check result.
+     */
+    @available(*, deprecated,
+    message: "Use imageCachedType(forKey:processorIdentifier:) instead. CacheCheckResult.none indicates not being cached.",
+    renamed: "imageCachedType(forKey:processorIdentifier:)")
+    open func isImageCached(forKey key: String, processorIdentifier identifier: String = "") -> CacheCheckResult {
+        let result = imageCachedType(forKey: key, processorIdentifier: identifier)
+        switch result {
+        case .memory, .disk:
+            return CacheCheckResult(cached: true, cacheType: result)
+        case .none:
+            return CacheCheckResult(cached: false, cacheType: nil)
+        }
+    }
+}
+
+extension Kingfisher where Base: Image {
+    var imageCost: Int {
+        return images == nil ?
+            Int(size.height * size.width * scale * scale) :
+            Int(size.height * size.width * scale * scale) * images!.count
+    }
+}
+
+extension Dictionary {
+    func keysSortedByValue(_ isOrderedBefore: (Value, Value) -> Bool) -> [Key] {
+        return Array(self).sorted{ isOrderedBefore($0.1, $1.1) }.map{ $0.0 }
+    }
+}
+
+#if !os(macOS) && !os(watchOS)
+// MARK: - For App Extensions
+extension UIApplication: KingfisherCompatible { }
+extension Kingfisher where Base: UIApplication {
+    public static var shared: UIApplication? {
+        let selector = NSSelectorFromString("sharedApplication")
+        guard Base.responds(to: selector) else { return nil }
+        return Base.perform(selector).takeUnretainedValue() as? UIApplication
+    }
+}
+#endif
+
+extension String {
+    func computedKey(with identifier: String) -> String {
+        if identifier.isEmpty {
+            return self
+        } else {
+            return appending("@\(identifier)")
+        }
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/ImageDownloader.swift b/iOS/Pods/Kingfisher/Sources/ImageDownloader.swift
new file mode 100755 (executable)
index 0000000..7ee7578
--- /dev/null
@@ -0,0 +1,637 @@
+//
+//  ImageDownloader.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+
+/// Progress update block of downloader.
+public typealias ImageDownloaderProgressBlock = DownloadProgressBlock
+
+/// Completion block of downloader.
+public typealias ImageDownloaderCompletionHandler = ((_ image: Image?, _ error: NSError?, _ url: URL?, _ originalData: Data?) -> Void)
+
+/// Download task.
+public struct RetrieveImageDownloadTask {
+    let internalTask: URLSessionDataTask
+    
+    /// Downloader by which this task is intialized.
+    public private(set) weak var ownerDownloader: ImageDownloader?
+
+    
+    /// Cancel this download task. It will trigger the completion handler with an NSURLErrorCancelled error.
+    /// If you want to cancel all downloading tasks, call `cancelAll()` of `ImageDownloader` instance.
+    public func cancel() {
+        ownerDownloader?.cancel(self)
+    }
+    
+    /// The original request URL of this download task.
+    public var url: URL? {
+        return internalTask.originalRequest?.url
+    }
+    
+    /// The relative priority of this download task. 
+    /// It represents the `priority` property of the internal `NSURLSessionTask` of this download task.
+    /// The value for it is between 0.0~1.0. Default priority is value of 0.5.
+    /// See documentation on `priority` of `NSURLSessionTask` for more about it.
+    public var priority: Float {
+        get {
+            return internalTask.priority
+        }
+        set {
+            internalTask.priority = newValue
+        }
+    }
+}
+
+///The code of errors which `ImageDownloader` might encountered.
+public enum KingfisherError: Int {
+    
+    /// badData: The downloaded data is not an image or the data is corrupted.
+    case badData = 10000
+    
+    /// notModified: The remote server responsed a 304 code. No image data downloaded.
+    case notModified = 10001
+    
+    /// The HTTP status code in response is not valid. If an invalid
+    /// code error received, you could check the value under `KingfisherErrorStatusCodeKey` 
+    /// in `userInfo` to see the code.
+    case invalidStatusCode = 10002
+    
+    /// notCached: The image rquested is not in cache but .onlyFromCache is activated.
+    case notCached = 10003
+    
+    /// The URL is invalid.
+    case invalidURL = 20000
+    
+    /// The downloading task is cancelled before started.
+    case downloadCancelledBeforeStarting = 30000
+}
+
+/// Key will be used in the `userInfo` of `.invalidStatusCode`
+public let KingfisherErrorStatusCodeKey = "statusCode"
+
+/// Protocol of `ImageDownloader`.
+public protocol ImageDownloaderDelegate: class {
+    /**
+    Called when the `ImageDownloader` object successfully downloaded an image from specified URL.
+    
+    - parameter downloader: The `ImageDownloader` object finishes the downloading.
+    - parameter image:      Downloaded image.
+    - parameter url:        URL of the original request URL.
+    - parameter response:   The response object of the downloading process.
+    */
+    func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?)
+    
+    /**
+    Called when the `ImageDownloader` object starts to download an image from specified URL.
+     
+    - parameter downloader: The `ImageDownloader` object starts the downloading.
+    - parameter url:        URL of the original request.
+    - parameter response:   The request object of the downloading process.
+    */
+    func imageDownloader(_ downloader: ImageDownloader, willDownloadImageForURL url: URL, with request: URLRequest?)
+    
+    /**
+    Check if a received HTTP status code is valid or not. 
+    By default, a status code between 200 to 400 (excluded) is considered as valid.
+    If an invalid code is received, the downloader will raise an .invalidStatusCode error.
+    It has a `userInfo` which includes this statusCode and localizedString error message.
+     
+    - parameter code: The received HTTP status code.
+    - parameter downloader: The `ImageDownloader` object asking for validate status code.
+     
+    - returns: Whether this HTTP status code is valid or not.
+     
+    - Note: If the default 200 to 400 valid code does not suit your need, 
+            you can implement this method to change that behavior.
+    */
+    func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool
+    
+    /**
+     Called when the `ImageDownloader` object successfully downloaded image data from specified URL.
+     
+     - parameter downloader: The `ImageDownloader` object finishes data downloading.
+     - parameter data:       Downloaded data.
+     - parameter url:        URL of the original request URL.
+     
+     - returns: The data from which Kingfisher should use to create an image.
+     
+     - Note: This callback can be used to preprocess raw image data
+             before creation of UIImage instance (i.e. decrypting or verification).
+     */
+    func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data?
+}
+
+extension ImageDownloaderDelegate {
+    public func imageDownloader(_ downloader: ImageDownloader, didDownload image: Image, for url: URL, with response: URLResponse?) {}
+    
+    public func imageDownloader(_ downloader: ImageDownloader, willDownloadImageForURL url: URL, with request: URLRequest?) {}
+    public func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool {
+        return (200..<400).contains(code)
+    }
+    public func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? {
+        return data
+    }
+}
+
+/// Protocol indicates that an authentication challenge could be handled.
+public protocol AuthenticationChallengeResponsable: class {
+    /**
+     Called when an session level authentication challenge is received.
+     This method provide a chance to handle and response to the authentication challenge before downloading could start.
+     
+     - parameter downloader:        The downloader which receives this challenge.
+     - parameter challenge:         An object that contains the request for authentication.
+     - parameter completionHandler: A handler that your delegate method must call.
+     
+     - Note: This method is a forward from `URLSessionDelegate.urlSession(:didReceiveChallenge:completionHandler:)`. Please refer to the document of it in `URLSessionDelegate`.
+     */
+    func downloader(_ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
+
+    /**
+     Called when an session level authentication challenge is received.
+     This method provide a chance to handle and response to the authentication challenge before downloading could start.
+     
+     - parameter downloader:        The downloader which receives this challenge.
+     - 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.
+     
+     - Note: This method is a forward from `URLSessionTaskDelegate.urlSession(:task:didReceiveChallenge:completionHandler:)`. Please refer to the document of it in `URLSessionTaskDelegate`.
+     */
+    func downloader(_ downloader: ImageDownloader, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
+}
+
+extension AuthenticationChallengeResponsable {
+    
+    func downloader(_ downloader: ImageDownloader, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+    
+        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
+            if let trustedHosts = downloader.trustedHosts, trustedHosts.contains(challenge.protectionSpace.host) {
+                let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
+                completionHandler(.useCredential, credential)
+                return
+            }
+        }
+        
+        completionHandler(.performDefaultHandling, nil)
+    }
+    
+    func downloader(_ downloader: ImageDownloader, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+        
+        completionHandler(.performDefaultHandling, nil)
+    }
+
+}
+
+/// `ImageDownloader` represents a downloading manager for requesting the image with a URL from server.
+open class ImageDownloader {
+    
+    class ImageFetchLoad {
+        var contents = [(callback: CallbackPair, options: KingfisherOptionsInfo)]()
+        var responseData = NSMutableData()
+
+        var downloadTaskCount = 0
+        var downloadTask: RetrieveImageDownloadTask?
+        var cancelSemaphore: DispatchSemaphore?
+    }
+    
+    // MARK: - Public property
+    /// The duration before the download is timeout. Default is 15 seconds.
+    open var downloadTimeout: TimeInterval = 15.0
+    
+    /// A set of trusted hosts when receiving server trust challenges. A challenge with host name contained in this set will be ignored. 
+    /// You can use this set to specify the self-signed site. It only will be used if you don't specify the `authenticationChallengeResponder`. 
+    /// If `authenticationChallengeResponder` is set, this property will be ignored and the implemention of `authenticationChallengeResponder` will be used instead.
+    open var trustedHosts: Set<String>?
+    
+    /// Use this to set supply a configuration for the downloader. By default, NSURLSessionConfiguration.ephemeralSessionConfiguration() will be used. 
+    /// You could change the configuration before a downloaing task starts. A configuration without persistent storage for caches is requsted for downloader working correctly.
+    open var sessionConfiguration = URLSessionConfiguration.ephemeral {
+        didSet {
+            session?.invalidateAndCancel()
+            session = URLSession(configuration: sessionConfiguration, delegate: sessionHandler, delegateQueue: OperationQueue.main)
+        }
+    }
+    
+    /// Whether the download requests should use pipeling or not. Default is false.
+    open var requestsUsePipelining = false
+    
+    fileprivate let sessionHandler: ImageDownloaderSessionHandler
+    fileprivate var session: URLSession?
+    
+    /// Delegate of this `ImageDownloader` object. See `ImageDownloaderDelegate` protocol for more.
+    open weak var delegate: ImageDownloaderDelegate?
+    
+    /// A responder for authentication challenge. 
+    /// Downloader will forward the received authentication challenge for the downloading session to this responder.
+    open weak var authenticationChallengeResponder: AuthenticationChallengeResponsable?
+    
+    // MARK: - Internal property
+    let barrierQueue: DispatchQueue
+    let processQueue: DispatchQueue
+    let cancelQueue: DispatchQueue
+    
+    typealias CallbackPair = (progressBlock: ImageDownloaderProgressBlock?, completionHandler: ImageDownloaderCompletionHandler?)
+    
+    var fetchLoads = [URL: ImageFetchLoad]()
+    
+    // MARK: - Public method
+    /// The default downloader.
+    public static let `default` = ImageDownloader(name: "default")
+    
+    /**
+    Init a downloader with name.
+    
+    - parameter name: The name for the downloader. It should not be empty.
+    
+    - returns: The downloader object.
+    */
+    public init(name: String) {
+        if name.isEmpty {
+            fatalError("[Kingfisher] You should specify a name for the downloader. A downloader with empty name is not permitted.")
+        }
+        
+        barrierQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Barrier.\(name)", attributes: .concurrent)
+        processQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Process.\(name)", attributes: .concurrent)
+        cancelQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Cancel.\(name)")
+        
+        sessionHandler = ImageDownloaderSessionHandler()
+
+        // Provide a default implement for challenge responder.
+        authenticationChallengeResponder = sessionHandler
+        session = URLSession(configuration: sessionConfiguration, delegate: sessionHandler, delegateQueue: .main)
+    }
+    
+    deinit {
+        session?.invalidateAndCancel()
+    }
+    
+    func fetchLoad(for url: URL) -> ImageFetchLoad? {
+        var fetchLoad: ImageFetchLoad?
+        barrierQueue.sync(flags: .barrier) { fetchLoad = fetchLoads[url] }
+        return fetchLoad
+    }
+    
+    /**
+     Download an image with a URL and option.
+     
+     - parameter url:               Target URL.
+     - parameter retrieveImageTask: The task to cooporate with cache. Pass `nil` if you are not trying to use downloader and cache.
+     - parameter options:           The options could control download behavior. See `KingfisherOptionsInfo`.
+     - parameter progressBlock:     Called when the download progress updated.
+     - parameter completionHandler: Called when the download progress finishes.
+     
+     - returns: A downloading task. You could call `cancel` on it to stop the downloading process.
+     */
+    @discardableResult
+    open func downloadImage(with url: URL,
+                       retrieveImageTask: RetrieveImageTask? = nil,
+                       options: KingfisherOptionsInfo? = nil,
+                       progressBlock: ImageDownloaderProgressBlock? = nil,
+                       completionHandler: ImageDownloaderCompletionHandler? = nil) -> RetrieveImageDownloadTask?
+    {
+        if let retrieveImageTask = retrieveImageTask, retrieveImageTask.cancelledBeforeDownloadStarting {
+            completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.downloadCancelledBeforeStarting.rawValue, userInfo: nil), nil, nil)
+            return nil
+        }
+        
+        let timeout = self.downloadTimeout == 0.0 ? 15.0 : self.downloadTimeout
+        
+        // We need to set the URL as the load key. So before setup progress, we need to ask the `requestModifier` for a final URL.
+        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: timeout)
+        request.httpShouldUsePipelining = requestsUsePipelining
+
+        if let modifier = options?.modifier {
+            guard let r = modifier.modified(for: request) else {
+                completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.downloadCancelledBeforeStarting.rawValue, userInfo: nil), nil, nil)
+                return nil
+            }
+            request = r
+        }
+        
+        // There is a possiblility that request modifier changed the url to `nil` or empty.
+        guard let url = request.url, !url.absoluteString.isEmpty else {
+            completionHandler?(nil, NSError(domain: KingfisherErrorDomain, code: KingfisherError.invalidURL.rawValue, userInfo: nil), nil, nil)
+            return nil
+        }
+        
+        var downloadTask: RetrieveImageDownloadTask?
+        setup(progressBlock: progressBlock, with: completionHandler, for: url, options: options) {(session, fetchLoad) -> Void in
+            if fetchLoad.downloadTask == nil {
+                let dataTask = session.dataTask(with: request)
+                
+                fetchLoad.downloadTask = RetrieveImageDownloadTask(internalTask: dataTask, ownerDownloader: self)
+                
+                dataTask.priority = options?.downloadPriority ?? URLSessionTask.defaultPriority
+                dataTask.resume()
+                self.delegate?.imageDownloader(self, willDownloadImageForURL: url, with: request)
+                
+                // Hold self while the task is executing.
+                self.sessionHandler.downloadHolder = self
+            }
+            
+            fetchLoad.downloadTaskCount += 1
+            downloadTask = fetchLoad.downloadTask
+            
+            retrieveImageTask?.downloadTask = downloadTask
+        }
+        return downloadTask
+    }
+    
+}
+
+// MARK: - Download method
+extension ImageDownloader {
+    
+    // A single key may have multiple callbacks. Only download once.
+    func setup(progressBlock: ImageDownloaderProgressBlock?, with completionHandler: ImageDownloaderCompletionHandler?, for url: URL, options: KingfisherOptionsInfo?, started: @escaping ((URLSession, ImageFetchLoad) -> Void)) {
+
+        func prepareFetchLoad() {
+            barrierQueue.sync(flags: .barrier) {
+                let loadObjectForURL = fetchLoads[url] ?? ImageFetchLoad()
+                let callbackPair = (progressBlock: progressBlock, completionHandler: completionHandler)
+                
+                loadObjectForURL.contents.append((callbackPair, options ?? KingfisherEmptyOptionsInfo))
+                
+                fetchLoads[url] = loadObjectForURL
+                
+                if let session = session {
+                    started(session, loadObjectForURL)
+                }
+            }
+        }
+        
+        if let fetchLoad = fetchLoad(for: url), fetchLoad.downloadTaskCount == 0 {
+            if fetchLoad.cancelSemaphore == nil {
+                fetchLoad.cancelSemaphore = DispatchSemaphore(value: 0)
+            }
+            cancelQueue.async {
+                _ = fetchLoad.cancelSemaphore?.wait(timeout: .distantFuture)
+                fetchLoad.cancelSemaphore = nil
+                prepareFetchLoad()
+            }
+        } else {
+            prepareFetchLoad()
+        }
+    }
+    
+    private func cancelTaskImpl(_ task: RetrieveImageDownloadTask, fetchLoad: ImageFetchLoad? = nil, ignoreTaskCount: Bool = false) {
+        
+        func getFetchLoad(from task: RetrieveImageDownloadTask) -> ImageFetchLoad? {
+            guard let URL = task.internalTask.originalRequest?.url,
+                  let imageFetchLoad = self.fetchLoads[URL] else
+            {
+                return nil
+            }
+            return imageFetchLoad
+        }
+        
+        guard let imageFetchLoad = fetchLoad ?? getFetchLoad(from: task) else {
+            return
+        }
+
+        imageFetchLoad.downloadTaskCount -= 1
+        if ignoreTaskCount || imageFetchLoad.downloadTaskCount == 0 {
+            task.internalTask.cancel()
+        }
+    }
+    
+    func cancel(_ task: RetrieveImageDownloadTask) {
+        barrierQueue.sync(flags: .barrier) { cancelTaskImpl(task) }
+    }
+    
+    /// Cancel all downloading tasks. It will trigger the completion handlers for all not-yet-finished
+    /// downloading tasks with an NSURLErrorCancelled error.
+    ///
+    /// If you need to only cancel a certain task, call `cancel()` on the `RetrieveImageDownloadTask`
+    /// returned by the downloading methods.
+    public func cancelAll() {
+        barrierQueue.sync(flags: .barrier) {
+            fetchLoads.forEach { v in
+                let fetchLoad = v.value
+                guard let task = fetchLoad.downloadTask else { return }
+                cancelTaskImpl(task, fetchLoad: fetchLoad, ignoreTaskCount: true)
+            }
+        }
+    }
+}
+
+// MARK: - NSURLSessionDataDelegate
+
+/// Delegate class for `NSURLSessionTaskDelegate`.
+/// The session object will hold its delegate until it gets invalidated.
+/// If we use `ImageDownloader` as the session delegate, it will not be released.
+/// So we need an additional handler to break the retain cycle.
+// See https://github.com/onevcat/Kingfisher/issues/235
+final class ImageDownloaderSessionHandler: NSObject, URLSessionDataDelegate, AuthenticationChallengeResponsable {
+    
+    // The holder will keep downloader not released while a data task is being executed.
+    // It will be set when the task started, and reset when the task finished.
+    var downloadHolder: ImageDownloader?
+    
+    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
+        
+        guard let downloader = downloadHolder else {
+            completionHandler(.cancel)
+            return
+        }
+        
+        if let statusCode = (response as? HTTPURLResponse)?.statusCode,
+           let url = dataTask.originalRequest?.url,
+            !(downloader.delegate ?? downloader).isValidStatusCode(statusCode, for: downloader)
+        {
+            let error = NSError(domain: KingfisherErrorDomain,
+                                code: KingfisherError.invalidStatusCode.rawValue,
+                                userInfo: [KingfisherErrorStatusCodeKey: statusCode, NSLocalizedDescriptionKey: HTTPURLResponse.localizedString(forStatusCode: statusCode)])
+            callCompletionHandlerFailure(error: error, url: url)
+        }
+        
+        completionHandler(.allow)
+    }
+    
+    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
+
+        guard let downloader = downloadHolder else {
+            return
+        }
+
+        if let url = dataTask.originalRequest?.url, let fetchLoad = downloader.fetchLoad(for: url) {
+            fetchLoad.responseData.append(data)
+            
+            if let expectedLength = dataTask.response?.expectedContentLength {
+                for content in fetchLoad.contents {
+                    DispatchQueue.main.async {
+                        content.callback.progressBlock?(Int64(fetchLoad.responseData.length), expectedLength)
+                    }
+                }
+            }
+        }
+    }
+    
+    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
+        
+        guard let url = task.originalRequest?.url else {
+            return
+        }
+        
+        guard error == nil else {
+            callCompletionHandlerFailure(error: error!, url: url)
+            return
+        }
+        
+        processImage(for: task, url: url)
+    }
+    
+    /**
+    This method is exposed since the compiler requests. Do not call it.
+    */
+    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+        guard let downloader = downloadHolder else {
+            return
+        }
+        
+        downloader.authenticationChallengeResponder?.downloader(downloader, didReceive: challenge, completionHandler: completionHandler)
+    }
+    
+    func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+        guard let downloader = downloadHolder else {
+            return
+        }
+        
+        downloader.authenticationChallengeResponder?.downloader(downloader, task: task, didReceive: challenge, completionHandler: completionHandler)
+    }
+    
+    private func cleanFetchLoad(for url: URL) {
+        guard let downloader = downloadHolder else {
+            return
+        }
+
+        downloader.barrierQueue.sync(flags: .barrier) {
+            downloader.fetchLoads.removeValue(forKey: url)
+            if downloader.fetchLoads.isEmpty {
+                downloadHolder = nil
+            }
+        }
+    }
+    
+    private func callCompletionHandlerFailure(error: Error, url: URL) {
+        guard let downloader = downloadHolder, let fetchLoad = downloader.fetchLoad(for: url) else {
+            return
+        }
+        
+        // We need to clean the fetch load first, before actually calling completion handler.
+        cleanFetchLoad(for: url)
+        
+        var leftSignal: Int
+        repeat {
+            leftSignal = fetchLoad.cancelSemaphore?.signal() ?? 0
+        } while leftSignal != 0
+        
+        for content in fetchLoad.contents {
+            content.options.callbackDispatchQueue.safeAsync {
+                content.callback.completionHandler?(nil, error as NSError, url, nil)
+            }
+        }
+    }
+    
+    private func processImage(for task: URLSessionTask, url: URL) {
+
+        guard let downloader = downloadHolder else {
+            return
+        }
+        
+        // We are on main queue when receiving this.
+        downloader.processQueue.async {
+            
+            guard let fetchLoad = downloader.fetchLoad(for: url) else {
+                return
+            }
+            
+            self.cleanFetchLoad(for: url)
+            
+            let data: Data?
+            let fetchedData = fetchLoad.responseData as Data
+            
+            if let delegate = downloader.delegate {
+                data = delegate.imageDownloader(downloader, didDownload: fetchedData, for: url)
+            } else {
+                data = fetchedData
+            }
+            
+            // Cache the processed images. So we do not need to re-process the image if using the same processor.
+            // Key is the identifier of processor.
+            var imageCache: [String: Image] = [:]
+            for content in fetchLoad.contents {
+                
+                let options = content.options
+                let completionHandler = content.callback.completionHandler
+                let callbackQueue = options.callbackDispatchQueue
+                
+                let processor = options.processor
+                var image = imageCache[processor.identifier]
+                if let data = data, image == nil {
+                    image = processor.process(item: .data(data), options: options)
+                    // Add the processed image to cache. 
+                    // If `image` is nil, nothing will happen (since the key is not existing before).
+                    imageCache[processor.identifier] = image
+                }
+                
+                if let image = image {
+
+                    downloader.delegate?.imageDownloader(downloader, didDownload: image, for: url, with: task.response)
+
+                    let imageModifier = options.imageModifier
+                    let finalImage = imageModifier.modify(image)
+
+                    if options.backgroundDecode {
+                        let decodedImage = finalImage.kf.decoded
+                        callbackQueue.safeAsync { completionHandler?(decodedImage, nil, url, data) }
+                    } else {
+                        callbackQueue.safeAsync { completionHandler?(finalImage, nil, url, data) }
+                    }
+                    
+                } else {
+                    if let res = task.response as? HTTPURLResponse , res.statusCode == 304 {
+                        let notModified = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notModified.rawValue, userInfo: nil)
+                        completionHandler?(nil, notModified, url, nil)
+                        continue
+                    }
+                    
+                    let badData = NSError(domain: KingfisherErrorDomain, code: KingfisherError.badData.rawValue, userInfo: nil)
+                    callbackQueue.safeAsync { completionHandler?(nil, badData, url, nil) }
+                }
+            }
+        }
+    }
+}
+
+// Placeholder. For retrieving extension methods of ImageDownloaderDelegate
+extension ImageDownloader: ImageDownloaderDelegate {}
+
diff --git a/iOS/Pods/Kingfisher/Sources/ImageModifier.swift b/iOS/Pods/Kingfisher/Sources/ImageModifier.swift
new file mode 100644 (file)
index 0000000..bb50211
--- /dev/null
@@ -0,0 +1,191 @@
+//
+//  ImageModifier.swift
+//  Kingfisher
+//
+//  Created by Ethan Gill on 2017/11/28.
+//
+//  Copyright (c) 2018 Ethan Gill <ethan.gill@me.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 Foundation
+
+/// An `ImageModifier` can be used to change properties on an Image in between
+/// cache serialization and use of the image.
+public protocol ImageModifier {
+    /// Modify an input `Image`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: The return value will be unmodified if modifying is not possible on
+    ///         the current platform.
+    /// - Note: Most modifiers support UIImage or NSImage, but not CGImage.
+    func modify(_ image: Image) -> Image
+}
+
+extension ImageModifier {
+    func modify(_ image: Image?) -> Image? {
+        guard let image = image else {
+            return nil
+        }
+        return modify(image)
+    }
+}
+
+typealias ModifierImp = ((Image) -> Image)
+
+fileprivate struct GeneralModifier: ImageModifier {
+    let identifier: String
+    let m: ModifierImp
+    func modify(_ image: Image) -> Image {
+        return m(image)
+    }
+}
+
+/// The default modifier.
+/// Does nothing and returns the image it was given
+public struct DefaultImageModifier: ImageModifier {
+
+    /// A default `DefaultImageModifier` which can be used everywhere.
+    public static let `default` = DefaultImageModifier()
+
+    /// Initialize a `DefaultImageModifier`
+    private init() {}
+
+    /// Modify an input `Image`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: See documentation of `ImageModifier` protocol for more.
+    public func modify(_ image: Image) -> Image {
+        return image
+    }
+}
+
+/// A custom modifier.
+/// Can be initialized with a block to modify images in a custom way
+public struct AnyImageModifier: ImageModifier {
+
+    /// A block which modifies images, or returns the original image
+    /// if modification cannot be performed.
+    let block: (Image) -> Image
+
+    /// Initialize an `AnyImageModifier`
+    public init(modify: @escaping (Image) -> Image) {
+        block = modify
+    }
+
+    /// Modifies an input `Image` using this `AnyImageModifier`'s `block`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: See documentation of `ImageModifier` protocol for more.
+    public func modify(_ image: Image) -> Image {
+        return block(image)
+    }
+}
+
+#if os(iOS) || os(tvOS) || os(watchOS)
+import UIKit
+
+/// Modifier for setting the rendering mode of images.
+/// Only UI-based images are supported; if a non-UI image is passed in, the
+/// modifier will do nothing.
+public struct RenderingModeImageModifier: ImageModifier {
+
+    /// The rendering mode to apply to the image.
+    public let renderingMode: UIImageRenderingMode
+
+    /// Initialize a `RenderingModeImageModifier`
+    ///
+    /// - parameter renderingMode: The rendering mode to apply to the image.
+    ///                            Default is .automatic
+    public init(renderingMode: UIImageRenderingMode = .automatic) {
+        self.renderingMode = renderingMode
+    }
+
+    /// Modify an input `Image`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: See documentation of `ImageModifier` protocol for more.
+    public func modify(_ image: Image) -> Image {
+        return image.withRenderingMode(renderingMode)
+    }
+}
+
+/// Modifier for setting the `flipsForRightToLeftLayoutDirection` property of images.
+/// Only UI-based images are supported; if a non-UI image is passed in, the
+/// modifier will do nothing.
+public struct FlipsForRightToLeftLayoutDirectionImageModifier: ImageModifier {
+    /// Initialize a `FlipsForRightToLeftLayoutDirectionImageModifier`
+    ///
+    /// - Note: On versions of iOS lower than 9.0, the image will be returned
+    ///         unmodified.
+    public init() {}
+
+    /// Modify an input `Image`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: See documentation of `ImageModifier` protocol for more.
+    public func modify(_ image: Image) -> Image {
+        if #available(iOS 9.0, *) {
+            return image.imageFlippedForRightToLeftLayoutDirection()
+        } else {
+            return image
+        }
+    }
+}
+
+/// Modifier for setting the `alignmentRectInsets` property of images.
+/// Only UI-based images are supported; if a non-UI image is passed in, the
+/// modifier will do nothing.
+public struct AlignmentRectInsetsImageModifier: ImageModifier {
+
+    /// The alignment insets to apply to the image
+    public let alignmentInsets: UIEdgeInsets
+
+    /// Initialize a `AlignmentRectInsetsImageModifier`
+    public init(alignmentInsets: UIEdgeInsets) {
+        self.alignmentInsets = alignmentInsets
+    }
+
+    /// Modify an input `Image`.
+    ///
+    /// - parameter image:   Image which will be modified by `self`
+    ///
+    /// - returns: The modified image.
+    ///
+    /// - Note: See documentation of `ImageModifier` protocol for more.
+    public func modify(_ image: Image) -> Image {
+        return image.withAlignmentRectInsets(alignmentInsets)
+    }
+}
+#endif
diff --git a/iOS/Pods/Kingfisher/Sources/ImagePrefetcher.swift b/iOS/Pods/Kingfisher/Sources/ImagePrefetcher.swift
new file mode 100755 (executable)
index 0000000..5c45014
--- /dev/null
@@ -0,0 +1,266 @@
+//
+//  ImagePrefetcher.swift
+//  Kingfisher
+//
+//  Created by Claire Knight <claire.knight@moggytech.co.uk> on 24/02/2016
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+
+#if os(macOS)
+    import AppKit
+#else
+    import UIKit
+#endif
+
+
+/// Progress update block of prefetcher. 
+///
+/// - `skippedResources`: An array of resources that are already cached before the prefetching starting.
+/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all.
+/// - `completedResources`: An array of resources that are downloaded and cached successfully.
+public typealias PrefetcherProgressBlock = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void)
+
+/// Completion block of prefetcher.
+///
+/// - `skippedResources`: An array of resources that are already cached before the prefetching starting.
+/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all.
+/// - `completedResources`: An array of resources that are downloaded and cached successfully.
+public typealias PrefetcherCompletionHandler = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void)
+
+/// `ImagePrefetcher` represents a downloading manager for requesting many images via URLs, then caching them.
+/// This is useful when you know a list of image resources and want to download them before showing.
+public class ImagePrefetcher {
+    
+    /// The maximum concurrent downloads to use when prefetching images. Default is 5.
+    public var maxConcurrentDownloads = 5
+    
+    private let prefetchResources: [Resource]
+    private let optionsInfo: KingfisherOptionsInfo
+    private var progressBlock: PrefetcherProgressBlock?
+    private var completionHandler: PrefetcherCompletionHandler?
+    
+    private var tasks = [URL: RetrieveImageDownloadTask]()
+    
+    private var pendingResources: ArraySlice<Resource>
+    private var skippedResources = [Resource]()
+    private var completedResources = [Resource]()
+    private var failedResources = [Resource]()
+    
+    private var stopped = false
+    
+    // The created manager used for prefetch. We will use the helper method in manager.
+    private let manager: KingfisherManager
+    
+    private var finished: Bool {
+        return failedResources.count + skippedResources.count + completedResources.count == prefetchResources.count && self.tasks.isEmpty
+    }
+    
+    /**
+     Init an image prefetcher with an array of URLs.
+     
+     The prefetcher should be initiated with a list of prefetching targets. The URLs list is immutable. 
+     After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process.
+     The images already cached will be skipped without downloading again.
+     
+     - parameter urls:              The URLs which should be prefetched.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called every time an resource is downloaded, skipped or cancelled.
+     - parameter completionHandler: Called when the whole prefetching process finished.
+     
+     - returns: An `ImagePrefetcher` object.
+     
+     - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as 
+     the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`.
+     Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method.
+     */
+    public convenience init(urls: [URL],
+                         options: KingfisherOptionsInfo? = nil,
+                   progressBlock: PrefetcherProgressBlock? = nil,
+               completionHandler: PrefetcherCompletionHandler? = nil)
+    {
+        let resources: [Resource] = urls.map { $0 }
+        self.init(resources: resources, options: options, progressBlock: progressBlock, completionHandler: completionHandler)
+    }
+    
+    /**
+     Init an image prefetcher with an array of resources.
+     
+     The prefetcher should be initiated with a list of prefetching targets. The resources list is immutable.
+     After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process.
+     The images already cached will be skipped without downloading again.
+     
+     - parameter resources:         The resources which should be prefetched. See `Resource` type for more.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called every time an resource is downloaded, skipped or cancelled.
+     - parameter completionHandler: Called when the whole prefetching process finished.
+     
+     - returns: An `ImagePrefetcher` object.
+     
+     - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as
+     the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`.
+     Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method.
+     */
+    public init(resources: [Resource],
+                  options: KingfisherOptionsInfo? = nil,
+            progressBlock: PrefetcherProgressBlock? = nil,
+        completionHandler: PrefetcherCompletionHandler? = nil)
+    {
+        prefetchResources = resources
+        pendingResources = ArraySlice(resources)
+        
+        // We want all callbacks from main queue, so we ignore the call back queue in options
+        let optionsInfoWithoutQueue = options?.removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil))
+        self.optionsInfo = optionsInfoWithoutQueue ?? KingfisherEmptyOptionsInfo
+        
+        let cache = self.optionsInfo.targetCache
+        let downloader = self.optionsInfo.downloader
+        manager = KingfisherManager(downloader: downloader, cache: cache)
+        
+        self.progressBlock = progressBlock
+        self.completionHandler = completionHandler
+    }
+    
+    /**
+     Start to download the resources and cache them. This can be useful for background downloading
+     of assets that are required for later use in an app. This code will not try and update any UI
+     with the results of the process.
+     */
+    public func start()
+    {
+        // Since we want to handle the resources cancellation in main thread only.
+        DispatchQueue.main.safeAsync {
+            
+            guard !self.stopped else {
+                assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.")
+                self.handleComplete()
+                return
+            }
+            
+            guard self.maxConcurrentDownloads > 0 else {
+                assertionFailure("There should be concurrent downloads value should be at least 1.")
+                self.handleComplete()
+                return
+            }
+            
+            guard self.prefetchResources.count > 0 else {
+                self.handleComplete()
+                return
+            }
+            
+            let initialConcurentDownloads = min(self.prefetchResources.count, self.maxConcurrentDownloads)
+            for _ in 0 ..< initialConcurentDownloads {
+                if let resource = self.pendingResources.popFirst() {
+                    self.startPrefetching(resource)
+                }
+            }
+        }
+    }
+
+   
+    /**
+     Stop current downloading progress, and cancel any future prefetching activity that might be occuring.
+     */
+    public func stop() {
+        DispatchQueue.main.safeAsync {
+            if self.finished { return }
+            self.stopped = true
+            self.tasks.values.forEach { $0.cancel() }
+        }
+    }
+    
+    func downloadAndCache(_ resource: Resource) {
+
+        let downloadTaskCompletionHandler: CompletionHandler = { (image, error, _, _) -> Void in
+            self.tasks.removeValue(forKey: resource.downloadURL)
+            if let _ = error {
+                self.failedResources.append(resource)
+            } else {
+                self.completedResources.append(resource)
+            }
+            
+            self.reportProgress()
+            if self.stopped {
+                if self.tasks.isEmpty {
+                    self.failedResources.append(contentsOf: self.pendingResources)
+                    self.handleComplete()
+                }
+            } else {
+                self.reportCompletionOrStartNext()
+            }
+        }
+        
+        let downloadTask = manager.downloadAndCacheImage(
+            with: resource.downloadURL,
+            forKey: resource.cacheKey,
+            retrieveImageTask: RetrieveImageTask(),
+            progressBlock: nil,
+            completionHandler: downloadTaskCompletionHandler,
+            options: optionsInfo)
+        
+        if let downloadTask = downloadTask {
+            tasks[resource.downloadURL] = downloadTask
+        }
+    }
+    
+    func append(cached resource: Resource) {
+        skippedResources.append(resource)
+        reportProgress()
+        reportCompletionOrStartNext()
+    }
+    
+    func startPrefetching(_ resource: Resource)
+    {
+        if optionsInfo.forceRefresh {
+            downloadAndCache(resource)
+        } else {
+            let alreadyInCache = manager.cache.imageCachedType(forKey: resource.cacheKey,
+                                                             processorIdentifier: optionsInfo.processor.identifier).cached
+            if alreadyInCache {
+                append(cached: resource)
+            } else {
+                downloadAndCache(resource)
+            }
+        }
+    }
+    
+    func reportProgress() {
+        progressBlock?(skippedResources, failedResources, completedResources)
+    }
+    
+    func reportCompletionOrStartNext() {
+        DispatchQueue.main.async {
+            if let resource = self.pendingResources.popFirst() {
+                self.startPrefetching(resource)
+            } else {
+                guard self.tasks.isEmpty else { return }
+                self.handleComplete()
+            }
+        }
+    }
+    
+    func handleComplete() {
+        completionHandler?(skippedResources, failedResources, completedResources)
+        completionHandler = nil
+        progressBlock = nil
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/ImageProcessor.swift b/iOS/Pods/Kingfisher/Sources/ImageProcessor.swift
new file mode 100644 (file)
index 0000000..3e1458e
--- /dev/null
@@ -0,0 +1,713 @@
+//
+//  ImageProcessor.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2016/08/26.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+import CoreGraphics
+
+#if os(macOS)
+import AppKit
+#endif
+
+/// The item which could be processed by an `ImageProcessor`
+///
+/// - image: Input image
+/// - data:  Input data
+public enum ImageProcessItem {
+    case image(Image)
+    case data(Data)
+}
+
+/// An `ImageProcessor` would be used to convert some downloaded data to an image.
+public protocol ImageProcessor {
+    /// Identifier of the processor. It will be used to identify the processor when 
+    /// caching and retrieving an image. You might want to make sure that processors with
+    /// same properties/functionality have the same identifiers, so correct processed images
+    /// could be retrived with proper key.
+    /// 
+    /// - Note: Do not supply an empty string for a customized processor, which is already taken by
+    /// the `DefaultImageProcessor`. It is recommended to use a reverse domain name notation
+    /// string of your own for the identifier.
+    var identifier: String { get }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: The return value will be `nil` if processing failed while converting data to image.
+    ///         If input item is already an image and there is any errors in processing, the input 
+    ///         image itself will be returned.
+    /// - Note: Most processor only supports CG-based images. 
+    ///         watchOS is not supported for processers containing filter, the input image will be returned directly on watchOS.
+    func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image?
+}
+
+typealias ProcessorImp = ((ImageProcessItem, KingfisherOptionsInfo) -> Image?)
+
+public extension ImageProcessor {
+    
+    /// Append an `ImageProcessor` to another. The identifier of the new `ImageProcessor` 
+    /// will be "\(self.identifier)|>\(another.identifier)".
+    ///
+    /// - parameter another: An `ImageProcessor` you want to append to `self`.
+    ///
+    /// - returns: The new `ImageProcessor` will process the image in the order
+    ///            of the two processors concatenated.
+    public func append(another: ImageProcessor) -> ImageProcessor {
+        let newIdentifier = identifier.appending("|>\(another.identifier)")
+        return GeneralProcessor(identifier: newIdentifier) {
+            item, options in
+            if let image = self.process(item: item, options: options) {
+                return another.process(item: .image(image), options: options)
+            } else {
+                return nil
+            }
+        }
+    }
+}
+
+func ==(left: ImageProcessor, right: ImageProcessor) -> Bool {
+    return left.identifier == right.identifier
+}
+
+func !=(left: ImageProcessor, right: ImageProcessor) -> Bool {
+    return !(left == right)
+}
+
+fileprivate struct GeneralProcessor: ImageProcessor {
+    let identifier: String
+    let p: ProcessorImp
+    func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        return p(item, options)
+    }
+}
+
+/// The default processor. It convert the input data to a valid image.
+/// Images of .PNG, .JPEG and .GIF format are supported.
+/// If an image is given, `DefaultImageProcessor` will do nothing on it and just return that image.
+public struct DefaultImageProcessor: ImageProcessor {
+    
+    /// A default `DefaultImageProcessor` could be used across.
+    public static let `default` = DefaultImageProcessor()
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier = ""
+    
+    /// Initialize a `DefaultImageProcessor`
+    public init() {}
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    /// 
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+        case .data(let data):
+            return Kingfisher<Image>.image(
+                data: data,
+                scale: options.scaleFactor,
+                preloadAllAnimationData: options.preloadAllAnimationData,
+                onlyFirstFrame: options.onlyLoadFirstFrame)
+        }
+    }
+}
+
+public struct RectCorner: OptionSet {
+    public let rawValue: Int
+    public static let topLeft = RectCorner(rawValue: 1 << 0)
+    public static let topRight = RectCorner(rawValue: 1 << 1)
+    public static let bottomLeft = RectCorner(rawValue: 1 << 2)
+    public static let bottomRight = RectCorner(rawValue: 1 << 3)
+    public static let all: RectCorner = [.topLeft, .topRight, .bottomLeft, .bottomRight]
+    
+    public init(rawValue: Int) {
+        self.rawValue = rawValue
+    }
+    
+    var cornerIdentifier: String {
+        if self == .all {
+            return ""
+        }
+        return "_corner(\(rawValue))"
+    }
+}
+
+#if !os(macOS)
+/// Processor for adding an blend mode to images. Only CG-based images are supported.
+public struct BlendImageProcessor: ImageProcessor {
+
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+
+    /// Blend Mode will be used to blend the input image.
+    public let blendMode: CGBlendMode
+    /// Alpha will be used when blend image.
+    public let alpha: CGFloat
+
+    /// Background color of the output image. If `nil`, it will stay transparent.
+    public let backgroundColor: Color?
+
+    /// Initialize an `BlendImageProcessor`
+    ///
+    /// - parameter blendMode:       Blend Mode will be used to blend the input image.
+    /// - parameter alpha:           Alpha will be used when blend image.
+    ///                              From 0.0 to 1.0. 1.0 means solid image, 0.0 means transparent image.
+    ///                              Default is 1.0.
+    /// - parameter backgroundColor: Backgroud color to apply for the output image. Default is `nil`.
+    public init(blendMode: CGBlendMode, alpha: CGFloat = 1.0, backgroundColor: Color? = nil) {
+        self.blendMode = blendMode
+        self.alpha = alpha
+        self.backgroundColor = backgroundColor
+        var identifier = "com.onevcat.Kingfisher.BlendImageProcessor(\(blendMode.rawValue),\(alpha))"
+        if let color = backgroundColor {
+            identifier.append("_\(color.hex)")
+        }
+        self.identifier = identifier
+    }
+
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.image(withBlendMode: blendMode, alpha: alpha, backgroundColor: backgroundColor)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+#endif
+
+#if os(macOS)
+/// Processor for adding an compositing operation to images. Only CG-based images are supported in macOS.
+public struct CompositingImageProcessor: ImageProcessor {
+
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+
+    /// Compositing operation will be used to the input image.
+    public let compositingOperation: NSCompositingOperation
+
+    /// Alpha will be used when compositing image.
+    public let alpha: CGFloat
+
+    /// Background color of the output image. If `nil`, it will stay transparent.
+    public let backgroundColor: Color?
+
+    /// Initialize an `CompositingImageProcessor`
+    ///
+    /// - parameter compositingOperation: Compositing operation will be used to the input image.
+    /// - parameter alpha:                Alpha will be used when compositing image.
+    ///                                   From 0.0 to 1.0. 1.0 means solid image, 0.0 means transparent image.
+    ///                                   Default is 1.0.
+    /// - parameter backgroundColor:      Backgroud color to apply for the output image. Default is `nil`.
+    public init(compositingOperation: NSCompositingOperation,
+                alpha: CGFloat = 1.0,
+                backgroundColor: Color? = nil)
+    {
+        self.compositingOperation = compositingOperation
+        self.alpha = alpha
+        self.backgroundColor = backgroundColor
+        var identifier = "com.onevcat.Kingfisher.CompositingImageProcessor(\(compositingOperation.rawValue),\(alpha))"
+        if let color = backgroundColor {
+            identifier.append("_\(color.hex)")
+        }
+        self.identifier = identifier
+    }
+
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.image(withCompositingOperation: compositingOperation, alpha: alpha, backgroundColor: backgroundColor)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+#endif
+
+/// Processor for making round corner images. Only CG-based images are supported in macOS, 
+/// if a non-CG image passed in, the processor will do nothing.
+public struct RoundCornerImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+
+    /// Corner radius will be applied in processing.
+    public let cornerRadius: CGFloat
+    
+    /// The target corners which will be applied rounding.
+    public let roundingCorners: RectCorner
+    
+    /// Target size of output image should be. If `nil`, the image will keep its original size after processing.
+    public let targetSize: CGSize?
+
+    /// Background color of the output image. If `nil`, it will stay transparent.
+    public let backgroundColor: Color?
+
+    /// Initialize a `RoundCornerImageProcessor`
+    ///
+    /// - parameter cornerRadius:    Corner radius will be applied in processing.
+    /// - parameter targetSize:      Target size of output image should be. If `nil`, 
+    ///                              the image will keep its original size after processing.
+    ///                              Default is `nil`.
+    /// - parameter corners:         The target corners which will be applied rounding. Default is `.all`.
+    /// - parameter backgroundColor: Backgroud color to apply for the output image. Default is `nil`.
+    public init(cornerRadius: CGFloat, targetSize: CGSize? = nil, roundingCorners corners: RectCorner = .all, backgroundColor: Color? = nil) {
+        self.cornerRadius = cornerRadius
+        self.targetSize = targetSize
+        self.roundingCorners = corners
+        self.backgroundColor = backgroundColor
+
+        self.identifier = {
+            var identifier = ""
+
+            if let size = targetSize {
+                identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)_\(size)\(corners.cornerIdentifier))"
+            } else {
+                identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)\(corners.cornerIdentifier))"
+            }
+            if let backgroundColor = backgroundColor {
+                identifier += "_\(backgroundColor)"
+            }
+
+            return identifier
+        }()
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            let size = targetSize ?? image.kf.size
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.image(withRoundRadius: cornerRadius, fit: size, roundingCorners: roundingCorners, backgroundColor: backgroundColor)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+
+/// Specify how a size adjusts itself to fit a target size.
+///
+/// - none: Not scale the content.
+/// - aspectFit: Scale the content to fit the size of the view by maintaining the aspect ratio.
+/// - aspectFill: Scale the content to fill the size of the view
+public enum ContentMode {
+    case none
+    case aspectFit
+    case aspectFill
+}
+
+/// Processor for resizing images. Only CG-based images are supported in macOS.
+public struct ResizingImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// The reference size for resizing operation.
+    public let referenceSize: CGSize
+    
+    /// Target content mode of output image should be.
+    /// Default to ContentMode.none
+    public let targetContentMode: ContentMode
+    
+    /// Initialize a `ResizingImageProcessor`.
+    ///
+    /// - Parameters:
+    ///   - referenceSize: The reference size for resizing operation.
+    ///   - mode: Target content mode of output image should be.
+    ///
+    /// - Note:
+    ///   The instance of `ResizingImageProcessor` will follow its `mode` property
+    ///   and try to resizing the input images to fit or fill the `referenceSize`.
+    ///   That means if you are using a `mode` besides of `.none`, you may get an
+    ///   image with its size not be the same as the `referenceSize`.
+    ///
+    ///   **Example**: With input image size: {100, 200}, 
+    ///   `referenceSize`: {100, 100}, `mode`: `.aspectFit`,
+    ///   you will get an output image with size of {50, 100}, which "fit"s
+    ///   the `referenceSize`.
+    ///
+    ///   If you need an output image exactly to be a specified size, append or use
+    ///   a `CroppingImageProcessor`.
+    public init(referenceSize: CGSize, mode: ContentMode = .none) {
+        self.referenceSize = referenceSize
+        self.targetContentMode = mode
+        
+        if mode == .none {
+            self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(referenceSize))"
+        } else {
+            self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(referenceSize), \(mode))"
+        }
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.resize(to: referenceSize, for: targetContentMode)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Processor for adding blur effect to images. `Accelerate.framework` is used underhood for 
+/// a better performance. A simulated Gaussian blur with specified blur radius will be applied.
+public struct BlurImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// Blur radius for the simulated Gaussian blur.
+    public let blurRadius: CGFloat
+
+    /// Initialize a `BlurImageProcessor`
+    ///
+    /// - parameter blurRadius: Blur radius for the simulated Gaussian blur.
+    public init(blurRadius: CGFloat) {
+        self.blurRadius = blurRadius
+        self.identifier = "com.onevcat.Kingfisher.BlurImageProcessor(\(blurRadius))"
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            let radius = blurRadius * options.scaleFactor
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.blurred(withRadius: radius)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Processor for adding an overlay to images. Only CG-based images are supported in macOS.
+public struct OverlayImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// Overlay color will be used to overlay the input image.
+    public let overlay: Color
+    
+    /// Fraction will be used when overlay the color to image.
+    public let fraction: CGFloat
+    
+    /// Initialize an `OverlayImageProcessor`
+    ///
+    /// - parameter overlay:  Overlay color will be used to overlay the input image.
+    /// - parameter fraction: Fraction will be used when overlay the color to image. 
+    ///                       From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay.
+    public init(overlay: Color, fraction: CGFloat = 0.5) {
+        self.overlay = overlay
+        self.fraction = fraction
+        self.identifier = "com.onevcat.Kingfisher.OverlayImageProcessor(\(overlay.hex)_\(fraction))"
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.overlaying(with: overlay, fraction: fraction)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Processor for tint images with color. Only CG-based images are supported.
+public struct TintImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// Tint color will be used to tint the input image.
+    public let tint: Color
+    
+    /// Initialize a `TintImageProcessor`
+    ///
+    /// - parameter tint: Tint color will be used to tint the input image.
+    public init(tint: Color) {
+        self.tint = tint
+        self.identifier = "com.onevcat.Kingfisher.TintImageProcessor(\(tint.hex))"
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.tinted(with: tint)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Processor for applying some color control to images. Only CG-based images are supported.
+/// watchOS is not supported.
+public struct ColorControlsProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// Brightness changing to image.
+    public let brightness: CGFloat
+    
+    /// Contrast changing to image.
+    public let contrast: CGFloat
+    
+    /// Saturation changing to image.
+    public let saturation: CGFloat
+    
+    /// InputEV changing to image.
+    public let inputEV: CGFloat
+    
+    /// Initialize a `ColorControlsProcessor`
+    ///
+    /// - parameter brightness: Brightness changing to image.
+    /// - parameter contrast:   Contrast changing to image.
+    /// - parameter saturation: Saturation changing to image.
+    /// - parameter inputEV:    InputEV changing to image.
+    public init(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) {
+        self.brightness = brightness
+        self.contrast = contrast
+        self.saturation = saturation
+        self.inputEV = inputEV
+        self.identifier = "com.onevcat.Kingfisher.ColorControlsProcessor(\(brightness)_\(contrast)_\(saturation)_\(inputEV))"
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.adjusted(brightness: brightness, contrast: contrast, saturation: saturation, inputEV: inputEV)
+        case .data(_):
+            return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Processor for applying black and white effect to images. Only CG-based images are supported.
+/// watchOS is not supported.
+public struct BlackWhiteProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier = "com.onevcat.Kingfisher.BlackWhiteProcessor"
+    
+    /// Initialize a `BlackWhiteProcessor`
+    public init() {}
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        return ColorControlsProcessor(brightness: 0.0, contrast: 1.0, saturation: 0.0, inputEV: 0.7)
+            .process(item: item, options: options)
+    }
+}
+
+/// Processor for cropping an image. Only CG-based images are supported.
+/// watchOS is not supported.
+public struct CroppingImageProcessor: ImageProcessor {
+    
+    /// Identifier of the processor.
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public let identifier: String
+    
+    /// Target size of output image should be.
+    public let size: CGSize
+    
+    /// Anchor point from which the output size should be calculate.
+    /// The anchor point is consisted by two values between 0.0 and 1.0.
+    /// It indicates a related point in current image. 
+    /// See `CroppingImageProcessor.init(size:anchor:)` for more.
+    public let anchor: CGPoint
+    
+    /// Initialize a `CroppingImageProcessor`
+    ///
+    /// - Parameters:
+    ///   - size: Target size of output image should be.
+    ///   - anchor: The anchor point from which the size should be calculated.
+    ///             Default is `CGPoint(x: 0.5, y: 0.5)`, which means the center of input image.
+    /// - Note:
+    ///   The anchor point is consisted by two values between 0.0 and 1.0.
+    ///   It indicates a related point in current image, eg: (0.0, 0.0) for top-left
+    ///   corner, (0.5, 0.5) for center and (1.0, 1.0) for bottom-right corner.
+    ///   The `size` property of `CroppingImageProcessor` will be used along with
+    ///   `anchor` to calculate a target rectange in the size of image.
+    ///    
+    ///   The target size will be automatically calculated with a reasonable behavior.
+    ///   For example, when you have an image size of `CGSize(width: 100, height: 100)`,
+    ///   and a target size of `CGSize(width: 20, height: 20)`: 
+    ///   - with a (0.0, 0.0) anchor (top-left), the crop rect will be `{0, 0, 20, 20}`; 
+    ///   - with a (0.5, 0.5) anchor (center), it will be `{40, 40, 20, 20}`
+    ///   - while with a (1.0, 1.0) anchor (bottom-right), it will be `{80, 80, 20, 20}`
+    public init(size: CGSize, anchor: CGPoint = CGPoint(x: 0.5, y: 0.5)) {
+        self.size = size
+        self.anchor = anchor
+        self.identifier = "com.onevcat.Kingfisher.CroppingImageProcessor(\(size)_\(anchor))"
+    }
+    
+    /// Process an input `ImageProcessItem` item to an image for this processor.
+    ///
+    /// - parameter item:    Input item which will be processed by `self`
+    /// - parameter options: Options when processing the item.
+    ///
+    /// - returns: The processed image.
+    ///
+    /// - Note: See documentation of `ImageProcessor` protocol for more.
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            return image.kf.scaled(to: options.scaleFactor)
+                        .kf.crop(to: size, anchorOn: anchor)
+        case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options)
+        }
+    }
+}
+
+/// Concatenate two `ImageProcessor`s. `ImageProcessor.appen(another:)` is used internally.
+///
+/// - parameter left:  First processor.
+/// - parameter right: Second processor.
+///
+/// - returns: The concatenated processor.
+public func >>(left: ImageProcessor, right: ImageProcessor) -> ImageProcessor {
+    return left.append(another: right)
+}
+
+extension Color {
+    var hex: String {
+        var r: CGFloat = 0
+        var g: CGFloat = 0
+        var b: CGFloat = 0
+        var a: CGFloat = 0
+
+        #if os(macOS)
+        (usingColorSpace(.sRGB) ?? self).getRed(&r, green: &g, blue: &b, alpha: &a)
+        #else
+        getRed(&r, green: &g, blue: &b, alpha: &a)
+        #endif
+
+        let rInt = Int(r * 255) << 24
+        let gInt = Int(g * 255) << 16
+        let bInt = Int(b * 255) << 8
+        let aInt = Int(a * 255)
+        
+        let rgba = rInt | gInt | bInt | aInt
+        
+        return String(format:"#%08x", rgba)
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/ImageTransition.swift b/iOS/Pods/Kingfisher/Sources/ImageTransition.swift
new file mode 100755 (executable)
index 0000000..413c2c5
--- /dev/null
@@ -0,0 +1,128 @@
+//
+//  ImageTransition.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/9/18.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+// Not implemented for macOS and watchOS yet.
+    
+import AppKit
+
+/// Image transition is not supported on macOS.
+public enum ImageTransition {
+    case none
+    var duration: TimeInterval {
+        return 0
+    }
+}
+
+#elseif os(watchOS)
+import UIKit
+/// Image transition is not supported on watchOS.
+public enum ImageTransition {
+    case none
+    var duration: TimeInterval {
+        return 0
+    }
+}
+#else
+import UIKit
+
+/**
+Transition effect which will be used when an image downloaded and set by `UIImageView` extension API in Kingfisher.
+You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo` 
+to enable the animation transition.
+
+Apple's UIViewAnimationOptions is used under the hood.
+For custom transition, you should specified your own transition options, animations and 
+comletion handler as well.
+*/
+public enum ImageTransition {
+    ///  No animation transistion.
+    case none
+    
+    /// Fade in the loaded image.
+    case fade(TimeInterval)
+
+    /// Flip from left transition.
+    case flipFromLeft(TimeInterval)
+
+    /// Flip from right transition.
+    case flipFromRight(TimeInterval)
+    
+    /// Flip from top transition.
+    case flipFromTop(TimeInterval)
+    
+    /// Flip from bottom transition.
+    case flipFromBottom(TimeInterval)
+    
+    /// Custom transition.
+    case custom(duration: TimeInterval,
+                 options: UIViewAnimationOptions,
+              animations: ((UIImageView, UIImage) -> Void)?,
+              completion: ((Bool) -> Void)?)
+    
+    var duration: TimeInterval {
+        switch self {
+        case .none:                          return 0
+        case .fade(let duration):            return duration
+            
+        case .flipFromLeft(let duration):    return duration
+        case .flipFromRight(let duration):   return duration
+        case .flipFromTop(let duration):     return duration
+        case .flipFromBottom(let duration):  return duration
+            
+        case .custom(let duration, _, _, _): return duration
+        }
+    }
+    
+    var animationOptions: UIViewAnimationOptions {
+        switch self {
+        case .none:                         return []
+        case .fade(_):                      return .transitionCrossDissolve
+            
+        case .flipFromLeft(_):              return .transitionFlipFromLeft
+        case .flipFromRight(_):             return .transitionFlipFromRight
+        case .flipFromTop(_):               return .transitionFlipFromTop
+        case .flipFromBottom(_):            return .transitionFlipFromBottom
+            
+        case .custom(_, let options, _, _): return options
+        }
+    }
+    
+    var animations: ((UIImageView, UIImage) -> Void)? {
+        switch self {
+        case .custom(_, _, let animations, _): return animations
+        default: return { $0.image = $1 }
+        }
+    }
+    
+    var completion: ((Bool) -> Void)? {
+        switch self {
+        case .custom(_, _, _, let completion): return completion
+        default: return nil
+        }
+    }
+}
+#endif
diff --git a/iOS/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift b/iOS/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift
new file mode 100755 (executable)
index 0000000..cb230fb
--- /dev/null
@@ -0,0 +1,263 @@
+//
+//  ImageView+Kingfisher.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+
+#if os(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+
+// MARK: - Extension methods.
+/**
+ *    Set image to use from web.
+ */
+extension Kingfisher where Base: ImageView {
+    /**
+     Set an image with a resource, a placeholder image, options, progress handler and completion handler.
+     
+     - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
+     - parameter placeholder:       A placeholder image when retrieving the image at URL.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called when the image downloading progress gets updated.
+     - parameter completionHandler: Called when the image retrieved and set.
+     
+     - returns: A task represents the retrieving process.
+     
+     - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
+     The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
+     
+     If `resource` is `nil`, the `placeholder` image will be set and
+     `completionHandler` will be called with both `error` and `image` being `nil`.
+     */
+    @discardableResult
+    public func setImage(with resource: Resource?,
+                         placeholder: Placeholder? = nil,
+                         options: KingfisherOptionsInfo? = nil,
+                         progressBlock: DownloadProgressBlock? = nil,
+                         completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
+    {
+        guard let resource = resource else {
+            self.placeholder = placeholder
+            setWebURL(nil)
+            completionHandler?(nil, nil, .none, nil)
+            return .empty
+        }
+        
+        var options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
+        let noImageOrPlaceholderSet = base.image == nil && self.placeholder == nil
+        
+        if !options.keepCurrentImageWhileLoading || noImageOrPlaceholderSet { // Always set placeholder while there is no image/placehoer yet.
+            self.placeholder = placeholder
+        }
+
+        let maybeIndicator = indicator
+        maybeIndicator?.startAnimatingView()
+        
+        setWebURL(resource.downloadURL)
+
+        if base.shouldPreloadAllAnimation() {
+            options.append(.preloadAllAnimationData)
+        }
+        
+        let task = KingfisherManager.shared.retrieveImage(
+            with: resource,
+            options: options,
+            progressBlock: { receivedSize, totalSize in
+                guard resource.downloadURL == self.webURL else {
+                    return
+                }
+                if let progressBlock = progressBlock {
+                    progressBlock(receivedSize, totalSize)
+                }
+            },
+            completionHandler: {[weak base] image, error, cacheType, imageURL in
+                DispatchQueue.main.safeAsync {
+                    maybeIndicator?.stopAnimatingView()
+                    guard let strongBase = base, imageURL == self.webURL else {
+                        completionHandler?(image, error, cacheType, imageURL)
+                        return
+                    }
+                    
+                    self.setImageTask(nil)
+                    guard let image = image else {
+                        completionHandler?(nil, error, cacheType, imageURL)
+                        return
+                    }
+                    
+                    guard let transitionItem = options.lastMatchIgnoringAssociatedValue(.transition(.none)),
+                        case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else
+                    {
+                        self.placeholder = nil
+                        strongBase.image = image
+                        completionHandler?(image, error, cacheType, imageURL)
+                        return
+                    }
+                    
+                    #if !os(macOS)
+                        UIView.transition(with: strongBase, duration: 0.0, options: [],
+                                          animations: { maybeIndicator?.stopAnimatingView() },
+                                          completion: { _ in
+
+                                            self.placeholder = nil
+                                            UIView.transition(with: strongBase, duration: transition.duration,
+                                                              options: [transition.animationOptions, .allowUserInteraction],
+                                                              animations: {
+                                                                // Set image property in the animation.
+                                                                transition.animations?(strongBase, image)
+                                                              },
+                                                              completion: { finished in
+                                                                transition.completion?(finished)
+                                                                completionHandler?(image, error, cacheType, imageURL)
+                                                              })
+                                          })
+                    #endif
+                }
+            })
+        
+        setImageTask(task)
+        
+        return task
+    }
+    
+    /**
+     Cancel the image download task bounded to the image view if it is running.
+     Nothing will happen if the downloading has already finished.
+     */
+    public func cancelDownloadTask() {
+        imageTask?.cancel()
+    }
+}
+
+// MARK: - Associated Object
+private var lastURLKey: Void?
+private var indicatorKey: Void?
+private var indicatorTypeKey: Void?
+private var placeholderKey: Void?
+private var imageTaskKey: Void?
+
+extension Kingfisher where Base: ImageView {
+    /// Get the image URL binded to this image view.
+    public var webURL: URL? {
+        return objc_getAssociatedObject(base, &lastURLKey) as? URL
+    }
+    
+    fileprivate func setWebURL(_ url: URL?) {
+        objc_setAssociatedObject(base, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+    
+    /// Holds which indicator type is going to be used.
+    /// Default is .none, means no indicator will be shown.
+    public var indicatorType: IndicatorType {
+        get {
+            let indicator = objc_getAssociatedObject(base, &indicatorTypeKey) as? IndicatorType
+            return indicator ?? .none
+        }
+        
+        set {
+            switch newValue {
+            case .none:
+                indicator = nil
+            case .activity:
+                indicator = ActivityIndicator()
+            case .image(let data):
+                indicator = ImageIndicator(imageData: data)
+            case .custom(let anIndicator):
+                indicator = anIndicator
+            }
+            
+            objc_setAssociatedObject(base, &indicatorTypeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    /// Holds any type that conforms to the protocol `Indicator`.
+    /// The protocol `Indicator` has a `view` property that will be shown when loading an image.
+    /// It will be `nil` if `indicatorType` is `.none`.
+    public fileprivate(set) var indicator: Indicator? {
+        get {
+            guard let box = objc_getAssociatedObject(base, &indicatorKey) as? Box<Indicator> else {
+                return nil
+            }
+            return box.value
+        }
+        
+        set {
+            // Remove previous
+            if let previousIndicator = indicator {
+                previousIndicator.view.removeFromSuperview()
+            }
+            
+            // Add new
+            if var newIndicator = newValue {
+                // Set default indicator frame if the view's frame not set.
+                if newIndicator.view.frame == .zero {
+                    newIndicator.view.frame = base.frame
+                }
+                newIndicator.viewCenter = CGPoint(x: base.bounds.midX, y: base.bounds.midY)
+                newIndicator.view.isHidden = true
+                base.addSubview(newIndicator.view)
+            }
+            
+            // Save in associated object
+            // Wrap newValue with Box to workaround an issue that Swift does not recognize
+            // and casting protocol for associate object correctly. https://github.com/onevcat/Kingfisher/issues/872
+            objc_setAssociatedObject(base, &indicatorKey, newValue.map(Box.init), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    fileprivate var imageTask: RetrieveImageTask? {
+        return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask
+    }
+    
+    fileprivate func setImageTask(_ task: RetrieveImageTask?) {
+        objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+    
+    public fileprivate(set) var placeholder: Placeholder? {
+        get {
+            return objc_getAssociatedObject(base, &placeholderKey) as? Placeholder
+        }
+        
+        set {
+            if let previousPlaceholder = placeholder {
+                previousPlaceholder.remove(from: base)
+            }
+            
+            if let newPlaceholder = newValue {
+                newPlaceholder.add(to: base)
+            } else {
+                base.image = nil
+            }
+            
+            objc_setAssociatedObject(base, &placeholderKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+}
+
+
+@objc extension ImageView {
+    func shouldPreloadAllAnimation() -> Bool { return true }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/Indicator.swift b/iOS/Pods/Kingfisher/Sources/Indicator.swift
new file mode 100644 (file)
index 0000000..e67c96f
--- /dev/null
@@ -0,0 +1,199 @@
+//
+//  Indicator.swift
+//  Kingfisher
+//
+//  Created by João D. Moreira on 30/08/16.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+    import AppKit
+#else
+    import UIKit
+#endif
+
+#if os(macOS)
+    public typealias IndicatorView = NSView
+#else
+    public typealias IndicatorView = UIView
+#endif
+
+public enum IndicatorType {
+    /// No indicator.
+    case none
+    /// Use system activity indicator.
+    case activity
+    /// Use an image as indicator. GIF is supported.
+    case image(imageData: Data)
+    /// Use a custom indicator, which conforms to the `Indicator` protocol.
+    case custom(indicator: Indicator)
+}
+
+// MARK: - Indicator Protocol
+public protocol Indicator {
+    func startAnimatingView()
+    func stopAnimatingView()
+
+    var viewCenter: CGPoint { get set }
+    var view: IndicatorView { get }
+}
+
+extension Indicator {
+    #if os(macOS)
+    public var viewCenter: CGPoint {
+        get {
+            let frame = view.frame
+            return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 )
+        }
+        set {
+            let frame = view.frame
+            let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0,
+                                  y: newValue.y - frame.size.height / 2.0,
+                                  width: frame.size.width,
+                                  height: frame.size.height)
+            view.frame = newFrame
+        }
+    }
+    #else
+    public var viewCenter: CGPoint {
+        get {
+            return view.center
+        }
+        set {
+            view.center = newValue
+        }
+    }
+    #endif
+}
+
+// MARK: - ActivityIndicator
+// Displays a NSProgressIndicator / UIActivityIndicatorView
+final class ActivityIndicator: Indicator {
+
+    #if os(macOS)
+    private let activityIndicatorView: NSProgressIndicator
+    #else
+    private let activityIndicatorView: UIActivityIndicatorView
+    #endif
+    private var animatingCount = 0
+
+    var view: IndicatorView {
+        return activityIndicatorView
+    }
+
+    func startAnimatingView() {
+        animatingCount += 1
+        // Alrady animating
+        if animatingCount == 1 {
+            #if os(macOS)
+                activityIndicatorView.startAnimation(nil)
+            #else
+                activityIndicatorView.startAnimating()
+            #endif
+            activityIndicatorView.isHidden = false
+        }
+    }
+
+    func stopAnimatingView() {
+        animatingCount = max(animatingCount - 1, 0)
+        if animatingCount == 0 {
+            #if os(macOS)
+                activityIndicatorView.stopAnimation(nil)
+            #else
+                activityIndicatorView.stopAnimating()
+            #endif
+            activityIndicatorView.isHidden = true
+        }
+    }
+
+    init() {
+        #if os(macOS)
+            activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
+            activityIndicatorView.controlSize = .small
+            activityIndicatorView.style = .spinning
+        #else
+            #if os(tvOS)
+                let indicatorStyle = UIActivityIndicatorViewStyle.white
+            #else
+                let indicatorStyle = UIActivityIndicatorViewStyle.gray
+            #endif
+            activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle)
+            activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin]
+        #endif
+    }
+}
+
+// MARK: - ImageIndicator
+// Displays an ImageView. Supports gif
+final class ImageIndicator: Indicator {
+    private let animatedImageIndicatorView: ImageView
+
+    var view: IndicatorView {
+        return animatedImageIndicatorView
+    }
+
+    init?(imageData data: Data, processor: ImageProcessor = DefaultImageProcessor.default, options: KingfisherOptionsInfo = KingfisherEmptyOptionsInfo) {
+
+        var options = options
+        // Use normal image view to show animations, so we need to preload all animation data.
+        if !options.preloadAllAnimationData {
+            options.append(.preloadAllAnimationData)
+        }
+        
+        guard let image = processor.process(item: .data(data), options: options) else {
+            return nil
+        }
+
+        animatedImageIndicatorView = ImageView()
+        animatedImageIndicatorView.image = image
+        animatedImageIndicatorView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
+        
+        #if os(macOS)
+            // Need for gif to animate on macOS
+            self.animatedImageIndicatorView.imageScaling = .scaleNone
+            self.animatedImageIndicatorView.canDrawSubviewsIntoLayer = true
+        #else
+            animatedImageIndicatorView.contentMode = .center
+            animatedImageIndicatorView.autoresizingMask = [.flexibleLeftMargin,
+                                                           .flexibleRightMargin,
+                                                           .flexibleBottomMargin,
+                                                           .flexibleTopMargin]
+        #endif
+    }
+
+    func startAnimatingView() {
+        #if os(macOS)
+            animatedImageIndicatorView.animates = true
+        #else
+            animatedImageIndicatorView.startAnimating()
+        #endif
+        animatedImageIndicatorView.isHidden = false
+    }
+
+    func stopAnimatingView() {
+        #if os(macOS)
+            animatedImageIndicatorView.animates = false
+        #else
+            animatedImageIndicatorView.stopAnimating()
+        #endif
+        animatedImageIndicatorView.isHidden = true
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/Kingfisher.h b/iOS/Pods/Kingfisher/Sources/Kingfisher.h
new file mode 100644 (file)
index 0000000..0e11d43
--- /dev/null
@@ -0,0 +1,37 @@
+//
+//  Kingfisher.h
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for Kingfisher.
+FOUNDATION_EXPORT double KingfisherVersionNumber;
+
+//! Project version string for Kingfisher.
+FOUNDATION_EXPORT const unsigned char KingfisherVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <Kingfisher/PublicHeader.h>
+
+
diff --git a/iOS/Pods/Kingfisher/Sources/Kingfisher.swift b/iOS/Pods/Kingfisher/Sources/Kingfisher.swift
new file mode 100644 (file)
index 0000000..75450db
--- /dev/null
@@ -0,0 +1,73 @@
+//
+//  Kingfisher.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 16/9/14.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+import ImageIO
+
+#if os(macOS)
+    import AppKit
+    public typealias Image = NSImage
+    public typealias View = NSView
+    public typealias Color = NSColor
+    public typealias ImageView = NSImageView
+    public typealias Button = NSButton
+#else
+    import UIKit
+    public typealias Image = UIImage
+    public typealias Color = UIColor
+    #if !os(watchOS)
+    public typealias ImageView = UIImageView
+    public typealias View = UIView
+    public typealias Button = UIButton
+    #endif
+#endif
+
+public final class Kingfisher<Base> {
+    public let base: Base
+    public init(_ base: Base) {
+        self.base = base
+    }
+}
+
+/**
+ A type that has Kingfisher extensions.
+ */
+public protocol KingfisherCompatible {
+    associatedtype CompatibleType
+    var kf: CompatibleType { get }
+}
+
+public extension KingfisherCompatible {
+    public var kf: Kingfisher<Self> {
+        get { return Kingfisher(self) }
+    }
+}
+
+extension Image: KingfisherCompatible { }
+#if !os(watchOS)
+extension ImageView: KingfisherCompatible { }
+extension Button: KingfisherCompatible { }
+#endif
diff --git a/iOS/Pods/Kingfisher/Sources/KingfisherManager.swift b/iOS/Pods/Kingfisher/Sources/KingfisherManager.swift
new file mode 100755 (executable)
index 0000000..1f8a8f4
--- /dev/null
@@ -0,0 +1,265 @@
+//
+//  KingfisherManager.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+
+public typealias DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> Void)
+public typealias CompletionHandler = ((_ image: Image?, _ error: NSError?, _ cacheType: CacheType, _ imageURL: URL?) -> Void)
+
+/// RetrieveImageTask represents a task of image retrieving process.
+/// It contains an async task of getting image from disk and from network.
+public final class RetrieveImageTask {
+    
+    public static let empty = RetrieveImageTask()
+    
+    // If task is canceled before the download task started (which means the `downloadTask` is nil),
+    // the download task should not begin.
+    var cancelledBeforeDownloadStarting: Bool = false
+    
+    /// The network retrieve task in this image task.
+    public var downloadTask: RetrieveImageDownloadTask?
+    
+    /**
+    Cancel current task. If this task is already done, do nothing.
+    */
+    public func cancel() {
+        if let downloadTask = downloadTask {
+            downloadTask.cancel()
+        } else {
+            cancelledBeforeDownloadStarting = true
+        }
+    }
+}
+
+/// Error domain of Kingfisher
+public let KingfisherErrorDomain = "com.onevcat.Kingfisher.Error"
+
+/// Main manager class of Kingfisher. It connects Kingfisher downloader and cache.
+/// You can use this class to retrieve an image via a specified URL from web or cache.
+public class KingfisherManager {
+    
+    /// Shared manager used by the extensions across Kingfisher.
+    public static let shared = KingfisherManager()
+    
+    /// Cache used by this manager
+    public var cache: ImageCache
+    
+    /// Downloader used by this manager
+    public var downloader: ImageDownloader
+    
+    /// Default options used by the manager. This option will be used in 
+    /// Kingfisher manager related methods, including all image view and 
+    /// button extension methods. You can also passing the options per image by 
+    /// sending an `options` parameter to Kingfisher's APIs, the per image option 
+    /// will overwrite the default ones if exist.
+    ///
+    /// - Note: This option will not be applied to independent using of `ImageDownloader` or `ImageCache`.
+    public var defaultOptions = KingfisherEmptyOptionsInfo
+    
+    var currentDefaultOptions: KingfisherOptionsInfo {
+        return [.downloader(downloader), .targetCache(cache)] + defaultOptions
+    }
+    
+    convenience init() {
+        self.init(downloader: .default, cache: .default)
+    }
+    
+    init(downloader: ImageDownloader, cache: ImageCache) {
+        self.downloader = downloader
+        self.cache = cache
+    }
+    
+    /**
+    Get an image with resource.
+    If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first.
+    If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`.
+    These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more.
+    
+    - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
+    - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+    - parameter progressBlock:     Called every time downloaded data changed. This could be used as a progress UI.
+    - parameter completionHandler: Called when the whole retrieving process finished.
+    
+    - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task.
+    */
+    @discardableResult
+    public func retrieveImage(with resource: Resource,
+        options: KingfisherOptionsInfo?,
+        progressBlock: DownloadProgressBlock?,
+        completionHandler: CompletionHandler?) -> RetrieveImageTask
+    {
+        let task = RetrieveImageTask()
+        let options = currentDefaultOptions + (options ?? KingfisherEmptyOptionsInfo)
+        if options.forceRefresh {
+            _ = downloadAndCacheImage(
+                with: resource.downloadURL,
+                forKey: resource.cacheKey,
+                retrieveImageTask: task,
+                progressBlock: progressBlock,
+                completionHandler: completionHandler,
+                options: options)
+        } else {
+            tryToRetrieveImageFromCache(
+                forKey: resource.cacheKey,
+                with: resource.downloadURL,
+                retrieveImageTask: task,
+                progressBlock: progressBlock,
+                completionHandler: completionHandler,
+                options: options)
+        }
+        
+        return task
+    }
+
+    @discardableResult
+    func downloadAndCacheImage(with url: URL,
+                             forKey key: String,
+                      retrieveImageTask: RetrieveImageTask,
+                          progressBlock: DownloadProgressBlock?,
+                      completionHandler: CompletionHandler?,
+                                options: KingfisherOptionsInfo) -> RetrieveImageDownloadTask?
+    {
+        let downloader = options.downloader
+        return downloader.downloadImage(with: url, retrieveImageTask: retrieveImageTask, options: options,
+            progressBlock: { receivedSize, totalSize in
+                progressBlock?(receivedSize, totalSize)
+            },
+            completionHandler: { image, error, imageURL, originalData in
+
+                let targetCache = options.targetCache
+                if let error = error, error.code == KingfisherError.notModified.rawValue {
+                    // Not modified. Try to find the image from cache.
+                    // (The image should be in cache. It should be guaranteed by the framework users.)
+                    targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> Void in
+                        completionHandler?(cacheImage, nil, cacheType, url)
+                    })
+                    return
+                }
+                
+                if let image = image, let originalData = originalData {
+                    targetCache.store(image,
+                                      original: originalData,
+                                      forKey: key,
+                                      processorIdentifier:options.processor.identifier,
+                                      cacheSerializer: options.cacheSerializer,
+                                      toDisk: !options.cacheMemoryOnly,
+                                      completionHandler: nil)
+                    if options.cacheOriginalImage && options.processor != DefaultImageProcessor.default {
+                        let originalCache = options.originalCache
+                        let defaultProcessor = DefaultImageProcessor.default
+                        if let originalImage = defaultProcessor.process(item: .data(originalData), options: options) {
+                            originalCache.store(originalImage,
+                                              original: originalData,
+                                              forKey: key,
+                                              processorIdentifier: defaultProcessor.identifier,
+                                              cacheSerializer: options.cacheSerializer,
+                                              toDisk: !options.cacheMemoryOnly,
+                                              completionHandler: nil)
+                        }
+                    }
+                }
+
+                completionHandler?(image, error, .none, url)
+
+            })
+    }
+    
+    func tryToRetrieveImageFromCache(forKey key: String,
+                                       with url: URL,
+                              retrieveImageTask: RetrieveImageTask,
+                                  progressBlock: DownloadProgressBlock?,
+                              completionHandler: CompletionHandler?,
+                                        options: KingfisherOptionsInfo)
+    {
+
+        let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> Void in
+            completionHandler?(image, error, cacheType, imageURL)
+        }
+        
+        func handleNoCache() {
+            if options.onlyFromCache {
+                let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil)
+                diskTaskCompletionHandler(nil, error, .none, url)
+                return
+            }
+            self.downloadAndCacheImage(
+                with: url,
+                forKey: key,
+                retrieveImageTask: retrieveImageTask,
+                progressBlock: progressBlock,
+                completionHandler: diskTaskCompletionHandler,
+                options: options)
+            
+        }
+        
+        let targetCache = options.targetCache
+        // First, try to get the exactly image from cache
+        targetCache.retrieveImage(forKey: key, options: options) { image, cacheType in
+            // If found, we could finish now.
+            if image != nil {
+                diskTaskCompletionHandler(image, nil, cacheType, url)
+                return
+            }
+            
+            // If not found, and we are using a default processor, download it!
+            let processor = options.processor
+            guard processor != DefaultImageProcessor.default else {
+                handleNoCache()
+                return
+            }
+            
+            // If processor is not the default one, we have a chance to check whether
+            // the original image is already in cache.
+            let originalCache = options.originalCache
+            let optionsWithoutProcessor = options.removeAllMatchesIgnoringAssociatedValue(.processor(processor))
+            originalCache.retrieveImage(forKey: key, options: optionsWithoutProcessor) { image, cacheType in
+                // If we found the original image, there is no need to download it again.
+                // We could just apply processor to it now.
+                guard let image = image else {
+                    handleNoCache()
+                    return
+                }
+                
+                guard let processedImage = processor.process(item: .image(image), options: options) else {
+                    diskTaskCompletionHandler(nil, nil, .none, url)
+                    return
+                }
+                targetCache.store(processedImage,
+                                  original: nil,
+                                  forKey: key,
+                                  processorIdentifier:options.processor.identifier,
+                                  cacheSerializer: options.cacheSerializer,
+                                  toDisk: !options.cacheMemoryOnly,
+                                  completionHandler: nil)
+                diskTaskCompletionHandler(processedImage, nil, .none, url)
+            }
+        }
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift b/iOS/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift
new file mode 100755 (executable)
index 0000000..ebe4b31
--- /dev/null
@@ -0,0 +1,355 @@
+//
+//  KingfisherOptionsInfo.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/23.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+import AppKit
+#else
+import UIKit
+#endif
+    
+
+/**
+*      KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem]. You can use the enum of option item with value to control some behaviors of Kingfisher.
+*/
+public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem]
+let KingfisherEmptyOptionsInfo = [KingfisherOptionsInfoItem]()
+
+/**
+Items could be added into KingfisherOptionsInfo.
+*/
+public enum KingfisherOptionsInfoItem {
+    /// The associated value of this member should be an ImageCache object. Kingfisher will use the specified
+    /// cache object when handling related operations, including trying to retrieve the cached images and store
+    /// the downloaded image to it.
+    case targetCache(ImageCache)
+    
+    /// Cache for storing and retrieving original image.
+    /// Preferred prior to targetCache for storing and retrieving original images if specified.
+    /// Only used if a non-default image processor is involved.
+    case originalCache(ImageCache)
+    
+    /// The associated value of this member should be an ImageDownloader object. Kingfisher will use this
+    /// downloader to download the images.
+    case downloader(ImageDownloader)
+    
+    /// Member for animation transition when using UIImageView. Kingfisher will use the `ImageTransition` of
+    /// this enum to animate the image in if it is downloaded from web. The transition will not happen when the
+    /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when
+    /// the image being retrieved from cache, set `ForceTransition` as well.
+    case transition(ImageTransition)
+    
+    /// Associated `Float` value will be set as the priority of image download task. The value for it should be
+    /// between 0.0~1.0. If this option not set, the default value (`NSURLSessionTaskPriorityDefault`) will be used.
+    case downloadPriority(Float)
+    
+    /// If set, `Kingfisher` will ignore the cache and try to fire a download task for the resource.
+    case forceRefresh
+
+    /// If set, `Kingfisher` will try to retrieve the image from memory cache first. If the image is not in memory
+    /// cache, then it will ignore the disk cache but download the image again from network. This is useful when
+    /// you want to display a changeable image behind the same url, while avoiding download it again and again.
+    case fromMemoryCacheOrRefresh
+    
+    /// If set, setting the image to an image view will happen with transition even when retrieved from cache.
+    /// See `Transition` option for more.
+    case forceTransition
+    
+    ///  If set, `Kingfisher` will only cache the value in memory but not in disk.
+    case cacheMemoryOnly
+    
+    /// If set, `Kingfisher` will only try to retrieve the image from cache not from network.
+    case onlyFromCache
+    
+    /// Decode the image in background thread before using.
+    case backgroundDecode
+    
+    /// The associated value of this member will be used as the target queue of dispatch callbacks when
+    /// retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks.
+    case callbackDispatchQueue(DispatchQueue?)
+    
+    /// The associated value of this member will be used as the scale factor when converting retrieved data to an image.
+    /// It is the image scale, instead of your screen scale. You may need to specify the correct scale when you dealing 
+    /// with 2x or 3x retina images.
+    case scaleFactor(CGFloat)
+
+    /// Whether all the animated image data should be preloaded. Default it false, which means following frames will be
+    /// loaded on need. If true, all the animated image data will be loaded and decoded into memory. This option is mainly
+    /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload
+    /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use
+    /// corresponding image view type instead of setting this option.
+    case preloadAllAnimationData
+    
+    /// The `ImageDownloadRequestModifier` contained will be used to change the request before it being sent.
+    /// This is the last chance you can modify the request. You can modify the request for some customizing purpose,
+    /// such as adding auth token to the header, do basic HTTP auth or something like url mapping. The original request
+    /// will be sent without any modification by default.
+    case requestModifier(ImageDownloadRequestModifier)
+    
+    /// Processor for processing when the downloading finishes, a processor will convert the downloaded data to an image
+    /// and/or apply some filter on it. If a cache is connected to the downloader (it happens when you are using
+    /// KingfisherManager or the image extension methods), the converted image will also be sent to cache as well as the
+    /// image view. `DefaultImageProcessor.default` will be used by default.
+    case processor(ImageProcessor)
+    
+    /// Supply an `CacheSerializer` to convert some data to an image object for
+    /// retrieving from disk cache or vice versa for storing to disk cache.
+    /// `DefaultCacheSerializer.default` will be used by default.
+    case cacheSerializer(CacheSerializer)
+
+    /// Modifier for modifying an image right before it is used.
+    /// If the image was fetched directly from the downloader, the modifier will
+    /// run directly after the processor.
+    /// If the image is being fetched from a cache, the modifier will run after
+    /// the cacheSerializer.
+    /// Use `ImageModifier` when you need to set properties on a concrete type
+    /// of `Image`, such as a `UIImage`, that do not persist when caching the image.
+    case imageModifier(ImageModifier)
+    
+    /// Keep the existing image while setting another image to an image view.
+    /// By setting this option, the placeholder image parameter of imageview extension method
+    /// will be ignored and the current image will be kept while loading or downloading the new image.
+    case keepCurrentImageWhileLoading
+    
+    /// If set, Kingfisher will only load the first frame from a animated image data file as a single image.
+    /// Loading a lot of animated images may take too much memory. It will be useful when you want to display a
+    /// static preview of the first frame from a animated image.
+    /// This option will be ignored if the target image is not animated image data.
+    case onlyLoadFirstFrame
+    
+    /// If set and an `ImageProcessor` is used, Kingfisher will try to cache both 
+    /// the final result and original image. Kingfisher will have a chance to use 
+    /// the original image when another processor is applied to the same resouce, 
+    /// instead of downloading it again.
+    case cacheOriginalImage
+}
+
+precedencegroup ItemComparisonPrecedence {
+    associativity: none
+    higherThan: LogicalConjunctionPrecedence
+}
+
+infix operator <== : ItemComparisonPrecedence
+
+// This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values.
+func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool {
+    switch (lhs, rhs) {
+    case (.targetCache(_), .targetCache(_)): return true
+    case (.originalCache(_), .originalCache(_)): return true
+    case (.downloader(_), .downloader(_)): return true
+    case (.transition(_), .transition(_)): return true
+    case (.downloadPriority(_), .downloadPriority(_)): return true
+    case (.forceRefresh, .forceRefresh): return true
+    case (.fromMemoryCacheOrRefresh, .fromMemoryCacheOrRefresh): return true
+    case (.forceTransition, .forceTransition): return true
+    case (.cacheMemoryOnly, .cacheMemoryOnly): return true
+    case (.onlyFromCache, .onlyFromCache): return true
+    case (.backgroundDecode, .backgroundDecode): return true
+    case (.callbackDispatchQueue(_), .callbackDispatchQueue(_)): return true
+    case (.scaleFactor(_), .scaleFactor(_)): return true
+    case (.preloadAllAnimationData, .preloadAllAnimationData): return true
+    case (.requestModifier(_), .requestModifier(_)): return true
+    case (.processor(_), .processor(_)): return true
+    case (.cacheSerializer(_), .cacheSerializer(_)): return true
+    case (.imageModifier(_), .imageModifier(_)): return true
+    case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): return true
+    case (.onlyLoadFirstFrame, .onlyLoadFirstFrame): return true
+    case (.cacheOriginalImage, .cacheOriginalImage): return true
+    default: return false
+    }
+}
+
+
+extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
+    func lastMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? {
+        return reversed().first { $0 <== target }
+    }
+    
+    func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] {
+        return filter { !($0 <== target) }
+    }
+}
+
+public extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
+    /// The target `ImageCache` which is used.
+    public var targetCache: ImageCache {
+        if let item = lastMatchIgnoringAssociatedValue(.targetCache(.default)),
+            case .targetCache(let cache) = item
+        {
+            return cache
+        }
+        return ImageCache.default
+    }
+    
+    /// The original `ImageCache` which is used.
+    public var originalCache: ImageCache {
+        if let item = lastMatchIgnoringAssociatedValue(.originalCache(.default)),
+            case .originalCache(let cache) = item
+        {
+            return cache
+        }
+        return targetCache
+    }
+    
+    /// The `ImageDownloader` which is specified.
+    public var downloader: ImageDownloader {
+        if let item = lastMatchIgnoringAssociatedValue(.downloader(.default)),
+            case .downloader(let downloader) = item
+        {
+            return downloader
+        }
+        return ImageDownloader.default
+    }
+    
+    /// Member for animation transition when using UIImageView.
+    public var transition: ImageTransition {
+        if let item = lastMatchIgnoringAssociatedValue(.transition(.none)),
+            case .transition(let transition) = item
+        {
+            return transition
+        }
+        return ImageTransition.none
+    }
+    
+    /// A `Float` value set as the priority of image download task. The value for it should be
+    /// between 0.0~1.0.
+    public var downloadPriority: Float {
+        if let item = lastMatchIgnoringAssociatedValue(.downloadPriority(0)),
+            case .downloadPriority(let priority) = item
+        {
+            return priority
+        }
+        return URLSessionTask.defaultPriority
+    }
+    
+    /// Whether an image will be always downloaded again or not.
+    public var forceRefresh: Bool {
+        return contains{ $0 <== .forceRefresh }
+    }
+
+    /// Whether an image should be got only from memory cache or download.
+    public var fromMemoryCacheOrRefresh: Bool {
+        return contains{ $0 <== .fromMemoryCacheOrRefresh }
+    }
+    
+    /// Whether the transition should always happen or not.
+    public var forceTransition: Bool {
+        return contains{ $0 <== .forceTransition }
+    }
+    
+    /// Whether cache the image only in memory or not.
+    public var cacheMemoryOnly: Bool {
+        return contains{ $0 <== .cacheMemoryOnly }
+    }
+    
+    /// Whether only load the images from cache or not.
+    public var onlyFromCache: Bool {
+        return contains{ $0 <== .onlyFromCache }
+    }
+    
+    /// Whether the image should be decoded in background or not.
+    public var backgroundDecode: Bool {
+        return contains{ $0 <== .backgroundDecode }
+    }
+
+    /// Whether the image data should be all loaded at once if it is an animated image.
+    public var preloadAllAnimationData: Bool {
+        return contains { $0 <== .preloadAllAnimationData }
+    }
+    
+    /// The queue of callbacks should happen from Kingfisher.
+    public var callbackDispatchQueue: DispatchQueue {
+        if let item = lastMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)),
+            case .callbackDispatchQueue(let queue) = item
+        {
+            return queue ?? DispatchQueue.main
+        }
+        return DispatchQueue.main
+    }
+    
+    /// The scale factor which should be used for the image.
+    public var scaleFactor: CGFloat {
+        if let item = lastMatchIgnoringAssociatedValue(.scaleFactor(0)),
+            case .scaleFactor(let scale) = item
+        {
+            return scale
+        }
+        return 1.0
+    }
+    
+    /// The `ImageDownloadRequestModifier` will be used before sending a download request.
+    public var modifier: ImageDownloadRequestModifier {
+        if let item = lastMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)),
+            case .requestModifier(let modifier) = item
+        {
+            return modifier
+        }
+        return NoModifier.default
+    }
+    
+    /// `ImageProcessor` for processing when the downloading finishes.
+    public var processor: ImageProcessor {
+        if let item = lastMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)),
+            case .processor(let processor) = item
+        {
+            return processor
+        }
+        return DefaultImageProcessor.default
+    }
+
+    /// `ImageModifier` for modifying right before the image is displayed.
+    public var imageModifier: ImageModifier {
+        if let item = lastMatchIgnoringAssociatedValue(.imageModifier(DefaultImageModifier.default)),
+            case .imageModifier(let imageModifier) = item
+        {
+            return imageModifier
+        }
+        return DefaultImageModifier.default
+    }
+    
+    /// `CacheSerializer` to convert image to data for storing in cache.
+    public var cacheSerializer: CacheSerializer {
+        if let item = lastMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)),
+            case .cacheSerializer(let cacheSerializer) = item
+        {
+            return cacheSerializer
+        }
+        return DefaultCacheSerializer.default
+    }
+    
+    /// Keep the existing image while setting another image to an image view. 
+    /// Or the placeholder will be used while downloading.
+    public var keepCurrentImageWhileLoading: Bool {
+        return contains { $0 <== .keepCurrentImageWhileLoading }
+    }
+    
+    public var onlyLoadFirstFrame: Bool {
+        return contains { $0 <== .onlyLoadFirstFrame }
+    }
+    
+    public var cacheOriginalImage: Bool {
+        return contains { $0 <== .cacheOriginalImage }
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/Placeholder.swift b/iOS/Pods/Kingfisher/Sources/Placeholder.swift
new file mode 100755 (executable)
index 0000000..3ebaf0b
--- /dev/null
@@ -0,0 +1,82 @@
+//
+//  Placeholder.swift
+//  Kingfisher
+//
+//  Created by Tieme van Veen on 28/08/2017.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if os(macOS)
+    import AppKit
+#else
+    import UIKit
+#endif
+
+
+/// Represent a placeholder type which could be set while loading as well as
+/// loading finished without getting an image.
+public protocol Placeholder {
+    
+    /// How the placeholder should be added to a given image view.
+    func add(to imageView: ImageView)
+    
+    /// How the placeholder should be removed from a given image view.
+    func remove(from imageView: ImageView)
+}
+
+/// Default implementation of an image placeholder. The image will be set or 
+/// reset directly for `image` property of the image view.
+extension Placeholder where Self: Image {
+    
+    /// How the placeholder should be added to a given image view.
+    public func add(to imageView: ImageView) { imageView.image = self }
+    
+    /// How the placeholder should be removed from a given image view.
+    public func remove(from imageView: ImageView) { imageView.image = nil }
+}
+
+extension Image: Placeholder {}
+
+/// Default implementation of an arbitrary view as placeholder. The view will be 
+/// added as a subview when adding and be removed from its super view when removing.
+///
+/// To use your customize View type as placeholder, simply let it conforming to 
+/// `Placeholder` by `extension MyView: Placeholder {}`.
+extension Placeholder where Self: View {
+    
+    /// How the placeholder should be added to a given image view.
+    public func add(to imageView: ImageView) {
+        imageView.addSubview(self)
+
+        self.translatesAutoresizingMaskIntoConstraints = false
+        NSLayoutConstraint.activate([
+            NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: imageView, attribute: .centerX, multiplier: 1, constant: 0),
+            NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: imageView, attribute: .centerY, multiplier: 1, constant: 0),
+            NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: 1, constant: 0),
+            NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .width, multiplier: 1, constant: 0)
+            ])
+    }
+
+    /// How the placeholder should be removed from a given image view.
+    public func remove(from imageView: ImageView) {
+        self.removeFromSuperview()
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/RequestModifier.swift b/iOS/Pods/Kingfisher/Sources/RequestModifier.swift
new file mode 100644 (file)
index 0000000..1e099bf
--- /dev/null
@@ -0,0 +1,53 @@
+//
+//  RequestModifier.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 2016/09/05.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+/// Request modifier of image downloader.
+public protocol ImageDownloadRequestModifier {
+    func modified(for request: URLRequest) -> URLRequest?
+}
+
+struct NoModifier: ImageDownloadRequestModifier {
+    static let `default` = NoModifier()
+    private init() {}
+    func modified(for request: URLRequest) -> URLRequest? {
+        return request
+    }
+}
+
+public struct AnyModifier: ImageDownloadRequestModifier {
+    
+    let block: (URLRequest) -> URLRequest?
+    
+    public func modified(for request: URLRequest) -> URLRequest? {
+        return block(request)
+    }
+    
+    public init(modify: @escaping (URLRequest) -> URLRequest? ) {
+        block = modify
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/Resource.swift b/iOS/Pods/Kingfisher/Sources/Resource.swift
new file mode 100755 (executable)
index 0000000..c71f24d
--- /dev/null
@@ -0,0 +1,74 @@
+//
+//  Resource.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+
+/// `Resource` protocol defines how to download and cache a resource from network.
+public protocol Resource {
+    /// The key used in cache.
+    var cacheKey: String { get }
+    
+    /// The target image URL.
+    var downloadURL: URL { get }
+}
+
+/**
+ ImageResource is a simple combination of `downloadURL` and `cacheKey`.
+ When passed to image view set methods, Kingfisher will try to download the target 
+ image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache.
+ */
+public struct ImageResource: Resource {
+    /// The key used in cache.
+    public let cacheKey: String
+    
+    /// The target image URL.
+    public let downloadURL: URL
+    
+    /**
+     Create a resource.
+     
+     - parameter downloadURL: The target image URL.
+     - parameter cacheKey:    The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key.
+     
+     - returns: A resource.
+     */
+    public init(downloadURL: URL, cacheKey: String? = nil) {
+        self.downloadURL = downloadURL
+        self.cacheKey = cacheKey ?? downloadURL.absoluteString
+    }
+}
+
+/**
+ URL conforms to `Resource` in Kingfisher.
+ The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`.
+ If you need customize the url and/or cache key, use `ImageResource` instead.
+ */
+extension URL: Resource {
+    public var cacheKey: String { return absoluteString }
+    public var downloadURL: URL { return self }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/String+MD5.swift b/iOS/Pods/Kingfisher/Sources/String+MD5.swift
new file mode 100755 (executable)
index 0000000..e07168c
--- /dev/null
@@ -0,0 +1,297 @@
+//
+//  String+MD5.swift
+//  Kingfisher
+//
+// To date, adding CommonCrypto to a Swift framework is problematic. See:
+// http://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework
+// We're using a subset and modified version of CryptoSwift as an alternative.
+// The following is an altered source version that only includes MD5. The original software can be found at:
+// https://github.com/krzyzanowskim/CryptoSwift
+// This is the original copyright notice:
+
+/*
+Copyright (C) 2014 Marcin Krzyżanowski <marcin.krzyzanowski@gmail.com>
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from the use of this software.
+Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+- This notice may not be removed or altered from any source or binary distribution.
+*/
+
+import Foundation
+
+public struct StringProxy {
+    fileprivate let base: String
+    init(proxy: String) {
+        base = proxy
+    }
+}
+
+extension String: KingfisherCompatible {
+    public typealias CompatibleType = StringProxy
+    public var kf: CompatibleType {
+        return StringProxy(proxy: self)
+    }
+}
+
+extension StringProxy {
+    var md5: String {
+        if let data = base.data(using: .utf8, allowLossyConversion: true) {
+
+            let message = data.withUnsafeBytes { bytes -> [UInt8] in
+                return Array(UnsafeBufferPointer(start: bytes, count: data.count))
+            }
+
+            let MD5Calculator = MD5(message)
+            let MD5Data = MD5Calculator.calculate()
+
+            var MD5String = String()
+            for c in MD5Data {
+                MD5String += String(format: "%02x", c)
+            }
+            return MD5String
+
+        } else {
+            return base
+        }
+    }
+}
+
+
+/** array of bytes, little-endian representation */
+func arrayOfBytes<T>(_ value: T, length: Int? = nil) -> [UInt8] {
+    let totalBytes = length ?? (MemoryLayout<T>.size * 8)
+    
+    let valuePointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
+    valuePointer.pointee = value
+
+    let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { (bytesPointer) -> [UInt8] in
+        var bytes = [UInt8](repeating: 0, count: totalBytes)
+        for j in 0..<min(MemoryLayout<T>.size, totalBytes) {
+            bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee
+        }
+        return bytes
+    }
+
+    #if swift(>=4.1)
+    valuePointer.deinitialize(count: 1)
+    valuePointer.deallocate()
+    #else
+    valuePointer.deinitialize()
+    valuePointer.deallocate(capacity: 1)
+    #endif
+    
+    return bytes
+}
+
+extension Int {
+    /** Array of bytes with optional padding (little-endian) */
+    func bytes(_ totalBytes: Int = MemoryLayout<Int>.size) -> [UInt8] {
+        return arrayOfBytes(self, length: totalBytes)
+    }
+    
+}
+
+extension NSMutableData {
+    
+    /** Convenient way to append bytes */
+    func appendBytes(_ arrayOfBytes: [UInt8]) {
+        append(arrayOfBytes, length: arrayOfBytes.count)
+    }
+    
+}
+
+protocol HashProtocol {
+    var message: Array<UInt8> { get }
+    
+    /** Common part for hash calculation. Prepare header data. */
+    func prepare(_ len: Int) -> Array<UInt8>
+}
+
+extension HashProtocol {
+    
+    func prepare(_ len: Int) -> Array<UInt8> {
+        var tmpMessage = message
+        
+        // Step 1. Append Padding Bits
+        tmpMessage.append(0x80) // append one bit (UInt8 with one bit) to message
+        
+        // append "0" bit until message length in bits ≡ 448 (mod 512)
+        var msgLength = tmpMessage.count
+        var counter = 0
+        
+        while msgLength % len != (len - 8) {
+            counter += 1
+            msgLength += 1
+        }
+        
+        tmpMessage += Array<UInt8>(repeating: 0, count: counter)
+        return tmpMessage
+    }
+}
+
+func toUInt32Array(_ slice: ArraySlice<UInt8>) -> Array<UInt32> {
+    var result = Array<UInt32>()
+    result.reserveCapacity(16)
+    
+    for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout<UInt32>.size) {
+        let d0 = UInt32(slice[idx.advanced(by: 3)]) << 24
+        let d1 = UInt32(slice[idx.advanced(by: 2)]) << 16
+        let d2 = UInt32(slice[idx.advanced(by: 1)]) << 8
+        let d3 = UInt32(slice[idx])
+        let val: UInt32 = d0 | d1 | d2 | d3
+                         
+        result.append(val)
+    }
+    return result
+}
+
+struct BytesIterator: IteratorProtocol {
+    
+    let chunkSize: Int
+    let data: [UInt8]
+    
+    init(chunkSize: Int, data: [UInt8]) {
+        self.chunkSize = chunkSize
+        self.data = data
+    }
+    
+    var offset = 0
+    
+    mutating func next() -> ArraySlice<UInt8>? {
+        let end = min(chunkSize, data.count - offset)
+        let result = data[offset..<offset + end]
+        offset += result.count
+        return result.count > 0 ? result : nil
+    }
+}
+
+struct BytesSequence: Sequence {
+    let chunkSize: Int
+    let data: [UInt8]
+    
+    func makeIterator() -> BytesIterator {
+        return BytesIterator(chunkSize: chunkSize, data: data)
+    }
+}
+
+func rotateLeft(_ value: UInt32, bits: UInt32) -> UInt32 {
+    return ((value << bits) & 0xFFFFFFFF) | (value >> (32 - bits))
+}
+
+class MD5: HashProtocol {
+    
+    static let size = 16 // 128 / 8
+    let message: [UInt8]
+    
+    init (_ message: [UInt8]) {
+        self.message = message
+    }
+    
+    /** specifies the per-round shift amounts */
+    private let shifts: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+                                    5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
+                                    4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+                                    6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]
+    
+    /** binary integer part of the sines of integers (Radians) */
+    private let sines: [UInt32] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+                               0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+                               0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+                               0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+                               0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+                               0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+                               0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+                               0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+                               0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+                               0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+                               0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
+                               0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+                               0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+                               0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+                               0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+                               0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391]
+    
+    private let hashes: [UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
+    
+    func calculate() -> [UInt8] {
+        var tmpMessage = prepare(64)
+        tmpMessage.reserveCapacity(tmpMessage.count + 4)
+        
+        // hash values
+        var hh = hashes
+        
+        // Step 2. Append Length a 64-bit representation of lengthInBits
+        let lengthInBits = (message.count * 8)
+        let lengthBytes = lengthInBits.bytes(64 / 8)
+        tmpMessage += lengthBytes.reversed()
+
+        // Process the message in successive 512-bit chunks:
+        let chunkSizeBytes = 512 / 8 // 64
+
+        for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) {
+            // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
+            var M = toUInt32Array(chunk)
+            assert(M.count == 16, "Invalid array")
+            
+            // Initialize hash value for this chunk:
+            var A: UInt32 = hh[0]
+            var B: UInt32 = hh[1]
+            var C: UInt32 = hh[2]
+            var D: UInt32 = hh[3]
+            
+            var dTemp: UInt32 = 0
+            
+            // Main loop
+            for j in 0 ..< sines.count {
+                var g = 0
+                var F: UInt32 = 0
+                
+                switch j {
+                case 0...15:
+                    F = (B & C) | ((~B) & D)
+                    g = j
+                    break
+                case 16...31:
+                    F = (D & B) | (~D & C)
+                    g = (5 * j + 1) % 16
+                    break
+                case 32...47:
+                    F = B ^ C ^ D
+                    g = (3 * j + 5) % 16
+                    break
+                case 48...63:
+                    F = C ^ (B | (~D))
+                    g = (7 * j) % 16
+                    break
+                default:
+                    break
+                }
+                dTemp = D
+                D = C
+                C = B
+                B = B &+ rotateLeft((A &+ F &+ sines[j] &+ M[g]), bits: shifts[j])
+                A = dTemp
+            }
+            
+            hh[0] = hh[0] &+ A
+            hh[1] = hh[1] &+ B
+            hh[2] = hh[2] &+ C
+            hh[3] = hh[3] &+ D
+        }
+        
+        var result = [UInt8]()
+        result.reserveCapacity(hh.count / 4)
+        
+        hh.forEach {
+            let itemLE = $0.littleEndian
+            let r1 = UInt8(itemLE & 0xff)
+            let r2 = UInt8((itemLE >> 8) & 0xff)
+            let r3 = UInt8((itemLE >> 16) & 0xff)
+            let r4 = UInt8((itemLE >> 24) & 0xff)
+            result += [r1, r2, r3, r4]
+        }
+        return result
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/ThreadHelper.swift b/iOS/Pods/Kingfisher/Sources/ThreadHelper.swift
new file mode 100755 (executable)
index 0000000..a737b6e
--- /dev/null
@@ -0,0 +1,40 @@
+//
+//  ThreadHelper.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/10/9.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+extension DispatchQueue {
+    // This method will dispatch the `block` to self.
+    // If `self` is the main queue, and current thread is main thread, the block
+    // will be invoked immediately instead of being dispatched.
+    func safeAsync(_ block: @escaping ()->()) {
+        if self === DispatchQueue.main && Thread.isMainThread {
+            block()
+        } else {
+            async { block() }
+        }
+    }
+}
diff --git a/iOS/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift b/iOS/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift
new file mode 100755 (executable)
index 0000000..a06bb11
--- /dev/null
@@ -0,0 +1,274 @@
+//
+//  UIButton+Kingfisher.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/13.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import UIKit
+
+// MARK: - Set Images
+/**
+ *     Set image to use in button from web for a specified state.
+ */
+extension Kingfisher where Base: UIButton {
+    /**
+     Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and
+     completion handler.
+     
+     - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
+     - parameter state:             The state that uses the specified image.
+     - parameter placeholder:       A placeholder image when retrieving the image at URL.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called when the image downloading progress gets updated.
+     - parameter completionHandler: Called when the image retrieved and set.
+     
+     - returns: A task represents the retrieving process.
+     
+     - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
+     The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
+     
+     If `resource` is `nil`, the `placeholder` image will be set and
+     `completionHandler` will be called with both `error` and `image` being `nil`.
+     */
+    @discardableResult
+    public func setImage(with resource: Resource?,
+                         for state: UIControlState,
+                         placeholder: UIImage? = nil,
+                         options: KingfisherOptionsInfo? = nil,
+                         progressBlock: DownloadProgressBlock? = nil,
+                         completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
+    {
+        guard let resource = resource else {
+            base.setImage(placeholder, for: state)
+            setWebURL(nil, for: state)
+            completionHandler?(nil, nil, .none, nil)
+            return .empty
+        }
+        
+        let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
+        if !options.keepCurrentImageWhileLoading {
+            base.setImage(placeholder, for: state)
+        }
+        
+        setWebURL(resource.downloadURL, for: state)
+        let task = KingfisherManager.shared.retrieveImage(
+            with: resource,
+            options: options,
+            progressBlock: { receivedSize, totalSize in
+                guard resource.downloadURL == self.webURL(for: state) else {
+                    return
+                }
+                if let progressBlock = progressBlock {
+                    progressBlock(receivedSize, totalSize)
+                }
+            },
+            completionHandler: {[weak base] image, error, cacheType, imageURL in
+                DispatchQueue.main.safeAsync {
+                    guard let strongBase = base, imageURL == self.webURL(for: state) else {
+                        completionHandler?(image, error, cacheType, imageURL)
+                        return
+                    }
+                    self.setImageTask(nil)
+                    if image != nil {
+                        strongBase.setImage(image, for: state)
+                    }
+
+                    completionHandler?(image, error, cacheType, imageURL)
+                }
+            })
+        
+        setImageTask(task)
+        return task
+    }
+    
+    /**
+     Cancel the image download task bounded to the image view if it is running.
+     Nothing will happen if the downloading has already finished.
+     */
+    public func cancelImageDownloadTask() {
+        imageTask?.cancel()
+    }
+    
+    /**
+     Set the background image to use for a specified state with a resource,
+     a placeholder image, options progress handler and completion handler.
+     
+     - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
+     - parameter state:             The state that uses the specified image.
+     - parameter placeholder:       A placeholder image when retrieving the image at URL.
+     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+     - parameter progressBlock:     Called when the image downloading progress gets updated.
+     - parameter completionHandler: Called when the image retrieved and set.
+     
+     - returns: A task represents the retrieving process.
+     
+     - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
+     The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
+     
+     If `resource` is `nil`, the `placeholder` image will be set and
+     `completionHandler` will be called with both `error` and `image` being `nil`.
+     */
+    @discardableResult
+    public func setBackgroundImage(with resource: Resource?,
+                                   for state: UIControlState,
+                                   placeholder: UIImage? = nil,
+                                   options: KingfisherOptionsInfo? = nil,
+                                   progressBlock: DownloadProgressBlock? = nil,
+                                   completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
+    {
+        guard let resource = resource else {
+            base.setBackgroundImage(placeholder, for: state)
+            setBackgroundWebURL(nil, for: state)
+            completionHandler?(nil, nil, .none, nil)
+            return .empty
+        }
+        
+        let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
+        if !options.keepCurrentImageWhileLoading {
+            base.setBackgroundImage(placeholder, for: state)
+        }
+        
+        setBackgroundWebURL(resource.downloadURL, for: state)
+        let task = KingfisherManager.shared.retrieveImage(
+            with: resource,
+            options: options,
+            progressBlock: { receivedSize, totalSize in
+                guard resource.downloadURL == self.backgroundWebURL(for: state) else {
+                    return
+                }
+                if let progressBlock = progressBlock {
+                    progressBlock(receivedSize, totalSize)
+                }
+            },
+            completionHandler: { [weak base] image, error, cacheType, imageURL in
+                DispatchQueue.main.safeAsync {
+                    guard let strongBase = base, imageURL == self.backgroundWebURL(for: state) else {
+                        completionHandler?(image, error, cacheType, imageURL)
+                        return
+                    }
+                    self.setBackgroundImageTask(nil)
+                    if image != nil {
+                        strongBase.setBackgroundImage(image, for: state)
+                    }
+                    completionHandler?(image, error, cacheType, imageURL)
+                }
+            })
+        
+        setBackgroundImageTask(task)
+        return task
+    }
+    
+    /**
+     Cancel the background image download task bounded to the image view if it is running.
+     Nothing will happen if the downloading has already finished.
+     */
+    public func cancelBackgroundImageDownloadTask() {
+        backgroundImageTask?.cancel()
+    }
+
+}
+
+// MARK: - Associated Object
+private var lastURLKey: Void?
+private var imageTaskKey: Void?
+
+extension Kingfisher where Base: UIButton {
+    /**
+     Get the image URL binded to this button for a specified state.
+     
+     - parameter state: The state that uses the specified image.
+     
+     - returns: Current URL for image.
+     */
+    public func webURL(for state: UIControlState) -> URL? {
+        return webURLs[NSNumber(value:state.rawValue)] as? URL
+    }
+    
+    fileprivate func setWebURL(_ url: URL?, for state: UIControlState) {
+        webURLs[NSNumber(value:state.rawValue)] = url
+    }
+    
+    fileprivate var webURLs: NSMutableDictionary {
+        var dictionary = objc_getAssociatedObject(base, &lastURLKey) as? NSMutableDictionary
+        if dictionary == nil {
+            dictionary = NSMutableDictionary()
+            setWebURLs(dictionary!)
+        }
+        return dictionary!
+    }
+    
+    fileprivate func setWebURLs(_ URLs: NSMutableDictionary) {
+        objc_setAssociatedObject(base, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+    
+    fileprivate var imageTask: RetrieveImageTask? {
+        return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask
+    }
+    
+    fileprivate func setImageTask(_ task: RetrieveImageTask?) {
+        objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+}
+
+
+private var lastBackgroundURLKey: Void?
+private var backgroundImageTaskKey: Void?
+
+
+extension Kingfisher where Base: UIButton {
+    /**
+     Get the background image URL binded to this button for a specified state.
+     
+     - parameter state: The state that uses the specified background image.
+     
+     - returns: Current URL for background image.
+     */
+    public func backgroundWebURL(for state: UIControlState) -> URL? {
+        return backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL
+    }
+    
+    fileprivate func setBackgroundWebURL(_ url: URL?, for state: UIControlState) {
+        backgroundWebURLs[NSNumber(value:state.rawValue)] = url
+    }
+    
+    fileprivate var backgroundWebURLs: NSMutableDictionary {
+        var dictionary = objc_getAssociatedObject(base, &lastBackgroundURLKey) as? NSMutableDictionary
+        if dictionary == nil {
+            dictionary = NSMutableDictionary()
+            setBackgroundWebURLs(dictionary!)
+        }
+        return dictionary!
+    }
+    
+    fileprivate func setBackgroundWebURLs(_ URLs: NSMutableDictionary) {
+        objc_setAssociatedObject(base, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+    
+    fileprivate var backgroundImageTask: RetrieveImageTask? {
+        return objc_getAssociatedObject(base, &backgroundImageTaskKey) as? RetrieveImageTask
+    }
+    
+    fileprivate func setBackgroundImageTask(_ task: RetrieveImageTask?) {
+        objc_setAssociatedObject(base, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+    }
+}
diff --git a/iOS/Pods/MBProgressHUD/LICENSE b/iOS/Pods/MBProgressHUD/LICENSE
new file mode 100644 (file)
index 0000000..1c0d59b
--- /dev/null
@@ -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/iOS/Pods/MBProgressHUD/MBProgressHUD.h b/iOS/Pods/MBProgressHUD/MBProgressHUD.h
new file mode 100644 (file)
index 0000000..a7b54ee
--- /dev/null
@@ -0,0 +1,443 @@
+//
+//  MBProgressHUD.h
+//  Version 1.1.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 <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+@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)(void);
+
+
+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 that hasn't finished 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 that hasn't finished 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<MBProgressHUDDelegate> 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 <NSObject>
+
+@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;
+
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
+/**
+ * The blur effect style, when using MBProgressHUDBackgroundStyleBlur.
+ * Defaults to UIBlurEffectStyleLight.
+ */
+@property (nonatomic) UIBlurEffectStyle blurEffectStyle;
+#endif
+
+/**
+ * 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/iOS/Pods/MBProgressHUD/MBProgressHUD.m b/iOS/Pods/MBProgressHUD/MBProgressHUD.m
new file mode 100644 (file)
index 0000000..1bcf9e9
--- /dev/null
@@ -0,0 +1,1495 @@
+//
+// MBProgressHUD.m
+// Version 1.1.0
+// Created by Matej Bukovinski on 2.4.09.
+//
+
+#import "MBProgressHUD.h"
+#import <tgmath.h>
+
+
+#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]) {
+            MBProgressHUD *hud = (MBProgressHUD *)subview;
+            if (hud.hasFinished == NO) {
+                return hud;
+            }
+        }
+    }
+    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 {
+    // Cancel any scheduled hideDelayed: calls
+    [self.hideDelayTimer invalidate];
+
+    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<MBProgressHUDDelegate> 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.frame = 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 overlapping 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 and Border
+    CGFloat radius = (rect.size.height / 2) - 2;
+    CGContextMoveToPoint(context, 2, rect.size.height/2);
+    CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
+    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);
+    CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
+    CGContextDrawPath(context, kCGPathFillStroke);
+    
+    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 __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
+            _blurEffectStyle = UIBlurEffectStyleLight;
+#endif
+            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];
+    }
+}
+
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV
+
+- (void)setBlurEffectStyle:(UIBlurEffectStyle)blurEffectStyle {
+    if (_blurEffectStyle == blurEffectStyle) {
+        return;
+    }
+
+    _blurEffectStyle = blurEffectStyle;
+
+    [self updateForBackgroundStyle];
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////
+#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:self.blurEffectStyle];
+            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 (^)(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/iOS/Pods/MBProgressHUD/README.mdown b/iOS/Pods/MBProgressHUD/README.mdown
new file mode 100644 (file)
index 0000000..21ba843
--- /dev/null
@@ -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.
+
+[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/1-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/1.png)
+[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/2-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/2.png)
+[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/3-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/3.png)
+[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/4-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/4.png)
+[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/5-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/5.png)
+[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/6-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/6.png)
+[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/7-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/7.png)
+
+**NOTE:** The class has recently undergone 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.1.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.1.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 <MBProgressHUD/MBProgressHUD.h>`.
+
+## 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.label.text = @"Loading";
+[self doSomethingInBackgroundWithProgressCallback:^(float progress) {
+       hud.progress = progress;
+} completionCallback:^{
+       [hud hideAnimated: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.label.text = @"Loading";
+NSProgress *progress = [self doSomethingInBackgroundCompletion:^{
+       [hud hideAnimated:YES];
+}];
+hud.progressObject = progress;
+```
+
+Keep in mind that UI updates, inclining calls to MBProgressHUD should always be done on the main thread.
+
+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/iOS/Pods/MZDownloadManager/LICENSE b/iOS/Pods/MZDownloadManager/LICENSE
new file mode 100644 (file)
index 0000000..23f3f59
--- /dev/null
@@ -0,0 +1,25 @@
+Copyright (c) 2016, Arpad Goretity https://github.com/H2CO3/HCDownload.git
+Copyright (c) 2016, Muhammad Zeeshan https://github.com/mzeeshanid/MZDownloadManager.git
+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 the <organization> 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 <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/iOS/Pods/MZDownloadManager/MZDownloadManager/Classes/MZDownloadManager.swift b/iOS/Pods/MZDownloadManager/MZDownloadManager/Classes/MZDownloadManager.swift
new file mode 100644 (file)
index 0000000..ccdda6b
--- /dev/null
@@ -0,0 +1,446 @@
+//
+//  MZDownloadManager.swift
+//  MZDownloadManager
+//
+//  Created by Muhammad Zeeshan on 19/04/2016.
+//  Copyright © 2016 ideamakerz. All rights reserved.
+//
+
+import UIKit
+fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
+  switch (lhs, rhs) {
+  case let (l?, r?):
+    return l < r
+  case (nil, _?):
+    return true
+  default:
+    return false
+  }
+}
+
+fileprivate func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
+  switch (lhs, rhs) {
+  case let (l?, r?):
+    return l > r
+  default:
+    return rhs < lhs
+  }
+}
+
+
+@objc public protocol MZDownloadManagerDelegate: class {
+    /**A delegate method called each time whenever any download task's progress is updated
+     */
+    @objc func downloadRequestDidUpdateProgress(_ downloadModel: MZDownloadModel, index: Int)
+    /**A delegate method called when interrupted tasks are repopulated
+     */
+    @objc func downloadRequestDidPopulatedInterruptedTasks(_ downloadModel: [MZDownloadModel])
+    /**A delegate method called each time whenever new download task is start downloading
+     */
+    @objc optional func downloadRequestStarted(_ downloadModel: MZDownloadModel, index: Int)
+    /**A delegate method called each time whenever running download task is paused. If task is already paused the action will be ignored
+     */
+    @objc optional func downloadRequestDidPaused(_ downloadModel: MZDownloadModel, index: Int)
+    /**A delegate method called each time whenever any download task is resumed. If task is already downloading the action will be ignored
+     */
+    @objc optional func downloadRequestDidResumed(_ downloadModel: MZDownloadModel, index: Int)
+    /**A delegate method called each time whenever any download task is resumed. If task is already downloading the action will be ignored
+     */
+    @objc optional func downloadRequestDidRetry(_ downloadModel: MZDownloadModel, index: Int)
+    /**A delegate method called each time whenever any download task is cancelled by the user
+     */
+    @objc optional func downloadRequestCanceled(_ downloadModel: MZDownloadModel, index: Int)
+    /**A delegate method called each time whenever any download task is finished successfully
+     */
+    @objc optional func downloadRequestFinished(_ downloadModel: MZDownloadModel, index: Int)
+    /**A delegate method called each time whenever any download task is failed due to any reason
+     */
+    @objc optional func downloadRequestDidFailedWithError(_ error: NSError, downloadModel: MZDownloadModel, index: Int)
+    /**A delegate method called each time whenever specified destination does not exists. It will be called on the session queue. It provides the opportunity to handle error appropriately
+     */
+    @objc optional func downloadRequestDestinationDoestNotExists(_ downloadModel: MZDownloadModel, index: Int, location: URL)
+    
+}
+
+open class MZDownloadManager: NSObject {
+    
+    fileprivate var sessionManager: URLSession!
+    
+    fileprivate var backgroundSessionCompletionHandler: (() -> Void)?
+    
+    fileprivate let TaskDescFileNameIndex = 0
+    fileprivate let TaskDescFileURLIndex = 1
+    fileprivate let TaskDescFileDestinationIndex = 2
+    
+    fileprivate weak var delegate: MZDownloadManagerDelegate?
+    
+    open var downloadingArray: [MZDownloadModel] = []
+    
+    public convenience init(session sessionIdentifer: String, delegate: MZDownloadManagerDelegate) {
+        self.init()
+        
+        self.delegate = delegate
+        self.sessionManager = backgroundSession(identifier: sessionIdentifer)
+        self.populateOtherDownloadTasks()
+    }
+    
+    public convenience init(session sessionIdentifer: String, delegate: MZDownloadManagerDelegate, completion: (() -> Void)?) {
+        self.init(session: sessionIdentifer, delegate: delegate)
+        self.backgroundSessionCompletionHandler = completion
+    }
+    
+    fileprivate func backgroundSession(identifier: String) -> URLSession {
+        let sessionConfiguration = URLSessionConfiguration.background(withIdentifier: identifier)
+        let session = Foundation.URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)
+        return session
+    }
+}
+
+// MARK: Private Helper functions
+
+extension MZDownloadManager {
+    
+    fileprivate func downloadTasks() -> [URLSessionDownloadTask] {
+        var tasks: [URLSessionDownloadTask] = []
+        let semaphore : DispatchSemaphore = DispatchSemaphore(value: 0)
+        sessionManager.getTasksWithCompletionHandler { (dataTasks, uploadTasks, downloadTasks) -> Void in
+            tasks = downloadTasks
+            semaphore.signal()
+        }
+        
+        let _ = semaphore.wait(timeout: DispatchTime.distantFuture)
+        
+        debugPrint("MZDownloadManager: pending tasks \(tasks)")
+        
+        return tasks
+    }
+    
+    fileprivate func populateOtherDownloadTasks() {
+        
+        let downloadTasks = self.downloadTasks()
+        
+        for downloadTask in downloadTasks {
+            let taskDescComponents: [String] = downloadTask.taskDescription!.components(separatedBy: ",")
+            let fileName = taskDescComponents[TaskDescFileNameIndex]
+            let fileURL = taskDescComponents[TaskDescFileURLIndex]
+            let destinationPath = taskDescComponents[TaskDescFileDestinationIndex]
+            
+            let downloadModel = MZDownloadModel.init(fileName: fileName, fileURL: fileURL, destinationPath: destinationPath)
+            downloadModel.task = downloadTask
+            downloadModel.startTime = Date()
+            
+            if downloadTask.state == .running {
+                downloadModel.status = TaskStatus.downloading.description()
+                downloadingArray.append(downloadModel)
+            } else if(downloadTask.state == .suspended) {
+                downloadModel.status = TaskStatus.paused.description()
+                downloadingArray.append(downloadModel)
+            } else {
+                downloadModel.status = TaskStatus.failed.description()
+            }
+        }
+    }
+    
+    fileprivate func isValidResumeData(_ resumeData: Data?) -> Bool {
+        
+        guard resumeData != nil || resumeData?.count > 0 else {
+            return false
+        }
+        
+        do {
+            var resumeDictionary : AnyObject!
+            resumeDictionary = try PropertyListSerialization.propertyList(from: resumeData!, options: PropertyListSerialization.MutabilityOptions(), format: nil) as AnyObject!
+            var localFilePath = (resumeDictionary?["NSURLSessionResumeInfoLocalPath"] as? String)
+            
+            if localFilePath == nil || localFilePath?.count < 1 {
+                localFilePath = (NSTemporaryDirectory() as String) + (resumeDictionary["NSURLSessionResumeInfoTempFileName"] as! String)
+            }
+            
+            let fileManager : FileManager! = FileManager.default
+            debugPrint("resume data file exists: \(fileManager.fileExists(atPath: localFilePath! as String))")
+            return fileManager.fileExists(atPath: localFilePath! as String)
+        } catch let error as NSError {
+            debugPrint("resume data is nil: \(error)")
+            return false
+        }
+    }
+}
+
+extension MZDownloadManager: URLSessionDownloadDelegate {
+    
+    public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
+        for (index, downloadModel) in self.downloadingArray.enumerated() {
+            if downloadTask.isEqual(downloadModel.task) {
+                DispatchQueue.main.async(execute: { () -> Void in
+                    
+                    let receivedBytesCount = Double(downloadTask.countOfBytesReceived)
+                    let totalBytesCount = Double(downloadTask.countOfBytesExpectedToReceive)
+                    let progress = Float(receivedBytesCount / totalBytesCount)
+                    
+                    let taskStartedDate = downloadModel.startTime!
+                    let timeInterval = taskStartedDate.timeIntervalSinceNow
+                    let downloadTime = TimeInterval(-1 * timeInterval)
+                    
+                    let speed = Float(totalBytesWritten) / Float(downloadTime)
+                    
+                    let remainingContentLength = totalBytesExpectedToWrite - totalBytesWritten
+                    
+                    let remainingTime = remainingContentLength / Int64(speed)
+                    let hours = Int(remainingTime) / 3600
+                    let minutes = (Int(remainingTime) - hours * 3600) / 60
+                    let seconds = Int(remainingTime) - hours * 3600 - minutes * 60
+                    
+                    let totalFileSize = MZUtility.calculateFileSizeInUnit(totalBytesExpectedToWrite)
+                    let totalFileSizeUnit = MZUtility.calculateUnit(totalBytesExpectedToWrite)
+                    
+                    let downloadedFileSize = MZUtility.calculateFileSizeInUnit(totalBytesWritten)
+                    let downloadedSizeUnit = MZUtility.calculateUnit(totalBytesWritten)
+                    
+                    let speedSize = MZUtility.calculateFileSizeInUnit(Int64(speed))
+                    let speedUnit = MZUtility.calculateUnit(Int64(speed))
+                    
+                    downloadModel.remainingTime = (hours, minutes, seconds)
+                    downloadModel.file = (totalFileSize, totalFileSizeUnit as String)
+                    downloadModel.downloadedFile = (downloadedFileSize, downloadedSizeUnit as String)
+                    downloadModel.speed = (speedSize, speedUnit as String)
+                    downloadModel.progress = progress
+                    
+                    self.downloadingArray[index] = downloadModel
+                    
+                    self.delegate?.downloadRequestDidUpdateProgress(downloadModel, index: index)
+                })
+                break
+            }
+        }
+    }
+    
+    public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
+        for (index, downloadModel) in downloadingArray.enumerated() {
+            if downloadTask.isEqual(downloadModel.task) {
+                let fileName = downloadModel.fileName as NSString
+                let basePath = downloadModel.destinationPath == "" ? MZUtility.baseFilePath : downloadModel.destinationPath
+                let destinationPath = (basePath as NSString).appendingPathComponent(fileName as String)
+                
+                let fileManager : FileManager = FileManager.default
+                
+                //If all set just move downloaded file to the destination
+                if fileManager.fileExists(atPath: basePath) {
+                    let fileURL = URL(fileURLWithPath: destinationPath as String)
+                    debugPrint("directory path = \(destinationPath)")
+                    
+                    do {
+                        try fileManager.moveItem(at: location, to: fileURL)
+                    } catch let error as NSError {
+                        debugPrint("Error while moving downloaded file to destination path:\(error)")
+                        DispatchQueue.main.async(execute: { () -> Void in
+                            self.delegate?.downloadRequestDidFailedWithError?(error, downloadModel: downloadModel, index: index)
+                        })
+                    }
+                } else {
+                    //Opportunity to handle the folder doesnot exists error appropriately.
+                    //Move downloaded file to destination
+                    //Delegate will be called on the session queue
+                    //Otherwise blindly give error Destination folder does not exists
+                    
+                    if let _ = self.delegate?.downloadRequestDestinationDoestNotExists {
+                        self.delegate?.downloadRequestDestinationDoestNotExists?(downloadModel, index: index, location: location)
+                    } else {
+                        let error = NSError(domain: "FolderDoesNotExist", code: 404, userInfo: [NSLocalizedDescriptionKey : "Destination folder does not exists"])
+                        self.delegate?.downloadRequestDidFailedWithError?(error, downloadModel: downloadModel, index: index)
+                    }
+                }
+                
+                break
+            }
+        }
+    }
+    
+    public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
+        debugPrint("task id: \(task.taskIdentifier)")
+        /***** Any interrupted tasks due to any reason will be populated in failed state after init *****/
+        
+        DispatchQueue.main.async {
+            
+            let err = error as NSError?
+            
+            if (err?.userInfo[NSURLErrorBackgroundTaskCancelledReasonKey] as? NSNumber)?.intValue == NSURLErrorCancelledReasonUserForceQuitApplication || (err?.userInfo[NSURLErrorBackgroundTaskCancelledReasonKey] as? NSNumber)?.intValue == NSURLErrorCancelledReasonBackgroundUpdatesDisabled {
+                
+                let downloadTask = task as! URLSessionDownloadTask
+                let taskDescComponents: [String] = downloadTask.taskDescription!.components(separatedBy: ",")
+                let fileName = taskDescComponents[self.TaskDescFileNameIndex]
+                let fileURL = taskDescComponents[self.TaskDescFileURLIndex]
+                let destinationPath = taskDescComponents[self.TaskDescFileDestinationIndex]
+                
+                let downloadModel = MZDownloadModel.init(fileName: fileName, fileURL: fileURL, destinationPath: destinationPath)
+                downloadModel.status = TaskStatus.failed.description()
+                downloadModel.task = downloadTask
+                
+                let resumeData = err?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data
+                
+                var newTask = downloadTask
+                if self.isValidResumeData(resumeData) == true {
+                    newTask = self.sessionManager.downloadTask(withResumeData: resumeData!)
+                } else {
+                    newTask = self.sessionManager.downloadTask(with: URL(string: fileURL as String)!)
+                }
+                
+                newTask.taskDescription = downloadTask.taskDescription
+                downloadModel.task = newTask
+                
+                self.downloadingArray.append(downloadModel)
+                
+                self.delegate?.downloadRequestDidPopulatedInterruptedTasks(self.downloadingArray)
+                
+            } else {
+                for(index, object) in self.downloadingArray.enumerated() {
+                    let downloadModel = object
+                    if task.isEqual(downloadModel.task) {
+                        if err?.code == NSURLErrorCancelled || err == nil {
+                            self.downloadingArray.remove(at: index)
+                            
+                            if err == nil {
+                                self.delegate?.downloadRequestFinished?(downloadModel, index: index)
+                            } else {
+                                self.delegate?.downloadRequestCanceled?(downloadModel, index: index)
+                            }
+                            
+                        } else {
+                            let resumeData = err?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data
+                            var newTask = task
+                            if self.isValidResumeData(resumeData) == true {
+                                newTask = self.sessionManager.downloadTask(withResumeData: resumeData!)
+                            } else {
+                                newTask = self.sessionManager.downloadTask(with: URL(string: downloadModel.fileURL)!)
+                            }
+                            
+                            newTask.taskDescription = task.taskDescription
+                            downloadModel.status = TaskStatus.failed.description()
+                            downloadModel.task = newTask as? URLSessionDownloadTask
+                            
+                            self.downloadingArray[index] = downloadModel
+                            
+                            if let error = err {
+                                self.delegate?.downloadRequestDidFailedWithError?(error, downloadModel: downloadModel, index: index)
+                            } else {
+                                let error: NSError = NSError(domain: "MZDownloadManagerDomain", code: 1000, userInfo: [NSLocalizedDescriptionKey : "Unknown error occurred"])
+                                
+                                self.delegate?.downloadRequestDidFailedWithError?(error, downloadModel: downloadModel, index: index)
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    
+    public func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
+        if let backgroundCompletion = self.backgroundSessionCompletionHandler {
+            DispatchQueue.main.async(execute: {
+                backgroundCompletion()
+            })
+        }
+        debugPrint("All tasks are finished")
+    }
+}
+
+//MARK: Public Helper Functions
+
+extension MZDownloadManager {
+    
+    @objc public func addDownloadTask(_ fileName: String, fileURL: String, destinationPath: String) {
+        
+        let url = URL(string: fileURL as String)!
+        let request = URLRequest(url: url)
+        
+        let downloadTask = sessionManager.downloadTask(with: request)
+        downloadTask.taskDescription = [fileName, fileURL, destinationPath].joined(separator: ",")
+        downloadTask.resume()
+        
+        debugPrint("session manager:\(sessionManager) url:\(url) request:\(request)")
+        
+        let downloadModel = MZDownloadModel.init(fileName: fileName, fileURL: fileURL, destinationPath: destinationPath)
+        downloadModel.startTime = Date()
+        downloadModel.status = TaskStatus.downloading.description()
+        downloadModel.task = downloadTask
+        
+        downloadingArray.append(downloadModel)
+        delegate?.downloadRequestStarted?(downloadModel, index: downloadingArray.count - 1)
+    }
+    
+    @objc public func addDownloadTask(_ fileName: String, fileURL: String) {
+        addDownloadTask(fileName, fileURL: fileURL, destinationPath: "")
+    }
+    
+    @objc public func pauseDownloadTaskAtIndex(_ index: Int) {
+        
+        let downloadModel = downloadingArray[index]
+        
+        guard downloadModel.status != TaskStatus.paused.description() else {
+            return
+        }
+        
+        let downloadTask = downloadModel.task
+        downloadTask!.suspend()
+        downloadModel.status = TaskStatus.paused.description()
+        downloadModel.startTime = Date()
+        
+        downloadingArray[index] = downloadModel
+        
+        delegate?.downloadRequestDidPaused?(downloadModel, index: index)
+    }
+    
+    @objc public func resumeDownloadTaskAtIndex(_ index: Int) {
+        
+        let downloadModel = downloadingArray[index]
+        
+        guard downloadModel.status != TaskStatus.downloading.description() else {
+            return
+        }
+        
+        let downloadTask = downloadModel.task
+        downloadTask!.resume()
+        downloadModel.status = TaskStatus.downloading.description()
+        
+        downloadingArray[index] = downloadModel
+        
+        delegate?.downloadRequestDidResumed?(downloadModel, index: index)
+    }
+    
+    @objc public func retryDownloadTaskAtIndex(_ index: Int) {
+        let downloadModel = downloadingArray[index]
+        
+        guard downloadModel.status != TaskStatus.downloading.description() else {
+            return
+        }
+        
+        let downloadTask = downloadModel.task
+        
+        downloadTask!.resume()
+        downloadModel.status = TaskStatus.downloading.description()
+        downloadModel.startTime = Date()
+        downloadModel.task = downloadTask
+        
+        downloadingArray[index] = downloadModel
+    }
+    
+    @objc public func cancelTaskAtIndex(_ index: Int) {
+        let downloadInfo = downloadingArray[index]
+        let downloadTask = downloadInfo.task
+        downloadTask!.cancel()
+    }
+    
+    @objc public func presentNotificationForDownload(_ notifAction: String, notifBody: String) {
+        let application = UIApplication.shared
+        let applicationState = application.applicationState
+        
+        if applicationState == UIApplicationState.background {
+            let localNotification = UILocalNotification()
+            localNotification.alertBody = notifBody
+            localNotification.alertAction = notifAction
+            localNotification.soundName = UILocalNotificationDefaultSoundName
+            localNotification.applicationIconBadgeNumber += 1
+            application.presentLocalNotificationNow(localNotification)
+        }
+    }
+}
diff --git a/iOS/Pods/MZDownloadManager/MZDownloadManager/Classes/MZDownloadModel.swift b/iOS/Pods/MZDownloadManager/MZDownloadManager/Classes/MZDownloadModel.swift
new file mode 100644 (file)
index 0000000..fd82170
--- /dev/null
@@ -0,0 +1,63 @@
+//
+//  MZDownloadModel.swift
+//  MZDownloadManager
+//
+//  Created by Muhammad Zeeshan on 19/04/2016.
+//  Copyright © 2016 ideamakerz. All rights reserved.
+//
+
+import UIKit
+
+public enum TaskStatus: Int {
+    case unknown, gettingInfo, downloading, paused, failed
+    
+    public func description() -> String {
+        switch self {
+        case .gettingInfo:
+            return "GettingInfo"
+        case .downloading:
+            return "Downloading"
+        case .paused:
+            return "Paused"
+        case .failed:
+            return "Failed"
+        default:
+            return "Unknown"
+        }
+    }
+}
+
+open class MZDownloadModel: NSObject {
+    
+    open var fileName: String!
+    open var fileURL: String!
+    open var status: String = TaskStatus.gettingInfo.description()
+    
+    open var file: (size: Float, unit: String)?
+    open var downloadedFile: (size: Float, unit: String)?
+    
+    open var remainingTime: (hours: Int, minutes: Int, seconds: Int)?
+    
+    open var speed: (speed: Float, unit: String)?
+    
+    open var progress: Float = 0
+    
+    open var task: URLSessionDownloadTask?
+    
+    open var startTime: Date?
+    
+    fileprivate(set) open var destinationPath: String = ""
+    
+    fileprivate convenience init(fileName: String, fileURL: String) {
+        self.init()
+        
+        self.fileName = fileName
+        self.fileURL = fileURL
+    }
+    
+    convenience init(fileName: String, fileURL: String, destinationPath: String) {
+        self.init(fileName: fileName, fileURL: fileURL)
+        
+        self.destinationPath = destinationPath
+    }
+}
diff --git a/iOS/Pods/MZDownloadManager/MZDownloadManager/Classes/MZUtility.swift b/iOS/Pods/MZDownloadManager/MZDownloadManager/Classes/MZUtility.swift
new file mode 100644 (file)
index 0000000..000e67a
--- /dev/null
@@ -0,0 +1,113 @@
+//
+//  MZUtility.swift
+//  MZDownloadManager
+//
+//  Created by Muhammad Zeeshan on 22/10/2014.
+//  Copyright (c) 2014 ideamakerz. All rights reserved.
+//
+
+import UIKit
+
+open class MZUtility: NSObject {
+    
+    @objc open static let DownloadCompletedNotif: String = {
+        return "com.MZDownloadManager.DownloadCompletedNotif"
+    }()
+    
+    @objc open static let baseFilePath: String = {
+        return (NSHomeDirectory() as NSString).appendingPathComponent("Documents") as String
+    }()
+
+    @objc open class func getUniqueFileNameWithPath(_ filePath : NSString) -> NSString {
+        let fullFileName        : NSString = filePath.lastPathComponent as NSString
+        let fileName            : NSString = fullFileName.deletingPathExtension as NSString
+        let fileExtension       : NSString = fullFileName.pathExtension as NSString
+        var suggestedFileName   : NSString = fileName
+        
+        var isUnique            : Bool = false
+        var fileNumber          : Int = 0
+        
+        let fileManger          : FileManager = FileManager.default
+        
+        repeat {
+            var fileDocDirectoryPath : NSString?
+            
+            if fileExtension.length > 0 {
+                fileDocDirectoryPath = "\(filePath.deletingLastPathComponent)/\(suggestedFileName).\(fileExtension)" as NSString?
+            } else {
+                fileDocDirectoryPath = "\(filePath.deletingLastPathComponent)/\(suggestedFileName)" as NSString?
+            }
+            
+            let isFileAlreadyExists : Bool = fileManger.fileExists(atPath: fileDocDirectoryPath! as String)
+            
+            if isFileAlreadyExists {
+                fileNumber += 1
+                suggestedFileName = "\(fileName)(\(fileNumber))" as NSString
+            } else {
+                isUnique = true
+                if fileExtension.length > 0 {
+                    suggestedFileName = "\(suggestedFileName).\(fileExtension)" as NSString
+                }
+            }
+        
+        } while isUnique == false
+        
+        return suggestedFileName
+    }
+    
+    @objc open class func calculateFileSizeInUnit(_ contentLength : Int64) -> Float {
+        let dataLength : Float64 = Float64(contentLength)
+        if dataLength >= (1024.0*1024.0*1024.0) {
+            return Float(dataLength/(1024.0*1024.0*1024.0))
+        } else if dataLength >= 1024.0*1024.0 {
+            return Float(dataLength/(1024.0*1024.0))
+        } else if dataLength >= 1024.0 {
+            return Float(dataLength/1024.0)
+        } else {
+            return Float(dataLength)
+        }
+    }
+    
+    @objc open class func calculateUnit(_ contentLength : Int64) -> NSString {
+        if(contentLength >= (1024*1024*1024)) {
+            return "GB"
+        } else if contentLength >= (1024*1024) {
+            return "MB"
+        } else if contentLength >= 1024 {
+            return "KB"
+        } else {
+            return "Bytes"
+        }
+    }
+    
+    @objc open class func addSkipBackupAttributeToItemAtURL(_ docDirectoryPath : NSString) -> Bool {
+        let url : URL = URL(fileURLWithPath: docDirectoryPath as String)
+        let fileManager = FileManager.default
+        if fileManager.fileExists(atPath: url.path) {
+            
+            do {
+                try (url as NSURL).setResourceValue(NSNumber(value: true as Bool), forKey: URLResourceKey.isExcludedFromBackupKey)
+                return true
+            } catch let error as NSError {
+                print("Error excluding \(url.lastPathComponent) from backup \(error)")
+                return false
+            }
+
+        } else {
+            return false
+        }
+    }
+    
+    @objc open class func getFreeDiskspace() -> NSNumber? {
+        let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
+        let systemAttributes: AnyObject?
+        do {
+            systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: documentDirectoryPath.last!) as AnyObject?
+            let freeSize = systemAttributes?[FileAttributeKey.systemFreeSize] as? NSNumber
+            return freeSize
+        } catch let error as NSError {
+            print("Error Obtaining System Memory Info: Domain = \(error.domain), Code = \(error.code)")
+            return nil;
+        }
+    }
+}
diff --git a/iOS/Pods/MZDownloadManager/README.md b/iOS/Pods/MZDownloadManager/README.md
new file mode 100644 (file)
index 0000000..9998a7f
--- /dev/null
@@ -0,0 +1,70 @@
+# MZDownloadManager
+
+[![CI Status](http://img.shields.io/travis/mzeeshanid/MZDownloadManager.svg?style=flat)](https://travis-ci.org/mzeeshanid/MZDownloadManager)
+[![Version](https://img.shields.io/cocoapods/v/MZDownloadManager.svg?style=flat)](http://cocoapods.org/pods/MZDownloadManager)
+[![License](https://img.shields.io/cocoapods/l/MZDownloadManager.svg?style=flat)](http://cocoapods.org/pods/MZDownloadManager)
+[![Platform](https://img.shields.io/cocoapods/p/MZDownloadManager.svg?style=flat)](http://cocoapods.org/pods/MZDownloadManager)
+
+![mzdownload manager hero](https://cloud.githubusercontent.com/assets/2767152/18860606/655c21ea-8498-11e6-9bf9-05b5405d119a.jpg)
+
+## Features
+
+This download manager uses the iOS 7 NSURLSession api to download files.
++ Can download large files if app is in background.
++ Can download files if app is in background.
++ Can download multiple files at a time.
++ It can resume interrupted downloads.
++ User can also pause the download.
++ User can retry any download if any error occurred during download.
+
+<h3>Screencast:</h3>
+http://screencast.com/t/Rzm0xoRjGF
+
+## Usage
+
+To run the example project, clone the repo, and run `pod install` from the Example directory first.
+
+## Requirements
+
++ Xcode 8
++ Minimum deployment target is iOS 9.
++ For resuming downloads server must have resuming support.
+
+## Installation
+
+MZDownloadManager is available through [CocoaPods](http://cocoapods.org). To install
+it, simply add the following line to your Podfile:
+
+```ruby
+pod "MZDownloadManager"
+```
+
+## Update
+
+New helper functions added to support downloading at custom path. Example project is also updated about the usage.
+
+To download file at custom path you can use the following instance method of MZDownloadManager:
+    
+```public func addDownloadTask(fileName: String, fileURL: String, destinationPath: String)```
+      
+#### When download completes:
+    
+* It will check if the destination folder still exists then it will move the downloaded file at the specified destination and call success delegate method.
+* If destination folder does not exists, following delegate method will provide an opportunity to handle the downloaded file appropriately.
+
+```optional func downloadRequestDestinationDoestNotExists(downloadModel: MZDownloadModel, index: Int, location: NSURL)```
+
+* If the above delegate method is not implemented then it will just called the failure method.
+
+> Important: This delegate method will be called on the session's queue.
+
+## Author
+
+Muhammad Zeeshan, mzeeshanid@yahoo.com
+
+If you find MZDownloadManager userful consider donating thanks ;)</br>
+[![Donate button](https://www.paypalobjects.com/en_US/DE/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BMKTVHYK6PUUG)
+
+## License
+
+MZDownloadManager is available under the BSD license. See the LICENSE file for more info.
diff --git a/iOS/Pods/Manifest.lock b/iOS/Pods/Manifest.lock
new file mode 100644 (file)
index 0000000..4338c7c
--- /dev/null
@@ -0,0 +1,180 @@
+PODS:
+  - AEXML (4.2.2)
+  - Alamofire (4.6.0)
+  - AlamofireActivityLogger (2.4.0):
+    - Alamofire (~> 4.5)
+  - Crashlytics (3.10.0):
+    - Fabric (~> 1.7.3)
+  - Fabric (1.7.3)
+  - Firebase/Core (5.9.0):
+    - Firebase/CoreOnly
+    - FirebaseAnalytics (= 5.2.0)
+  - Firebase/CoreOnly (5.9.0):
+    - FirebaseCore (= 5.1.4)
+  - Firebase/Messaging (5.9.0):
+    - Firebase/CoreOnly
+    - FirebaseMessaging (= 3.1.2)
+  - FirebaseAnalytics (5.2.0):
+    - FirebaseCore (~> 5.1)
+    - FirebaseInstanceID (~> 3.2)
+    - GoogleAppMeasurement (~> 5.2)
+    - GoogleUtilities/AppDelegateSwizzler (~> 5.2)
+    - GoogleUtilities/MethodSwizzler (~> 5.2)
+    - GoogleUtilities/Network (~> 5.2)
+    - "GoogleUtilities/NSData+zlib (~> 5.2)"
+    - nanopb (~> 0.3)
+  - FirebaseCore (5.1.4):
+    - GoogleUtilities/Logger (~> 5.2)
+  - FirebaseInstanceID (3.2.2):
+    - FirebaseCore (~> 5.1)
+    - GoogleUtilities/Environment (~> 5.3)
+    - GoogleUtilities/UserDefaults (~> 5.3)
+  - FirebaseMessaging (3.1.2):
+    - FirebaseCore (~> 5.0)
+    - FirebaseInstanceID (~> 3.0)
+    - GoogleUtilities/Reachability (~> 5.2)
+    - Protobuf (~> 3.1)
+  - FolioReaderKit (1.3.0):
+    - AEXML (= 4.2.2)
+    - FontBlaster (= 4.0.1)
+    - JSQWebViewController (= 6.0.0)
+    - MenuItemKit (= 3.0.0)
+    - RealmSwift (= 3.1.1)
+    - SSZipArchive (= 2.1.1)
+    - ZFDragableModalTransition (= 0.6)
+  - FontBlaster (4.0.1)
+  - GoogleAppMeasurement (5.2.0):
+    - GoogleUtilities/AppDelegateSwizzler (~> 5.2)
+    - GoogleUtilities/MethodSwizzler (~> 5.2)
+    - GoogleUtilities/Network (~> 5.2)
+    - "GoogleUtilities/NSData+zlib (~> 5.2)"
+    - nanopb (~> 0.3)
+  - GoogleUtilities/AppDelegateSwizzler (5.3.0):
+    - GoogleUtilities/Environment
+    - GoogleUtilities/Logger
+    - GoogleUtilities/Network
+  - GoogleUtilities/Environment (5.3.0)
+  - GoogleUtilities/Logger (5.3.0):
+    - GoogleUtilities/Environment
+  - GoogleUtilities/MethodSwizzler (5.3.0):
+    - GoogleUtilities/Logger
+  - GoogleUtilities/Network (5.3.0):
+    - GoogleUtilities/Logger
+    - "GoogleUtilities/NSData+zlib"
+    - GoogleUtilities/Reachability
+  - "GoogleUtilities/NSData+zlib (5.3.0)"
+  - GoogleUtilities/Reachability (5.3.0):
+    - GoogleUtilities/Logger
+  - GoogleUtilities/UserDefaults (5.3.0):
+    - GoogleUtilities/Logger
+  - JSQWebViewController (6.0.0)
+  - Kingfisher (4.7.0)
+  - MatomoTracker (5.2.0):
+    - MatomoTracker/Core (= 5.2.0)
+  - MatomoTracker/Core (5.2.0)
+  - MBProgressHUD (1.1.0)
+  - MenuItemKit (3.0.0)
+  - MZDownloadManager (3.4)
+  - nanopb (0.3.8):
+    - nanopb/decode (= 0.3.8)
+    - nanopb/encode (= 0.3.8)
+  - nanopb/decode (0.3.8)
+  - nanopb/encode (0.3.8)
+  - OAuthSwift (1.2.2)
+  - Protobuf (3.6.1)
+  - Realm (3.1.1):
+    - Realm/Headers (= 3.1.1)
+  - Realm/Headers (3.1.1)
+  - RealmSwift (3.1.1):
+    - Realm (= 3.1.1)
+  - SideMenu (3.1.5)
+  - SSZipArchive (2.1.1)
+  - SwiftKeychainWrapper (3.0.1)
+  - Toast-Swift (3.0.1)
+  - ZFDragableModalTransition (0.6)
+
+DEPENDENCIES:
+  - Alamofire
+  - AlamofireActivityLogger
+  - Crashlytics
+  - Fabric
+  - Firebase/Core
+  - Firebase/Messaging
+  - FolioReaderKit
+  - Kingfisher
+  - MatomoTracker
+  - MBProgressHUD
+  - MZDownloadManager
+  - OAuthSwift
+  - SideMenu
+  - SwiftKeychainWrapper
+  - Toast-Swift
+
+SPEC REPOS:
+  https://github.com/cocoapods/specs.git:
+    - AEXML
+    - Alamofire
+    - AlamofireActivityLogger
+    - Crashlytics
+    - Fabric
+    - Firebase
+    - FirebaseAnalytics
+    - FirebaseCore
+    - FirebaseInstanceID
+    - FirebaseMessaging
+    - FolioReaderKit
+    - FontBlaster
+    - GoogleAppMeasurement
+    - GoogleUtilities
+    - JSQWebViewController
+    - Kingfisher
+    - MatomoTracker
+    - MBProgressHUD
+    - MenuItemKit
+    - MZDownloadManager
+    - nanopb
+    - OAuthSwift
+    - Protobuf
+    - Realm
+    - RealmSwift
+    - SideMenu
+    - SSZipArchive
+    - SwiftKeychainWrapper
+    - Toast-Swift
+    - ZFDragableModalTransition
+
+SPEC CHECKSUMS:
+  AEXML: 5ebafc1b75e0bcf0f1b09b8ca8fed2d5a199479b
+  Alamofire: f41a599bd63041760b26d393ec1069d9d7b917f4
+  AlamofireActivityLogger: bff49b2cde8886e1ccb929c699f915472867fdda
+  Crashlytics: a989ef242b66605577a425733797f810bd2725eb
+  Fabric: bb495bb9a7a7677c6d03a1f8b83d95bc49b47e41
+  Firebase: 383fa29aca93e371cab776b48a5c66544d3c2003
+  FirebaseAnalytics: 831f1f127f4a75698e9875a87bf7e2668730d953
+  FirebaseCore: 2a84b6b325792a4319ef71ee18819dcba08d2fd7
+  FirebaseInstanceID: 78ba376fcd5b94c001f9999b2cbd3d1f1e56e78d
+  FirebaseMessaging: d6feeb06218d2675b4149b0ada925a6b707a74cf
+  FolioReaderKit: 162ea6b40cdb26d54730b01bce4a7bb81d4ac961
+  FontBlaster: 84229df8e3a7a6dacb190565bc543747a0c323f9
+  GoogleAppMeasurement: 2b3a023a61239c8d002e6e4fcf4abce8eddce0e0
+  GoogleUtilities: 760ccb53b7c7f40f9c02d8c241f76f841a7a6162
+  JSQWebViewController: 51041569b75d19dbb6b7fe0b7ae053c32409a9be
+  Kingfisher: da6b005aa96d37698e3e4f1ccfe96a5b9bbf27d6
+  MatomoTracker: d178d4847626738303e7ec507425c966e5eb8e85
+  MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
+  MenuItemKit: d9b539b28fdbbd7980d6f3668b9cd6daefeabd9b
+  MZDownloadManager: 8e9f186b2e804ffaeaf782c0cd3e22f27625cb7b
+  nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
+  OAuthSwift: 27b34fe80b76b67cd8f45571e0be2432bc9f6de1
+  Protobuf: 1eb9700044745f00181c136ef21b8ff3ad5a0fd5
+  Realm: 42d1c38a5b1bbcc828b48a7ce702cb86fc68adf4
+  RealmSwift: d31937ca6a6ee54acde64ec839523c0a3c04924b
+  SideMenu: 47dbf9e4d878062d8994aed43f6e4bf6c1fea30b
+  SSZipArchive: 14401ade5f8e82aba1ff03e9f88e9de60937ae60
+  SwiftKeychainWrapper: 38952a3636320ae61bad3513cadd870929de7a4a
+  Toast-Swift: ba0b01b095aada709d915f9beb1d38184a2721c3
+  ZFDragableModalTransition: 0d294eaaba6edfcb9839595de765f9ca06a4b524
+
+PODFILE CHECKSUM: 6437ef7c58988d6cdab4898dcb7ef0e1320aa58a
+
+COCOAPODS: 1.5.3
diff --git a/iOS/Pods/MatomoTracker/LICENSE.md b/iOS/Pods/MatomoTracker/LICENSE.md
new file mode 100644 (file)
index 0000000..e662c78
--- /dev/null
@@ -0,0 +1,5 @@
+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/iOS/Pods/MatomoTracker/MatomoTracker/Application.swift b/iOS/Pods/MatomoTracker/MatomoTracker/Application.swift
new file mode 100644 (file)
index 0000000..b988363
--- /dev/null
@@ -0,0 +1,53 @@
+public struct Application {
+    /// Creates an returns a new application object representing the current application
+    public static func makeCurrentApplication() -> Application {
+        let displayName = bundleDisplayNameForCurrentApplication()
+        let name = bundleNameForCurrentApplication()
+        let identifier = bundleIdentifierForCurrentApplication()
+        let version = bundleVersionForCurrentApplication()
+        let shortVersion = bundleShortVersionForCurrentApplication()
+        return Application(bundleDisplayName: displayName, bundleName: name, bundleIdentifier: identifier, bundleVersion: version, bundleShortVersion: shortVersion)
+    }
+    
+    /// The name of your app as displayed on the homescreen i.e. "My App"
+    public let bundleDisplayName: String?
+    
+    /// The bundle name of your app i.e. "my-app"
+    public let bundleName: String?
+    
+    /// The bundle identifier of your app i.e. "com.my-company.my-app"
+    public let bundleIdentifier: String?
+    
+    /// The bundle version a.k.a. build number as String i.e. "149"
+    public let bundleVersion: String?
+    
+    /// The app version as String i.e. "1.0.1"
+    public let bundleShortVersion: String?
+}
+
+extension Application {
+    /// Returns the name of the app as displayed on the homescreen
+    private static func bundleDisplayNameForCurrentApplication() -> String? {
+        return Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String
+    }
+    
+    /// Returns the bundle name of the app
+    private static func bundleNameForCurrentApplication() -> String? {
+        return Bundle.main.infoDictionary?["CFBundleName"] as? String
+    }
+    
+    /// Returns the bundle identifier
+    private static func bundleIdentifierForCurrentApplication() -> String? {
+        return Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String
+    }
+    
+    /// Returns the bundle version
+    private static func bundleVersionForCurrentApplication() -> String? {
+        return Bundle.main.infoDictionary?["CFBundleVersion"] as? String
+    }
+    
+    /// Returns the app version
+    private static func bundleShortVersionForCurrentApplication() -> String? {
+        return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/CustomDimension.swift b/iOS/Pods/MatomoTracker/MatomoTracker/CustomDimension.swift
new file mode 100644 (file)
index 0000000..38dece0
--- /dev/null
@@ -0,0 +1,15 @@
+import Foundation
+
+/// For more information on custom dimensions visit https://piwik.org/docs/custom-dimensions/
+public struct CustomDimension {
+    /// The index of the dimension. A dimension with this index must be setup in the Matomo backend.
+    let index: Int
+    
+    /// The value you want to set for this dimension.
+    let value: String
+    
+    public init(index: Int, value: String) {
+      self.index = index
+      self.value = value
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/CustomVariable.swift b/iOS/Pods/MatomoTracker/MatomoTracker/CustomVariable.swift
new file mode 100644 (file)
index 0000000..99dbe51
--- /dev/null
@@ -0,0 +1,15 @@
+
+import Foundation
+
+
+/// See Custom Variable documentation here: https://piwik.org/docs/custom-variables/
+public struct CustomVariable {
+    /// The index of the variable.
+    let index: UInt
+
+    /// The name of the variable.
+    let name: String
+    
+    /// The value of the variable.
+    let value: String
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/Device.swift b/iOS/Pods/MatomoTracker/MatomoTracker/Device.swift
new file mode 100644 (file)
index 0000000..9432b64
--- /dev/null
@@ -0,0 +1,196 @@
+import Foundation
+
+final public class Device: NSObject {
+    /// Creates an returns a new device object representing the current device
+    @objc public static func makeCurrentDevice() ->  Device {
+        let platform = currentPlatform()
+        let humanReadablePlatformName = humanReadablePlatformNameForCurrentDevice()
+        let os = osVersionForCurrentDevice()
+        let screenSize = screenSizeForCurrentDevice()
+        let nativeScreenSize = nativeScreenSizeForCurrentDevice()
+        return Device(platform: platform,
+                      humanReadablePlatformName: humanReadablePlatformName,
+                      osVersion: os,
+                      screenSize: screenSize,
+                      nativeScreenSize: nativeScreenSize)
+    }
+    
+    /// The platform name of the device i.e. "iPhone1,1" or "iPad3,6"
+    @objc public let platform: String
+    
+    /// A human readable version of the platform name i.e. "iPhone 6 Plus" or "iPad Air 2 (WiFi)"
+    /// Will be nil if no human readable string was found.
+    @objc public let humanReadablePlatformName: String?
+    
+    /// The version number of the OS as String i.e. "1.2" or "9.4"
+    @objc public let osVersion: String
+    
+    /// The screen size
+    @objc public let screenSize: CGSize
+    
+    /// The native screen size
+    /// Will be CGSize.zero if the value is not defined on the running platorm.
+    @objc public let nativeScreenSize: CGSize
+
+    required public init(platform: String, humanReadablePlatformName: String? = nil, osVersion: String, screenSize: CGSize, nativeScreenSize: CGSize? = nil) {
+        self.platform = platform
+        self.humanReadablePlatformName = humanReadablePlatformName
+        self.osVersion = osVersion
+        self.screenSize = screenSize
+        self.nativeScreenSize = nativeScreenSize != nil ? nativeScreenSize! : CGSize.zero
+
+        super.init()
+    }
+}
+
+extension Device {
+    /// The platform name of the current device i.e. "iPhone1,1" or "iPad3,6"
+    private static func currentPlatform() -> String  {
+        var size = 0
+        sysctlbyname("hw.machine", nil, &size, nil, 0)
+        var machine = [CChar](repeating: 0,  count: Int(size))
+        sysctlbyname("hw.machine", &machine, &size, nil, 0)
+        return String(cString: machine)
+    }
+    
+    /// Returns a human readable version of the current platform name i.e. "iPhone 6 Plus" or "iPad Air 2 (WiFi)"
+    /// Will return nil, if no human readable string could be found for the current platform
+    private static func humanReadablePlatformNameForCurrentDevice() -> String? {
+        let platform = currentPlatform()
+        switch platform {
+            
+        // iPhone
+        case "iPhone1,1":    return "iPhone 1G"
+        case "iPhone1,2":    return "iPhone 3G"
+        case "iPhone2,1":    return "iPhone 3GS"
+        case "iPhone3,1":    return "iPhone 4"
+        case "iPhone3,2":    return "iPhone 4 (Revision A)"
+        case "iPhone3,3":    return "Verizon iPhone 4"
+        case "iPhone4,1":    return "iPhone 4S"
+        case "iPhone5,1":    return "iPhone 5 (GSM)"
+        case "iPhone5,2":    return "iPhone 5 (GSM+CDMA)"
+        case "iPhone5,3":    return "iPhone 5c (GSM)"
+        case "iPhone5,4":    return "iPhone 5c (Global)"
+        case "iPhone6,1":    return "iPhone 5s (GSM)"
+        case "iPhone6,2":    return "iPhone 5s (Global)"
+        case "iPhone7,1":    return "iPhone 6 Plus"
+        case "iPhone7,2":    return "iPhone 6"
+        case "iPhone8,1":    return "iPhone 6s"
+        case "iPhone8,2":    return "iPhone 6s Plus"
+        case "iPhone8,4":    return "iPhone SE"
+        case "iPhone9,1":    return "iPhone 7 (GSM+CDMA)"
+        case "iPhone9,2":    return "iPhone 7 Plus (GSM+CDMA)"
+        case "iPhone9,3":    return "iPhone 7 (Global)"
+        case "iPhone9,4":    return "iPhone 7 Plus (Global)"
+        case "iPhone10,1":   return "iPhone 8 (GSM+CDMA)"
+        case "iPhone10,2":   return "iPhone 8 Plus (GSM+CDMA)"
+        case "iPhone10,3":   return "iPhone X (GSM+CDMA)"
+        case "iPhone10,4":   return "iPhone 8 (Global)"
+        case "iPhone10,5":   return "iPhone 8 Plus (Global)"
+        case "iPhone10,6":   return "iPhone X (Global)"
+            
+        // iPod
+        case "iPod1,1":      return "iPod Touch 1G"
+        case "iPod2,1":      return "iPod Touch 2G"
+        case "iPod3,1":      return "iPod Touch 3G"
+        case "iPod4,1":      return "iPod Touch 4G"
+        case "iPod5,1":      return "iPod Touch 5G"
+        case "iPod7,1":      return "iPod Touch 6G"
+            
+        // iPad
+        case "iPad1,1":      return "iPad 1"
+        case "iPad2,1":      return "iPad 2 (WiFi)"
+        case "iPad2,2":      return "iPad 2 (GSM)"
+        case "iPad2,3":      return "iPad 2 (CDMA)"
+        case "iPad2,4":      return "iPad 2 (WiFi)"
+        case "iPad2,5":      return "iPad Mini 1 (WiFi)"
+        case "iPad2,6":      return "iPad Mini 1 (GSM)"
+        case "iPad2,7":      return "iPad Mini 1 (GSM+CDMA)"
+        case "iPad3,1":      return "iPad 3 (WiFi)"
+        case "iPad3,2":      return "iPad 3 (GSM+CDMA)"
+        case "iPad3,3":      return "iPad 3 (GSM)"
+        case "iPad3,4":      return "iPad 4 (WiFi)"
+        case "iPad3,5":      return "iPad 4 (GSM)"
+        case "iPad3,6":      return "iPad 4 (GSM+CDMA)"
+        case "iPad4,1":      return "iPad Air 1 (WiFi)"
+        case "iPad4,2":      return "iPad Air 1 (Cellular)"
+        case "iPad4,3":      return "iPad Air"
+        case "iPad4,4":      return "iPad Mini 2 (WiFi)"
+        case "iPad4,5":      return "iPad Mini 2 (Cellular)"
+        case "iPad4,6":      return "iPad Mini 2 (Rev)"
+        case "iPad4,7":      return "iPad Mini 3 (WiFi)"
+        case "iPad4,8":      return "iPad Mini 3 (A1600)"
+        case "iPad4,9":      return "iPad Mini 3 (A1601)"
+        case "iPad5,1":      return "iPad Mini 4 (WiFi)"
+        case "iPad5,2":      return "iPad Mini 4 (Cellular)"
+        case "iPad5,3":      return "iPad Air 2 (WiFi)"
+        case "iPad5,4":      return "iPad Air 2 (Cellular)"
+        case "iPad6,3":      return "iPad Pro 9.7 (WiFi)"
+        case "iPad6,4":      return "iPad Pro 9.7 (Cellular)"
+        case "iPad6,7":      return "iPad Pro 12.9 (WiFi)"
+        case "iPad6,8":      return "iPad Pro 12.9 (Cellular)"
+            
+        // Apple Watch
+        case "Watch1,1":      return "Apple Watch 38mm"
+        case "Watch1,2":      return "Apple Watch 42mm"
+        case "Watch2,3":      return "Apple Watch 38mm (Series 2)"
+        case "Watch2,4":      return "Apple Watch 42mm (Series 2)"
+        case "Watch2,6":      return "Apple Watch 38mm (Series 1)"
+        case "Watch2,7":      return "Apple Watch 42mm (Series 1)"
+            
+        // Apple TV
+        case "AppleTV2,1":      return "Apple TV 2G"
+        case "AppleTV3,1":      return "Apple TV 3G"
+        case "AppleTV3,2":      return "Apple TV 3G (2013)"
+        case "AppleTV5,3":      return "Apple TV 4G"
+            
+        case "i386":         return "Simulator"
+        case "x86_64":       return "Simulator"
+            
+        default: return nil
+        }
+    }
+}
+
+#if os(OSX)
+    import AppKit
+    extension Device {
+        /// Returns the version number of the current OS as String i.e. "1.2" or "9.4"
+        internal static func osVersionForCurrentDevice() -> String  {
+            let version = ProcessInfo.processInfo.operatingSystemVersion
+            return "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
+        }
+        
+        // Returns the screen size in points
+        internal static func screenSizeForCurrentDevice() ->  CGSize {
+            guard let mainScreen = NSScreen.main else { return CGSize.zero }
+            return mainScreen.visibleFrame.size
+        }
+        
+        // Returns the screen size in pixels
+        internal static func nativeScreenSizeForCurrentDevice() ->  CGSize? {
+            return nil
+        }
+    }
+#elseif os(iOS) || os(tvOS)
+    import UIKit
+    extension Device {
+        
+        /// Returns the version number of the current OS as String i.e. "1.2" or "9.4"
+        internal static func osVersionForCurrentDevice() -> String  {
+            return UIDevice.current.systemVersion
+        }
+        
+        // Returns the screen size in points
+        internal static func screenSizeForCurrentDevice() ->  CGSize {
+            let bounds = UIScreen.main.bounds
+            return bounds.size
+        }
+        
+        // Returns the screen size in pixels
+        internal static func nativeScreenSizeForCurrentDevice() ->  CGSize {
+            let bounds = UIScreen.main.nativeBounds
+            return bounds.size
+        }
+    }
+#endif
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/Dispatcher.swift b/iOS/Pods/MatomoTracker/MatomoTracker/Dispatcher.swift
new file mode 100644 (file)
index 0000000..9414ca9
--- /dev/null
@@ -0,0 +1,10 @@
+import Foundation
+
+public protocol Dispatcher {
+    
+    var baseURL: URL { get }
+    
+    var userAgent: String? { get }
+    
+    func send(events: [Event], success: @escaping ()->(), failure: @escaping (_ error: Error)->())
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/Event.swift b/iOS/Pods/MatomoTracker/MatomoTracker/Event.swift
new file mode 100644 (file)
index 0000000..14e274d
--- /dev/null
@@ -0,0 +1,121 @@
+//
+//  Event.swift
+//  PiwikTracker
+//
+//  Created by Cornelius Horstmann on 21.12.16.
+//  Copyright © 2016 PIWIK. All rights reserved.
+//
+
+import Foundation
+import CoreGraphics
+
+/// Represents an event of any kind.
+///
+/// - Note: Should we store the resolution in the Event (cleaner) or add it before transmission (smaller)?
+/// - Todo: 
+///     - Add Campaign Parameters: _rcn, _rck
+///     - Add Action info
+///     - Event Tracking info
+///     - Add Content Tracking info
+///     - Add Ecommerce info
+///
+/// # Key Mapping:
+/// Most properties represent a key defined at: [Tracking HTTP API](https://developer.piwik.org/api-reference/tracking-api). Keys that are not supported for now are:
+///
+/// - idsite, rec, rand, apiv, res, cookie,
+/// - All Plugins: fla, java, dir, qt, realp, pdf, wma, gears, ag
+/// - cid: We will use the uid instead of the cid.
+public struct Event {
+    let siteId: String
+    let uuid: NSUUID
+    let visitor: Visitor
+    let session: Session
+    
+    /// The Date and Time the event occurred.
+    /// api-key: h, m, s
+    let date: Date
+    
+    /// The full URL for the current action. 
+    /// api-key: url
+    let url: URL?
+    
+    /// api-key: action_name
+    let actionName: [String]
+    
+    /// The language of the device.
+    /// Should be in the format of the Accept-Language HTTP header field.
+    /// api-key: lang
+    let language: String
+    
+    /// Should be set to true for the first event of a session.
+    /// api-key: new_visit
+    let isNewSession: Bool
+    
+    /// Currently only used for Campaigns
+    /// api-key: urlref
+    let referer: URL?
+    let screenResolution : CGSize = Device.makeCurrentDevice().screenSize
+    
+    /// api-key: _cvar
+    let customVariables: [CustomVariable]
+    
+    /// Event tracking
+    /// https://piwik.org/docs/event-tracking/
+    let eventCategory: String?
+    let eventAction: String?
+    let eventName: String?
+    let eventValue: Float?
+    
+    /// Campaign tracking
+    /// https://matomo.org/docs/tracking-campaigns/
+    let campaignName: String?
+    let campaignKeyword: String?
+
+    /// Search tracking
+    /// api-keys: search, search_cat, search_count
+    let searchQuery: String?
+    let searchCategory: String?
+    let searchResultsCount: Int?
+    
+    let dimensions: [CustomDimension]
+    
+    let customTrackingParameters: [String:String]
+    
+    /// Content tracking
+    /// https://matomo.org/docs/content-tracking/
+    let contentName: String?
+    let contentPiece: String?
+    let contentTarget: String?
+    let contentInteraction: String?
+}
+
+extension Event {
+    public init(tracker: MatomoTracker, action: [String], url: URL? = nil, referer: URL? = nil, eventCategory: String? = nil, eventAction: String? = nil, eventName: String? = nil, eventValue: Float? = nil, customTrackingParameters: [String:String] = [:], searchQuery: String? = nil, searchCategory: String? = nil, searchResultsCount: Int? = nil, dimensions: [CustomDimension] = [], variables: [CustomVariable] = [], contentName: String? = nil, contentInteraction: String? = nil, contentPiece: String? = nil, contentTarget: String? = nil) {
+        self.siteId = tracker.siteId
+        self.uuid = NSUUID()
+        self.visitor = tracker.visitor
+        self.session = tracker.session
+        self.date = Date()
+        self.url = url ?? tracker.contentBase?.appendingPathComponent(action.joined(separator: "/"))
+        self.actionName = action
+        self.language = Locale.httpAcceptLanguage
+        self.isNewSession = tracker.nextEventStartsANewSession
+        self.referer = referer
+        self.eventCategory = eventCategory
+        self.eventAction = eventAction
+        self.eventName = eventName
+        self.eventValue = eventValue
+        self.searchQuery = searchQuery
+        self.searchCategory = searchCategory
+        self.searchResultsCount = searchResultsCount
+        self.dimensions = tracker.dimensions + dimensions
+        self.campaignName = tracker.campaignName
+        self.campaignKeyword = tracker.campaignKeyword
+        self.customTrackingParameters = customTrackingParameters
+        self.customVariables = tracker.customVariables + variables
+        self.contentName = contentName
+        self.contentPiece = contentPiece
+        self.contentTarget = contentTarget
+        self.contentInteraction = contentInteraction
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/EventSerializer.swift b/iOS/Pods/MatomoTracker/MatomoTracker/EventSerializer.swift
new file mode 100644 (file)
index 0000000..afa3172
--- /dev/null
@@ -0,0 +1,111 @@
+//
+//  EventSerializer.swift
+//  PiwikTracker
+//
+//  Created by Cornelius Horstmann on 11.01.18.
+//  Copyright © 2018 PIWIK. All rights reserved.
+//
+
+import Foundation
+
+final class EventSerializer {
+    internal func jsonData(for events: [Event]) throws -> Data {
+        let eventsAsQueryItems = events.map({ $0.queryItems })
+        let serializedEvents = eventsAsQueryItems.map({ items in
+            items.flatMap({ item in
+                guard let value = item.value,
+                    let encodedValue = value.addingPercentEncoding(withAllowedCharacters: .urlQueryParameterAllowed) else { return nil }
+                return "\(item.name)=\(encodedValue)"
+            }).joined(separator: "&")
+        })
+        let body = ["requests": serializedEvents.map({ "?\($0)" })]
+        return try JSONSerialization.data(withJSONObject: body, options: [])
+    }
+}
+
+fileprivate extension Event {
+    
+    private func customVariableParameterValue() -> String {
+        let customVariableParameterValue: [String] = customVariables.map { "\"\($0.index)\":[\"\($0.name)\",\"\($0.value)\"]" }
+        return "{\(customVariableParameterValue.joined(separator: ","))}"
+    }
+
+    var queryItems: [URLQueryItem] {
+        get {
+            let items = [
+                URLQueryItem(name: "idsite", value: siteId),
+                URLQueryItem(name: "rec", value: "1"),
+                // Visitor
+                URLQueryItem(name: "_id", value: visitor.id),
+                URLQueryItem(name: "uid", value: visitor.userId),
+                
+                // Session
+                URLQueryItem(name: "_idvc", value: "\(session.sessionsCount)"),
+                URLQueryItem(name: "_viewts", value: "\(Int(session.lastVisit.timeIntervalSince1970))"),
+                URLQueryItem(name: "_idts", value: "\(Int(session.firstVisit.timeIntervalSince1970))"),
+                
+                URLQueryItem(name: "url", value:url?.absoluteString),
+                URLQueryItem(name: "action_name", value: actionName.joined(separator: "/")),
+                URLQueryItem(name: "lang", value: language),
+                URLQueryItem(name: "urlref", value: referer?.absoluteString),
+                URLQueryItem(name: "new_visit", value: isNewSession ? "1" : nil),
+                
+                URLQueryItem(name: "h", value: DateFormatter.hourDateFormatter.string(from: date)),
+                URLQueryItem(name: "m", value: DateFormatter.minuteDateFormatter.string(from: date)),
+                URLQueryItem(name: "s", value: DateFormatter.secondsDateFormatter.string(from: date)),
+                
+                //screen resolution
+                URLQueryItem(name: "res", value:String(format: "%1.0fx%1.0f", screenResolution.width, screenResolution.height)),
+                
+                URLQueryItem(name: "e_c", value: eventCategory),
+                URLQueryItem(name: "e_a", value: eventAction),
+                URLQueryItem(name: "e_n", value: eventName),
+                URLQueryItem(name: "e_v", value: eventValue != nil ? "\(eventValue!)" : nil),
+                
+                URLQueryItem(name: "_rcn", value: campaignName),
+                URLQueryItem(name: "_rck", value: campaignKeyword),
+
+                URLQueryItem(name: "search", value: searchQuery),
+                URLQueryItem(name: "search_cat", value: searchCategory),
+                URLQueryItem(name: "search_count", value: searchResultsCount != nil ? "\(searchResultsCount!)" : nil),
+                
+                URLQueryItem(name: "c_n", value: contentName),
+                URLQueryItem(name: "c_p", value: contentPiece),
+                URLQueryItem(name: "c_t", value: contentTarget),
+                URLQueryItem(name: "c_i", value: contentInteraction),
+                ].filter { $0.value != nil }
+
+            let dimensionItems = dimensions.map { URLQueryItem(name: "dimension\($0.index)", value: $0.value) }
+            let customItems = customTrackingParameters.map { return URLQueryItem(name: $0.key, value: $0.value) }
+            let customVariableItems = customVariables.count > 0 ? [URLQueryItem(name: "_cvar", value: customVariableParameterValue())] : []
+
+            return items + dimensionItems + customItems + customVariableItems
+        }
+    }
+}
+
+fileprivate extension DateFormatter {
+    static let hourDateFormatter: DateFormatter = {
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateFormat = "HH"
+        return dateFormatter
+    }()
+    static let minuteDateFormatter: DateFormatter = {
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateFormat = "mm"
+        return dateFormatter
+    }()
+    static let secondsDateFormatter: DateFormatter = {
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateFormat = "ss"
+        return dateFormatter
+    }()
+}
+
+fileprivate extension CharacterSet {
+    
+    /// Returns the character set for characters allowed in a query parameter URL component.
+    fileprivate static var urlQueryParameterAllowed: CharacterSet {
+        return CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "&/?"))
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/Locale+HttpAcceptLanguage.swift b/iOS/Pods/MatomoTracker/MatomoTracker/Locale+HttpAcceptLanguage.swift
new file mode 100644 (file)
index 0000000..48cb932
--- /dev/null
@@ -0,0 +1,15 @@
+import Foundation
+
+extension Locale {
+    static var httpAcceptLanguage: String {
+        var components: [String] = []
+        for (index, languageCode) in preferredLanguages.enumerated() {
+            let quality = 1.0 - (Double(index) * 0.1)
+            components.append("\(languageCode);q=\(quality)")
+            if quality <= 0.5 {
+                break
+            }
+        }
+        return components.joined(separator: ",")
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/Logger.swift b/iOS/Pods/MatomoTracker/MatomoTracker/Logger.swift
new file mode 100644 (file)
index 0000000..93eaac3
--- /dev/null
@@ -0,0 +1,76 @@
+import Foundation
+
+@objc public enum LogLevel: Int {
+    case verbose = 10
+    case debug = 20
+    case info = 30
+    case warning = 40
+    case error = 50
+    
+    var shortcut: String {
+        switch self {
+        case .error: return "E"
+        case .warning: return "W"
+        case .info: return "I"
+        case .debug: return "D"
+        case .verbose: return "V"
+        }
+    }
+}
+
+/// The Logger protocol defines a common interface that is used to log every message from the sdk.
+/// You can easily writer your own to perform custom logging.
+@objc public protocol Logger {
+    /// This method should perform the logging. It can be called from every thread. The implementation has
+    /// to handle synchronizing different threads.
+    ///
+    /// - Parameters:
+    ///   - message: A closure that produces the message itself.
+    ///   - level: The loglevel of the message.
+    ///   - file: The filename where the log was created.
+    ///   - function: The funciton where the log was created.
+    ///   - line: Then line where the log was created.
+    func log(_ message: @autoclosure () -> String, with level: LogLevel, file: String, function: String, line: Int)
+}
+
+extension Logger {
+    func verbose(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) {
+        log(message, with: .verbose, file: file, function: function, line: line)
+    }
+    func debug(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) {
+        log(message, with: .debug, file: file, function: function, line: line)
+    }
+    func info(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) {
+        log(message, with: .info, file: file, function: function, line: line)
+    }
+    func warning(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) {
+        log(message, with: .warning, file: file, function: function, line: line)
+    }
+    func error(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) {
+        log(message, with: .error, file: file, function: function, line: line)
+    }
+}
+
+/// This Logger does nothing and skips every logging.
+public final class DisabledLogger: Logger {
+    public func log(_ message: @autoclosure () -> String, with level: LogLevel, file: String = #file, function: String = #function, line: Int = #line) { }
+}
+
+/// This Logger loggs every message to the console with a `print` statement.
+@objc public final class DefaultLogger: NSObject, Logger {
+    private let dispatchQueue = DispatchQueue(label: "DefaultLogger", qos: .background)
+    private let minLevel: LogLevel
+    
+    @objc public init(minLevel: LogLevel) {
+        self.minLevel = minLevel
+        super.init()
+    }
+    
+    public func log(_ message: @autoclosure () -> String, with level: LogLevel, file: String = #file, function: String = #function, line: Int = #line) {
+        guard level.rawValue >= minLevel.rawValue else { return }
+        let messageToPrint = message()
+        dispatchQueue.async {
+            print("MatomoTracker [\(level.shortcut)] \(messageToPrint)")
+        }
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/MainThread.swift b/iOS/Pods/MatomoTracker/MatomoTracker/MainThread.swift
new file mode 100644 (file)
index 0000000..c661f12
--- /dev/null
@@ -0,0 +1,5 @@
+import Foundation
+
+func assertMainThread(_ file: StaticString = #file, line: UInt = #line) {
+    assert(Thread.isMainThread, "\(file):\(line) must run on the main thread!")
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/MatomoTracker.swift b/iOS/Pods/MatomoTracker/MatomoTracker/MatomoTracker.swift
new file mode 100644 (file)
index 0000000..60e8e42
--- /dev/null
@@ -0,0 +1,390 @@
+import Foundation
+
+/// The Matomo Tracker is a Swift framework to send analytics to the Matomo server.
+///
+/// ## Basic Usage
+/// * Use the track methods to track your views, events and more.
+final public class MatomoTracker: NSObject {
+    
+    /// Defines if the user opted out of tracking. When set to true, every event
+    /// will be discarded immediately. This property is persisted between app launches.
+    @objc public var isOptedOut: Bool {
+        get {
+            return matomoUserDefaults.optOut
+        }
+        set {
+            matomoUserDefaults.optOut = newValue
+        }
+    }
+    
+    /// Will be used to associate all future events with a given userID. This property
+    /// is persisted between app launches.
+    @objc public var visitorId: String? {
+        get {
+            return matomoUserDefaults.visitorUserId
+        }
+        set {
+            matomoUserDefaults.visitorUserId = newValue
+            visitor = Visitor.current(in: matomoUserDefaults)
+        }
+    }
+    
+    internal var matomoUserDefaults: MatomoUserDefaults
+    private let dispatcher: Dispatcher
+    private var queue: Queue
+    internal let siteId: String
+
+    internal var dimensions: [CustomDimension] = []
+    
+    internal var customVariables: [CustomVariable] = []
+    
+    /// This logger is used to perform logging of all sorts of Matomo related information.
+    /// Per default it is a `DefaultLogger` with a `minLevel` of `LogLevel.warning`. You can
+    /// set your own Logger with a custom `minLevel` or a complete custom logging mechanism.
+    @objc public var logger: Logger = DefaultLogger(minLevel: .warning)
+    
+    /// The `contentBase` is used to build the url of an Event, if the Event hasn't got a url set.
+    /// This autogenerated url will then have the format <contentBase>/<actions>.
+    /// Per default the `contentBase` is http://<Application Bundle Name>.
+    /// Set the `contentBase` to nil, if you don't want to auto generate a url.
+    @objc public var contentBase: URL?
+    
+    internal static var _sharedInstance: MatomoTracker?
+    
+    /// Create and Configure a new Tracker
+    ///
+    /// - Parameters:
+    ///   - siteId: The unique site id generated by the server when a new site was created.
+    ///   - queue: The queue to use to store all analytics until it is dispatched to the server.
+    ///   - dispatcher: The dispatcher to use to transmit all analytics to the server.
+    required public init(siteId: String, queue: Queue, dispatcher: Dispatcher) {
+        self.siteId = siteId
+        self.queue = queue
+        self.dispatcher = dispatcher
+        self.contentBase = URL(string: "http://\(Application.makeCurrentApplication().bundleIdentifier ?? "unknown")")
+        self.matomoUserDefaults = MatomoUserDefaults(suiteName: "\(siteId)\(dispatcher.baseURL.absoluteString)")
+        self.visitor = Visitor.current(in: matomoUserDefaults)
+        self.session = Session.current(in: matomoUserDefaults)
+        super.init()
+        startNewSession()
+        startDispatchTimer()
+    }
+    
+    /// Create and Configure a new Tracker
+    ///
+    /// A volatile memory queue will be used to store the analytics data. All not transmitted data will be lost when the application gets terminated.
+    /// The URLSessionDispatcher will be used to transmit the data to the server.
+    ///
+    /// - Parameters:
+    ///   - siteId: The unique site id generated by the server when a new site was created.
+    ///   - baseURL: The url of the Matomo server. This url has to end in `piwik.php`.
+    ///   - userAgent: An optional parameter for custom user agent.
+    @objc convenience public init(siteId: String, baseURL: URL, userAgent: String? = nil) {
+        assert(baseURL.absoluteString.hasSuffix("piwik.php"), "The baseURL is expected to end in piwik.php")
+        
+        let queue = MemoryQueue()
+        let dispatcher = URLSessionDispatcher(baseURL: baseURL, userAgent: userAgent)
+        self.init(siteId: siteId, queue: queue, dispatcher: dispatcher)
+    }
+    
+    internal func queue(event: Event) {
+        guard Thread.isMainThread else {
+            DispatchQueue.main.sync {
+                self.queue(event: event)
+            }
+            return
+        }
+        guard !isOptedOut else { return }
+        logger.verbose("Queued event: \(event)")
+        queue.enqueue(event: event)
+        nextEventStartsANewSession = false
+    }
+    
+    // MARK: dispatching
+    
+    private let numberOfEventsDispatchedAtOnce = 20
+    private(set) var isDispatching = false
+    
+    
+    /// Manually start the dispatching process. You might want to call this method in AppDelegates `applicationDidEnterBackground` to transmit all data
+    /// whenever the user leaves the application.
+    @objc public func dispatch() {
+        guard !isDispatching else {
+            logger.verbose("MatomoTracker is already dispatching.")
+            return
+        }
+        guard queue.eventCount > 0 else {
+            logger.info("No need to dispatch. Dispatch queue is empty.")
+            startDispatchTimer()
+            return
+        }
+        logger.info("Start dispatching events")
+        isDispatching = true
+        dispatchBatch()
+    }
+    
+    private func dispatchBatch() {
+        guard Thread.isMainThread else {
+            DispatchQueue.main.sync {
+                self.dispatchBatch()
+            }
+            return
+        }
+        queue.first(limit: numberOfEventsDispatchedAtOnce) { events in
+            guard events.count > 0 else {
+                // there are no more events queued, finish dispatching
+                self.isDispatching = false
+                self.startDispatchTimer()
+                self.logger.info("Finished dispatching events")
+                return
+            }
+            self.dispatcher.send(events: events, success: {
+                DispatchQueue.main.async {
+                    self.queue.remove(events: events, completion: {
+                        self.logger.info("Dispatched batch of \(events.count) events.")
+                        DispatchQueue.main.async {
+                            self.dispatchBatch()
+                        }
+                    })
+                }
+            }, failure: { error in
+                self.isDispatching = false
+                self.startDispatchTimer()
+                self.logger.warning("Failed dispatching events with error \(error)")
+            })
+        }
+    }
+    
+    // MARK: dispatch timer
+    
+    @objc public var dispatchInterval: TimeInterval = 30.0 {
+        didSet {
+            startDispatchTimer()
+        }
+    }
+    private var dispatchTimer: Timer?
+    
+    private func startDispatchTimer() {
+        guard Thread.isMainThread else {
+            DispatchQueue.main.sync {
+                self.startDispatchTimer()
+            }
+            return
+        }
+        guard dispatchInterval > 0  else { return } // Discussion: Do we want the possibility to dispatch synchronous? That than would be dispatchInterval = 0
+        if let dispatchTimer = dispatchTimer {
+            dispatchTimer.invalidate()
+            self.dispatchTimer = nil
+        }
+        self.dispatchTimer = Timer.scheduledTimer(timeInterval: dispatchInterval, target: self, selector: #selector(dispatch), userInfo: nil, repeats: false)
+    }
+    
+    internal var visitor: Visitor
+    internal var session: Session
+    internal var nextEventStartsANewSession = true
+
+    internal var campaignName: String? = nil
+    internal var campaignKeyword: String? = nil
+    
+    /// Adds the name and keyword for the current campaign.
+    /// This is usually very helpfull if you use deeplinks into your app.
+    ///
+    /// More information on campaigns: [https://matomo.org/docs/tracking-campaigns/](https://matomo.org/docs/tracking-campaigns/)
+    ///
+    /// - Parameters:
+    ///   - name: The name of the campaign.
+    ///   - keyword: The keyword of the campaign.
+    @objc public func trackCampaign(name: String?, keyword: String?) {
+        campaignName = name
+        campaignKeyword = keyword
+    }
+    
+    /// There are several ways to track content impressions and interactions manually, semi-automatically and automatically. Please be aware that content impressions will be tracked using bulk tracking which will always send a POST request, even if  GET is configured which is the default. For more details have a look at the in-depth guide to Content Tracking.
+    /// More information on content: [https://matomo.org/docs/content-tracking/](https://matomo.org/docs/content-tracking/)
+    ///
+    /// - Parameters:
+    ///   - name: The name of the content. For instance 'Ad Foo Bar'
+    ///   - piece: The actual content piece. For instance the path to an image, video, audio, any text
+    ///   - target: The target of the content. For instance the URL of a landing page
+    ///   - interaction: The name of the interaction with the content. For instance a 'click'
+    @objc public func trackContentImpression(name: String, piece: String?, target: String?) {
+        track(Event(tracker: self, action: [], contentName: name, contentPiece: piece, contentTarget: target))
+    }
+    @objc public func trackContentInteraction(name: String, interaction: String, piece: String?, target: String?) {
+        track(Event(tracker: self, action: [], contentName: name, contentInteraction: interaction, contentPiece: piece, contentTarget: target))
+    }
+}
+
+extension MatomoTracker {
+    /// Starts a new Session
+    ///
+    /// Use this function to manually start a new Session. A new Session will be automatically created only on app start.
+    /// You can use the AppDelegates `applicationWillEnterForeground` to start a new visit whenever the app enters foreground.
+    public func startNewSession() {
+        matomoUserDefaults.previousVisit = matomoUserDefaults.currentVisit
+        matomoUserDefaults.currentVisit = Date()
+        matomoUserDefaults.totalNumberOfVisits += 1
+        self.session = Session.current(in: matomoUserDefaults)
+    }
+}
+
+extension MatomoTracker {
+    
+    /// Tracks a custom Event
+    ///
+    /// - Parameter event: The event that should be tracked.
+    public func track(_ event: Event) {
+        queue(event: event)
+        
+        if (event.campaignName == campaignName && event.campaignKeyword == campaignKeyword) {
+            campaignName = nil
+            campaignKeyword = nil
+        }
+    }
+    
+    /// Tracks a screenview.
+    ///
+    /// This method can be used to track hierarchical screen names, e.g. screen/settings/register. Use this to create a hierarchical and logical grouping of screen views in the Matomo web interface.
+    ///
+    /// - Parameter view: An array of hierarchical screen names.
+    /// - Parameter url: The optional url of the page that was viewed.
+    /// - Parameter dimensions: An optional array of dimensions, that will be set only in the scope of this view.
+    public func track(view: [String], url: URL? = nil, dimensions: [CustomDimension] = []) {
+        let event = Event(tracker: self, action: view, url: url, dimensions: dimensions)
+        queue(event: event)
+    }
+    
+    /// Tracks an event as described here: https://matomo.org/docs/event-tracking/
+    ///
+    /// - Parameters:
+    ///   - category: The Category of the Event
+    ///   - action: The Action of the Event
+    ///   - name: The optional name of the Event
+    ///   - value: The optional value of the Event
+    ///   - dimensions: An optional array of dimensions, that will be set only in the scope of this event.
+    ///   - url: The optional url of the page that was viewed.
+    public func track(eventWithCategory category: String, action: String, name: String? = nil, value: Float? = nil, dimensions: [CustomDimension] = [], url: URL? = nil) {
+        let event = Event(tracker: self, action: [], url: url, eventCategory: category, eventAction: action, eventName: name, eventValue: value, dimensions: dimensions)
+        queue(event: event)
+    }
+}
+
+extension MatomoTracker {
+    
+    /// Tracks a search result page as described here: https://matomo.org/docs/site-search/
+    ///
+    /// - Parameters:
+    ///   - query: The string the user was searching for
+    ///   - category: An optional category which the user was searching in
+    ///   - resultCount: The number of results that were displayed for that search
+    ///   - dimensions: An optional array of dimensions, that will be set only in the scope of this event.
+    ///   - url: The optional url of the page that was viewed.
+    public func trackSearch(query: String, category: String?, resultCount: Int?, dimensions: [CustomDimension] = [], url: URL? = nil) {
+        let event = Event(tracker: self, action: [], url: url, searchQuery: query, searchCategory: category, searchResultsCount: resultCount, dimensions: dimensions)
+        queue(event: event)
+    }
+}
+
+extension MatomoTracker {
+    /// Set a permanent custom dimension.
+    ///
+    /// Use this method to set a dimension that will be send with every event. This is best for Custom Dimensions in scope "Visit". A typical example could be any device information or the version of the app the visitor is using.
+    ///
+    /// For more information on custom dimensions visit https://matomo.org/docs/custom-dimensions/
+    ///
+    /// - Parameter value: The value you want to set for this dimension.
+    /// - Parameter index: The index of the dimension. A dimension with this index must be setup in the Matomo backend.
+    @available(*, deprecated, message: "use setDimension: instead")
+    public func set(value: String, forIndex index: Int) {
+        let dimension = CustomDimension(index: index, value: value)
+        remove(dimensionAtIndex: dimension.index)
+        dimensions.append(dimension)
+    }
+    
+    /// Set a permanent custom dimension.
+    ///
+    /// Use this method to set a dimension that will be send with every event. This is best for Custom Dimensions in scope "Visit". A typical example could be any device information or the version of the app the visitor is using.
+    ///
+    /// For more information on custom dimensions visit https://matomo.org/docs/custom-dimensions/
+    ///
+    /// - Parameter dimension: The Dimension to set
+    public func set(dimension: CustomDimension) {
+        remove(dimensionAtIndex: dimension.index)
+        dimensions.append(dimension)
+    }
+    
+    /// Set a permanent custom dimension by value and index.
+    ///
+    /// This is a convenience alternative to set(dimension:) and calls the exact same functionality. Also, it is accessible from Objective-C.
+    ///
+    /// - Parameter value: The value for the new Custom Dimension
+    /// - Parameter forIndex: The index of the new Custom Dimension
+    @objc public func setDimension(_ value: String, forIndex index: Int) {
+        set(dimension: CustomDimension( index: index, value: value ));
+    }
+    
+    /// Removes a previously set custom dimension.
+    ///
+    /// Use this method to remove a dimension that was set using the `set(value: String, forDimension index: Int)` method.
+    ///
+    /// - Parameter index: The index of the dimension.
+    @objc public func remove(dimensionAtIndex index: Int) {
+        dimensions = dimensions.filter({ dimension in
+            dimension.index != index
+        })
+    }
+}
+
+
+extension MatomoTracker {
+
+    /// Set a permanent new Custom Variable.
+    ///
+    /// - Parameter dimension: The Custom Variable to set
+    public func set(customVariable: CustomVariable) {
+        removeCustomVariable(withIndex: customVariable.index)
+        customVariables.append(customVariable)
+    }
+
+    /// Set a permanent new Custom Variable.
+    ///
+    /// - Parameter name: The index of the new Custom Variable
+    /// - Parameter name: The name of the new Custom Variable
+    /// - Parameter value: The value of the new Custom Variable
+    @objc public func setCustomVariable(withIndex index: UInt, name: String, value: String) {
+        set(customVariable: CustomVariable(index: index, name: name, value: value))
+    }
+    
+    /// Remove a previously set Custom Variable.
+    ///
+    /// - Parameter index: The index of the Custom Variable.
+    @objc public func removeCustomVariable(withIndex index: UInt) {
+        customVariables = customVariables.filter { $0.index != index }
+    }
+}
+
+// Objective-c compatibility extension
+extension MatomoTracker {
+    @objc public func track(view: [String], url: URL? = nil) {
+        track(view: view, url: url, dimensions: [])
+    }
+    
+    @objc public func track(eventWithCategory category: String, action: String, name: String? = nil, number: NSNumber? = nil, url: URL? = nil) {
+        let value = number == nil ? nil : number!.floatValue
+        track(eventWithCategory: category, action: action, name: name, value: value, url: url)
+    }
+    
+    @available(*, deprecated, message: "use trackEventWithCategory:action:name:number:url instead")
+    @objc public func track(eventWithCategory category: String, action: String, name: String? = nil, number: NSNumber? = nil) {
+        track(eventWithCategory: category, action: action, name: name, number: number, url: nil)
+    }
+    
+    @objc public func trackSearch(query: String, category: String?, resultCount: Int, url: URL? = nil) {
+        trackSearch(query: query, category: category, resultCount: resultCount, url: url)
+    }}
+
+extension MatomoTracker {
+    public func copyFromOldSharedInstance() {
+        matomoUserDefaults.copy(from: UserDefaults.standard)
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/MatomoUserDefaults.swift b/iOS/Pods/MatomoTracker/MatomoTracker/MatomoUserDefaults.swift
new file mode 100644 (file)
index 0000000..3acf4cf
--- /dev/null
@@ -0,0 +1,110 @@
+import Foundation
+
+/// MatomoUserDefaults is a wrapper for the UserDefaults with properties
+/// mapping onto values stored in the UserDefaults.
+/// All getter and setter are sideeffect free and automatically syncronize
+/// after writing.
+internal struct MatomoUserDefaults {
+    let userDefaults: UserDefaults
+
+    init(suiteName: String?) {
+        userDefaults = UserDefaults(suiteName: suiteName)!
+    }
+    
+    var totalNumberOfVisits: Int {
+        get {
+            return userDefaults.integer(forKey: MatomoUserDefaults.Key.totalNumberOfVisits)
+        }
+        set {
+            userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.totalNumberOfVisits)
+            userDefaults.synchronize()
+        }
+    }
+    
+    var firstVisit: Date? {
+        get {
+            return userDefaults.object(forKey: MatomoUserDefaults.Key.firstVistsTimestamp) as? Date
+        }
+        set {
+            userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.firstVistsTimestamp)
+            userDefaults.synchronize()
+        }
+    }
+    
+    var previousVisit: Date? {
+        get {
+            return userDefaults.object(forKey: MatomoUserDefaults.Key.previousVistsTimestamp) as? Date
+        }
+        set {
+            userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.previousVistsTimestamp)
+            userDefaults.synchronize()
+        }
+    }
+    
+    var currentVisit: Date? {
+        get {
+            return userDefaults.object(forKey: MatomoUserDefaults.Key.currentVisitTimestamp) as? Date
+        }
+        set {
+            userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.currentVisitTimestamp)
+            userDefaults.synchronize()
+        }
+    }
+    
+    var optOut: Bool {
+        get {
+            return userDefaults.bool(forKey: MatomoUserDefaults.Key.optOut)
+        }
+        set {
+            userDefaults.set(newValue, forKey: MatomoUserDefaults.Key.optOut)
+            userDefaults.synchronize()
+        }
+    }
+    
+    var clientId: String? {
+        get {
+            return userDefaults.string(forKey: MatomoUserDefaults.Key.clientID)
+        }
+        set {
+            userDefaults.setValue(newValue, forKey: MatomoUserDefaults.Key.clientID)
+            userDefaults.synchronize()
+        }
+    }
+    
+    var visitorUserId: String? {
+        get {
+            return userDefaults.string(forKey: MatomoUserDefaults.Key.visitorUserID);
+        }
+        set {
+            userDefaults.setValue(newValue, forKey: MatomoUserDefaults.Key.visitorUserID);
+            userDefaults.synchronize()
+        }
+    }
+}
+
+extension MatomoUserDefaults {
+    public mutating func copy(from userDefaults: UserDefaults) {
+        totalNumberOfVisits = UserDefaults.standard.integer(forKey: MatomoUserDefaults.Key.totalNumberOfVisits)
+        firstVisit = UserDefaults.standard.object(forKey: MatomoUserDefaults.Key.firstVistsTimestamp) as? Date
+        previousVisit = UserDefaults.standard.object(forKey: MatomoUserDefaults.Key.previousVistsTimestamp) as? Date
+        currentVisit = UserDefaults.standard.object(forKey: MatomoUserDefaults.Key.currentVisitTimestamp) as? Date
+        optOut = UserDefaults.standard.bool(forKey: MatomoUserDefaults.Key.optOut)
+        clientId = UserDefaults.standard.string(forKey: MatomoUserDefaults.Key.clientID)
+        visitorUserId = UserDefaults.standard.string(forKey: MatomoUserDefaults.Key.visitorUserID)
+    }
+}
+
+extension MatomoUserDefaults {
+    internal struct Key {
+        static let totalNumberOfVisits = "PiwikTotalNumberOfVistsKey"
+        static let currentVisitTimestamp = "PiwikCurrentVisitTimestampKey"
+        static let previousVistsTimestamp = "PiwikPreviousVistsTimestampKey"
+        static let firstVistsTimestamp = "PiwikFirstVistsTimestampKey"
+        
+        // Note:    To be compatible with previous versions, the clientID key retains its old value,
+        //          even though it is now a misnomer since adding visitorUserID makes it a bit confusing.
+        static let clientID = "PiwikVisitorIDKey"
+        static let visitorUserID = "PiwikVisitorUserIDKey"
+        static let optOut = "PiwikOptOutKey"
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/MemoryQueue.swift b/iOS/Pods/MatomoTracker/MatomoTracker/MemoryQueue.swift
new file mode 100644 (file)
index 0000000..8ef8192
--- /dev/null
@@ -0,0 +1,29 @@
+import Foundation
+
+/// The MemoryQueue is a **not thread safe** in memory Queue.
+public final class MemoryQueue: NSObject, Queue {
+    private var items = [Event]()
+    
+    public var eventCount: Int {
+        return items.count
+    }
+    
+    public func enqueue(events: [Event], completion: (()->())?) {
+        assertMainThread()
+        items.append(contentsOf: events)
+        completion?()
+    }
+    
+    public func first(limit: Int, completion: (_ items: [Event])->()) {
+        assertMainThread()
+        let amount = [limit,eventCount].min()!
+        let dequeuedItems = Array(items[0..<amount])
+        completion(dequeuedItems)
+    }
+    
+    public func remove(events: [Event], completion: ()->()) {
+        assertMainThread()
+        items = items.filter({ event in !events.contains(where: { eventToRemove in eventToRemove.uuid == event.uuid })})
+        completion()
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/Queue.swift b/iOS/Pods/MatomoTracker/MatomoTracker/Queue.swift
new file mode 100644 (file)
index 0000000..bc0b1fc
--- /dev/null
@@ -0,0 +1,20 @@
+import Foundation
+
+public protocol Queue {
+    
+    var eventCount: Int { get }
+    
+    mutating func enqueue(events: [Event], completion: (()->())?)
+    
+    /// Returns the first `limit` events ordered by Event.date
+    func first(limit: Int, completion: (_ items: [Event])->())
+    
+    /// Removes the events from the queue
+    mutating func remove(events: [Event], completion: ()->())
+}
+
+extension Queue {
+    mutating func enqueue(event: Event, completion: (()->())? = nil) {
+        enqueue(events: [event], completion: completion)
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/Session.swift b/iOS/Pods/MatomoTracker/MatomoTracker/Session.swift
new file mode 100644 (file)
index 0000000..5c8c954
--- /dev/null
@@ -0,0 +1,41 @@
+//
+//  Session.swift
+//  PiwikTracker
+//
+//  Created by Cornelius Horstmann on 26.02.17.
+//  Copyright © 2017 PIWIK. All rights reserved.
+//
+
+import Foundation
+
+struct Session {
+    /// The number of sessions of the current user.
+    /// api-key: _idvc
+    let sessionsCount: Int
+    
+    /// The timestamp of the previous visit.
+    /// Discussion: Should this be now for the first request?
+    /// api-key: _viewts
+    let lastVisit: Date
+    
+    /// The timestamp of the fist visit.
+    /// Discussion: Should this be now for the first request?
+    /// api-key: _idts
+    let firstVisit: Date
+}
+
+extension Session {
+    static func current(in matomoUserDefaults: MatomoUserDefaults) -> Session {
+        let firstVisit: Date
+        var matomoUserDefaults = matomoUserDefaults
+        if let existingFirstVisit = matomoUserDefaults.firstVisit {
+            firstVisit = existingFirstVisit
+        } else {
+            firstVisit = Date()
+            matomoUserDefaults.firstVisit = firstVisit
+        }
+        let sessionCount = matomoUserDefaults.totalNumberOfVisits
+        let lastVisit = matomoUserDefaults.previousVisit ?? Date()
+        return Session(sessionsCount: sessionCount, lastVisit: lastVisit, firstVisit: firstVisit)
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/URLSessionDispatcher.swift b/iOS/Pods/MatomoTracker/MatomoTracker/URLSessionDispatcher.swift
new file mode 100644 (file)
index 0000000..30fa473
--- /dev/null
@@ -0,0 +1,90 @@
+import Foundation
+
+#if os(OSX)
+    import WebKit
+#elseif os(iOS)
+    import UIKit
+#endif
+
+final class URLSessionDispatcher: Dispatcher {
+    
+    let serializer = EventSerializer()
+    let timeout: TimeInterval
+    let session: URLSession
+    let baseURL: URL
+
+    private(set) var userAgent: String?
+    
+    /// Generate a URLSessionDispatcher instance
+    ///
+    /// - Parameters:
+    ///   - baseURL: The url of the Matomo server. This url has to end in `piwik.php`.
+    ///   - userAgent: An optional parameter for custom user agent.
+    init(baseURL: URL, userAgent: String? = nil) {                
+        self.baseURL = baseURL
+        self.timeout = 5
+        self.session = URLSession.shared
+        DispatchQueue.main.async {
+            self.userAgent = userAgent ?? URLSessionDispatcher.defaultUserAgent()
+        }
+    }
+    
+    private static func defaultUserAgent() -> String {
+        assertMainThread()
+        #if os(OSX)
+            let webView = WebView(frame: .zero)
+            let currentUserAgent = webView.stringByEvaluatingJavaScript(from: "navigator.userAgent") ?? ""
+        #elseif os(iOS)
+            let webView = UIWebView(frame: .zero)
+            var currentUserAgent = webView.stringByEvaluatingJavaScript(from: "navigator.userAgent") ?? ""
+            if let regex = try? NSRegularExpression(pattern: "\\((iPad|iPhone);", options: .caseInsensitive) {
+                let deviceModel = Device.makeCurrentDevice().platform
+                currentUserAgent = regex.stringByReplacingMatches(
+                    in: currentUserAgent,
+                    options: .withTransparentBounds,
+                    range: NSRange(location: 0, length: currentUserAgent.count),
+                    withTemplate: "(\(deviceModel);"
+                )
+            }
+        #elseif os(tvOS)
+            let currentUserAgent = ""
+        #endif
+        return currentUserAgent.appending(" MatomoTracker SDK URLSessionDispatcher")
+    }
+    
+    func send(events: [Event], success: @escaping ()->(), failure: @escaping (_ error: Error)->()) {
+        let jsonBody: Data
+        do {
+            jsonBody = try serializer.jsonData(for: events)
+        } catch  {
+            failure(error)
+            return
+        }
+        let request = buildRequest(baseURL: baseURL, method: "POST", contentType: "application/json; charset=utf-8", body: jsonBody)
+        send(request: request, success: success, failure: failure)
+    }
+    
+    private func buildRequest(baseURL: URL, method: String, contentType: String? = nil, body: Data? = nil) -> URLRequest {
+        var request = URLRequest(url: baseURL, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: timeout)
+        request.httpMethod = method
+        body.map { request.httpBody = $0 }
+        contentType.map { request.setValue($0, forHTTPHeaderField: "Content-Type") }
+        userAgent.map { request.setValue($0, forHTTPHeaderField: "User-Agent") }
+        return request
+    }
+    
+    private func send(request: URLRequest, success: @escaping ()->(), failure: @escaping (_ error: Error)->()) {
+        let task = session.dataTask(with: request) { data, response, error in
+            // should we check the response?
+            // let dataString = String(data: data!, encoding: String.Encoding.utf8)
+            if let error = error {
+                failure(error)
+            } else {
+                success()
+            }
+        }
+        task.resume()
+    }
+    
+}
+
diff --git a/iOS/Pods/MatomoTracker/MatomoTracker/Visitor.swift b/iOS/Pods/MatomoTracker/MatomoTracker/Visitor.swift
new file mode 100644 (file)
index 0000000..613bb07
--- /dev/null
@@ -0,0 +1,44 @@
+//
+//  Visitor.swift
+//  PiwikTracker
+//
+//  Created by Cornelius Horstmann on 26.02.17.
+//  Copyright © 2017 PIWIK. All rights reserved.
+//
+
+import Foundation
+
+struct Visitor {
+    /// Unique ID per visitor (device in this case). Should be
+    /// generated upon first start and never changed after.
+    /// api-key: _id
+    let id: String
+    
+    /// An optional user identifier such as email or username.
+    /// api-key: uid
+    let userId: String?
+}
+
+extension Visitor {
+    static func current(in matomoUserDefaults: MatomoUserDefaults) -> Visitor {
+        var matomoUserDefaults = matomoUserDefaults
+        let id: String
+        if let existingId = matomoUserDefaults.clientId {
+            id = existingId
+        } else {
+            let newId = newVisitorID()
+            matomoUserDefaults.clientId = newId
+            id = newId
+        }
+        let userId = matomoUserDefaults.visitorUserId
+        return Visitor(id: id, userId: userId)
+    }
+    
+    static func newVisitorID() -> String {
+        let uuid = UUID().uuidString
+        let sanitizedUUID = uuid.replacingOccurrences(of: "-", with: "")
+        let start = sanitizedUUID.startIndex
+        let end = sanitizedUUID.index(start, offsetBy: 16)
+        return String(sanitizedUUID[start..<end])
+    }
+}
diff --git a/iOS/Pods/MatomoTracker/README.md b/iOS/Pods/MatomoTracker/README.md
new file mode 100644 (file)
index 0000000..202491e
--- /dev/null
@@ -0,0 +1,243 @@
+# MatomoTracker (former PiwikTracker) iOS SDK
+
+The MatomoTracker is an iOS, tvOS and macOS SDK for sending app analytics to a Matomo server. MatomoTracker can be used from Swift and [Objective-C](#objective-c-compatibility).
+
+**Fancy help improve this SDK? Check [this list](https://github.com/matomo-org/matomo-sdk-ios/issues?utf8=✓&q=is%3Aopen%20is%3Aissue%20label%3Adiscussion%20label%3Aswift3) to see what is left and can be improved.**
+
+[![Build Status](https://travis-ci.org/matomo-org/matomo-sdk-ios.svg?branch=develop)](https://travis-ci.org/matomo-org/matomo-sdk-ios)
+
+## Installation
+### [CocoaPods](https://cocoapods.org)
+
+Use the following in your Podfile.
+
+```
+pod 'MatomoTracker', '~> 5.2'
+```
+
+Then run `pod install`. In every file you want to use the MatomoTracker, don't forget to import the framework with `import MatomoTracker`.
+
+### Carthage
+
+[Carthage](https://github.com/Carthage/Carthage) is a non intrusive way to install MatomoTracker to your project. It makes no changes to your Xcode project and workspace. Add the following to your Cartfile:
+
+```
+github "matomo-org/matomo-sdk-ios"
+```
+
+## Usage
+### Matomo Instance
+
+The Matomo iOS SDK doesn't provide a instance of the PiwikTracker. In order to be able to track data you have to create an instance first.
+
+```Swift
+let matomoTracker = MatomoTracker(siteId: "23", baseURL: URL(string: "https://demo2.matomo.org/piwik.php")!)
+```
+
+
+The `siteId` is the ID that you can get if you [add a website](https://matomo.org/docs/manage-websites/#add-a-website) within the Matomo web interface. The `baseURL` it the URL to your Matomo web instance and has to include the "piwik.php" string.
+
+You can either pass around this instance, or add an extension to the `MatomoTracker` class and add a shared instance property.
+
+```Swift
+extension MatomoTracker {
+    static let shared: MatomoTracker = MatomoTracker(siteId: "1", baseURL: URL(string: "https://example.com/piwik.php")!)
+}
+```
+
+The `siteId` is the ID that you can get if you [add a website](https://matomo.org/docs/manage-websites/#add-a-website) within the Matomo web interface. The `baseURL` is the URL to your Matomo web instance and has to include the "piwik.php" string.
+
+You can use multiple instances within one application.
+
+#### Opting Out
+
+The MatomoTracker SDK supports opting out of tracking. Please use the `isOptedOut` property of the MatomoTracker to define if the user opted out of tracking.
+
+```Swift
+matomoTracker.isOptedOut = true
+```
+
+### Tracking Page Views
+
+The MatomoTracker can track hierarchical screen names, e.g. screen/settings/register. Use this to create a hierarchical and logical grouping of screen views in the Matomo web interface.
+
+```Swift
+matomoTracker.track(view: ["path","to","your","page"])
+```
+
+You can also set the url of the page.
+```Swift
+let url = URL(string: "https://matomo.org/get-involved/")
+matomoTracker.track(view: ["community","get-involved"], url: url)
+```
+
+### Tracking Events
+
+Events can be used to track user interactions such as taps on a button. An event consists of four parts:
+
+- Category
+- Action
+- Name (optional, recommended)
+- Value (optional)
+
+```Swift
+matomoTracker.track(eventWithCategory: "player", action: "slide", name: "volume", value: 35.1)
+```
+
+This will log that the user slid the volume slider on the player to 35.1%.
+
+### Tracking search
+
+The `MatomoTracker` can track how users use your app internal search. You can track what keywords were searched for, what categories they use, the number of results for a certain search and what searches resulted in no results.
+
+```Swift
+matomoTracker.trackSearch(query: "Best mobile tracking", category: "Technology", resultCount: 15)
+```
+
+### Custom Dimension
+
+The Matomo SDK currently supports Custom Dimensions for the Visit Scope. Using Custom Dimensions you can add properties to the whole visit, such as "Did the user finish the tutorial?", "Is the user a paying user?" or "Which version of the Application is being used?" and such. Before sending custom dimensions please make sure Custom Dimensions are [properly installed and configured](https://matomo.org/docs/custom-dimensions/). You will need the `ID` of your configured Dimension.
+
+After that you can set a new Dimension,
+
+```Swift
+matomoTracker.set(value: "1.0.0-beta2", forIndex: 1)
+```
+
+or remove an already set dimension.
+
+```Swift
+matomoTracker.remove(dimensionAtIndex: 1)
+```
+
+Dimensions in the Visit Scope will be sent along every Page View or Event. Custom Dimensions are not persisted by the SDK and have to be re-configured upon application startup.
+
+### Custom User ID
+
+To add a [custom User ID](https://matomo.org/docs/user-id/), simply set the value you'd like to use on the `visitorId` field of the tracker:
+
+```Swift
+matomoTracker.visitorId = "coolUsername123"
+```
+
+All future events being tracked by the SDK will be associated with this userID, as opposed to the default UUID created for each Visitor.
+
+### Campaign Tracking
+
+The Matomo iOS SDK supports [campaign tracking](https://matomo.org/docs/tracking-campaigns/).
+
+```Swift
+matomoTracker.trackCampaign(name: @"campaign_name", keyword: @"campaign_keyword")
+```
+
+### Content Tracking
+
+The Matomo iOS SDK supports [content tracking](https://matomo.org/docs/content-tracking/).
+
+```Swift
+matomoTracker.trackContentImpression(name: "preview-liveaboard", piece: "Malaysia", target: "https://dummy.matomo.org/liveaboard/malaysia")
+matomoTracker.trackContentInteraction(name: "preview-liveaboard", interaction: "tap", piece: "Malaysia", target: "https://dummy.matomo.org/liveaboard/malaysia")
+```
+
+## Advanced Usage
+### Manual dispatching
+
+The MatomoTracker will dispatch events every 30 seconds automatically. If you want to dispatch events manually, you can use the `dispatch()` function. You can, for example, dispatch whenever the application enter the background.
+
+```Swift
+func applicationDidEnterBackground(_ application: UIApplication) {
+  matomoTracker.dispatch()
+}
+```
+
+### Session Management
+
+The MatomoTracker starts a new session whenever the application starts. If you want to start a new session manually, you can use the `startNewSession()` function. You can, for example, start a new session whenever the user enters the application.
+
+```Swift
+func applicationWillEnterForeground(_ application: UIApplication) {
+  matomoTracker.startNewSession()
+}
+```
+
+### Logging
+
+The MatomoTracker per default logs `warning` and `error` messages to the console. You can change the `LogLevel`.
+
+```Swift
+matomoTracker.logger = DefaultLogger(minLevel: .verbose)
+matomoTracker.logger = DefaultLogger(minLevel: .debug)
+matomoTracker.logger = DefaultLogger(minLevel: .info)
+matomoTracker.logger = DefaultLogger(minLevel: .warning)
+matomoTracker.logger = DefaultLogger(minLevel: .error)
+```
+
+You can also write your own `Logger` and send the logs wherever you want. Just write a new class/struct and let it conform to the `Logger` protocol.
+
+### Custom User Agent
+The `MatomoTracker` will create a default user agent derived from the WKWebView user agent.
+You can instantiate the `MatomoTracker` using your own user agent.
+
+```Swift
+let matomoTracker = MatomoTracker(siteId: "5", baseURL: URL(string: "http://your.server.org/path-to-matomo/piwik.php")!, userAgent: "Your custom user agent")
+```
+
+### Objective-C compatibility
+
+Version 4 (and higher) of this SDK is written in Swift, but you can use it in your Objective-C project as well. If you don't want to update you can still use the unsupported older [version 3](https://github.com/matomo-org/matomo-sdk-ios/tree/version-3). Using the Swift SDK from Objective-C should be pretty straight forward.
+
+```objc
+MatomoTracker *matomoTracker = [[MatomoTracker alloc] initWithSiteId:@"5" baseURL:[NSURL URLWithString:@"http://your.server.org/path-to-matomo/piwik.php"] userAgent:nil];
+[matomoTracker trackWithView:@[@"example"] url:nil];
+[matomoTracker trackWithEventWithCategory:@"category" action:@"action" name:nil number:nil url:nil];
+[matomoTracker dispatch];
+matomoTracker.logger = [[DefaultLogger alloc] initWithMinLevel:LogLevelVerbose];
+```
+
+### Sending custom events
+
+Instead of using the convenience functions for events and screen views for example you can create your event manually and even send custom tracking parameters. This feature isn't available from Objective-C.
+
+```Swift
+func sendCustomEvent() {
+  guard let matomoTracker = MatomoTracker.shared else { return }
+  let downloadURL = URL(string: "https://builds.matomo.org/piwik.zip")!
+  let event = Event(tracker: matomoTracker, action: ["menu", "custom tracking parameters"], url: downloadURL, customTrackingParameters: ["download": downloadURL.absoluteString])
+  matomoTracker.track(event)
+}
+```
+
+All custom events will be URL-encoded and dispatched along with the default Event parameters. Please read the [Tracking API Documentation](https://developer.matomo.org/api-reference/tracking-api) for more information on which parameters can be used.
+
+Also: You cannot override Custom Parameter keys that are already defined by the Event itself. If you set those keys in the `customTrackingParameters` they will be discarded.
+
+### Automatic url generation
+
+You can define the url property on every `Event`. If none is defined, the SDK will try to generate a url based on the `contentBase` of the `MatomoTracker`. If the `contentBase` is nil, no url will be generated. If the `contentBase` is set, it will append the actions of the event to it and use it as the url. Per default the `contentBase` is generated using the application bundle identifier. For example `http://org.matomo.skd`. This will not result in resolvable urls, but enables the backend to analyse and structure them.
+
+### Event dispatching
+
+Whenever you track an event or a page view it is stored in memory first. In every dispatch run a batch of those events are sent to the server. If the device is offline or the server doesn't respond these events will be kept and resent at a later time. Events currently aren't stored on disk and will be lost if the application is terminated. [#137](https://github.com/matomo-org/matomo-sdk-ios/issues/137)
+
+## Contributing
+Please read [CONTRIBUTING.md](https://github.com/matomo-org/matomo-sdk-ios/blob/swift3/CONTRIBUTING.md) for details.
+
+## ToDo
+### These features aren't implemented yet
+
+- Basic functionality
+  - [Persisting non dispatched events](https://github.com/matomo-org/matomo-sdk-ios/issues/137)
+- Tracking of more things
+  - Social Interactions
+  - Goals and Conversions
+  - Outlinks
+  - Downloads
+  - [Ecommerce Transactions](https://github.com/matomo-org/matomo-sdk-ios/issues/110)
+  - Content Impressions / Content Interactions
+- Customizing the tracker
+  - set the dispatch interval
+  - use different dispatchers (Alamofire)
+
+## License
+
+MatomoTracker is available under the [MIT license](LICENSE.md).
diff --git a/iOS/Pods/MenuItemKit/LICENSE b/iOS/Pods/MenuItemKit/LICENSE
new file mode 100644 (file)
index 0000000..8b75c40
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2016 — Present CHEN Xian-an <xianan.chen@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/iOS/Pods/MenuItemKit/MenuItemKit/Internals.swift b/iOS/Pods/MenuItemKit/MenuItemKit/Internals.swift
new file mode 100644 (file)
index 0000000..494df14
--- /dev/null
@@ -0,0 +1,56 @@
+//
+//  Internals.swift
+//  MenuItemKit
+//
+//  Created by CHEN Xian’an on 1/17/16.
+//  Copyright © 2016 lazyapps. All rights reserved.
+//
+
+import ObjectiveC.runtime
+
+let imageItemIdetifier = "\u{FEFF}\u{200B}"
+
+let blockIdentifierPrefix = "_menuitemkit_block_"
+
+func setNewIMPWithBlock<T>(_ block: T, forSelector selector: Selector, toClass klass: AnyClass) {
+  let method = class_getInstanceMethod(klass, selector)
+  let imp = imp_implementationWithBlock(unsafeBitCast(block, to: AnyObject.self))
+  if !class_addMethod(klass, selector, imp, method_getTypeEncoding(method!)) {
+    method_setImplementation(method!, imp)
+  }
+}
+
+@nonobjc extension NSObject {
+
+  var imageBox: Box<UIImage?> {
+    let key: StaticString = #function
+    return associatedBoxForKey(key, initialValue: nil)
+  }
+
+  var actionBox: Box<MenuItemAction?> {
+    let key: StaticString = #function
+    return associatedBoxForKey(key, initialValue: nil)
+  }
+
+  func associatedBoxForKey<T>(_ key: StaticString, initialValue: @autoclosure () -> T) -> Box<T> {
+    guard let box = objc_getAssociatedObject(self, key.utf8Start) as? Box<T> else {
+      let box = Box(initialValue())
+      objc_setAssociatedObject(self, key.utf8Start, box as AnyObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+      return box
+    }
+
+    return box
+  }
+
+}
+
+// MARK: Box wrapper
+final class Box<T> {
+
+  var value: T
+
+  init(_ val: T) {
+    value = val
+  }
+  
+}
diff --git a/iOS/Pods/MenuItemKit/MenuItemKit/MenuItemKit.h b/iOS/Pods/MenuItemKit/MenuItemKit/MenuItemKit.h
new file mode 100644 (file)
index 0000000..d0bdcb4
--- /dev/null
@@ -0,0 +1,19 @@
+//
+//  MenuItemKit.h
+//  MenuItemKit
+//
+//  Created by CHEN Xian’an on 1/16/16.
+//  Copyright © 2016 lazyapps. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+//! Project version number for MenuItemKit.
+FOUNDATION_EXPORT double MenuItemKitVersionNumber;
+
+//! Project version string for MenuItemKit.
+FOUNDATION_EXPORT const unsigned char MenuItemKitVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <MenuItemKit/PublicHeader.h>
+
+typedef void (^MenuItemAction)(UIMenuItem * _Nonnull);
diff --git a/iOS/Pods/MenuItemKit/MenuItemKit/Swizzlings.m b/iOS/Pods/MenuItemKit/MenuItemKit/Swizzlings.m
new file mode 100644 (file)
index 0000000..0b0d79b
--- /dev/null
@@ -0,0 +1,33 @@
+//
+//  Swizzlings.m
+//  MenuItemKit
+//
+//  Created by CHEN Xian’an on 1/17/16.
+//  Copyright © 2016 lazyapps. All rights reserved.
+//
+
+@import UIKit;
+
+@implementation NSObject (MenuItemKit)
+
++ (void)load
+{
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    for (id klass in @[[UIMenuController class], [UILabel class], [NSString class]]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+      [klass performSelector:NSSelectorFromString(@"_mik_load")];
+#pragma clang diagnostic pop
+    }
+  });
+}
+
++ (NSMethodSignature *)_mik_fakeSignature
+{
+  return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
+}
+
+
+
+@end
\ No newline at end of file
diff --git a/iOS/Pods/MenuItemKit/MenuItemKit/Swizzlings.swift b/iOS/Pods/MenuItemKit/MenuItemKit/Swizzlings.swift
new file mode 100644 (file)
index 0000000..1b35c59
--- /dev/null
@@ -0,0 +1,252 @@
+//
+//  UIMenuController.swift
+//  MenuItemKit
+//
+//  Created by CHEN Xian’an on 1/17/16.
+//  Copyright © 2016 lazyapps. All rights reserved.
+//
+
+import UIKit
+import ObjectiveC.runtime
+
+// This is inspired by https://github.com/steipete/PSMenuItem
+private func swizzle(class klass: AnyClass) {
+  objc_sync_enter(klass)
+  defer { objc_sync_exit(klass) }
+  let key: StaticString = #function
+  guard objc_getAssociatedObject(klass, key.utf8Start) == nil else { return }
+  if true {
+    // swizzle canBecomeFirstResponder
+    let selector = #selector(getter: UIResponder.canBecomeFirstResponder)
+    let block: @convention(block) (AnyObject) -> Bool = { _ in true }
+    setNewIMPWithBlock(block, forSelector: selector, toClass: klass)
+  }
+
+  if true {
+    // swizzle canPerformAction:withSender:
+    let selector = #selector(UIResponder.canPerformAction(_:withSender:))
+    let origIMP = class_getMethodImplementation(klass, selector)
+    typealias IMPType = @convention(c) (AnyObject, Selector, Selector, AnyObject) -> Bool
+    let origIMPC = unsafeBitCast(origIMP, to: IMPType.self)
+    let block: @convention(block) (AnyObject, Selector, AnyObject) -> Bool = {
+      return UIMenuItem.isMenuItemKitSelector($1) ? true : origIMPC($0, selector, $1, $2)
+    }
+
+    setNewIMPWithBlock(block, forSelector: selector, toClass: klass)
+  }
+
+  if true {
+    // swizzle methodSignatureForSelector:
+    let selector = NSSelectorFromString("methodSignatureForSelector:")
+    let origIMP = class_getMethodImplementation(klass, selector)
+    typealias IMPType = @convention(c) (AnyObject, Selector, Selector) -> AnyObject
+    let origIMPC = unsafeBitCast(origIMP, to: IMPType.self)
+    let block: @convention(block) (AnyObject, Selector) -> AnyObject = {
+      if UIMenuItem.isMenuItemKitSelector($1) {
+        // `NSMethodSignature` is not allowed in Swift, this is a workaround
+        return NSObject.perform(NSSelectorFromString("_mik_fakeSignature")).takeUnretainedValue()
+      }
+
+      return origIMPC($0, selector, $1)
+    }
+
+    setNewIMPWithBlock(block, forSelector: selector, toClass: klass)
+  }
+
+  if true {
+    // swizzle forwardInvocation:
+    // `NSInvocation` is not allowed in Swift, so we just use AnyObject
+    let selector = NSSelectorFromString("forwardInvocation:")
+    let origIMP = class_getMethodImplementation(klass, selector)
+    typealias IMPType = @convention(c) (AnyObject, Selector, AnyObject) -> ()
+    let origIMPC = unsafeBitCast(origIMP, to: IMPType.self)
+    let block: @convention(block) (AnyObject, AnyObject) -> () = {
+      if UIMenuItem.isMenuItemKitSelector($1.selector) {
+        guard let item = UIMenuController.shared.findMenuItemBySelector($1.selector) else { return }
+        item.actionBox.value?(item)
+      } else {
+        origIMPC($0, selector, $1)
+      }
+    }
+
+    setNewIMPWithBlock(block, forSelector: selector, toClass: klass)
+  }
+
+  objc_setAssociatedObject(klass, key.utf8Start, true, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+}
+
+private extension UIMenuController {
+
+  @objc class func _mik_load() {
+    if true {
+      let selector = #selector(setter: menuItems)
+      let origIMP = class_getMethodImplementation(self, selector)
+      typealias IMPType = @convention(c) (AnyObject, Selector, AnyObject) -> ()
+      let origIMPC = unsafeBitCast(origIMP, to: IMPType.self)
+      let block: @convention(block) (AnyObject, AnyObject) -> () = {
+        if let firstResp = UIResponder.mik_firstResponder {
+          swizzle(class: type(of: firstResp))
+        }
+
+        origIMPC($0, selector, makeUniqueImageTitles($1))
+      }
+
+      setNewIMPWithBlock(block, forSelector: selector, toClass: self)
+    }
+
+    if true {
+      let selector = #selector(setTargetRect(_:in:))
+      let origIMP = class_getMethodImplementation(self, selector)
+      typealias IMPType = @convention(c) (AnyObject, Selector, CGRect, UIView) -> ()
+      let origIMPC = unsafeBitCast(origIMP, to: IMPType.self)
+      let block: @convention(block) (AnyObject, CGRect, UIView) -> () = {
+        if let firstResp = UIResponder.mik_firstResponder {
+          swizzle(class: type(of: firstResp))
+        } else {
+          swizzle(class: type(of: $2))
+          // Must call `becomeFirstResponder` since there's no firstResponder yet
+          $2.becomeFirstResponder()
+        }
+
+        origIMPC($0, selector, $1, $2)
+      }
+
+      setNewIMPWithBlock(block, forSelector: selector, toClass: self)
+    }
+  }
+
+  static func makeUniqueImageTitles(_ itemsObj: AnyObject) -> AnyObject {
+    guard let items = itemsObj as? [UIMenuItem] else { return itemsObj }
+    var dic = [String: [UIMenuItem]]()
+    items.filter { $0.title.hasSuffix(imageItemIdetifier) }.forEach { item in
+      if dic[item.title] == nil { dic[item.title] = [] }
+      dic[item.title]?.append(item)
+    }
+
+    dic.filter { $1.count > 1 }.flatMap { $1 }.enumerated().forEach { index, item in
+      item.title = (0...index).map { _ in imageItemIdetifier }.joined(separator: "")
+    }
+
+    return items as AnyObject
+  }
+
+  func findImageItemByTitle(_ title: String?) -> UIMenuItem? {
+    guard title?.hasSuffix(imageItemIdetifier) == true else { return nil }
+    return menuItems?.lazy.filter { $0.title == title }.first
+  }
+
+  func findMenuItemBySelector(_ selector: Selector?) -> UIMenuItem? {
+    guard let selector = selector else { return nil }
+    return menuItems?.lazy.filter { sel_isEqual($0.action, selector) }.first
+  }
+
+  func findMenuItemBySelector(_ selector: String?) -> UIMenuItem? {
+    guard let selStr = selector else { return nil }
+    return findMenuItemBySelector(NSSelectorFromString(selStr))
+  }
+
+}
+
+private extension UILabel {
+
+  @objc class func _mik_load() {
+    if true {
+      let selector = #selector(drawText(in:))
+      let origIMP = class_getMethodImplementation(self, selector)
+      typealias IMPType = @convention(c) (UILabel, Selector, CGRect) -> ()
+      let origIMPC = unsafeBitCast(origIMP, to: IMPType.self)
+      let block: @convention(block) (UILabel, CGRect) -> () = { label, rect in
+        guard
+          let item = UIMenuController.shared.findImageItemByTitle(label.text),
+          let _ = item.imageBox.value
+        else { return origIMPC(label, selector, rect) }
+      }
+
+      setNewIMPWithBlock(block, forSelector: selector, toClass: self)
+    }
+
+    if true {
+      let selector = #selector(layoutSubviews)
+      let origIMP = class_getMethodImplementation(self, selector)
+      typealias IMPType = @convention(c) (UILabel, Selector) -> ()
+      let origIMPC = unsafeBitCast(origIMP, to: IMPType.self)
+      let block: @convention(block) (UILabel) -> () = { label in
+        guard
+          let item = UIMenuController.shared.findImageItemByTitle(label.text),
+          let image = item.imageBox.value
+        else { return origIMPC(label, selector) }
+
+        // Workaround for #9: https://github.com/cxa/MenuItemKit/issues/9
+        let point = CGPoint(
+          x: (label.bounds.width  - image.size.width)  / 2,
+          y: (label.bounds.height - image.size.height) / 2)
+        let imageView: Box<UIImageView> = label.associatedBoxForKey(#function, initialValue: { [weak label] in
+          let imgView = UIImageView(frame: .zero)
+          label?.addSubview(imgView)
+          return imgView
+        }())
+
+        imageView.value.image = image
+        imageView.value.frame = CGRect(origin: point, size: image.size)
+      }
+
+      setNewIMPWithBlock(block, forSelector: selector, toClass: self)
+    }
+
+    if true {
+      let selector = #selector(setter: frame)
+      let origIMP = class_getMethodImplementation(self, selector)
+      typealias IMPType = @convention(c) (UILabel, Selector, CGRect) -> ()
+      let origIMPC = unsafeBitCast(origIMP, to: IMPType.self)
+      let block: @convention(block) (UILabel, CGRect) -> () = { label, rect in
+        let isImageItem = UIMenuController.shared.findImageItemByTitle(label.text)?.imageBox.value != nil
+        let rect = isImageItem ? label.superview?.bounds ?? rect : rect
+        origIMPC(label, selector, rect)
+      }
+
+      setNewIMPWithBlock(block, forSelector: selector, toClass: self)
+    }
+  }
+
+}
+
+private extension NSString {
+
+  @objc class func _mik_load() {
+    let selector = #selector(size)
+    let origIMP = class_getMethodImplementation(self, selector)
+    typealias IMPType = @convention(c) (NSString, Selector, AnyObject) -> CGSize
+    let origIMPC = unsafeBitCast(origIMP, to: IMPType.self)
+    let block: @convention(block) (NSString, AnyObject) -> CGSize = { str, attr in
+      guard
+        let item = UIMenuController.shared.findImageItemByTitle(str as String),
+        let image = item.imageBox.value
+      else {
+        return origIMPC(str, selector, attr)
+      }
+
+      return image.size
+    }
+
+    setNewIMPWithBlock(block, forSelector: selector, toClass: self)
+  }
+
+}
+
+// MARK: Helper to find first responder
+// Source: http://stackoverflow.com/a/14135456/395213
+private weak var _currentFirstResponder: UIResponder? = nil
+
+private extension UIResponder {
+
+  static var mik_firstResponder: UIResponder? {
+    _currentFirstResponder = nil
+    UIApplication.shared.sendAction(#selector(mik_findFirstResponder(_:)), to: nil, from: nil, for: nil)
+    return _currentFirstResponder
+  }
+
+  @objc func mik_findFirstResponder(_ sender: AnyObject) {
+    _currentFirstResponder = self
+  }
+
+}
diff --git a/iOS/Pods/MenuItemKit/MenuItemKit/UIMenuItem.swift b/iOS/Pods/MenuItemKit/MenuItemKit/UIMenuItem.swift
new file mode 100644 (file)
index 0000000..3673351
--- /dev/null
@@ -0,0 +1,41 @@
+//
+//  UIMenuItem.swift
+//  MenuItemKit
+//
+//  Created by CHEN Xian’an on 1/16/16.
+//  Copyright © 2016 lazyapps. All rights reserved.
+//
+
+import UIKit
+import ObjectiveC.runtime
+
+public extension UIMenuItem {
+
+  @objc(mik_initWithTitle:image:action:)
+  convenience init(title: String, image: UIImage?, action: @escaping MenuItemAction) {
+    let title = image != nil ? title + imageItemIdetifier : title
+    self.init(title: title, action: Selector(blockIdentifierPrefix + UUID.stripedString + ":"))
+    imageBox.value = image
+    actionBox.value = action
+  }
+
+  @objc(mik_initWithTitle:action:)
+  convenience init(title: String, action: @escaping MenuItemAction) {
+    self.init(title: title, image: nil, action: action)
+  }
+
+
+  @objc(mik_isMenuItemKitSelector:)
+  static func isMenuItemKitSelector(_ sel: Selector) -> Bool {
+    return NSStringFromSelector(sel).hasPrefix(blockIdentifierPrefix)
+  }
+}
+
+// MARK: NSUUID
+private extension UUID {
+  
+  static var stripedString: String {
+    return UUID().uuidString.replacingOccurrences(of: "-", with: "_")
+  }
+  
+}
diff --git a/iOS/Pods/MenuItemKit/README.md b/iOS/Pods/MenuItemKit/README.md
new file mode 100644 (file)
index 0000000..4fea37d
--- /dev/null
@@ -0,0 +1,48 @@
+# MenuItemKit
+
+`MenuItemKit` provides image and block(closure) support for `UIMenuItem`.
+
+`MenuItemKit` is a Swift project but Objective-C is supported without any doubt.
+
+
+## How to use
+
+1. Add the `MenuItemKit` repository as a submodule of your application’s repository.
+2. Drag and drop `MenuItemKit.xcodeproj` into your application’s Xcode project or workspace.
+3. On the “General” tab of your application target’s settings, add `MenuItemKit.framework` to the “Embedded Binaries” section.
+
+If you would prefer to use Carthage or CocoaPods, please pull request.
+
+`MenuItemKit` is very easy to adopt, it provides only 2 APIs:
+
+``` swift
+typealias MenuItemAction = (UIMenuItem) -> ()
+
+extension UIMenuItem {
+    convenience init(title: String, image: UIImage?, action: MenuItemAction)
+    convenience init(title: String, action: MenuItemAction)
+}
+```
+
+For Objective-C, `MenuItemKit` provides prefixed API names for safe reasons:
+
+```objc
+@interface UIMenuItem (SWIFT_EXTENSION(MenuItemKit))
+- (nonnull instancetype)mik_initWithTitle:(NSString * _Nonnull)title image:(UIImage * _Nullable)image action:(MenuItemAction _Nonnull)action;
+- (nonnull instancetype)mik_initWithTitle:(NSString * _Nonnull)title action:(MenuItemAction _Nonnull)action;
+@end
+```
+
+Just init `UIMenuItem`s with above APIs, and set them to `menuItems` of `UIMenuItemController`. `MenuItemKit` will take care of the rest parts, you don't need to add any code related to responder chain in your view or view controller.
+
+Check dome projects (both Swift and Objective-C are provided) for more details.
+
+## About Me
+
+* Twitter: [@_cxa](https://twitter.com/_cxa)
+* Apps available in App Store: <http://lazyapps.com>
+* PayPal: xianan.chen+paypal 📧 gmail.com, buy me a cup of coffee if you find it's useful for you, thanks.
+
+## License
+
+`MenuItemKit` is released under the MIT license. In short, it's royalty-free but you must keep the copyright notice in your code or software distribution.
diff --git a/iOS/Pods/OAuthSwift/LICENSE b/iOS/Pods/OAuthSwift/LICENSE
new file mode 100644 (file)
index 0000000..fa84148
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Dongri Jin <dongriat@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/iOS/Pods/OAuthSwift/README.md b/iOS/Pods/OAuthSwift/README.md
new file mode 100644 (file)
index 0000000..c106961
--- /dev/null
@@ -0,0 +1,262 @@
+<p align="center">
+  <img src="Assets/OAuthSwift-icon.png?raw=true" alt="OAuthSwift"/>
+</p>
+
+# OAuthSwift
+
+Swift based OAuth library for iOS and macOS.
+
+## Support OAuth1.0, OAuth2.0
+
+Twitter, Flickr, Github, Instagram, Foursquare, Fitbit, Withings, Linkedin, Dropbox, Dribbble, Salesforce, BitBucket, GoogleDrive, Smugmug, Intuit, Zaim, Tumblr, Slack, Uber, Gitter, Facebook, Spotify, Typetalk, SoundCloud, etc
+
+## Sponsored by Auth0 <span><img src="https://user-images.githubusercontent.com/1801923/31792116-d4fca9ec-b512-11e7-92eb-56e8d3df8e70.png" height="28" align="top"></span>
+If you want to easily add authentication to Swift apps, feel free to check out Auth0's Swift SDK and free plan at [auth0.com/overview](https://auth0.com/overview?utm_source=GHsponsor&utm_medium=GHsponsor&utm_campaign=oauthswift&utm_content=auth)
+
+## Installation
+
+OAuthSwift is packaged as a Swift framework. Currently this is the simplest way to add it to your app:
+
+* Drag OAuthSwift.xcodeproj to your project in the Project Navigator.
+* Select your project and then your app target. Open the Build Phases panel.
+* Expand the Target Dependencies group, and add OAuthSwift framework.
+* import OAuthSwift whenever you want to use OAuthSwift.
+
+### Support Carthage
+
+* Install Carthage (https://github.com/Carthage/Carthage)
+* Create Cartfile file
+```
+github "OAuthSwift/OAuthSwift" ~> 1.2.0
+```
+* Run `carthage update`.
+* On your application targets’ “General” settings tab, in the “Embedded Binaries” section, drag and drop OAuthSwift.framework from the Carthage/Build/iOS folder on disk.
+
+### Support CocoaPods
+
+* Podfile
+
+```
+platform :ios, '10.0'
+use_frameworks!
+
+pod 'OAuthSwift', '~> 1.2.0'
+```
+
+### swift 3
+
+Use the `swift3` branch, or the tag `1.1.2` on main branch
+
+## How to
+### Setting URL Schemes
+In info tab of your target
+![Image](Assets/URLSchemes.png "Image")
+Replace oauth-swift by your application name
+
+### Handle URL in AppDelegate
+- On iOS implement `UIApplicationDelegate` method
+```swift
+func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
+  if (url.host == "oauth-callback") {
+    OAuthSwift.handle(url: url)
+  }
+  return true
+}
+```
+:warning: Any other application may try to open a URL with your url scheme. So you can check the source application, for instance for safari controller :
+```
+if (options[.sourceApplication] as? String == "com.apple.SafariViewService") {
+```
+
+- On macOS you must register an handler on `NSAppleEventManager` for event type `kAEGetURL` (see demo code)
+```swift
+func applicationDidFinishLaunching(_ aNotification: NSNotification) {
+    NSAppleEventManager.shared().setEventHandler(self, andSelector:#selector(AppDelegate.handleGetURL(event:withReplyEvent:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL))
+}
+func handleGetURL(event: NSAppleEventDescriptor!, withReplyEvent: NSAppleEventDescriptor!) {
+    if let urlString = event.paramDescriptor(forKeyword: AEKeyword(keyDirectObject))?.stringValue, let url = URL(string: urlString) {
+        OAuthSwift.handle(url: url)
+    }
+}
+```
+
+### Authorize with OAuth1.0
+```swift
+// create an instance and retain it
+oauthswift = OAuth1Swift(
+    consumerKey:    "********",
+    consumerSecret: "********",
+    requestTokenUrl: "https://api.twitter.com/oauth/request_token",
+    authorizeUrl:    "https://api.twitter.com/oauth/authorize",
+    accessTokenUrl:  "https://api.twitter.com/oauth/access_token"
+)
+// authorize
+let handle = oauthswift.authorize(
+    withCallbackURL: URL(string: "oauth-swift://oauth-callback/twitter")!,
+    success: { credential, response, parameters in
+      print(credential.oauthToken)
+      print(credential.oauthTokenSecret)
+      print(parameters["user_id"])
+      // Do your request
+    },
+    failure: { error in
+      print(error.localizedDescription)
+    }             
+)
+```
+### OAuth1 without authorization
+No urls to specify here
+```swift
+// create an instance and retain it
+oauthswift = OAuth1Swift(
+    consumerKey:    "********",
+    consumerSecret: "********"
+)
+// do your HTTP request without authorize
+oauthswift.client.get("https://api.example.com/foo/bar",
+    success: { response in
+        //....
+    },
+    failure: { error in
+        //...
+    }
+)
+```
+
+### Authorize with OAuth2.0
+```swift
+// create an instance and retain it
+oauthswift = OAuth2Swift(
+    consumerKey:    "********",
+    consumerSecret: "********",
+    authorizeUrl:   "https://api.instagram.com/oauth/authorize",
+    responseType:   "token"
+)
+let handle = oauthswift.authorize(
+    withCallbackURL: URL(string: "oauth-swift://oauth-callback/instagram")!,
+    scope: "likes+comments", state:"INSTAGRAM",
+    success: { credential, response, parameters in
+      print(credential.oauthToken)
+      // Do your request
+    },
+    failure: { error in
+      print(error.localizedDescription)
+    }
+)
+
+```
+
+See demo for more examples
+
+### Handle authorize URL
+The authorize URL allows the user to connect to a provider and give access to your application.
+
+By default this URL is opened into the external web browser (ie. safari), but apple does not allow it for app-store iOS applications.
+
+To change this behavior you must set an `OAuthSwiftURLHandlerType`, simple protocol to handle an `URL`
+```swift
+oauthswift.authorizeURLHandler = ..
+```
+For instance you can embed a web view into your application by providing a controller that displays a web view (`UIWebView`, `WKWebView`).
+Then this controller must implement `OAuthSwiftURLHandlerType` to load the URL into the web view
+```swift
+func handle(_ url: NSURL) {
+  let req = URLRequest(URL: targetURL)
+  self.webView.loadRequest(req)
+  ...
+```
+and present the view (`present(viewController`, `performSegue(withIdentifier: `, ...)
+*You can extend `OAuthWebViewController` for a default implementation of view presentation and dismiss*
+
+#### Use the SFSafariViewController (iOS9)
+A default implementation of `OAuthSwiftURLHandlerType` is provided using the `SFSafariViewController`, with automatic view dismiss.
+```swift
+oauthswift.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauthswift)
+```
+Of course you can create your own class or customize the controller by setting the variable `SafariURLHandler#factory`.
+
+### Make signed request
+
+Just call HTTP functions of `oauthswift.client`
+
+```swift
+oauthswift.client.get("https://api.linkedin.com/v1/people/~",
+      success: { response in
+        let dataString = response.string
+        print(dataString)
+      },
+      failure: { error in
+        print(error)
+      }
+)
+// same with request method
+oauthswift.client.request("https://api.linkedin.com/v1/people/~", .GET,
+      parameters: [:], headers: [:],
+      success: { ...
+```
+
+See more examples in the demo application: [ViewController.swift](/Demo/Common/ViewController.swift)
+
+## OAuth provider pages
+
+* [Twitter](https://dev.twitter.com/oauth)  
+* [Flickr](https://www.flickr.com/services/api/auth.oauth.html)  
+* [Github](https://developer.github.com/v3/oauth/)  
+* [Instagram](http://instagram.com/developer/authentication)  
+* [Foursquare](https://developer.foursquare.com/overview/auth)  
+* [Fitbit](https://dev.fitbit.com/build/reference/web-api/oauth2/)  
+* [Withings](http://oauth.withings.com/api)  
+* [Linkedin](https://developer.linkedin.com/docs/oauth2)  
+* [Dropbox](https://www.dropbox.com/developers/core/docs)  
+* [Dribbble](http://developer.dribbble.com/v1/oauth/)
+* [Salesforce](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/)
+* [BitBucket](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html)
+* [GoogleDrive](https://developers.google.com/drive/v2/reference/)
+* [Smugmug](https://smugmug.atlassian.net/wiki/display/API/OAuth)
+* [Intuit](https://developer.intuit.com/docs/0100_accounting/0060_authentication_and_authorization/oauth_management_api)
+* [Zaim](https://dev.zaim.net/home/api/authorize)
+* [Tumblr](https://www.tumblr.com/docs/en/api/v2#auth)
+* [Slack](https://api.slack.com/docs/oauth)
+* [Uber](https://developer.uber.com/docs/ride-requests/guides/authentication/introduction#oauth-20)
+* [Gitter](https://developer.gitter.im/docs/authentication)
+* [Facebook](https://developers.facebook.com/docs/facebook-login)
+* [Spotify](https://developer.spotify.com/web-api/authorization-guide/)
+* [Trello](https://developers.trello.com/authorize)
+* [Buffer](https://buffer.com/developers/api/oauth)
+* [Goodreads](https://www.goodreads.com/api/documentation#oauth)
+* [Typetalk](http://developer.nulab-inc.com/docs/typetalk/auth)
+* [SoundCloud](https://developers.soundcloud.com/docs/api/guide#authentication)
+* [Doper](https://doper.io/developer/oauth)
+* [NounProject](http://api.thenounproject.com/getting_started.html#authentication)
+
+## Images
+
+![Image](Assets/Services.png "Image")
+![Image](Assets/TwitterOAuth.png "Image")
+![Image](Assets/TwitterOAuthTokens.png "Image")
+
+## Contributing
+ See [CONTRIBUTING.md](.github/CONTRIBUTING.md)
+
+[Add a new service in demo app](https://github.com/OAuthSwift/OAuthSwift/wiki/Demo-application#add-a-new-service-in-demo-app)
+
+
+## Integration
+OAuthSwift could be used with others frameworks
+
+You can sign [Alamofire](https://github.com/Alamofire/Alamofire) request with [OAuthSwiftAlamofire](https://github.com/OAuthSwift/OAuthSwiftAlamofire)
+
+To achieve great asynchronous code you can use one of these integration frameworks
+- [OAuthSwiftFutures](https://github.com/OAuthSwift/OAuthSwiftFutures) - [BrightFutures](https://github.com/Thomvis/BrightFutures)
+- [OAuthRxSwift](https://github.com/OAuthSwift/OAuthRxSwift) - [RxSwift](https://github.com/ReactiveX/RxSwift)
+- [OAuthReactiveSwift](https://github.com/OAuthSwift/OAuthReactiveSwift) - [ReactiveSwift](https://github.com/ReactiveCocoa/ReactiveSwift)
+
+## License
+
+OAuthSwift is available under the MIT license. See the LICENSE file for more info.
+
+[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat
+            )](http://mit-license.org) [![Platform](https://img.shields.io/badge/platform-iOS_OSX_TVOS-lightgrey.svg?style=flat
+             )](https://developer.apple.com/resources/) [![Language](https://img.shields.io/badge/language-swift-orange.svg?style=flat
+             )](https://developer.apple.com/swift) [![Cocoapod](https://img.shields.io/cocoapods/v/OAuthSwift.svg?style=flat)](http://cocoadocs.org/docsets/OAuthSwift/)
+[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
diff --git a/iOS/Pods/OAuthSwift/Sources/Collection+OAuthSwift.swift b/iOS/Pods/OAuthSwift/Sources/Collection+OAuthSwift.swift
new file mode 100644 (file)
index 0000000..72b6cea
--- /dev/null
@@ -0,0 +1,34 @@
+//
+//  Collection+OAuthSwift.swift
+//  OAuthSwift
+//
+//  Created by phimage on 02/10/16.
+//  Copyright © 2016 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+extension Collection where Self.Iterator.Element == UInt8, Self.Index == Int {
+
+    var toUInt32: UInt32 {
+        assert(self.count > 3)
+        // XXX optimize do the job only for the first one...
+        return toUInt32Array()[0]
+    }
+
+    func toUInt32Array() -> [UInt32] {
+        var result = [UInt32]()
+        result.reserveCapacity(16)
+        for idx in stride(from: self.startIndex, to: self.endIndex, by: MemoryLayout<UInt32>.size) {
+            var val: UInt32 = 0
+            val |= self.count > 3 ? UInt32(self[idx.advanced(by: 3)]) << 24 : 0
+            val |= self.count > 2 ? UInt32(self[idx.advanced(by: 2)]) << 16 : 0
+            val |= self.count > 1 ? UInt32(self[idx.advanced(by: 1)]) << 8  : 0
+            //swiftlint:disable:next empty_count
+            val |= self.count > 0 ? UInt32(self[idx]) : 0
+            result.append(val)
+        }
+
+        return result
+    }
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/Data+OAuthSwift.swift b/iOS/Pods/OAuthSwift/Sources/Data+OAuthSwift.swift
new file mode 100644 (file)
index 0000000..6930347
--- /dev/null
@@ -0,0 +1,45 @@
+//
+//  Data+OAuthSwift.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 1/28/15.
+//  Copyright (c) 2015 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+extension Data {
+
+    internal init(data: Data) {
+        self.init()
+        self.append(data)
+    }
+
+    internal mutating func append(_ bytes: [UInt8]) {
+        self.append(bytes, count: bytes.count)
+    }
+    internal mutating func append(_ byte: UInt8) {
+        append([byte])
+    }
+    internal mutating func append(_ byte: UInt16) {
+        append(UInt8(byte >> 0 & 0xFF))
+        append(UInt8(byte >> 8 & 0xFF))
+    }
+    internal  mutating func append(_ byte: UInt32) {
+        append(UInt16(byte >>  0 & 0xFFFF))
+        append(UInt16(byte >> 16 & 0xFFFF))
+    }
+    internal mutating func append(_  byte: UInt64) {
+        append(UInt32(byte >>  0 & 0xFFFFFFFF))
+        append(UInt32(byte >> 32 & 0xFFFFFFFF))
+    }
+
+    var bytes: [UInt8] {
+        return Array(self)
+        /* let count = self.count / MemoryLayout<UInt8>.size
+         var bytesArray = [UInt8](repeating: 0, count: count)
+        self.copyBytes(to:&bytesArray, count: count * MemoryLayout<UInt8>.size)
+        return bytesArray*/
+    }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/Dictionary+OAuthSwift.swift b/iOS/Pods/OAuthSwift/Sources/Dictionary+OAuthSwift.swift
new file mode 100755 (executable)
index 0000000..f47a4e5
--- /dev/null
@@ -0,0 +1,79 @@
+//
+//  Dictionary+OAuthSwift.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 6/21/14.
+//  Copyright (c) 2014 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+extension Dictionary {
+
+    func join(_ other: Dictionary) -> Dictionary {
+        var joinedDictionary = Dictionary()
+
+        for (key, value) in self {
+            joinedDictionary.updateValue(value, forKey: key)
+        }
+
+        for (key, value) in other {
+            joinedDictionary.updateValue(value, forKey: key)
+        }
+
+        return joinedDictionary
+    }
+
+    var urlEncodedQuery: String {
+        var parts = [String]()
+
+        for (key, value) in self {
+            let keyString = "\(key)".urlEncoded
+            let valueString = "\(value)".urlEncoded
+            let query = "\(keyString)=\(valueString)"
+            parts.append(query)
+        }
+
+        return parts.joined(separator: "&")
+    }
+
+    mutating func merge<K, V>(_ dictionaries: Dictionary<K, V>...) {
+        for dict in dictionaries {
+            for (key, value) in dict {
+                if let v = value as? Value, let k = key as? Key {
+                    self.updateValue(v, forKey: k)
+                }
+            }
+        }
+    }
+
+    func map<K: Hashable, V> (_ transform: (Key, Value) -> (K, V)) -> [K: V] {
+        var results: [K: V] = [:]
+        for k in self.keys {
+            if let value = self[ k ] {
+                let (u, w) = transform(k, value)
+                results.updateValue(w, forKey: u)
+            }
+        }
+        return results
+    }
+}
+
+extension Dictionary {
+    @available(swift, introduced: 3.2, obsoleted: 4.0)
+    public func filter(_ isIncluded: (Key, Value) throws -> Bool) rethrows -> [Key: Value] {
+        var resultDictionary = [Key: Value](minimumCapacity: count)
+        for (key, value) in self {
+            if try isIncluded(key, value) {
+                resultDictionary[key] = value
+            }
+        }
+        return resultDictionary
+    }
+}
+
+func +=<K, V> (left: inout [K: V], right: [K: V]) { left.merge(right) }
+func +<K, V> (left: [K: V], right: [K: V]) -> [K: V] { return left.join(right) }
+func +=<K, V> (left: inout [K: V]?, right: [K: V]) {
+    if left != nil { left?.merge(right) } else { left = right }
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/HMAC.swift b/iOS/Pods/OAuthSwift/Sources/HMAC.swift
new file mode 100644 (file)
index 0000000..27b13c5
--- /dev/null
@@ -0,0 +1,55 @@
+//
+//  HMAC.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 1/28/15.
+//  Copyright (c) 2015 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+open class HMAC {
+
+    let key: [UInt8] = []
+
+    class internal func sha1(key: Data, message: Data) -> Data? {
+        let blockSize = 64
+        var key = key.bytes
+        let message = message.bytes
+
+        if key.count > blockSize {
+            key = SHA1(key).calculate()
+        } else if key.count < blockSize { // padding
+            key += [UInt8](repeating: 0, count: blockSize - key.count)
+        }
+
+        var ipad = [UInt8](repeating: 0x36, count: blockSize)
+        for idx in key.indices {
+            ipad[idx] = key[idx] ^ ipad[idx]
+        }
+
+        var opad = [UInt8](repeating: 0x5c, count: blockSize)
+        for idx in key.indices {
+            opad[idx] = key[idx] ^ opad[idx]
+        }
+
+        let ipadAndMessageHash = SHA1(ipad + message).calculate()
+        let mac = SHA1(opad + ipadAndMessageHash).calculate()
+
+        return Data(bytes: UnsafePointer<UInt8>(mac), count: mac.count)
+
+    }
+
+}
+
+extension HMAC: OAuthSwiftSignatureDelegate {
+    open static func sign(hashMethod: OAuthSwiftHashMethod, key: Data, message: Data) -> Data? {
+        switch hashMethod {
+        case .sha1:
+            return sha1(key: key, message: message)
+        case .none:
+            assertionFailure("Must no sign with none")
+            return nil
+        }
+    }
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/Int+OAuthSwift.swift b/iOS/Pods/OAuthSwift/Sources/Int+OAuthSwift.swift
new file mode 100644 (file)
index 0000000..022aff2
--- /dev/null
@@ -0,0 +1,33 @@
+//
+//  Int+OAuthSwift.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 1/28/15.
+//  Copyright (c) 2015 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+extension Int {
+    public func bytes(_ totalBytes: Int = MemoryLayout<Int>.size) -> [UInt8] {
+        return arrayOfBytes(self, length: totalBytes)
+    }
+}
+
+private func arrayOfBytes<T>(_ value: T, length: Int? = nil) -> [UInt8] {
+    let totalBytes = length ?? MemoryLayout<T>.size
+
+    let valuePointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
+    valuePointer.pointee = value
+
+    let bytesPointer = UnsafeMutablePointer<UInt8>(OpaquePointer(valuePointer))
+    var bytes = [UInt8](repeating: 0, count: totalBytes)
+    for j in 0..<min(MemoryLayout<T>.size, totalBytes) {
+        bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee
+    }
+
+    valuePointer.deinitialize(count: 1)
+    valuePointer.deallocate()
+
+    return bytes
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/NSError+OAuthSwift.swift b/iOS/Pods/OAuthSwift/Sources/NSError+OAuthSwift.swift
new file mode 100644 (file)
index 0000000..dc31919
--- /dev/null
@@ -0,0 +1,70 @@
+//
+//  NSError+OAuthSwift.swift
+//  OAuthSwift
+//
+//  Created by Goessler, Florian on 04/04/16.
+//  Copyright © 2016 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+public extension NSError {
+
+    /// Checks the headers contained in the userInfo whether this error was caused by an 
+    /// expired/invalid access token.
+    ///
+    /// Criteria for invalid token error: WWW-Authenticate header contains a field "error" with
+    /// value "invalid_token".
+    ///
+    /// Also implements a special handling for the Facebook API, which indicates invalid tokens in a 
+    /// different manner. See https://developers.facebook.com/docs/graph-api/using-graph-api#errors
+       public var isExpiredToken: Bool {
+        guard self.domain == NSURLErrorDomain else {
+            return false
+        }
+               if self.code == 401 {
+                       if let reponseHeaders = self.userInfo["Response-Headers"] as? [String: String],
+                               let authenticateHeader = reponseHeaders["WWW-Authenticate"] ?? reponseHeaders["Www-Authenticate"] {
+                               let headerDictionary = authenticateHeader.headerDictionary
+                               if let error = headerDictionary["error"], error == "invalid_token" || error == "\"invalid_token\"" {
+                                       return true
+                               }
+                       }
+            if let body = self.userInfo["Response-Body"] as? String,
+                let bodyData = body.data(using: OAuthSwiftDataEncoding),
+                let json = try? JSONSerialization.jsonObject(with: bodyData, options: []),
+                let jsonDic = json as? [String: AnyObject] {
+                if let error = jsonDic["error"] as? String, error == "invalid_token" || error == "\"invalid_token\"" {
+                    return true
+                }
+                if let errors = jsonDic["errors"] as? [[String: AnyObject]] {
+                    for error in errors {
+                        if let errorType = error["errorType"] as? String, errorType == "invalid_token" {
+                            return true
+                        }
+                    }
+                }
+            }
+               }
+
+        // Detect access token expiration errors from facebook
+        // Docu: https://developers.facebook.com/docs/graph-api/using-graph-api#errors
+        if self.code == 400 {
+            if let urlString = self.userInfo[NSURLErrorFailingURLErrorKey] as? String, urlString.contains("graph.facebook.com") {
+                if let body = self.userInfo["Response-Body"] as? String,
+                    let bodyData = body.data(using: OAuthSwiftDataEncoding),
+                    let json = try? JSONSerialization.jsonObject(with: bodyData, options: []),
+                    let jsonDic = json as? [String: AnyObject] {
+                    let errorCode = jsonDic["error"]?["code"] as? Int
+                    let errorSubCode = jsonDic["error"]?["error_subcode"] as? Int
+                    if (errorCode == 102 && errorSubCode == nil) || errorSubCode == 463 || errorSubCode == 467 {
+                        return true
+                    }
+                }
+            }
+        }
+
+               return false
+       }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/NotificationCenter+OAuthSwift.swift b/iOS/Pods/OAuthSwift/Sources/NotificationCenter+OAuthSwift.swift
new file mode 100644 (file)
index 0000000..2c5649b
--- /dev/null
@@ -0,0 +1,13 @@
+//
+//  NotificationCenter+OAuthSwift.swift
+//  OAuthSwift
+//
+//  Created by hiragram on 2017/04/04.
+//  Copyright © 2017年 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+public extension Notification.Name {
+    public static let OAuthSwiftHandleCallbackURL: Notification.Name = .init("OAuthSwiftCallbackNotificationName")
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuth1Swift.swift b/iOS/Pods/OAuthSwift/Sources/OAuth1Swift.swift
new file mode 100644 (file)
index 0000000..7e3f0f2
--- /dev/null
@@ -0,0 +1,177 @@
+//
+//  OAuth1Swift.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 6/22/14.
+//  Copyright (c) 2014 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+open class OAuth1Swift: OAuthSwift {
+
+    /// If your oauth provider doesn't provide `oauth_verifier`
+    /// set this value to true (default: false)
+    open var allowMissingOAuthVerifier: Bool = false
+
+    /// Optionally add callback URL to authorize Url (default: false)
+    open var addCallbackURLToAuthorizeURL: Bool = false
+
+    var consumerKey: String
+    var consumerSecret: String
+    var requestTokenUrl: String
+    var authorizeUrl: String
+    var accessTokenUrl: String
+
+    // MARK: init
+    public init(consumerKey: String, consumerSecret: String, requestTokenUrl: String, authorizeUrl: String, accessTokenUrl: String) {
+        self.consumerKey = consumerKey
+        self.consumerSecret = consumerSecret
+        self.requestTokenUrl = requestTokenUrl
+        self.authorizeUrl = authorizeUrl
+        self.accessTokenUrl = accessTokenUrl
+        super.init(consumerKey: consumerKey, consumerSecret: consumerSecret)
+        self.client.credential.version = .oauth1
+    }
+
+    public convenience override init(consumerKey: String, consumerSecret: String) {
+        self.init(consumerKey: consumerKey, consumerSecret: consumerSecret, requestTokenUrl: "", authorizeUrl: "", accessTokenUrl: "")
+    }
+
+    public convenience init?(parameters: ConfigParameters) {
+        guard let consumerKey = parameters["consumerKey"], let consumerSecret = parameters["consumerSecret"],
+            let requestTokenUrl = parameters["requestTokenUrl"], let authorizeUrl = parameters["authorizeUrl"], let accessTokenUrl = parameters["accessTokenUrl"] else {
+            return nil
+        }
+        self.init(consumerKey: consumerKey, consumerSecret: consumerSecret,
+          requestTokenUrl: requestTokenUrl,
+          authorizeUrl: authorizeUrl,
+          accessTokenUrl: accessTokenUrl)
+    }
+
+    open var parameters: ConfigParameters {
+        return [
+            "consumerKey": consumerKey,
+            "consumerSecret": consumerSecret,
+            "requestTokenUrl": requestTokenUrl,
+            "authorizeUrl": authorizeUrl,
+            "accessTokenUrl": accessTokenUrl
+        ]
+    }
+
+    // MARK: functions
+    // 0. Start
+    @discardableResult
+    open func authorize(withCallbackURL callbackURL: URL, success: @escaping TokenSuccessHandler, failure: FailureHandler?) -> OAuthSwiftRequestHandle? {
+
+        self.postOAuthRequestToken(callbackURL: callbackURL, success: { [unowned self] credential, _, _ in
+
+            self.observeCallback { [weak self] url in
+                guard let this = self else { OAuthSwift.retainError(failure); return }
+                var responseParameters = [String: String]()
+                if let query = url.query {
+                    responseParameters += query.parametersFromQueryString
+                }
+                if let fragment = url.fragment, !fragment.isEmpty {
+                    responseParameters += fragment.parametersFromQueryString
+                }
+                if let token = responseParameters["token"] {
+                    responseParameters["oauth_token"] = token
+                }
+
+                if let token = responseParameters["oauth_token"], !token.isEmpty {
+                    this.client.credential.oauthToken = token.safeStringByRemovingPercentEncoding
+                    if let oauth_verifier = responseParameters["oauth_verifier"] {
+                        this.client.credential.oauthVerifier = oauth_verifier.safeStringByRemovingPercentEncoding
+                    } else {
+                        if !this.allowMissingOAuthVerifier {
+                            failure?(OAuthSwiftError.configurationError(message: "Missing oauth_verifier. Maybe use allowMissingOAuthVerifier=true"))
+                            return
+                        }
+                    }
+                    this.postOAuthAccessTokenWithRequestToken(success: success, failure: failure)
+                } else {
+                    failure?(OAuthSwiftError.missingToken)
+                    return
+                }
+            }
+            // 2. Authorize
+            if let token = credential.oauthToken.urlQueryEncoded {
+                var urlString = self.authorizeUrl + (self.authorizeUrl.contains("?") ? "&" : "?")
+                urlString += "oauth_token=\(token)"
+                if self.addCallbackURLToAuthorizeURL {
+                    urlString += "&oauth_callback=\(callbackURL.absoluteString)"
+                }
+                if let queryURL = URL(string: urlString) {
+                    self.authorizeURLHandler.handle(queryURL)
+                } else {
+                    failure?(OAuthSwiftError.encodingError(urlString: urlString))
+                }
+            } else {
+                failure?(OAuthSwiftError.encodingError(urlString: credential.oauthToken)) //TODO specific error
+            }
+
+        }, failure: failure)
+
+        return self
+    }
+
+    @discardableResult
+    open func authorize(withCallbackURL urlString: String, success: @escaping TokenSuccessHandler, failure: FailureHandler?) -> OAuthSwiftRequestHandle? {
+        guard let url = URL(string: urlString) else {
+              failure?(OAuthSwiftError.encodingError(urlString: urlString))
+            return nil
+        }
+        return authorize(withCallbackURL: url, success: success, failure: failure)
+    }
+
+    // 1. Request token
+    func postOAuthRequestToken(callbackURL: URL, success: @escaping TokenSuccessHandler, failure: FailureHandler?) {
+        var parameters = [String: Any]()
+        parameters["oauth_callback"] = callbackURL.absoluteString
+
+        if let handle = self.client.post(
+            self.requestTokenUrl, parameters: parameters,
+            success: { [weak self] response in
+                guard let this = self else { OAuthSwift.retainError(failure); return }
+                let parameters = response.string?.parametersFromQueryString ?? [:]
+                if let oauthToken = parameters["oauth_token"] {
+                    this.client.credential.oauthToken = oauthToken.safeStringByRemovingPercentEncoding
+                }
+                if let oauthTokenSecret=parameters["oauth_token_secret"] {
+                    this.client.credential.oauthTokenSecret = oauthTokenSecret.safeStringByRemovingPercentEncoding
+                }
+                success(this.client.credential, response, parameters)
+            }, failure: failure
+            ) {
+            self.putHandle(handle, withKey: UUID().uuidString)
+        }
+    }
+
+    // 3. Get Access token
+    func postOAuthAccessTokenWithRequestToken(success: @escaping TokenSuccessHandler, failure: FailureHandler?) {
+        var parameters = [String: Any]()
+        parameters["oauth_token"] = self.client.credential.oauthToken
+        if !self.allowMissingOAuthVerifier {
+            parameters["oauth_verifier"] = self.client.credential.oauthVerifier
+        }
+
+        if let handle = self.client.post(
+            self.accessTokenUrl, parameters: parameters,
+            success: { [weak self] response in
+                guard let this = self else { OAuthSwift.retainError(failure); return }
+                let parameters = response.string?.parametersFromQueryString ?? [:]
+                if let oauthToken = parameters["oauth_token"] {
+                    this.client.credential.oauthToken = oauthToken.safeStringByRemovingPercentEncoding
+                }
+                if let oauthTokenSecret = parameters["oauth_token_secret"] {
+                    this.client.credential.oauthTokenSecret = oauthTokenSecret.safeStringByRemovingPercentEncoding
+                }
+                success(this.client.credential, response, parameters)
+            }, failure: failure
+            ) {
+            self.putHandle(handle, withKey: UUID().uuidString)
+        }
+    }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuth2Swift.swift b/iOS/Pods/OAuthSwift/Sources/OAuth2Swift.swift
new file mode 100644 (file)
index 0000000..8cbb624
--- /dev/null
@@ -0,0 +1,341 @@
+//
+//  OAuth2Swift.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 6/22/14.
+//  Copyright (c) 2014 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+open class OAuth2Swift: OAuthSwift {
+
+    /// If your oauth provider need to use basic authentification
+    /// set value to true (default: false)
+    open var accessTokenBasicAuthentification = false
+
+    /// Set to true to deactivate state check. Be careful of CSRF
+    open var allowMissingStateCheck: Bool = false
+
+    /// Encode callback url, some services require it to be encoded.
+    open var encodeCallbackURL: Bool = false
+
+    /// Encode callback url inside the query, this is second encoding phase when the entire query string gets assembled. In rare 
+    /// cases, like with Imgur, the url needs to be encoded only once and this value needs to be set to `false`.
+    open var encodeCallbackURLQuery: Bool = true
+
+    var consumerKey: String
+    var consumerSecret: String
+    var authorizeUrl: String
+    var accessTokenUrl: String?
+    var responseType: String
+    var contentType: String?
+
+    // MARK: init
+    public convenience init(consumerKey: String, consumerSecret: String, authorizeUrl: String, accessTokenUrl: String, responseType: String) {
+        self.init(consumerKey: consumerKey, consumerSecret: consumerSecret, authorizeUrl: authorizeUrl, responseType: responseType)
+        self.accessTokenUrl = accessTokenUrl
+    }
+
+    public convenience init(consumerKey: String, consumerSecret: String, authorizeUrl: String, accessTokenUrl: String, responseType: String, contentType: String) {
+        self.init(consumerKey: consumerKey, consumerSecret: consumerSecret, authorizeUrl: authorizeUrl, responseType: responseType)
+        self.accessTokenUrl = accessTokenUrl
+        self.contentType = contentType
+    }
+
+    public init(consumerKey: String, consumerSecret: String, authorizeUrl: String, responseType: String) {
+        self.consumerKey = consumerKey
+        self.consumerSecret = consumerSecret
+        self.authorizeUrl = authorizeUrl
+        self.responseType = responseType
+        super.init(consumerKey: consumerKey, consumerSecret: consumerSecret)
+        self.client.credential.version = .oauth2
+    }
+
+    public convenience init?(parameters: ConfigParameters) {
+        guard let consumerKey = parameters["consumerKey"], let consumerSecret = parameters["consumerSecret"],
+              let responseType = parameters["responseType"], let authorizeUrl = parameters["authorizeUrl"] else {
+            return nil
+        }
+        if let accessTokenUrl = parameters["accessTokenUrl"] {
+            self.init(consumerKey: consumerKey, consumerSecret: consumerSecret,
+                authorizeUrl: authorizeUrl, accessTokenUrl: accessTokenUrl, responseType: responseType)
+        } else {
+            self.init(consumerKey: consumerKey, consumerSecret: consumerSecret,
+                authorizeUrl: authorizeUrl, responseType: responseType)
+        }
+    }
+
+    open var parameters: ConfigParameters {
+        return [
+            "consumerKey": consumerKey,
+            "consumerSecret": consumerSecret,
+            "authorizeUrl": authorizeUrl,
+            "accessTokenUrl": accessTokenUrl ?? "",
+            "responseType": responseType
+        ]
+    }
+
+    // MARK: functions
+    @discardableResult
+    open func authorize(withCallbackURL callbackURL: URL?, scope: String, state: String, parameters: Parameters = [:], headers: OAuthSwift.Headers? = nil, success: @escaping TokenSuccessHandler, failure: FailureHandler?) -> OAuthSwiftRequestHandle? {
+
+        self.observeCallback { [weak self] url in
+            guard let this = self else {
+                OAuthSwift.retainError(failure)
+                return
+            }
+            var responseParameters = [String: String]()
+            if let query = url.query {
+                responseParameters += query.parametersFromQueryString
+            }
+            if let fragment = url.fragment, !fragment.isEmpty {
+                responseParameters += fragment.parametersFromQueryString
+            }
+            if let accessToken = responseParameters["access_token"] {
+                this.client.credential.oauthToken = accessToken.safeStringByRemovingPercentEncoding
+                if let expiresIn: String = responseParameters["expires_in"], let offset = Double(expiresIn) {
+                    this.client.credential.oauthTokenExpiresAt = Date(timeInterval: offset, since: Date())
+                }
+                success(this.client.credential, nil, responseParameters)
+            } else if let code = responseParameters["code"] {
+                if !this.allowMissingStateCheck {
+                    guard let responseState = responseParameters["state"] else {
+                        failure?(OAuthSwiftError.missingState)
+                        return
+                    }
+                    if responseState != state {
+                        failure?(OAuthSwiftError.stateNotEqual(state: state, responseState: responseState))
+                        return
+                    }
+                }
+                let callbackURLEncoded: URL?
+                if let callbackURL = callbackURL {
+                    callbackURLEncoded = URL(string: callbackURL.absoluteString.urlEncoded)!
+                } else {
+                    callbackURLEncoded = nil
+                }
+                if let handle = this.postOAuthAccessTokenWithRequestToken(
+                    byCode: code.safeStringByRemovingPercentEncoding,
+                    callbackURL: callbackURLEncoded, headers: headers, success: success, failure: failure) {
+                    this.putHandle(handle, withKey: UUID().uuidString)
+                }
+            } else if let error = responseParameters["error"] {
+                let description = responseParameters["error_description"] ?? ""
+                let message = NSLocalizedString(error, comment: description)
+                failure?(OAuthSwiftError.serverError(message: message))
+            } else {
+                let message = "No access_token, no code and no error provided by server"
+                failure?(OAuthSwiftError.serverError(message: message))
+            }
+        }
+
+        var queryErrorString = ""
+        let encodeError: (String, String) -> Void = { name, value in
+            if let newQuery = queryErrorString.urlQueryByAppending(parameter: name, value: value, encode: false) {
+                queryErrorString = newQuery
+            }
+        }
+
+        var queryString: String? = ""
+        queryString = queryString?.urlQueryByAppending(parameter: "client_id", value: self.consumerKey, encodeError)
+        if let callbackURL = callbackURL {
+            queryString = queryString?.urlQueryByAppending(parameter: "redirect_uri", value: self.encodeCallbackURL ? callbackURL.absoluteString.urlEncoded : callbackURL.absoluteString, encode: self.encodeCallbackURLQuery, encodeError)
+        }
+        queryString = queryString?.urlQueryByAppending(parameter: "response_type", value: self.responseType, encodeError)
+        queryString = queryString?.urlQueryByAppending(parameter: "scope", value: scope, encodeError)
+        queryString = queryString?.urlQueryByAppending(parameter: "state", value: state, encodeError)
+
+        for (name, value) in parameters {
+            queryString = queryString?.urlQueryByAppending(parameter: name, value: "\(value)", encodeError)
+        }
+
+        if let queryString = queryString {
+            let urlString = self.authorizeUrl.urlByAppending(query: queryString)
+            if let url: URL = URL(string: urlString) {
+                self.authorizeURLHandler.handle(url)
+                return self
+            } else {
+                failure?(OAuthSwiftError.encodingError(urlString: urlString))
+            }
+        } else {
+            let urlString = self.authorizeUrl.urlByAppending(query: queryErrorString)
+            failure?(OAuthSwiftError.encodingError(urlString: urlString))
+        }
+        self.cancel() // ie. remove the observer.
+        return nil
+    }
+
+    @discardableResult
+    open func authorize(withCallbackURL urlString: String, scope: String, state: String, parameters: Parameters = [:], headers: OAuthSwift.Headers? = nil, success: @escaping TokenSuccessHandler, failure: FailureHandler?) -> OAuthSwiftRequestHandle? {
+        guard let url = URL(string: urlString) else {
+            failure?(OAuthSwiftError.encodingError(urlString: urlString))
+            return nil
+        }
+        return authorize(withCallbackURL: url, scope: scope, state: state, parameters: parameters, headers: headers, success: success, failure: failure)
+    }
+
+    open func postOAuthAccessTokenWithRequestToken(byCode code: String, callbackURL: URL?, headers: OAuthSwift.Headers? = nil, success: @escaping TokenSuccessHandler, failure: FailureHandler?) -> OAuthSwiftRequestHandle? {
+        var parameters = OAuthSwift.Parameters()
+        parameters["client_id"] = self.consumerKey
+        parameters["client_secret"] = self.consumerSecret
+        parameters["code"] = code
+        parameters["grant_type"] = "authorization_code"
+        if let callbackURL = callbackURL {
+            parameters["redirect_uri"] = callbackURL.absoluteString.safeStringByRemovingPercentEncoding
+        }
+
+        return requestOAuthAccessToken(withParameters: parameters, headers: headers, success: success, failure: failure)
+    }
+
+    @discardableResult
+    open func renewAccessToken(withRefreshToken refreshToken: String, parameters: OAuthSwift.Parameters? = nil, headers: OAuthSwift.Headers? = nil, success: @escaping TokenSuccessHandler, failure: FailureHandler?) -> OAuthSwiftRequestHandle? {
+        var parameters = parameters ?? OAuthSwift.Parameters()
+        parameters["client_id"] = self.consumerKey
+        parameters["client_secret"] = self.consumerSecret
+        parameters["refresh_token"] = refreshToken
+        parameters["grant_type"] = "refresh_token"
+
+        return requestOAuthAccessToken(withParameters: parameters, headers: headers, success: success, failure: failure)
+    }
+
+    fileprivate func requestOAuthAccessToken(withParameters parameters: OAuthSwift.Parameters, headers: OAuthSwift.Headers? = nil, success: @escaping TokenSuccessHandler, failure: FailureHandler?) -> OAuthSwiftRequestHandle? {
+        let successHandler: OAuthSwiftHTTPRequest.SuccessHandler = { [weak self] response in
+            guard let this = self else {
+                OAuthSwift.retainError(failure)
+                return
+            }
+            let responseJSON: Any? = try? response.jsonObject(options: .mutableContainers)
+
+            let responseParameters: OAuthSwift.Parameters
+
+            if let jsonDico = responseJSON as? [String: Any] {
+                responseParameters = jsonDico
+            } else {
+                responseParameters = response.string?.parametersFromQueryString ?? [:]
+            }
+
+            guard let accessToken = responseParameters["access_token"] as? String else {
+                let message = NSLocalizedString("Could not get Access Token", comment: "Due to an error in the OAuth2 process, we couldn't get a valid token.")
+                failure?(OAuthSwiftError.serverError(message: message))
+                return
+            }
+
+            if let refreshToken = responseParameters["refresh_token"] as? String {
+                this.client.credential.oauthRefreshToken = refreshToken.safeStringByRemovingPercentEncoding
+            }
+
+            if let expiresIn = responseParameters["expires_in"] as? String, let offset = Double(expiresIn) {
+                this.client.credential.oauthTokenExpiresAt = Date(timeInterval: offset, since: Date())
+            } else if let expiresIn = responseParameters["expires_in"] as? Double {
+                this.client.credential.oauthTokenExpiresAt = Date(timeInterval: expiresIn, since: Date())
+            }
+
+            this.client.credential.oauthToken = accessToken.safeStringByRemovingPercentEncoding
+            success(this.client.credential, response, responseParameters)
+        }
+
+        guard let accessTokenUrl = accessTokenUrl else {
+            let message = NSLocalizedString("access token url not defined", comment: "access token url not defined with code type auth")
+            failure?(OAuthSwiftError.configurationError(message: message))
+            return nil
+        }
+
+        if self.contentType == "multipart/form-data" {
+            // Request new access token by disabling check on current token expiration. This is safe because the implementation wants the user to retrieve a new token.
+            return self.client.postMultiPartRequest(accessTokenUrl, method: .POST, parameters: parameters, headers: headers, checkTokenExpiration: false, success: successHandler, failure: failure)
+        } else {
+            // special headers
+            var finalHeaders: OAuthSwift.Headers? = headers
+            if accessTokenBasicAuthentification {
+
+                let authentification = "\(self.consumerKey):\(self.consumerSecret)".data(using: String.Encoding.utf8)
+                if let base64Encoded = authentification?.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0)) {
+                    finalHeaders += ["Authorization": "Basic \(base64Encoded)"] as OAuthSwift.Headers
+                }
+            }
+
+            // Request new access token by disabling check on current token expiration. This is safe because the implementation wants the user to retrieve a new token.
+            return self.client.request(accessTokenUrl, method: .POST, parameters: parameters, headers: finalHeaders, checkTokenExpiration: false, success: successHandler, failure: failure)
+        }
+    }
+
+    /**
+     Convenience method to start a request that must be authorized with the previously retrieved access token.
+     Since OAuth 2 requires support for the access token refresh mechanism, this method will take care to automatically
+     refresh the token if needed such that the developer only has to be concerned about the outcome of the request.
+     
+     - parameter url:            The url for the request.
+     - parameter method:         The HTTP method to use.
+     - parameter parameters:     The request's parameters.
+     - parameter headers:        The request's headers.
+     - parameter renewHeaders:   The request's headers if renewing. If nil, the `headers`` are used when renewing.
+     - parameter body:           The request's HTTP body.
+     - parameter onTokenRenewal: Optional callback triggered in case the access token renewal was required in order to properly authorize the request.
+     - parameter success:        The success block. Takes the successfull response and data as parameter.
+     - parameter failure:        The failure block. Takes the error as parameter.
+     */
+    @discardableResult
+    open func startAuthorizedRequest(_ url: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters, headers: OAuthSwift.Headers? = nil, renewHeaders: OAuthSwift.Headers? = nil, body: Data? = nil, onTokenRenewal: TokenRenewedHandler? = nil, success: @escaping OAuthSwiftHTTPRequest.SuccessHandler, failure: @escaping OAuthSwiftHTTPRequest.FailureHandler) -> OAuthSwiftRequestHandle? {
+        // build request
+        return self.client.request(url, method: method, parameters: parameters, headers: headers, body: body, success: success) { (error) in
+            switch error {
+
+            case OAuthSwiftError.tokenExpired:
+                _ = self.renewAccessToken(withRefreshToken: self.client.credential.oauthRefreshToken, headers: renewHeaders ?? headers, success: { (credential, _, _) in
+                    // Ommit response parameters so they don't override the original ones
+                    // We have successfully renewed the access token.
+
+                    // If provided, fire the onRenewal closure
+                    if let renewalCallBack = onTokenRenewal {
+                        renewalCallBack(credential)
+                    }
+
+                    // Reauthorize the request again, this time with a brand new access token ready to be used.
+                    _ = self.startAuthorizedRequest(url, method: method, parameters: parameters, headers: headers, body: body, onTokenRenewal: onTokenRenewal, success: success, failure: failure)
+                }, failure: failure)
+            default:
+                failure(error)
+            }
+        }
+    }
+
+       // OAuth 2.0 Specification: https://tools.ietf.org/html/draft-ietf-oauth-v2-13#section-4.3
+    @discardableResult
+    open func authorize(username: String, password: String, scope: String?, headers: OAuthSwift.Headers? = nil, success: @escaping TokenSuccessHandler, failure: @escaping OAuthSwiftHTTPRequest.FailureHandler) -> OAuthSwiftRequestHandle? {
+
+        var parameters = OAuthSwift.Parameters()
+        parameters["client_id"] = self.consumerKey
+        parameters["client_secret"] = self.consumerSecret
+        parameters["username"] = username
+        parameters["password"] = password
+        parameters["grant_type"] = "password"
+
+        if let scope = scope {
+            parameters["scope"] = scope
+        }
+
+        return requestOAuthAccessToken(
+            withParameters: parameters,
+            headers: headers,
+            success: success,
+            failure: failure
+        )
+    }
+
+    @discardableResult
+    open func authorize(deviceToken deviceCode: String, grantType: String = "http://oauth.net/grant_type/device/1.0", success: @escaping TokenSuccessHandler, failure: @escaping OAuthSwiftHTTPRequest.FailureHandler) -> OAuthSwiftRequestHandle? {
+        var parameters = OAuthSwift.Parameters()
+        parameters["client_id"] = self.consumerKey
+        parameters["client_secret"] = self.consumerSecret
+        parameters["code"] = deviceCode
+        parameters["grant_type"] = grantType
+
+        return requestOAuthAccessToken(
+            withParameters: parameters,
+            success: success,
+            failure: failure
+        )
+    }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuthSwift.swift b/iOS/Pods/OAuthSwift/Sources/OAuthSwift.swift
new file mode 100644 (file)
index 0000000..b0c379a
--- /dev/null
@@ -0,0 +1,110 @@
+//
+//  OAuthSwift.swift
+//  OAuthSwift
+//
+//  Created by phimage on 04/12/15.
+//  Copyright © 2015 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+open class OAuthSwift: NSObject, OAuthSwiftRequestHandle {
+
+    // MARK: Properties
+
+    /// Client to make signed request
+    open var client: OAuthSwiftClient
+    /// Version of the protocol
+    open var version: OAuthSwiftCredential.Version { return self.client.credential.version }
+
+    /// Handle the authorize url into a web view or browser
+    open var authorizeURLHandler: OAuthSwiftURLHandlerType = OAuthSwiftOpenURLExternally.sharedInstance
+
+    fileprivate var currentRequests: [String: OAuthSwiftRequestHandle] = [:]
+
+    // MARK: init
+    init(consumerKey: String, consumerSecret: String) {
+        self.client = OAuthSwiftClient(consumerKey: consumerKey, consumerSecret: consumerSecret)
+    }
+
+    // MARK: callback notification
+    struct CallbackNotification {
+        @available(*, deprecated: 0.5, message: "Use Notification.Name.OAuthSwiftHandleCallbackURL")
+        static let notificationName = Notification.Name(rawValue: "OAuthSwiftCallbackNotificationName")
+        static let optionsURLKey = "OAuthSwiftCallbackNotificationOptionsURLKey"
+    }
+
+    /// Handle callback url which contains now token information
+    open class func handle(url: URL) {
+        let notification = Notification(name: NSNotification.Name.OAuthSwiftHandleCallbackURL, object: nil,
+            userInfo: [CallbackNotification.optionsURLKey: url])
+        notificationCenter.post(notification)
+    }
+
+    var observer: NSObjectProtocol?
+    open class var notificationCenter: NotificationCenter {
+        return NotificationCenter.default
+    }
+    open class var notificationQueue: OperationQueue {
+        return OperationQueue.main
+    }
+
+    func observeCallback(_ block: @escaping (_ url: URL) -> Void) {
+        self.observer = OAuthSwift.notificationCenter.addObserver(forName: NSNotification.Name.OAuthSwiftHandleCallbackURL, object: nil, queue: OperationQueue.main) { [weak self] notification in
+            self?.removeCallbackNotificationObserver()
+
+            if let urlFromUserInfo = notification.userInfo?[CallbackNotification.optionsURLKey] as? URL {
+                block(urlFromUserInfo)
+            } else {
+                // Internal error
+                assertionFailure()
+            }
+        }
+    }
+
+    /// Remove internal observer on authentification
+    public func removeCallbackNotificationObserver() {
+        if let observer = self.observer {
+            OAuthSwift.notificationCenter.removeObserver(observer)
+        }
+    }
+
+    /// Function to call when web view is dismissed without authentification
+    public func cancel() {
+        self.removeCallbackNotificationObserver()
+        for (_, request) in self.currentRequests {
+            request.cancel()
+        }
+        self.currentRequests = [:]
+    }
+
+    func putHandle(_ handle: OAuthSwiftRequestHandle, withKey key: String) {
+        // self.currentRequests[withKey] = handle
+        // TODO before storing handle, find a way to remove it when network request end (ie. all failure and success ie. complete)
+    }
+
+    /// Run block in main thread
+    static func main(block: @escaping () -> Void) {
+        if Thread.isMainThread {
+            block()
+        } else {
+            DispatchQueue.main.async {
+                block()
+            }
+        }
+    }
+
+}
+
+// MARK: - alias
+extension OAuthSwift {
+
+    public typealias Parameters = [String: Any]
+    public typealias Headers = [String: String]
+    public typealias ConfigParameters = [String: String]
+    /// MARK: callback alias
+    public typealias TokenSuccess = (credential: OAuthSwiftCredential, response: OAuthSwiftResponse?, parameters: Parameters)
+    public typealias TokenSuccessHandler = (_ credential: OAuthSwiftCredential, _ response: OAuthSwiftResponse?, _ parameters: Parameters) -> Void
+    public typealias FailureHandler = (_ error: OAuthSwiftError) -> Void
+    public typealias TokenRenewedHandler = (_ credential: OAuthSwiftCredential) -> Void
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuthSwiftClient.swift b/iOS/Pods/OAuthSwift/Sources/OAuthSwiftClient.swift
new file mode 100644 (file)
index 0000000..215ca63
--- /dev/null
@@ -0,0 +1,194 @@
+//
+//  OAuthSwiftClient.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 6/21/14.
+//  Copyright (c) 2014 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+public var OAuthSwiftDataEncoding: String.Encoding = .utf8
+
+@objc public protocol OAuthSwiftRequestHandle {
+    func cancel()
+}
+
+open class OAuthSwiftClient: NSObject {
+
+    fileprivate(set) open var credential: OAuthSwiftCredential
+    open var paramsLocation: OAuthSwiftHTTPRequest.ParamsLocation = .authorizationHeader
+    /// Contains default URL session configuration
+    open var sessionFactory = URLSessionFactory()
+
+    static let separator: String = "\r\n"
+    static var separatorData: Data = {
+        return OAuthSwiftClient.separator.data(using: OAuthSwiftDataEncoding)!
+    }()
+
+    // MARK: init
+    public init(credential: OAuthSwiftCredential) {
+        self.credential = credential
+    }
+
+    public convenience init(consumerKey: String, consumerSecret: String, version: OAuthSwiftCredential.Version = .oauth1) {
+        let credential = OAuthSwiftCredential(consumerKey: consumerKey, consumerSecret: consumerSecret)
+        credential.version = version
+        self.init(credential: credential)
+    }
+
+    public convenience init(consumerKey: String, consumerSecret: String, oauthToken: String, oauthTokenSecret: String, version: OAuthSwiftCredential.Version) {
+        self.init(consumerKey: consumerKey, consumerSecret: consumerSecret, version: version)
+        self.credential.oauthToken = oauthToken
+        self.credential.oauthTokenSecret = oauthTokenSecret
+    }
+
+    // MARK: client methods
+    @discardableResult
+    open func get(_ urlString: String, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.request(urlString, method: .GET, parameters: parameters, headers: headers, success: success, failure: failure)
+    }
+
+    @discardableResult
+    open func post(_ urlString: String, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, body: Data? = nil, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.request(urlString, method: .POST, parameters: parameters, headers: headers, body: body, success: success, failure: failure)
+    }
+
+    @discardableResult
+    open func put(_ urlString: String, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, body: Data? = nil, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.request(urlString, method: .PUT, parameters: parameters, headers: headers, body: body, success: success, failure: failure)
+    }
+
+    @discardableResult
+    open func delete(_ urlString: String, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.request(urlString, method: .DELETE, parameters: parameters, headers: headers, success: success, failure: failure)
+    }
+
+    @discardableResult
+    open func patch(_ urlString: String, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.request(urlString, method: .PATCH, parameters: parameters, headers: headers, success: success, failure: failure)
+    }
+
+    @discardableResult
+    open func request(_ urlString: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, body: Data? = nil, checkTokenExpiration: Bool = true, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {
+
+        if checkTokenExpiration && self.credential.isTokenExpired() {
+            failure?(OAuthSwiftError.tokenExpired(error: nil))
+            return nil
+        }
+
+        guard URL(string: urlString) != nil else {
+            failure?(OAuthSwiftError.encodingError(urlString: urlString))
+            return nil
+        }
+
+        if let request = makeRequest(urlString, method: method, parameters: parameters, headers: headers, body: body) {
+            request.start(success: success, failure: failure)
+            return request
+        }
+        return nil
+    }
+
+    open func makeRequest(_ request: URLRequest) -> OAuthSwiftHTTPRequest {
+        let request = OAuthSwiftHTTPRequest(request: request, paramsLocation: self.paramsLocation, sessionFactory: self.sessionFactory)
+        request.config.updateRequest(credential: self.credential)
+        return request
+    }
+
+    open func makeRequest(_ urlString: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, body: Data? = nil) -> OAuthSwiftHTTPRequest? {
+        guard let url = URL(string: urlString) else {
+            return nil
+        }
+
+        let request = OAuthSwiftHTTPRequest(url: url, method: method, parameters: parameters, paramsLocation: self.paramsLocation, httpBody: body, headers: headers ?? [:], sessionFactory: self.sessionFactory)
+        request.config.updateRequest(credential: self.credential)
+        return request
+    }
+
+    @discardableResult
+    public func postImage(_ urlString: String, parameters: OAuthSwift.Parameters, image: Data, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.multiPartRequest(url: urlString, method: .POST, parameters: parameters, image: image, success: success, failure: failure)
+    }
+
+    open func makeMultiPartRequest(_ urlString: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters = [:], multiparts: [OAuthSwiftMultipartData] = [], headers: OAuthSwift.Headers? = nil) -> OAuthSwiftHTTPRequest? {
+        let boundary = "AS-boundary-\(arc4random())-\(arc4random())"
+        let type = "multipart/form-data; boundary=\(boundary)"
+        let body = self.multiDataFromObject(parameters, multiparts: multiparts, boundary: boundary)
+
+        var finalHeaders = [kHTTPHeaderContentType: type]
+        finalHeaders += headers ?? [:]
+
+        return makeRequest(urlString, method: method, parameters: parameters, headers: finalHeaders, body: body)
+    }
+
+    func multiPartRequest(url: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters, image: Data, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {
+        let multiparts = [ OAuthSwiftMultipartData(name: "media", data: image, fileName: "file", mimeType: "image/jpeg") ]
+
+        if let request = makeMultiPartRequest(url, method: method, parameters: parameters, multiparts: multiparts) {
+            request.start(success: success, failure: failure)
+            return request
+        }
+
+        return nil
+    }
+
+    open func multiPartBody(from inputParameters: OAuthSwift.Parameters, boundary: String) -> Data {
+        var parameters = OAuthSwift.Parameters()
+        var multiparts = [OAuthSwiftMultipartData]()
+
+        for (key, value) in inputParameters {
+            if  let data = value as? Data, key == "media" {
+                let sectionType = "image/jpeg"
+                let sectionFilename = "file"
+                multiparts.append(OAuthSwiftMultipartData(name: key, data: data, fileName: sectionFilename, mimeType: sectionType))
+            } else {
+                parameters[key] = value
+            }
+        }
+
+        return multiDataFromObject(parameters, multiparts: multiparts, boundary: boundary)
+    }
+
+    @discardableResult
+    open func postMultiPartRequest(_ url: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters, headers: OAuthSwift.Headers? = nil, multiparts: [OAuthSwiftMultipartData] = [], checkTokenExpiration: Bool = true, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.FailureHandler?) -> OAuthSwiftRequestHandle? {
+
+        if checkTokenExpiration && self.credential.isTokenExpired() {
+            failure?(OAuthSwiftError.tokenExpired(error: nil))
+            return nil
+        }
+
+        if let request = makeMultiPartRequest(url, method: method, parameters: parameters, multiparts: multiparts, headers: headers) {
+            request.start(success: success, failure: failure)
+            return request
+        }
+        return nil
+    }
+
+    func multiDataFromObject(_ object: OAuthSwift.Parameters, multiparts: [OAuthSwiftMultipartData], boundary: String) -> Data {
+        var data = Data()
+
+        let prefixString = "--\(boundary)\r\n"
+        let prefixData = prefixString.data(using: OAuthSwiftDataEncoding)!
+
+        for (key, value) in object {
+            guard let valueData = "\(value)".data(using: OAuthSwiftDataEncoding) else {
+                continue
+            }
+            data.append(prefixData)
+            let multipartData = OAuthSwiftMultipartData(name: key, data: valueData, fileName: nil, mimeType: nil)
+            data.append(multipartData, encoding: OAuthSwiftDataEncoding, separatorData: OAuthSwiftClient.separatorData)
+        }
+
+        for multipart in multiparts {
+            data.append(prefixData)
+            data.append(multipart, encoding: OAuthSwiftDataEncoding, separatorData: OAuthSwiftClient.separatorData)
+        }
+
+        let endingString = "--\(boundary)--\r\n"
+        let endingData = endingString.data(using: OAuthSwiftDataEncoding)!
+        data.append(endingData)
+
+        return data
+    }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuthSwiftCredential.swift b/iOS/Pods/OAuthSwift/Sources/OAuthSwiftCredential.swift
new file mode 100644 (file)
index 0000000..ad81260
--- /dev/null
@@ -0,0 +1,428 @@
+//
+//  OAuthSwiftCredential.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 6/22/14.
+//  Copyright (c) 2014 Dongri Jin. All rights reserved.
+//
+import Foundation
+
+/// Allow to customize computed headers
+public protocol OAuthSwiftCredentialHeadersFactory {
+    func make(_ url: URL, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters, body: Data?) -> [String: String]
+}
+
+/// Allow to sign
+// swiftlint:disable:next class_delegate_protocol
+public protocol OAuthSwiftSignatureDelegate {
+    static func sign(hashMethod: OAuthSwiftHashMethod, key: Data, message: Data) -> Data?
+}
+
+// The hash method used.
+public enum OAuthSwiftHashMethod: String {
+    case sha1
+    case none
+
+    func hash(data: Data) -> Data? {
+        switch self {
+        case .sha1:
+            let mac = SHA1(data).calculate()
+            return Data(bytes: UnsafePointer<UInt8>(mac), count: mac.count)
+        case .none:
+            return data
+        }
+    }
+}
+
+/// The credential for authentification
+open class OAuthSwiftCredential: NSObject, NSSecureCoding, Codable {
+
+    public static let supportsSecureCoding = true
+
+    public enum Version: Codable {
+        case oauth1, oauth2
+
+        public var shortVersion: String {
+            switch self {
+            case .oauth1:
+                return "1.0"
+            case .oauth2:
+                return "2.0"
+            }
+        }
+
+        var toInt32: Int32 {
+            switch self {
+            case .oauth1:
+                return 1
+            case .oauth2:
+                return 2
+            }
+        }
+
+        init(_ value: Int32) {
+            switch value {
+            case 1:
+                self = .oauth1
+            case 2:
+                self = .oauth2
+            default:
+                self = .oauth1
+            }
+        }
+
+        public func encode(to encoder: Encoder) throws {
+            var container = encoder.singleValueContainer()
+            try container.encode(self.toInt32)
+        }
+
+        public init(from decoder: Decoder) throws {
+            self.init(try decoder.singleValueContainer().decode(Int32.self))
+        }
+    }
+
+    public enum SignatureMethod: String {
+        case HMAC_SHA1 = "HMAC-SHA1"
+        case RSA_SHA1 = "RSA-SHA1"
+        case PLAINTEXT = "PLAINTEXT"
+
+        public static var delegates: [SignatureMethod: OAuthSwiftSignatureDelegate.Type] =
+            [HMAC_SHA1: HMAC.self]
+
+        var hashMethod: OAuthSwiftHashMethod {
+            switch self {
+            case .HMAC_SHA1, .RSA_SHA1:
+                return .sha1
+            case .PLAINTEXT:
+                return .none
+            }
+        }
+
+        func sign(key: Data, message: Data) -> Data? {
+            if let delegate = SignatureMethod.delegates[self] {
+                return delegate.sign(hashMethod: self.hashMethod, key: key, message: message)
+            }
+            assert(self == .PLAINTEXT, "No signature method installed for \(self)")
+            return message
+        }
+
+    }
+
+    // MARK: attributes
+    open internal(set) var consumerKey = ""
+    open internal(set) var consumerSecret = ""
+    open var oauthToken = ""
+    open var oauthRefreshToken = ""
+    open var oauthTokenSecret = ""
+    open var oauthTokenExpiresAt: Date?
+    open internal(set) var oauthVerifier = ""
+    open var version: Version = .oauth1
+    open var signatureMethod: SignatureMethod = .HMAC_SHA1
+
+    /// hook to replace headers creation
+    open var headersFactory: OAuthSwiftCredentialHeadersFactory?
+
+    // MARK: init
+    override init() {
+    }
+
+    public init(consumerKey: String, consumerSecret: String) {
+        self.consumerKey = consumerKey
+        self.consumerSecret = consumerSecret
+    }
+
+    // MARK: NSCoding protocol
+    fileprivate struct NSCodingKeys {
+        static let bundleId = Bundle.main.bundleIdentifier
+            ?? Bundle(for: OAuthSwiftCredential.self).bundleIdentifier
+            ?? ""
+        static let base = bundleId + "."
+        static let consumerKey = base + "comsumer_key"
+        static let consumerSecret = base + "consumer_secret"
+        static let oauthToken = base + "oauth_token"
+        static let oauthRefreshToken = base + "oauth_refresh_token"
+        static let oauthTokenExpiresAt = base + "oauth_token_expires_at"
+        static let oauthTokenSecret = base + "oauth_token_secret"
+        static let oauthVerifier = base + "oauth_verifier"
+        static let version = base + "version"
+        static let signatureMethod = base + "signatureMethod"
+    }
+
+    /// Cannot declare a required initializer within an extension.
+    /// extension OAuthSwiftCredential: NSCoding {
+    public required convenience init?(coder decoder: NSCoder) {
+
+        guard let consumerKey = decoder
+            .decodeObject(of: NSString.self,
+                          forKey: NSCodingKeys.consumerKey) as String? else {
+            if #available(iOS 9, OSX 10.11, *) {
+                let error = CocoaError.error(.coderValueNotFound)
+                decoder.failWithError(error)
+            }
+            return nil
+        }
+
+        guard let consumerSecret = decoder
+            .decodeObject(of: NSString.self,
+                          forKey: NSCodingKeys.consumerSecret) as String? else {
+            if #available(iOS 9, OSX 10.11, *) {
+                let error = CocoaError.error(.coderValueNotFound)
+                decoder.failWithError(error)
+            }
+            return nil
+        }
+        self.init(consumerKey: consumerKey, consumerSecret: consumerSecret)
+
+        guard let oauthToken = decoder
+            .decodeObject(of: NSString.self,
+                          forKey: NSCodingKeys.oauthToken) as String? else {
+            if #available(iOS 9, OSX 10.11, *) {
+                let error = CocoaError.error(.coderValueNotFound)
+                decoder.failWithError(error)
+            }
+            return nil
+        }
+        self.oauthToken = oauthToken
+
+        guard let oauthRefreshToken = decoder
+            .decodeObject(of: NSString.self,
+                          forKey: NSCodingKeys.oauthRefreshToken) as String? else {
+            if #available(iOS 9, OSX 10.11, *) {
+                let error = CocoaError.error(.coderValueNotFound)
+                decoder.failWithError(error)
+            }
+            return nil
+        }
+        self.oauthRefreshToken = oauthRefreshToken
+
+        guard let oauthTokenSecret = decoder
+            .decodeObject(of: NSString.self,
+                          forKey: NSCodingKeys.oauthTokenSecret) as String? else {
+            if #available(iOS 9, OSX 10.11, *) {
+                let error = CocoaError.error(.coderValueNotFound)
+                decoder.failWithError(error)
+            }
+            return nil
+        }
+        self.oauthTokenSecret = oauthTokenSecret
+
+        guard let oauthVerifier = decoder
+            .decodeObject(of: NSString.self,
+                          forKey: NSCodingKeys.oauthVerifier) as String? else {
+            if #available(iOS 9, OSX 10.11, *) {
+                    let error = CocoaError.error(.coderValueNotFound)
+                    decoder.failWithError(error)
+            }
+            return nil
+        }
+        self.oauthVerifier = oauthVerifier
+
+        self.oauthTokenExpiresAt = decoder
+            .decodeObject(of: NSDate.self, forKey: NSCodingKeys.oauthTokenExpiresAt) as Date?
+        self.version = Version(decoder.decodeInt32(forKey: NSCodingKeys.version))
+        if case .oauth1 = version {
+            self.signatureMethod = SignatureMethod(rawValue: (decoder.decodeObject(of: NSString.self, forKey: NSCodingKeys.signatureMethod) as String?) ?? "HMAC_SHA1") ?? .HMAC_SHA1
+        }
+    }
+
+    open func encode(with coder: NSCoder) {
+        coder.encode(self.consumerKey, forKey: NSCodingKeys.consumerKey)
+        coder.encode(self.consumerSecret, forKey: NSCodingKeys.consumerSecret)
+        coder.encode(self.oauthToken, forKey: NSCodingKeys.oauthToken)
+        coder.encode(self.oauthRefreshToken, forKey: NSCodingKeys.oauthRefreshToken)
+        coder.encode(self.oauthTokenSecret, forKey: NSCodingKeys.oauthTokenSecret)
+        coder.encode(self.oauthVerifier, forKey: NSCodingKeys.oauthVerifier)
+        coder.encode(self.oauthTokenExpiresAt, forKey: NSCodingKeys.oauthTokenExpiresAt)
+        coder.encode(self.version.toInt32, forKey: NSCodingKeys.version)
+        if case .oauth1 = version {
+            coder.encode(self.signatureMethod.rawValue, forKey: NSCodingKeys.signatureMethod)
+        }
+    }
+    // } // End NSCoding extension
+
+    // MARK: Codable protocol
+    enum CodingKeys: String, CodingKey {
+        case consumerKey
+        case consumerSecret
+        case oauthToken
+        case oauthRefreshToken
+        case oauthTokenSecret
+        case oauthVerifier
+        case oauthTokenExpiresAt
+        case version
+        case signatureMethodRawValue
+    }
+
+    public func encode(to encoder: Encoder) throws {
+        var container = encoder.container(keyedBy: CodingKeys.self)
+        try container.encode(self.consumerKey, forKey: .consumerKey)
+        try container.encode(self.consumerSecret, forKey: .consumerSecret)
+        try container.encode(self.oauthToken, forKey: .oauthToken)
+        try container.encode(self.oauthRefreshToken, forKey: .oauthRefreshToken)
+        try container.encode(self.oauthTokenSecret, forKey: .oauthTokenSecret)
+        try container.encode(self.oauthVerifier, forKey: .oauthVerifier)
+        try container.encodeIfPresent(self.oauthTokenExpiresAt, forKey: .oauthTokenExpiresAt)
+        try container.encode(self.version, forKey: .version)
+        if case .oauth1 = version {
+            try container.encode(self.signatureMethod.rawValue, forKey: .signatureMethodRawValue)
+        }
+    }
+
+    public required convenience init(from decoder: Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+
+        self.init()
+
+        self.consumerKey = try container.decode(String.self, forKey: .consumerKey)
+        self.consumerSecret = try container.decode(String.self, forKey: .consumerSecret)
+
+        self.oauthToken = try container.decode(type(of: self.oauthToken), forKey: .oauthToken)
+        self.oauthRefreshToken = try container.decode(type(of: self.oauthRefreshToken), forKey: .oauthRefreshToken)
+        self.oauthTokenSecret = try container.decode(type(of: self.oauthTokenSecret), forKey: .oauthTokenSecret)
+        self.oauthVerifier = try container.decode(type(of: self.oauthVerifier), forKey: .oauthVerifier)
+        self.oauthTokenExpiresAt = try container.decodeIfPresent(Date.self, forKey: .oauthTokenExpiresAt)
+        self.version = try container.decode(type(of: self.version), forKey: .version)
+
+        if case .oauth1 = version {
+            self.signatureMethod = SignatureMethod(rawValue: try container.decode(type(of: self.signatureMethod.rawValue), forKey: .signatureMethodRawValue))!
+        }
+    }
+
+    // MARK: functions
+    /// for OAuth1 parameters must contains sorted query parameters and url must not contains query parameters
+    open func makeHeaders(_ url: URL, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters, body: Data? = nil) -> [String: String] {
+        if let factory = headersFactory {
+            return factory.make(url, method: method, parameters: parameters, body: body)
+        }
+        switch self.version {
+        case .oauth1:
+            return ["Authorization": self.authorizationHeader(method: method, url: url, parameters: parameters, body: body)]
+        case .oauth2:
+            return self.oauthToken.isEmpty ? [:] : ["Authorization": "Bearer \(self.oauthToken)"]
+        }
+    }
+
+    open func authorizationHeader(method: OAuthSwiftHTTPRequest.Method, url: URL, parameters: OAuthSwift.Parameters, body: Data? = nil) -> String {
+        let timestamp = String(Int64(Date().timeIntervalSince1970))
+        let nonce = OAuthSwiftCredential.generateNonce()
+        return self.authorizationHeader(method: method, url: url, parameters: parameters, body: body, timestamp: timestamp, nonce: nonce)
+    }
+
+    open class func generateNonce() -> String {
+        let uuidString: String = UUID().uuidString
+        return uuidString[0..<8]
+    }
+
+    open func authorizationHeader(method: OAuthSwiftHTTPRequest.Method, url: URL, parameters: OAuthSwift.Parameters, body: Data? = nil, timestamp: String, nonce: String) -> String {
+        assert(self.version == .oauth1)
+        let authorizationParameters = self.authorizationParametersWithSignature(method: method, url: url, parameters: parameters, body: body, timestamp: timestamp, nonce: nonce)
+
+        var parameterComponents = authorizationParameters.urlEncodedQuery.components(separatedBy: "&") as [String]
+        parameterComponents.sort { $0 < $1 }
+
+        var headerComponents = [String]()
+        for component in parameterComponents {
+            let subcomponent = component.components(separatedBy: "=") as [String]
+            if subcomponent.count == 2 {
+                headerComponents.append("\(subcomponent[0])=\"\(subcomponent[1])\"")
+            }
+        }
+
+        return "OAuth " + headerComponents.joined(separator: ", ")
+    }
+
+    open func authorizationParametersWithSignature(method: OAuthSwiftHTTPRequest.Method, url: URL, parameters: OAuthSwift.Parameters, body: Data? = nil) -> OAuthSwift.Parameters {
+        let timestamp = String(Int64(Date().timeIntervalSince1970))
+        let nonce = OAuthSwiftCredential.generateNonce()
+        return self.authorizationParametersWithSignature(method: method, url: url, parameters: parameters, body: body, timestamp: timestamp, nonce: nonce)
+    }
+
+    open func authorizationParametersWithSignature(method: OAuthSwiftHTTPRequest.Method, url: URL, parameters: OAuthSwift.Parameters, body: Data? = nil, timestamp: String, nonce: String) -> OAuthSwift.Parameters {
+        var authorizationParameters = self.authorizationParameters(body, timestamp: timestamp, nonce: nonce)
+
+        for (key, value) in parameters {
+            if key.hasPrefix("oauth_") {
+                authorizationParameters.updateValue(value, forKey: key)
+            }
+        }
+
+        let combinedParameters = authorizationParameters.join(parameters)
+
+        authorizationParameters["oauth_signature"] = self.signature(method: method, url: url, parameters: combinedParameters)
+
+        return authorizationParameters
+    }
+
+    open func authorizationParameters(_ body: Data?, timestamp: String, nonce: String) -> OAuthSwift.Parameters {
+        var authorizationParameters = OAuthSwift.Parameters()
+        authorizationParameters["oauth_version"] = self.version.shortVersion
+        authorizationParameters["oauth_signature_method"] =  self.signatureMethod.rawValue
+        authorizationParameters["oauth_consumer_key"] = self.consumerKey
+        authorizationParameters["oauth_timestamp"] = timestamp
+        authorizationParameters["oauth_nonce"] = nonce
+        if let b = body, let hash = self.signatureMethod.hashMethod.hash(data: b) {
+            authorizationParameters["oauth_body_hash"] = hash.base64EncodedString(options: [])
+        }
+
+        if !self.oauthToken.isEmpty {
+            authorizationParameters["oauth_token"] = self.oauthToken
+        }
+        return authorizationParameters
+    }
+
+    open func signature(method: OAuthSwiftHTTPRequest.Method, url: URL, parameters: OAuthSwift.Parameters) -> String {
+        let encodedTokenSecret = self.oauthTokenSecret.urlEncoded
+        let encodedConsumerSecret = self.consumerSecret.urlEncoded
+
+        let signingKey = "\(encodedConsumerSecret)&\(encodedTokenSecret)"
+
+        var parameterComponents = parameters.urlEncodedQuery.components(separatedBy: "&")
+        parameterComponents.sort {
+            let p0 = $0.components(separatedBy: "=")
+            let p1 = $1.components(separatedBy: "=")
+            if p0.first == p1.first { return p0.last ?? "" < p1.last ?? "" }
+            return p0.first ?? "" < p1.first ?? ""
+        }
+
+        let parameterString = parameterComponents.joined(separator: "&")
+        let encodedParameterString = parameterString.urlEncoded
+
+        let encodedURL = url.absoluteString.urlEncoded
+
+        let signatureBaseString = "\(method)&\(encodedURL)&\(encodedParameterString)"
+
+        let key = signingKey.data(using: .utf8)!
+        let msg = signatureBaseString.data(using: .utf8)!
+
+        let sha1 = self.signatureMethod.sign(key: key, message: msg)!
+        return sha1.base64EncodedString(options: [])
+    }
+
+    open func isTokenExpired() -> Bool {
+        if let expiresDate = oauthTokenExpiresAt {
+            return expiresDate <= Date()
+        }
+
+        // If no expires date is available we assume the token is still valid since it doesn't have an expiration date to check with.
+        return false
+    }
+
+    // MARK: Equatable
+
+    override open func isEqual(_ object: Any?) -> Bool {
+        guard let rhs = object as? OAuthSwiftCredential else {
+            return false
+        }
+        let lhs = self
+        return lhs.consumerKey == rhs.consumerKey
+            && lhs.consumerSecret == rhs.consumerSecret
+            && lhs.oauthToken == rhs.oauthToken
+            && lhs.oauthRefreshToken == rhs.oauthRefreshToken
+            && lhs.oauthTokenSecret == rhs.oauthTokenSecret
+            && lhs.oauthTokenExpiresAt == rhs.oauthTokenExpiresAt
+            && lhs.oauthVerifier == rhs.oauthVerifier
+            && lhs.version == rhs.version
+            && lhs.signatureMethod == rhs.signatureMethod
+    }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuthSwiftError.swift b/iOS/Pods/OAuthSwift/Sources/OAuthSwiftError.swift
new file mode 100644 (file)
index 0000000..5e0ba25
--- /dev/null
@@ -0,0 +1,153 @@
+//
+//  OAuthSwiftError.swift
+//  OAuthSwift
+//
+//  Created by phimage on 02/10/16.
+//  Copyright © 2016 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+// MARK: - OAuthSwift errors
+public enum OAuthSwiftError: Error {
+
+    /// Configuration problem with oauth provider.
+    case configurationError(message: String)
+    /// The provided token is expired, retrieve new token by using the refresh token
+    case tokenExpired(error: Error?)
+    /// State missing from request (you can set allowMissingStateCheck = true to ignore)
+    case missingState
+    /// Returned state value is wrong
+    case stateNotEqual(state: String, responseState: String)
+    /// Error from server
+    case serverError(message: String)
+    /// Failed to create URL \(urlString) not convertible to URL, please encode
+    case encodingError(urlString: String)
+    case authorizationPending
+    /// Failed to create request with \(urlString)
+    case requestCreation(message: String)
+    /// Authentification failed. No token
+    case missingToken
+    /// Please retain OAuthSwift object or handle
+    case retain
+    /// Request error
+    case requestError(error: Error, request: URLRequest)
+    /// Request cancelled
+    case cancelled
+
+    public static let Domain = "OAuthSwiftError"
+    public static let ResponseDataKey = "OAuthSwiftError.response.data"
+    public static let ResponseKey = "OAuthSwiftError.response"
+
+    fileprivate enum Code: Int {
+        case configurationError = -1
+        case tokenExpired = -2
+        case missingState = -3
+        case stateNotEqual = -4
+        case serverError = -5
+        case encodingError = -6
+        case authorizationPending = -7
+        case requestCreation = -8
+        case missingToken = -9
+        case retain = -10
+        case requestError = -11
+        case cancelled = -12
+    }
+
+    fileprivate var code: Code {
+        switch self {
+        case .configurationError: return Code.configurationError
+        case .tokenExpired: return Code.tokenExpired
+        case .missingState: return Code.missingState
+        case .stateNotEqual: return Code.stateNotEqual
+        case .serverError: return Code.serverError
+        case .encodingError: return Code.encodingError
+        case .authorizationPending: return Code.authorizationPending
+        case .requestCreation: return Code.requestCreation
+        case .missingToken: return Code.missingToken
+        case .retain: return Code.retain
+        case .requestError: return Code.requestError
+        case .cancelled : return Code.cancelled
+        }
+    }
+
+    public var underlyingError: Error? {
+        switch self {
+        case .tokenExpired(let e): return e
+        case .requestError(let e, _): return e
+        default: return nil
+        }
+    }
+
+    public var underlyingMessage: String? {
+        switch self {
+        case .serverError(let m): return m
+        case .configurationError(let m): return m
+        case .requestCreation(let m): return m
+        default: return nil
+        }
+    }
+
+}
+
+extension OAuthSwiftError: CustomStringConvertible {
+
+    public var description: String {
+        switch self {
+        case .configurationError(let m): return "configurationError[\(m)]"
+        case .tokenExpired(let e): return "tokenExpired[\(String(describing: e))]"
+        case .missingState: return "missingState"
+        case .stateNotEqual(let s, let e): return "stateNotEqual[\(s)<>\(e)]"
+        case .serverError(let m): return "serverError[\(m)]"
+        case .encodingError(let urlString): return "encodingError[\(urlString)]"
+        case .authorizationPending: return "authorizationPending"
+        case .requestCreation(let m): return "requestCreation[\(m)]"
+        case .missingToken: return "missingToken"
+        case .retain: return "retain"
+        case .requestError(let e, _): return "requestError[\(e)]"
+        case .cancelled : return "cancelled"
+        }
+    }
+}
+
+extension OAuthSwift {
+
+    static func retainError(_ failureHandler: FailureHandler?) {
+        #if !OAUTH_NO_RETAIN_ERROR
+            failureHandler?(OAuthSwiftError.retain)
+        #endif
+    }
+
+}
+
+// MARK: NSError
+extension OAuthSwiftError: CustomNSError {
+
+    public static var errorDomain: String { return OAuthSwiftError.Domain }
+
+    public var errorCode: Int { return self.code.rawValue }
+
+    /// The user-info dictionary.
+    public var errorUserInfo: [String: Any] {
+        switch self {
+        case .configurationError(let m): return ["message": m]
+        case .serverError(let m): return ["message": m]
+        case .requestCreation(let m): return ["message": m]
+
+        case .tokenExpired(let e): return ["error": e as Any]
+        case .requestError(let e, let request): return ["error": e, "request": request]
+
+        case .encodingError(let urlString): return ["url": urlString]
+
+        case .stateNotEqual(let s, let e): return ["state": s, "expected": e]
+        default: return [:]
+        }
+    }
+
+    public var _code: Int {
+        return self.code.rawValue
+    }
+    public var _domain: String {
+        return OAuthSwiftError.Domain
+    }
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuthSwiftHTTPRequest.swift b/iOS/Pods/OAuthSwift/Sources/OAuthSwiftHTTPRequest.swift
new file mode 100644 (file)
index 0000000..417563c
--- /dev/null
@@ -0,0 +1,497 @@
+//
+//  OAuthSwiftHTTPRequest.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 6/21/14.
+//  Copyright (c) 2014 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+let kHTTPHeaderContentType = "Content-Type"
+
+open class OAuthSwiftHTTPRequest: NSObject, OAuthSwiftRequestHandle {
+
+    public typealias SuccessHandler = (_ response: OAuthSwiftResponse) -> Void
+    public typealias FailureHandler = (_ error: OAuthSwiftError) -> Void
+
+    /// HTTP request method
+    /// https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
+    public enum Method: String {
+        case GET, POST, PUT, DELETE, PATCH, HEAD //, OPTIONS, TRACE, CONNECT
+
+        var isBody: Bool {
+            return self == .POST || self == .PUT || self == .PATCH
+        }
+    }
+
+    /// Where the additional parameters will be injected
+    @objc public enum ParamsLocation: Int {
+        case authorizationHeader, /*FormEncodedBody,*/ requestURIQuery
+    }
+
+    public var config: Config
+
+    private var request: URLRequest?
+    private var task: URLSessionTask?
+    private var session: URLSession!
+
+    fileprivate var cancelRequested = false
+
+    open static var executionContext: (@escaping () -> Void) -> Void = { block in
+        return DispatchQueue.main.async(execute: block)
+    }
+
+    // MARK: INIT
+
+    convenience init(url: URL, method: Method = .GET, parameters: OAuthSwift.Parameters = [:], paramsLocation: ParamsLocation = .authorizationHeader, httpBody: Data? = nil, headers: OAuthSwift.Headers = [:], sessionFactory: URLSessionFactory = .default) {
+        self.init(config: Config(url: url, httpMethod: method, httpBody: httpBody, headers: headers, parameters: parameters, paramsLocation: paramsLocation, sessionFactory: sessionFactory))
+    }
+
+    convenience init(request: URLRequest, paramsLocation: ParamsLocation = .authorizationHeader, sessionFactory: URLSessionFactory = .default) {
+        self.init(config: Config(urlRequest: request, paramsLocation: paramsLocation, sessionFactory: sessionFactory))
+    }
+
+    init(config: Config) {
+        self.config = config
+    }
+
+    /// START request
+    func start(success: SuccessHandler?, failure: FailureHandler?) {
+        guard request == nil else { return } // Don't start the same request twice!
+
+        let successHandler = success
+        let failureHandler = failure
+
+        do {
+            self.request = try self.makeRequest()
+        } catch let error as NSError {
+            failureHandler?(OAuthSwiftError.requestCreation(message: error.localizedDescription))
+            self.request = nil
+            return
+        }
+
+        OAuthSwiftHTTPRequest.executionContext {
+            // perform lock here to prevent cancel calls on another thread while creating the request
+            objc_sync_enter(self)
+            defer { objc_sync_exit(self) }
+            if self.cancelRequested {
+                return
+            }
+
+            self.session = self.config.sessionFactory.build()
+            let usedRequest = self.request!
+
+            if self.config.sessionFactory.useDataTaskClosure {
+                let completionHandler: (Data?, URLResponse?, Error?) -> Void = { data, resp, error in
+                    OAuthSwiftHTTPRequest.completionHandler(successHandler: success,
+                                                            failureHandler: failure,
+                                                            request: usedRequest,
+                                                            data: data,
+                                                            resp: resp,
+                                                            error: error)
+                }
+                self.task = self.session.dataTask(with: usedRequest, completionHandler: completionHandler)
+            } else {
+                self.task = self.session.dataTask(with: usedRequest)
+            }
+
+            self.task?.resume()
+            self.session.finishTasksAndInvalidate()
+
+            #if os(iOS)
+                #if !OAUTH_APP_EXTENSIONS
+                    UIApplication.shared.isNetworkActivityIndicatorVisible = self.config.sessionFactory.isNetworkActivityIndicatorVisible
+                #endif
+            #endif
+        }
+    }
+
+    /// Function called when receiving data from server.
+    public static func completionHandler(successHandler: SuccessHandler?, failureHandler: FailureHandler?, request: URLRequest, data: Data?, resp: URLResponse?, error: Error?) {
+        #if os(iOS)
+        #if !OAUTH_APP_EXTENSIONS
+        UIApplication.shared.isNetworkActivityIndicatorVisible = false
+        #endif
+        #endif
+
+        // MARK: failure error returned by server
+        if let error = error {
+            var oauthError: OAuthSwiftError = .requestError(error: error, request: request)
+            let nsError = error as NSError
+            if nsError.code == NSURLErrorCancelled {
+                oauthError = .cancelled
+            } else if nsError.isExpiredToken {
+                oauthError = .tokenExpired(error: error)
+            }
+
+            failureHandler?(oauthError)
+            return
+        }
+
+        // MARK: failure no response or data returned by server
+        guard let response = resp as? HTTPURLResponse, let responseData = data else {
+            let badRequestCode = 400
+            let localizedDescription = OAuthSwiftHTTPRequest.descriptionForHTTPStatus(badRequestCode, responseString: "")
+            var userInfo: [String: Any] = [
+                NSLocalizedDescriptionKey: localizedDescription
+            ]
+            if let response = resp { // there is only no data
+                userInfo[OAuthSwiftError.ResponseKey] = response
+            }
+            if let response = resp as? HTTPURLResponse {
+                userInfo["Response-Headers"] = response.allHeaderFields
+            }
+            let error = NSError(domain: OAuthSwiftError.Domain, code: badRequestCode, userInfo: userInfo)
+            failureHandler?(.requestError(error:error, request: request))
+            return
+        }
+
+        // MARK: failure code > 400
+        guard response.statusCode < 400 else {
+            var localizedDescription = String()
+            let responseString = String(data: responseData, encoding: OAuthSwiftDataEncoding)
+
+            // Try to get error information from data as json
+            let responseJSON = try? JSONSerialization.jsonObject(with: responseData, options: .mutableContainers)
+            if let responseJSON = responseJSON as? OAuthSwift.Parameters {
+                if let code = responseJSON["error"] as? String, let description = responseJSON["error_description"] as? String {
+
+                    localizedDescription = NSLocalizedString("\(code) \(description)", comment: "")
+                    if code == "authorization_pending" {
+                        failureHandler?(.authorizationPending)
+                        return
+                    }
+                }
+            } else {
+                localizedDescription = OAuthSwiftHTTPRequest.descriptionForHTTPStatus(response.statusCode, responseString: String(data: responseData, encoding: OAuthSwiftDataEncoding)!)
+            }
+
+            var userInfo: [String: Any] = [
+                NSLocalizedDescriptionKey: localizedDescription,
+                "Response-Headers": response.allHeaderFields,
+                OAuthSwiftError.ResponseKey: response,
+                OAuthSwiftError.ResponseDataKey: responseData
+            ]
+            if let string = responseString {
+                userInfo["Response-Body"] = string
+            }
+            if let urlString = response.url?.absoluteString {
+                userInfo[NSURLErrorFailingURLErrorKey] = urlString
+            }
+
+            let error = NSError(domain: NSURLErrorDomain, code: response.statusCode, userInfo: userInfo)
+            if error.isExpiredToken {
+                failureHandler?(.tokenExpired(error: error))
+            } else {
+                failureHandler?(.requestError(error: error, request: request))
+            }
+            return
+        }
+
+        // MARK: success
+        successHandler?(OAuthSwiftResponse(data: responseData, response: response, request: request))
+    }
+
+    open func cancel() {
+        // perform lock here to prevent cancel calls on another thread while creating the request
+        objc_sync_enter(self)
+        defer { objc_sync_exit(self) }
+        // either cancel the request if it's already running or set the flag to prohibit creation of the request
+        if let task = task {
+            task.cancel()
+        } else {
+            cancelRequested = true
+        }
+    }
+
+    open func makeRequest() throws -> URLRequest {
+        return try OAuthSwiftHTTPRequest.makeRequest(config: self.config)
+    }
+
+    open class func makeRequest(config: Config) throws -> URLRequest {
+        var request = config.urlRequest
+        return try setupRequestForOAuth(request: &request,
+                                        parameters: config.parameters,
+                                        dataEncoding: config.dataEncoding,
+                                        paramsLocation: config.paramsLocation
+        )
+    }
+
+    open class func makeRequest(
+        url: Foundation.URL,
+        method: Method,
+        headers: OAuthSwift.Headers,
+        parameters: OAuthSwift.Parameters,
+        dataEncoding: String.Encoding,
+        body: Data? = nil,
+        paramsLocation: ParamsLocation = .authorizationHeader) throws -> URLRequest {
+
+        var request = URLRequest(url: url)
+        request.httpMethod = method.rawValue
+        for (key, value) in headers {
+            request.setValue(value, forHTTPHeaderField: key)
+        }
+
+        return try setupRequestForOAuth(
+            request: &request,
+            parameters: parameters,
+            dataEncoding: dataEncoding,
+            body: body,
+            paramsLocation: paramsLocation
+        )
+    }
+
+    open class func setupRequestForOAuth(
+        request: inout URLRequest,
+        parameters: OAuthSwift.Parameters,
+        dataEncoding: String.Encoding = OAuthSwiftDataEncoding,
+        body: Data? = nil,
+        paramsLocation: ParamsLocation = .authorizationHeader) throws -> URLRequest {
+
+        let finalParameters: OAuthSwift.Parameters
+        switch paramsLocation {
+        case .authorizationHeader:
+            finalParameters = parameters.filter { key, _ in !key.hasPrefix("oauth_") }
+        case .requestURIQuery:
+            finalParameters = parameters
+        }
+
+        if let b = body {
+            request.httpBody = b
+        } else {
+            if !finalParameters.isEmpty {
+                let charset = dataEncoding.charset
+                let headers = request.allHTTPHeaderFields ?? [:]
+                if request.httpMethod == "GET" || request.httpMethod == "HEAD" || request.httpMethod == "DELETE" {
+                    let queryString = finalParameters.urlEncodedQuery
+                    let url = request.url!
+                    request.url = url.urlByAppending(queryString: queryString)
+                    if headers[kHTTPHeaderContentType] == nil {
+                        request.setValue("application/x-www-form-urlencoded; charset=\(charset)", forHTTPHeaderField: kHTTPHeaderContentType)
+                    }
+                } else {
+                    if let contentType = headers[kHTTPHeaderContentType], contentType.contains("application/json") {
+                        let jsonData = try JSONSerialization.data(withJSONObject: finalParameters, options: [])
+                        request.setValue("application/json; charset=\(charset)", forHTTPHeaderField: kHTTPHeaderContentType)
+                        request.httpBody = jsonData
+                    } else if let contentType = headers[kHTTPHeaderContentType], contentType.contains("multipart/form-data") {
+                    // snip
+                    } else {
+                        request.setValue("application/x-www-form-urlencoded; charset=\(charset)", forHTTPHeaderField: kHTTPHeaderContentType)
+                        let queryString = finalParameters.urlEncodedQuery
+                        request.httpBody = queryString.data(using: dataEncoding, allowLossyConversion: true)
+                    }
+                }
+            }
+        }
+        return request
+    }
+
+}
+
+// MARK: - Request configuraiton
+extension OAuthSwiftHTTPRequest {
+
+    /// Configuration for request
+    public struct Config {
+
+        /// URLRequest (url, method, ...)
+        public var urlRequest: URLRequest
+        /// These parameters are either added to the query string for GET, HEAD and DELETE requests or
+        /// used as the http body in case of POST, PUT or PATCH requests.
+        ///
+        /// If used in the body they are either encoded as JSON or as encoded plaintext based on the Content-Type header field.
+        public var parameters: OAuthSwift.Parameters
+        public let paramsLocation: ParamsLocation
+        public let dataEncoding: String.Encoding
+        public let sessionFactory: URLSessionFactory
+
+        /// Shortcut
+        public var httpMethod: Method {
+            if let requestMethod = urlRequest.httpMethod {
+                return Method(rawValue: requestMethod) ?? .GET
+            }
+            return .GET
+        }
+
+        public var url: Foundation.URL? {
+            return urlRequest.url
+        }
+
+        // MARK: init
+        public init(url: URL, httpMethod: Method = .GET, httpBody: Data? = nil, headers: OAuthSwift.Headers = [:], timeoutInterval: TimeInterval = 60, httpShouldHandleCookies: Bool = false, parameters: OAuthSwift.Parameters, paramsLocation: ParamsLocation = .authorizationHeader, dataEncoding: String.Encoding = OAuthSwiftDataEncoding, sessionFactory: URLSessionFactory = .default) {
+            var urlRequest = URLRequest(url: url)
+            urlRequest.httpMethod = httpMethod.rawValue
+            urlRequest.httpBody = httpBody
+            urlRequest.allHTTPHeaderFields = headers
+            urlRequest.timeoutInterval = timeoutInterval
+            urlRequest.httpShouldHandleCookies = httpShouldHandleCookies
+            self.init(urlRequest: urlRequest, parameters: parameters, paramsLocation: paramsLocation, dataEncoding: dataEncoding, sessionFactory: sessionFactory)
+        }
+
+        public init(urlRequest: URLRequest, parameters: OAuthSwift.Parameters = [:], paramsLocation: ParamsLocation = .authorizationHeader, dataEncoding: String.Encoding = OAuthSwiftDataEncoding, sessionFactory: URLSessionFactory = .default) {
+            self.urlRequest = urlRequest
+            self.parameters = parameters
+            self.paramsLocation = paramsLocation
+            self.dataEncoding = dataEncoding
+            self.sessionFactory = sessionFactory
+        }
+
+        /// Modify request with authentification
+        public mutating func updateRequest(credential: OAuthSwiftCredential) {
+            let method = self.httpMethod
+            let url = self.urlRequest.url!
+            let headers: OAuthSwift.Headers = self.urlRequest.allHTTPHeaderFields ?? [:]
+            let paramsLocation = self.paramsLocation
+            let parameters = self.parameters
+
+            var signatureUrl = url
+            var signatureParameters = parameters
+
+            // Check if body must be hashed (oauth1)
+            let body: Data? = nil
+            if method.isBody {
+                if let contentType = headers[kHTTPHeaderContentType]?.lowercased() {
+
+                    if contentType.contains("application/json") {
+                        // TODO: oauth_body_hash create body before signing if implementing body hashing
+                        /*do {
+                         let jsonData: Data = try JSONSerialization.jsonObject(parameters, options: [])
+                         request.HTTPBody = jsonData
+                         requestHeaders["Content-Length"] = "\(jsonData.length)"
+                         body = jsonData
+                         }
+                         catch {
+                         }*/
+
+                        signatureParameters = [:] // parameters are not used for general signature (could only be used for body hashing
+                    }
+                    // else other type are not supported, see setupRequestForOAuth()
+                }
+            }
+
+            // Need to account for the fact that some consumers will have additional parameters on the
+            // querystring, including in the case of fetching a request token. Especially in the case of
+            // additional parameters on the request, authorize, or access token exchanges, we need to
+            // normalize the URL and add to the parametes collection.
+
+            var queryStringParameters = OAuthSwift.Parameters()
+            var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false )
+            if let queryItems = urlComponents?.queryItems {
+                for queryItem in queryItems {
+                    let value = queryItem.value?.safeStringByRemovingPercentEncoding ?? ""
+                    queryStringParameters.updateValue(value, forKey: queryItem.name)
+                }
+            }
+
+            // According to the OAuth1.0a spec, the url used for signing is ONLY scheme, path, and query
+            if !queryStringParameters.isEmpty {
+                urlComponents?.query = nil
+                // This is safe to unwrap because these just came from an NSURL
+                signatureUrl = urlComponents?.url ?? url
+            }
+            signatureParameters = signatureParameters.join(queryStringParameters)
+
+            var requestHeaders = OAuthSwift.Headers()
+            switch paramsLocation {
+            case .authorizationHeader:
+                //Add oauth parameters in the Authorization header
+                requestHeaders += credential.makeHeaders(signatureUrl, method: method, parameters: signatureParameters, body: body)
+            case .requestURIQuery:
+                //Add oauth parameters as request parameters
+                self.parameters += credential.authorizationParametersWithSignature(method: method, url: signatureUrl, parameters: signatureParameters, body: body)
+            }
+
+            self.urlRequest.allHTTPHeaderFields = requestHeaders + headers
+        }
+
+    }
+}
+
+// MARK: - session configuration
+
+/// configure how URLSession is initialized
+public struct URLSessionFactory {
+
+    public static let `default` = URLSessionFactory()
+
+    public var configuration = URLSessionConfiguration.default
+    public var queue = OperationQueue.main
+    /// An optional delegate for the URLSession
+    public weak var delegate: URLSessionDelegate?
+
+    /// Monitor session: see UIApplication.shared.isNetworkActivityIndicatorVisible
+    public var isNetworkActivityIndicatorVisible = true
+
+    /// By default use a closure to receive data from server.
+    /// If you set to false, you must in `delegate` take care of server response.
+    /// and maybe call in delegate `OAuthSwiftHTTPRequest.completionHandler`
+    public var useDataTaskClosure = true
+
+    /// Create a new URLSession
+    func build() -> URLSession {
+        return URLSession(configuration: self.configuration, delegate: self.delegate, delegateQueue: self.queue)
+    }
+}
+
+// MARK: - status code mapping
+
+extension OAuthSwiftHTTPRequest {
+
+    class func descriptionForHTTPStatus(_ status: Int, responseString: String) -> String {
+
+        var s = "HTTP Status \(status)"
+
+        var description: String?
+        // http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+        if status == 400 { description = "Bad Request" }
+        if status == 401 { description = "Unauthorized" }
+        if status == 402 { description = "Payment Required" }
+        if status == 403 { description = "Forbidden" }
+        if status == 404 { description = "Not Found" }
+        if status == 405 { description = "Method Not Allowed" }
+        if status == 406 { description = "Not Acceptable" }
+        if status == 407 { description = "Proxy Authentication Required" }
+        if status == 408 { description = "Request Timeout" }
+        if status == 409 { description = "Conflict" }
+        if status == 410 { description = "Gone" }
+        if status == 411 { description = "Length Required" }
+        if status == 412 { description = "Precondition Failed" }
+        if status == 413 { description = "Payload Too Large" }
+        if status == 414 { description = "URI Too Long" }
+        if status == 415 { description = "Unsupported Media Type" }
+        if status == 416 { description = "Requested Range Not Satisfiable" }
+        if status == 417 { description = "Expectation Failed" }
+        if status == 422 { description = "Unprocessable Entity" }
+        if status == 423 { description = "Locked" }
+        if status == 424 { description = "Failed Dependency" }
+        if status == 425 { description = "Unassigned" }
+        if status == 426 { description = "Upgrade Required" }
+        if status == 427 { description = "Unassigned" }
+        if status == 428 { description = "Precondition Required" }
+        if status == 429 { description = "Too Many Requests" }
+        if status == 430 { description = "Unassigned" }
+        if status == 431 { description = "Request Header Fields Too Large" }
+        if status == 432 { description = "Unassigned" }
+        if status == 500 { description = "Internal Server Error" }
+        if status == 501 { description = "Not Implemented" }
+        if status == 502 { description = "Bad Gateway" }
+        if status == 503 { description = "Service Unavailable" }
+        if status == 504 { description = "Gateway Timeout" }
+        if status == 505 { description = "HTTP Version Not Supported" }
+        if status == 506 { description = "Variant Also Negotiates" }
+        if status == 507 { description = "Insufficient Storage" }
+        if status == 508 { description = "Loop Detected" }
+        if status == 509 { description = "Unassigned" }
+        if status == 510 { description = "Not Extended" }
+        if status == 511 { description = "Network Authentication Required" }
+
+        if description != nil {
+            s += ": " + description! + ", Response: " + responseString
+        }
+
+        return s
+    }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuthSwiftMultipartData.swift b/iOS/Pods/OAuthSwift/Sources/OAuthSwiftMultipartData.swift
new file mode 100644 (file)
index 0000000..30d35e1
--- /dev/null
@@ -0,0 +1,48 @@
+//
+//  OAuthSwiftMultipartData.swift
+//  OAuthSwift
+//
+//  Created by Tomohiro Kawaji on 12/18/15.
+//  Copyright (c) 2015 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+public struct OAuthSwiftMultipartData {
+
+    public var name: String
+    public var data: Data
+    public var fileName: String?
+    public var mimeType: String?
+
+    public init(name: String, data: Data, fileName: String?, mimeType: String?) {
+        self.name = name
+        self.data = data
+        self.fileName = fileName
+        self.mimeType = mimeType
+    }
+
+}
+
+extension Data {
+
+    public mutating func append(_ multipartData: OAuthSwiftMultipartData, encoding: String.Encoding, separatorData: Data) {
+        var filenameClause = ""
+        if let filename = multipartData.fileName {
+            filenameClause = " filename=\"\(filename)\""
+        }
+        let contentDispositionString = "Content-Disposition: form-data; name=\"\(multipartData.name)\";\(filenameClause)\r\n"
+        let contentDispositionData = contentDispositionString.data(using: encoding)!
+        self.append(contentDispositionData)
+
+        if let mimeType = multipartData.mimeType {
+            let contentTypeString = "Content-Type: \(mimeType)\r\n"
+            let contentTypeData = contentTypeString.data(using: encoding)!
+            self.append(contentTypeData)
+        }
+
+        self.append(separatorData)
+        self.append(multipartData.data)
+        self.append(separatorData)
+    }
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuthSwiftResponse.swift b/iOS/Pods/OAuthSwift/Sources/OAuthSwiftResponse.swift
new file mode 100644 (file)
index 0000000..8940b82
--- /dev/null
@@ -0,0 +1,50 @@
+//
+//  OAuthSwiftResponse.swift
+//  OAuthSwift
+//
+//  Created by phimage on 04/11/16.
+//  Copyright © 2016 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+/// Response object
+@objc
+public class OAuthSwiftResponse: NSObject { // not a struct for objc
+    /// The data returned by the server.
+    public var data: Data
+    /// The server's response to the URL request.
+    public var response: HTTPURLResponse
+    /// The URL request sent to the server.
+    public var request: URLRequest?
+
+    public init(data: Data, response: HTTPURLResponse, request: URLRequest?) {
+        self.data = data
+        self.response = response
+        self.request = request
+    }
+
+}
+
+/// Extends this object to convert data into your business objects
+extension OAuthSwiftResponse {
+
+    public func dataString(encoding: String.Encoding = OAuthSwiftDataEncoding) -> String? {
+        return String(data: self.data, encoding: encoding)
+    }
+
+    /// `data` converted to string using data encoding
+    public var string: String? {
+        return dataString()
+    }
+
+    /// Convert to json object using JSONSerialization
+    public func jsonObject(options opt: JSONSerialization.ReadingOptions = []) throws -> Any {
+        return try JSONSerialization.jsonObject(with: self.data, options: opt)
+    }
+
+    /// Convert to object using PropertyListSerialization
+    public func propertyList(options opt: PropertyListSerialization.ReadOptions = [], format: UnsafeMutablePointer<PropertyListSerialization.PropertyListFormat>? = nil) throws -> Any {
+        return try PropertyListSerialization.propertyList(from: self.data, options: opt, format: format)
+    }
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuthSwiftURLHandlerType.swift b/iOS/Pods/OAuthSwift/Sources/OAuthSwiftURLHandlerType.swift
new file mode 100644 (file)
index 0000000..d5b5426
--- /dev/null
@@ -0,0 +1,167 @@
+//
+//  OAuthSwiftURLHandlerType.swift
+//  OAuthSwift
+//
+//  Created by phimage on 11/05/15.
+//  Copyright (c) 2015 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+#elseif os(watchOS)
+    import WatchKit
+#elseif os(OSX)
+    import AppKit
+#endif
+
+@objc public protocol OAuthSwiftURLHandlerType {
+    func handle(_ url: URL)
+}
+
+// MARK: Open externally
+open class OAuthSwiftOpenURLExternally: OAuthSwiftURLHandlerType {
+
+    public static var sharedInstance: OAuthSwiftOpenURLExternally = OAuthSwiftOpenURLExternally()
+
+    @objc open func handle(_ url: URL) {
+        #if os(iOS) || os(tvOS)
+            #if !OAUTH_APP_EXTENSIONS
+                UIApplication.shared.openURL(url)
+            #endif
+        #elseif os(watchOS)
+        // WATCHOS: not implemented
+        #elseif os(OSX)
+            NSWorkspace.shared.open(url)
+        #endif
+    }
+}
+
+// MARK: Open SFSafariViewController
+#if os(iOS)
+import SafariServices
+
+    @available(iOS 9.0, *)
+    open class SafariURLHandler: NSObject, OAuthSwiftURLHandlerType, SFSafariViewControllerDelegate {
+
+        public typealias UITransion = (_ controller: SFSafariViewController, _ handler: SafariURLHandler) -> Void
+
+        weak open var oauthSwift: OAuthSwift?
+        open var present: UITransion
+        open var dismiss: UITransion
+        /// retains observers
+        var observers = [String: NSObjectProtocol]()
+
+        open var factory: (_ URL: URL) -> SFSafariViewController = {URL in
+            return SFSafariViewController(url: URL)
+        }
+
+        /// delegates
+        open weak var delegate: SFSafariViewControllerDelegate?
+
+        // configure default presentation and dismissal code
+
+        open var animated: Bool = true
+        open var presentCompletion: (() -> Void)?
+        open var dismissCompletion: (() -> Void)?
+        open var delay: UInt32? = 1
+
+        /// init
+        public init(viewController: UIViewController, oauthSwift: OAuthSwift) {
+            self.oauthSwift = oauthSwift
+            self.present = { [weak viewController] controller, handler in
+                viewController?.present(controller, animated: handler.animated, completion: handler.presentCompletion)
+            }
+            self.dismiss = { [weak viewController] _, handler in
+                viewController?.dismiss(animated: handler.animated, completion: handler.dismissCompletion)
+            }
+        }
+
+        public init(present: @escaping UITransion, dismiss: @escaping UITransion, oauthSwift: OAuthSwift) {
+            self.oauthSwift = oauthSwift
+            self.present = present
+            self.dismiss = dismiss
+        }
+
+        @objc open func handle(_ url: URL) {
+            let controller = factory(url)
+            controller.delegate = self
+
+            // present controller in main thread
+            OAuthSwift.main { [weak self] in
+                guard let this = self else {
+                    return
+                }
+                if let delay = this.delay { // sometimes safari show a blank view..
+                    sleep(delay)
+                }
+                this.present(controller, this)
+            }
+
+            let key = UUID().uuidString
+
+            observers[key] = OAuthSwift.notificationCenter.addObserver(
+                forName: NSNotification.Name.OAuthSwiftHandleCallbackURL,
+                object: nil,
+                queue: OperationQueue.main,
+                using: { [weak self] _ in
+                    guard let this = self else {
+                        return
+                    }
+                    if let observer = this.observers[key] {
+                        OAuthSwift.notificationCenter.removeObserver(observer)
+                        this.observers.removeValue(forKey: key)
+                    }
+                    OAuthSwift.main {
+                        this.dismiss(controller, this)
+                    }
+                }
+            )
+        }
+
+        /// Clear internal observers on authentification flow
+        open func clearObservers() {
+            clearLocalObservers()
+            self.oauthSwift?.removeCallbackNotificationObserver()
+        }
+
+        open func clearLocalObservers() {
+            for (_, observer) in observers {
+                OAuthSwift.notificationCenter.removeObserver(observer)
+            }
+            observers.removeAll()
+        }
+
+        /// SFSafariViewControllerDelegate
+        public func safariViewController(_ controller: SFSafariViewController, activityItemsFor URL: Foundation.URL, title: String?) -> [UIActivity] {
+            return self.delegate?.safariViewController?(controller, activityItemsFor: URL, title: title) ?? []
+        }
+
+        public func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
+            // "Done" pressed
+            self.clearObservers()
+            self.delegate?.safariViewControllerDidFinish?(controller)
+        }
+
+        public func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) {
+            self.delegate?.safariViewController?(controller, didCompleteInitialLoad: didLoadSuccessfully)
+        }
+
+    }
+
+#endif
+
+// MARK: Open url using NSExtensionContext
+open class ExtensionContextURLHandler: OAuthSwiftURLHandlerType {
+
+    fileprivate var extensionContext: NSExtensionContext
+
+    public init(extensionContext: NSExtensionContext) {
+        self.extensionContext = extensionContext
+    }
+
+    @objc open func handle(_ url: URL) {
+        extensionContext.open(url, completionHandler: nil)
+    }
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/OAuthWebViewController.swift b/iOS/Pods/OAuthSwift/Sources/OAuthWebViewController.swift
new file mode 100644 (file)
index 0000000..ae2a6de
--- /dev/null
@@ -0,0 +1,193 @@
+//
+//  OAuthWebViewController.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 2/11/15.
+//  Copyright (c) 2015 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+#if os(iOS)  || os(tvOS)
+    import UIKit
+    public typealias OAuthViewController = UIViewController
+#elseif os(watchOS)
+    import WatchKit
+    public typealias OAuthViewController = WKInterfaceController
+#elseif os(OSX)
+    import AppKit
+    public typealias OAuthViewController = NSViewController
+#endif
+
+/// Delegate for OAuthWebViewController
+public protocol OAuthWebViewControllerDelegate: class {
+
+    #if os(iOS) || os(tvOS)
+    /// Did web view presented (work only without navigation controller)
+    func oauthWebViewControllerDidPresent()
+    /// Did web view dismiss (work only without navigation controller)
+    func oauthWebViewControllerDidDismiss()
+    #endif
+
+    func oauthWebViewControllerWillAppear()
+    func oauthWebViewControllerDidAppear()
+    func oauthWebViewControllerWillDisappear()
+    func oauthWebViewControllerDidDisappear()
+}
+
+/// A web view controller, which handler OAuthSwift authentification.
+open class OAuthWebViewController: OAuthViewController, OAuthSwiftURLHandlerType {
+
+    #if os(iOS) || os(tvOS) || os(OSX)
+    /// Delegate for this view
+    public weak var delegate: OAuthWebViewControllerDelegate?
+    #endif
+
+    #if os(iOS) || os(tvOS)
+    /// If controller have an navigation controller, application top view controller could be used if true
+    public var useTopViewControlerInsteadOfNavigation = false
+
+    /// Set false to disable present animation.
+    public var presentViewControllerAnimated = true
+    /// Set false to disable dismiss animation.
+    public var dismissViewControllerAnimated = true
+
+    public var topViewController: UIViewController? {
+        #if !OAUTH_APP_EXTENSIONS
+            return UIApplication.topViewController
+        #else
+            return nil
+        #endif
+    }
+    #elseif os(OSX)
+    /// How to present this view controller if parent view controller set
+    public enum Present {
+        case asModalWindow
+        case asSheet
+        case asPopover(relativeToRect: NSRect, ofView : NSView, preferredEdge: NSRectEdge, behavior: NSPopover.Behavior)
+        case transitionFrom(fromViewController: NSViewController, options: NSViewController.TransitionOptions)
+        case animator(animator: NSViewControllerPresentationAnimator)
+        case segue(segueIdentifier: String)
+    }
+    public var present: Present = .asModalWindow
+    #endif
+
+    open func handle(_ url: URL) {
+        // do UI in main thread
+        OAuthSwift.main { [unowned self] in
+             self.doHandle(url)
+        }
+    }
+
+    #if os(watchOS)
+    public static var userActivityType: String = "org.github.dongri.oauthswift.connect"
+    #endif
+
+    open func doHandle(_ url: URL) {
+        #if os(iOS) || os(tvOS)
+            let completion: () -> Void = { [unowned self] in
+                self.delegate?.oauthWebViewControllerDidPresent()
+            }
+            if let navigationController = self.navigationController, (!useTopViewControlerInsteadOfNavigation || self.topViewController == nil) {
+                navigationController.pushViewController(self, animated: presentViewControllerAnimated)
+            } else if let p = self.parent {
+                p.present(self, animated: presentViewControllerAnimated, completion: completion)
+            } else if let topViewController = topViewController {
+                topViewController.present(self, animated: presentViewControllerAnimated, completion: completion)
+            } else {
+                // assert no presentation
+                assertionFailure("Failed to present. Maybe add a parent")
+            }
+        #elseif os(watchOS)
+            if url.scheme == "http" || url.scheme == "https" {
+                self.updateUserActivity(OAuthWebViewController.userActivityType, userInfo: nil, webpageURL: url)
+            }
+        #elseif os(OSX)
+            if let p = self.parent { // default behaviour if this controller affected as child controller
+                switch self.present {
+                case .asSheet:
+                    p.presentViewControllerAsSheet(self)
+                case .asModalWindow:
+                    p.presentViewControllerAsModalWindow(self)
+                    // FIXME: if we present as window, window close must detected and oauthswift.cancel() must be called...
+                case .asPopover(let positioningRect, let positioningView, let preferredEdge, let behavior):
+                    p.presentViewController(self, asPopoverRelativeTo: positioningRect, of: positioningView, preferredEdge: preferredEdge, behavior: behavior)
+                case .transitionFrom(let fromViewController, let options):
+                    let completion: () -> Void = { /*[unowned self] in*/
+                        //self.delegate?.oauthWebViewControllerDidPresent()
+                    }
+                    p.transition(from: fromViewController, to: self, options: options, completionHandler: completion)
+                case .animator(let animator):
+                    p.presentViewController(self, animator: animator)
+                case .segue(let segueIdentifier):
+                    p.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: segueIdentifier), sender: self) // The segue must display self.view
+                }
+            } else if let window = self.view.window {
+                window.makeKeyAndOrderFront(nil)
+            } else {
+                assertionFailure("Failed to present. Add controller into a window or add a parent")
+            }
+            // or create an NSWindow or NSWindowController (/!\ keep a strong reference on it)
+        #endif
+    }
+
+    open func dismissWebViewController() {
+        #if os(iOS) || os(tvOS)
+            let completion: () -> Void = { [unowned self] in
+                self.delegate?.oauthWebViewControllerDidDismiss()
+            }
+            if let navigationController = self.navigationController, (!useTopViewControlerInsteadOfNavigation || self.topViewController == nil) {
+                navigationController.popViewController(animated: dismissViewControllerAnimated)
+            } else if let parentViewController = self.parent {
+                // The presenting view controller is responsible for dismissing the view controller it presented
+                parentViewController.dismiss(animated: dismissViewControllerAnimated, completion: completion)
+            } else if let topViewController = topViewController {
+                topViewController.dismiss(animated: dismissViewControllerAnimated, completion: completion)
+            } else {
+                // keep old code...
+                self.dismiss(animated: dismissViewControllerAnimated, completion: completion)
+            }
+        #elseif os(watchOS)
+            self.dismiss()
+        #elseif os(OSX)
+            if self.presenting != nil {
+                self.dismiss(nil)
+                if self.parent != nil {
+                    self.removeFromParentViewController()
+                }
+            } else if let window = self.view.window {
+                window.performClose(nil)
+            }
+        #endif
+    }
+
+    // MARK: overrides
+    #if os(iOS) || os(tvOS)
+    open override func viewWillAppear(_ animated: Bool) {
+        self.delegate?.oauthWebViewControllerWillAppear()
+    }
+    open override func viewDidAppear(_ animated: Bool) {
+        self.delegate?.oauthWebViewControllerDidAppear()
+    }
+    open override func viewWillDisappear(_ animated: Bool) {
+        self.delegate?.oauthWebViewControllerWillDisappear()
+    }
+    open override func viewDidDisappear(_ animated: Bool) {
+        self.delegate?.oauthWebViewControllerDidDisappear()
+    }
+    #elseif os(OSX)
+    open override func viewWillAppear() {
+        self.delegate?.oauthWebViewControllerWillAppear()
+    }
+    open override func viewDidAppear() {
+        self.delegate?.oauthWebViewControllerDidAppear()
+    }
+    open override func viewWillDisappear() {
+        self.delegate?.oauthWebViewControllerWillDisappear()
+    }
+    open override func viewDidDisappear() {
+        self.delegate?.oauthWebViewControllerDidDisappear()
+    }
+
+    #endif
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/Objc.swift b/iOS/Pods/OAuthSwift/Sources/Objc.swift
new file mode 100644 (file)
index 0000000..637bf82
--- /dev/null
@@ -0,0 +1,75 @@
+//
+//  Objc.swift
+//  OAuthSwift
+//
+//  Created by phimage on 05/11/16.
+//  Copyright © 2016 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+extension OAuthSwift {
+    // swiftlint:disable:next type_name
+    public typealias Obj_FailureHandler = (_ error: Error) -> Void
+}
+
+extension OAuth1Swift {
+
+    open func objc_authorize(withCallbackURL urlString: String, success: @escaping TokenSuccessHandler, failure: Obj_FailureHandler?) -> OAuthSwiftRequestHandle? {
+        guard let url = URL(string: urlString) else {
+            failure?(OAuthSwiftError.encodingError(urlString: urlString))
+            return nil
+        }
+        return authorize(withCallbackURL: url, success: success, failure: failure)
+    }
+
+}
+
+extension OAuth2Swift {
+
+    open func objc_authorize(withCallbackURL urlString: String, scope: String, state: String, parameters: Parameters = [:], headers: OAuthSwift.Headers? = nil, success: @escaping TokenSuccessHandler, failure: Obj_FailureHandler?) -> OAuthSwiftRequestHandle? {
+        guard let url = URL(string: urlString) else {
+            failure?(OAuthSwiftError.encodingError(urlString: urlString))
+            return nil
+        }
+        return authorize(withCallbackURL: url, scope: scope, state: state, parameters: parameters, headers: headers, success: success, failure: failure)
+    }
+
+       open func objc_renewAccessToken(withRefreshToken refreshToken: String, headers: OAuthSwift.Headers? = nil, success: @escaping TokenSuccessHandler, failure: Obj_FailureHandler?) -> OAuthSwiftRequestHandle? {
+               return renewAccessToken(withRefreshToken: refreshToken, headers: headers, success: success, failure: failure)
+       }
+
+}
+
+extension OAuthSwiftHTTPRequest {
+    // swiftlint:disable:next type_name
+    public typealias Obj_FailureHandler = (_ error: Error) -> Void
+}
+
+extension OAuthSwiftClient {
+
+    open func objc_request(_ urlString: String, method: OAuthSwiftHTTPRequest.Method, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, body: Data? = nil, checkTokenExpiration: Bool = true, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.Obj_FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return request(urlString, method: method, parameters: parameters, headers: headers, body: body, checkTokenExpiration: checkTokenExpiration, success: success, failure: failure)
+    }
+
+    open func get(_ urlString: String, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.Obj_FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.request(urlString, method: .GET, parameters: parameters, headers: headers, success: success, failure: failure)
+    }
+
+    open func post(_ urlString: String, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, body: Data? = nil, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.Obj_FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.request(urlString, method: .POST, parameters: parameters, headers: headers, body: body, success: success, failure: failure)
+    }
+
+    open func put(_ urlString: String, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, body: Data? = nil, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.Obj_FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.request(urlString, method: .PUT, parameters: parameters, headers: headers, body: body, success: success, failure: failure)
+    }
+
+    open func delete(_ urlString: String, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.Obj_FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.request(urlString, method: .DELETE, parameters: parameters, headers: headers, success: success, failure: failure)
+    }
+
+    open func patch(_ urlString: String, parameters: OAuthSwift.Parameters = [:], headers: OAuthSwift.Headers? = nil, success: OAuthSwiftHTTPRequest.SuccessHandler?, failure: OAuthSwiftHTTPRequest.Obj_FailureHandler?) -> OAuthSwiftRequestHandle? {
+        return self.request(urlString, method: .PATCH, parameters: parameters, headers: headers, success: success, failure: failure)
+    }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/SHA1.swift b/iOS/Pods/OAuthSwift/Sources/SHA1.swift
new file mode 100644 (file)
index 0000000..f430a1d
--- /dev/null
@@ -0,0 +1,152 @@
+//
+//  SHA1.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 1/28/15.
+//  Copyright (c) 2015 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+class SHA1 {
+
+    private var message: [UInt8]
+
+    fileprivate let h: [UInt32] = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]
+
+    init(_ message: Data) {
+        self.message = message.bytes
+    }
+    init(_ message: [UInt8]) {
+        self.message = message
+    }
+
+    /// Common part for hash calculation. Prepare header data.
+    func prepare(_ message: [UInt8], _ blockSize: Int, _ allowance: Int) -> [UInt8] {
+        var tmpMessage = message
+
+        // Step 1. Append Padding Bits
+        tmpMessage.append(0x80) // append one bit (Byte with one bit) to message
+
+        // append "0" bit until message length in bits ≡ 448 (mod 512)
+        var msgLength = tmpMessage.count
+        var counter = 0
+
+        while msgLength % blockSize != (blockSize - allowance) {
+            counter += 1
+            msgLength += 1
+        }
+
+        tmpMessage += [UInt8](repeating: 0, count: counter)
+
+        return tmpMessage
+    }
+
+    func calculate() -> [UInt8] {
+        var tmpMessage = self.prepare(self.message, 64, 64 / 8)
+
+        // hash values
+        var hh = h
+
+        // append message length, in a 64-bit big-endian integer. So now the message length is a multiple of 512 bits.
+        tmpMessage += (self.message.count * 8).bytes(64 / 8)
+
+        // Process the message in successive 512-bit chunks:
+        let chunkSizeBytes = 512 / 8 // 64
+        for chunk in BytesSequence(data: tmpMessage, chunkSize: chunkSizeBytes) {
+            // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15, big-endian
+            // Extend the sixteen 32-bit words into eighty 32-bit words:
+            var M: [UInt32] = [UInt32](repeating: 0, count: 80)
+            for x in 0..<M.count {
+                switch x {
+                case 0...15:
+
+                    let memorySize = MemoryLayout<UInt32>.size
+                    let start = chunk.startIndex + (x * memorySize)
+                    let end = start + memorySize
+                    let le = chunk[start..<end].toUInt32
+                    M[x] = le.bigEndian
+                default:
+                    M[x] = rotateLeft(M[x-3] ^ M[x-8] ^ M[x-14] ^ M[x-16], n: 1)
+                }
+            }
+
+            var A = hh[0]
+            var B = hh[1]
+            var C = hh[2]
+            var D = hh[3]
+            var E = hh[4]
+
+            // Main loop
+            for j in 0...79 {
+                var f: UInt32 = 0
+                var k: UInt32 = 0
+
+                switch j {
+                case 0...19:
+                    f = (B & C) | ((~B) & D)
+                    k = 0x5A827999
+                case 20...39:
+                    f = B ^ C ^ D
+                    k = 0x6ED9EBA1
+                case 40...59:
+                    f = (B & C) | (B & D) | (C & D)
+                    k = 0x8F1BBCDC
+                case 60...79:
+                    f = B ^ C ^ D
+                    k = 0xCA62C1D6
+                default:
+                    break
+                }
+
+                let temp = (rotateLeft(A, n: 5) &+ f &+ E &+ M[j] &+ k) & 0xffffffff
+                E = D
+                D = C
+                C = rotateLeft(B, n: 30)
+                B = A
+                A = temp
+
+            }
+
+            hh[0] = (hh[0] &+ A) & 0xffffffff
+            hh[1] = (hh[1] &+ B) & 0xffffffff
+            hh[2] = (hh[2] &+ C) & 0xffffffff
+            hh[3] = (hh[3] &+ D) & 0xffffffff
+            hh[4] = (hh[4] &+ E) & 0xffffffff
+        }
+
+        // Produce the final hash value (big-endian) as a 160 bit number:
+        var result = [UInt8]()
+        result.reserveCapacity(hh.count / 4)
+        hh.forEach {
+            let item = $0.bigEndian
+            result += [UInt8(item & 0xff), UInt8((item >> 8) & 0xff), UInt8((item >> 16) & 0xff), UInt8((item >> 24) & 0xff)]
+        }
+
+        return result
+    }
+
+    private func rotateLeft(_ v: UInt32, n: UInt32) -> UInt32 {
+        return ((v << n) & 0xFFFFFFFF) | (v >> (32 - n))
+    }
+
+}
+
+private struct BytesSequence<D: RandomAccessCollection>: Sequence where D.Iterator.Element == UInt8, D.Index == Int {
+    let data: D
+    let chunkSize: Int
+
+    func makeIterator() -> AnyIterator<D.SubSequence> {
+        var offset = data.startIndex
+        return AnyIterator {
+            let end = Swift.min(self.chunkSize, self.data.count - offset)
+            let result = self.data[offset..<offset + end]
+            offset = offset.advanced(by: result.count)
+            if !result.isEmpty {
+                return result
+            }
+            return nil
+        }
+    }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/String+OAuthSwift.swift b/iOS/Pods/OAuthSwift/Sources/String+OAuthSwift.swift
new file mode 100755 (executable)
index 0000000..0f0c32a
--- /dev/null
@@ -0,0 +1,123 @@
+//
+//  String+OAuthSwift.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 6/21/14.
+//  Copyright (c) 2014 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+extension String {
+
+    var parametersFromQueryString: [String: String] {
+        return dictionaryBySplitting("&", keyValueSeparator: "=")
+    }
+
+    /// Encodes url string making it ready to be passed as a query parameter. This encodes pretty much everything apart from
+    /// alphanumerics and a few other characters compared to standard query encoding.
+    var urlEncoded: String {
+        let customAllowedSet = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
+        return self.addingPercentEncoding(withAllowedCharacters: customAllowedSet)!
+    }
+
+    var urlQueryEncoded: String? {
+        return self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
+    }
+
+    /// Returns new url query string by appending query parameter encoding it first, if specified.
+    func urlQueryByAppending(parameter name: String, value: String, encode: Bool = true, _ encodeError: ((String, String) -> Void)? = nil) -> String? {
+        if value.isEmpty {
+            return self
+        } else if let value = encode ? value.urlQueryEncoded : value {
+            return "\(self)\(self.isEmpty ? "" : "&")\(name)=\(value)"
+        } else {
+            encodeError?(name, value)
+            return nil
+        }
+    }
+
+    /// Returns new url string by appending query string at the end.
+    func urlByAppending(query: String) -> String {
+        return "\(self)\(self.contains("?") ? "&" : "?")\(query)"
+    }
+
+    fileprivate func dictionaryBySplitting(_ elementSeparator: String, keyValueSeparator: String) -> [String: String] {
+        var string = self
+
+        if hasPrefix(elementSeparator) {
+            string = String(dropFirst(1))
+        }
+
+        var parameters = [String: String]()
+
+        let scanner = Scanner(string: string)
+
+        var key: NSString?
+        var value: NSString?
+
+        while !scanner.isAtEnd {
+            key = nil
+            scanner.scanUpTo(keyValueSeparator, into: &key)
+            scanner.scanString(keyValueSeparator, into: nil)
+
+            value = nil
+            scanner.scanUpTo(elementSeparator, into: &value)
+            scanner.scanString(elementSeparator, into: nil)
+
+            if let key = key as String? {
+                if let value = value as String? {
+                    if key.contains(elementSeparator) {
+                        var keys = key.components(separatedBy: elementSeparator)
+                        if let key = keys.popLast() {
+                            parameters.updateValue(value, forKey: String(key))
+                        }
+                        for flag in keys {
+                            parameters.updateValue("", forKey: flag)
+                        }
+                    } else {
+                        parameters.updateValue(value, forKey: key)
+                    }
+                } else {
+                    parameters.updateValue("", forKey: key)
+                }
+            }
+        }
+
+        return parameters
+    }
+
+    public var headerDictionary: OAuthSwift.Headers {
+        return dictionaryBySplitting(",", keyValueSeparator: "=")
+    }
+
+    var safeStringByRemovingPercentEncoding: String {
+        return self.removingPercentEncoding ?? self
+    }
+
+    mutating func dropLast() {
+        self.remove(at: self.index(before: self.endIndex))
+    }
+
+    subscript (bounds: CountableClosedRange<Int>) -> String {
+        let start = index(startIndex, offsetBy: bounds.lowerBound)
+        let end = index(startIndex, offsetBy: bounds.upperBound)
+        return String(self[start...end])
+    }
+
+    subscript (bounds: CountableRange<Int>) -> String {
+        let start = index(startIndex, offsetBy: bounds.lowerBound)
+        let end = index(startIndex, offsetBy: bounds.upperBound)
+        return String(self[start..<end])
+    }
+}
+
+extension String.Encoding {
+
+    var charset: String {
+        let charset = CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(self.rawValue))
+         // swiftlint:disable:next force_cast
+        return charset! as String
+    }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/UIApplication+OAuthSwift.swift b/iOS/Pods/OAuthSwift/Sources/UIApplication+OAuthSwift.swift
new file mode 100644 (file)
index 0000000..523f84c
--- /dev/null
@@ -0,0 +1,50 @@
+//
+//  UIApplication+OAuthSwift.swift
+//  OAuthSwift
+//
+//  Created by phimage on 11/12/15.
+//  Copyright © 2015 Dongri Jin. All rights reserved.
+//
+
+#if os(iOS) || os(tvOS)
+    import UIKit
+
+    extension UIApplication {
+        static var topViewController: UIViewController? {
+            #if !OAUTH_APP_EXTENSIONS
+                return UIApplication.shared.topViewController
+            #else
+                return nil
+            #endif
+        }
+
+        var topViewController: UIViewController? {
+            guard let rootController = self.keyWindow?.rootViewController else {
+                return nil
+            }
+            return UIViewController.topViewController(rootController)
+        }
+    }
+
+    extension UIViewController {
+
+        static func topViewController(_ viewController: UIViewController) -> UIViewController {
+            guard let presentedViewController = viewController.presentedViewController else {
+                return viewController
+            }
+            #if !topVCCastDisabled
+            if let navigationController = presentedViewController as? UINavigationController {
+                if let visibleViewController = navigationController.visibleViewController {
+                    return topViewController(visibleViewController)
+                }
+            } else if let tabBarController = presentedViewController as? UITabBarController {
+                if let selectedViewController = tabBarController.selectedViewController {
+                    return topViewController(selectedViewController)
+                }
+            }
+            #endif
+            return topViewController(presentedViewController)
+        }
+    }
+
+#endif
diff --git a/iOS/Pods/OAuthSwift/Sources/URL+OAuthSwift.swift b/iOS/Pods/OAuthSwift/Sources/URL+OAuthSwift.swift
new file mode 100755 (executable)
index 0000000..e963754
--- /dev/null
@@ -0,0 +1,29 @@
+//
+//  URL+OAuthSwift.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 6/21/14.
+//  Copyright (c) 2014 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+extension URL {
+
+    func urlByAppending(queryString: String) -> URL {
+        if queryString.utf16.isEmpty {
+            return self
+        }
+
+        var absoluteURLString = absoluteString
+
+        if absoluteURLString.hasSuffix("?") {
+            absoluteURLString.dropLast()
+        }
+
+        let string = absoluteURLString + (absoluteURLString.range(of: "?") != nil ? "&" : "?") + queryString
+
+        return URL(string: string)!
+    }
+
+}
diff --git a/iOS/Pods/OAuthSwift/Sources/Utils.swift b/iOS/Pods/OAuthSwift/Sources/Utils.swift
new file mode 100644 (file)
index 0000000..8d13615
--- /dev/null
@@ -0,0 +1,23 @@
+//
+//  Utils.swift
+//  OAuthSwift
+//
+//  Created by Dongri Jin on 1/28/15.
+//  Copyright (c) 2015 Dongri Jin. All rights reserved.
+//
+
+import Foundation
+
+public func generateState(withLength len: Int) -> String {
+    let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+    let length = UInt32(letters.count)
+
+    var randomString = ""
+    for _ in 0..<len {
+        let rand = arc4random_uniform(length)
+        let idx = letters.index(letters.startIndex, offsetBy: Int(rand))
+        let letter = letters[idx]
+        randomString += String(letter)
+    }
+    return randomString
+}
diff --git a/iOS/Pods/Pods.xcodeproj/project.pbxproj b/iOS/Pods/Pods.xcodeproj/project.pbxproj
new file mode 100644 (file)
index 0000000..7cdb006
--- /dev/null
@@ -0,0 +1,7361 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 48;
+       objects = {
+
+/* Begin PBXBuildFile section */
+               00742CD999CAA90421B9558657F3BFEF /* GPBCodedOutputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 382852248FE8C625CF0186C9D1FFCC34 /* GPBCodedOutputStream.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               008D5DDE0942B5C3EC51E6D4557DFD64 /* Placeholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0792F8241E343E3F566D735EF2DEF482 /* Placeholder.swift */; };
+               0091F3D28C168C2EF8B3E3EAFC631202 /* RLMArray_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 135AAE7A08E11EC8D750570317E36028 /* RLMArray_Private.h */; };
+               009D8E987B72AC718814C940C31AA86A /* FIRAnalyticsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 47C92578FFB7251147AE7AD4DAF28E6F /* FIRAnalyticsConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               00C6AAB66CB1FFC09AD31D6AA40DFE56 /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 32898A025087808B3DAD1D234840702F /* transact_log_handler.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               0127105DE35781CDA88A0FCE48F5E72D /* Realm.h in Headers */ = {isa = PBXBuildFile; fileRef = 144443F4C5352AA38AC82F621455EA3C /* Realm.h */; };
+               013E11191C3C5FE573B9AC49169D0958 /* UITableViewVibrantCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E200356C1953B2F03FD9BCED858D6A3 /* UITableViewVibrantCell.swift */; };
+               013F0A749F2617A13C9A9A78A8777AF0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               0175DD92F9F6E8C72EE40DF0B9A5AD31 /* FIRAnalyticsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1E7F0536D4F2862E841EA8D47CC44E /* FIRAnalyticsConfiguration.m */; };
+               01829BFAB608257CA7457154606A71DD /* FIRMessagingRemoteNotificationsProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 260BE158E1193A24E66F1F038B243149 /* FIRMessagingRemoteNotificationsProxy.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               018C816F7E0AD679CE02B4BDB3C6D26A /* ioapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 82DC2D8E239E4638D8DB80C8A06D83BB /* ioapi.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               01FECC2B22E3FE698936ED47BE514888 /* FRResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 959CC486954AB25818AE8D824A60C530 /* FRResource.swift */; };
+               023082A6D72A36532D6099A99A3E9101 /* GPBDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CB73575EACC7B667464A81741CD40B9 /* GPBDescriptor.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               02A4EDFF5D72B3EA0CC5CA897017DA2F /* ZipArchive.h in Headers */ = {isa = PBXBuildFile; fileRef = CC036F1EA6EB67C4A2C91AB3FCDEBC4C /* ZipArchive.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               0388D9D4A5313A85367043889A385D3C /* sync_metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 181C2DE81F42B9BF86D03575F19C0494 /* sync_metadata.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               042EFC1017533312205A943D889EB58B /* Raleway-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 67189D34B36DE89CF42B0389F09FB517 /* Raleway-Italic.ttf */; };
+               04CF5777491412E48AEE272D75A56F77 /* Swizzlings.m in Sources */ = {isa = PBXBuildFile; fileRef = E97010326B67089745CE74477A2868F3 /* Swizzlings.m */; };
+               0579E3E6B6D59F7FE9C5A6FFFCC06284 /* pwd2key.h in Headers */ = {isa = PBXBuildFile; fileRef = C28470CDCC9315FCDA60C6472439C9EE /* pwd2key.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               05A122F6664593DB1044E1C42632DE28 /* Empty.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CCB952DC44D32026263D1EB9F1D19CE /* Empty.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               06264590012751B63A95DA1482138DEF /* FIRMessagingRmqManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 45DFA3160A343E4AA095D885BBD9483B /* FIRMessagingRmqManager.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               0678FC4DB726192529A8A278153D0996 /* GPBProtocolBuffers_RuntimeSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = F2D84C6D27A7D7F7981ADF55AE61C3DA /* GPBProtocolBuffers_RuntimeSupport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               06B1C042C3FAE54528A986AF2C2A0978 /* FIRMessagingPacketQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 10EEF27CE0A2D40194B5166A61763970 /* FIRMessagingPacketQueue.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               06D4500B35FA21D2D87816AF3949002C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               07C0515BAB41A01EDFCB5164FB622170 /* ImageModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 972B088C9B63AE08FC00845F18AAD308 /* ImageModifier.swift */; };
+               0852BDA9C8F89EA2F10495E858DD76AF /* GPBBootstrap.h in Headers */ = {isa = PBXBuildFile; fileRef = 3304A45FA8673B2DACEE5AAA8C75E0FD /* GPBBootstrap.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               08D673E9336055FF08C6F1BB6B8719EB /* RLMRealmConfiguration.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 11A9CEA39CE194E1A1C87939E94FA086 /* RLMRealmConfiguration.h */; };
+               0946606FD8E76D9FAE68288CF5DCF45C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               0A1908969828EF0B672312582540E304 /* Collection+OAuthSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9A697406390F3FC0AD51D2C04D46A0 /* Collection+OAuthSwift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               0A2B15303BC26D2BC3C907BB9BDB6A30 /* Andada-Italic.otf in Resources */ = {isa = PBXBuildFile; fileRef = C440878C8B1131EE071571ECF86D6B26 /* Andada-Italic.otf */; };
+               0A4C5C81804C50CBDF57EF842DE97ED0 /* nanopb-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DC6F0E58FABA2B29D2CFE63A7F0DFB3 /* nanopb-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               0A6E6EC25086B38191D51E8D21C4D751 /* FIRComponentRegistrant.h in Headers */ = {isa = PBXBuildFile; fileRef = 4125B8ABC42EE8FDD40F2E6DF0DB3D20 /* FIRComponentRegistrant.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0B9FCB0EDC4BDBB175BC9BC2A09D90F6 /* FIRMessagingSyncMessageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 23841AC0A870055682FE7FBEE6920AE3 /* FIRMessagingSyncMessageManager.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               0CCFE5CDD79323DE83640752C7962817 /* RLMListBase.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 5A064D124C92CFA11CDFB748E3A1F23E /* RLMListBase.h */; };
+               0D86017D181E63BA2748D0B4675FEE9A /* FIRDependency.m in Sources */ = {isa = PBXBuildFile; fileRef = F3BDC06DD13BFE0A5C49CF6C90378D22 /* FIRDependency.m */; };
+               0E07CC88AC4C4B6AE45A8AA21F903232 /* system_configuration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0B31CAB3A5CA2AA601A8A6B0DBA8C713 /* system_configuration.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               0E213D1CF1D10689669D27E03D17D5CA /* GULNetworkConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = E6F1BACD1BE358CC3FB4627C58C15B63 /* GULNetworkConstants.m */; };
+               0E26CEA711582B8A2B09079170BB50D2 /* ZFModalTransitionAnimator.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D9516D4522CD7C42A017229D056F0A4 /* ZFModalTransitionAnimator.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               0E8E16BFB2519FDDFC29EEACEBC25784 /* OAuth2Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3A799A30601BBE93164EDFEE22F371 /* OAuth2Swift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               0F08213563CC77B70662B3934DEA4060 /* sync_config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 85F84124A6BFAFBBE1B88A9C531344EE /* sync_config.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               0F496EAE6D73FCAE5EB5C5B48751791E /* FolioReaderWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EEBA2C8A6B1441F5049FD44C2E246B2 /* FolioReaderWebView.swift */; };
+               0F833929C636360E89880A26CD11A792 /* FIRErrors.h in Headers */ = {isa = PBXBuildFile; fileRef = 56D082DC4F23AECFD551D310E0D3C41C /* FIRErrors.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F9B3B52ED08BD554C447CE7E9D4FE04 /* SourceContext.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 154EB52C148A3AC43AAFFEBEB198039F /* SourceContext.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               0FBF22CE66F876D9CB1D09F22B743658 /* index_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E996F2DB7B3E51D9BD5BD8819F76842 /* index_set.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               0FDA6BB0F64B7891997D6D361FC069AF /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0B520E2EE0857DCD4B89E33AA8F06E /* WebViewController.swift */; };
+               11677416550DE25FCABD80F8BE8CB0CA /* EventSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B896CF8A24113A3F12663506E53F6EAF /* EventSerializer.swift */; };
+               11C5C2EC4E2EBEAD60ACC8A93EC0B2EE /* GULSwizzler.h in Headers */ = {isa = PBXBuildFile; fileRef = 619880A6749DFFFD64BB91B2E35CD611 /* GULSwizzler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               11EDBB505C68DB2FF63F7B4EFD7EEA54 /* RLMRealmConfiguration+Sync.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A8D24EBF9A9A7024F37D1F9967496CD /* RLMRealmConfiguration+Sync.h */; };
+               1217AEF6A6F0148B88E2E6A62C33C56B /* AEXML-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F9188B8734549A2FE3A1E20B11E68AA9 /* AEXML-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               1291540BD398E4AF5A5684D613F4524A /* RLMSyncUser.h in Headers */ = {isa = PBXBuildFile; fileRef = 1482EE96CD4BCC0E2EDBBA3ED70FDCBD /* RLMSyncUser.h */; };
+               12F3CBE1723CC427D132A610539D1DC3 /* FolioReaderPageIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D063D80E2E978A2BE32BFE9214B50DBA /* FolioReaderPageIndicator.swift */; };
+               1312F66A5176BF8192E1B2177FF51356 /* Toast-Swift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 31C89ED169005A2C1DBDF20457DCAE15 /* Toast-Swift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               131730EA4B99E067FFA174A75E5DF9A4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05984A0DCE357F35A45231C58BADFD71 /* UIKit.framework */; };
+               131AE323F3D51BA77288DF6E07362B5B /* RLMSyncManager_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = AF31C2EDFE5591ADC92DB32F0BC409BC /* RLMSyncManager_Private.h */; };
+               1388AF87C19F63DA28CC1EC3C26C882C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               139B9379F776BC05B6B819E09BE6E1D2 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDF51D99BBE2062A17F2CC6D14953BE3 /* Error.swift */; };
+               1571E1BE0EED938CA88ADF8449F8A805 /* FolioReaderChapterListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A2C193ABFAE8C48F5DD4FE7D3A1CA6 /* FolioReaderChapterListCell.swift */; };
+               15AC8AA4B5ED7E5BF2E80483CBAC5DDE /* MainThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB30FE116FBBE62941E0322FB3B9496C /* MainThread.swift */; };
+               15C4562A91882668E0509A4C49265ACA /* GULLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = CE8CAA6373F642196F0C634C6207FFDF /* GULLogger.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               15C96676060BF2C17D270C3F029644B1 /* SHA1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7EFF3E888638C1BF7E636B5D3F02372 /* SHA1.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               15C9919A9A4506E500E6B4F9015499E7 /* FIRMessagingPendingTopicsList.m in Sources */ = {isa = PBXBuildFile; fileRef = 5192103CC06271B7D3368A703ACDCCCB /* FIRMessagingPendingTopicsList.m */; };
+               15DA3AAE5DCD260807FDE95C53133FC0 /* Pods-WolneLektury-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = CF6A19A1959A02404C04F8950A83B596 /* Pods-WolneLektury-dummy.m */; };
+               16156484634519DBF44EC9F53ED24CF4 /* FolioReaderKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 61D0B52E0F670DE2CB3F0AA75F6ECF4F /* FolioReaderKit-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               16F90DAC8AA5BC6E1B63E2D93F7B8F09 /* ZFDragableModalTransition-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F9ED9601D0552304E103D84E3E96D6B3 /* ZFDragableModalTransition-dummy.m */; };
+               179FCD8BC40E115DFA9C05F603A878A9 /* Resource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79142150002194346CF89C5F4514BB4 /* Resource.swift */; };
+               17B6937A0AB61D1319E38144527699FB /* Protobuf-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 164FD2FA81136FE310077B45A814E345 /* Protobuf-dummy.m */; };
+               17CFC5DE8EA6C5ADA822748DA2B65969 /* RLMObjectBase_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = E0EBEE00C0127E3C90E732DB5F01CF5D /* RLMObjectBase_Private.h */; };
+               17FA10574ADCDD6442F4E903EE64F75C /* RLMObjectSchema.mm in Sources */ = {isa = PBXBuildFile; fileRef = 46FC785119A479FD291D52486083C3F8 /* RLMObjectSchema.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               180ED06F9C965B69A63B39639903903D /* RLMSyncCredentials.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = FCE7E03A64E471CB3D6F8F1475E092A1 /* RLMSyncCredentials.h */; };
+               1871055BFED069026C1BA1F0EBF80D84 /* FIRMessagingDataMessageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D42DEFD122E76DA6A7E235F66B755A0 /* FIRMessagingDataMessageManager.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               1878441306907D7660D5DABA997E5407 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               1888CCC9E07EC907079DF9D6058014CD /* RLMPlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = ECC142A8EA3AE58507D64C2E6BF510CD /* RLMPlatform.h */; };
+               1892CAA5A2130855409FCD85E18B6437 /* RLMUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = C67BBEF0CB8A371D0C8647130CD00979 /* RLMUpdateChecker.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               18B254948F230DA2D3DB86665FE2C83B /* RLMManagedArray.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0858841A9D93BD6323F873933150A081 /* RLMManagedArray.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               19EFB114884F3F91E618EBAF871D978E /* RLMSyncConfiguration.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = DF16170DE63470802D26A252D0E24CE8 /* RLMSyncConfiguration.h */; };
+               1AE2A9FBCD4892379D6EA33EBB059E46 /* FIRBundleUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 557649FFA165485E24A1271D2638F344 /* FIRBundleUtil.m */; };
+               1B62DA07DA1516EFE97486D5806DDE46 /* results.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 75473FEF44F28C44015117474F2BD735 /* results.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               1BF8F13A87CE0930A8A2F706FF894DB6 /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C7BD9F4CEDFB5189A20C7BDA0DD11C47 /* weak_realm_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               1C1568B5E044F2A665B1647F74BD5356 /* RLMRealm+Sync.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D6410D66A868469BEB52C0E969B7A21 /* RLMRealm+Sync.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               1CD6F680112CA20AF60AA1F7785AF839 /* SwiftKeychainWrapper-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 165E1BA5EC237CD62B66F0F55037F269 /* SwiftKeychainWrapper-dummy.m */; };
+               1CEAB5B7698F0EB7EEFEC72B3BE26D1F /* FIROptions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE3134DC7AF610DBF7C87E8EDCEC2BED /* FIROptions.m */; };
+               1D7EC3E556502CA2CF523B3AF823BB32 /* MZDownloadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439526BFFA7B883CDBF438B9EA23C647 /* MZDownloadModel.swift */; };
+               1DED3FDEE669B446980A6E6F842F9549 /* String+OAuthSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFBD1D154E2BDAF6A560BD281251E63B /* String+OAuthSwift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               1E2A6F7480A4D6FCCB51D8E80FD20944 /* GtalkCore.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = B91A3C531E1F1C9F2523B12FBC6B31F5 /* GtalkCore.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               1F2009A78040CFF64E04DA7B632D840A /* fileenc.h in Headers */ = {isa = PBXBuildFile; fileRef = ED302E755753DDD0A6ED0FDF3C5FCB18 /* fileenc.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               1F7FA8B0C9C9A58334B316DD63EDF5FD /* FolioReaderKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EDB45F1273F71C411A0545C4CE445E3 /* FolioReaderKit.swift */; };
+               1FB017C8665127124E5622BFC14BAE01 /* FIRMessagingClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A28197B26F0C810272E9A84A7487DD3 /* FIRMessagingClient.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               1FC3F893E33BAEE1200C9435CDE0AE42 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               1FC3FD39157C2FFFF3869A1300730086 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59706CD0DE8A5C55FBF1750747E35736 /* SessionDelegate.swift */; };
+               1FF438BC2ED870907E34DC89D7C6EA03 /* RealmCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F0C4378148A65453A4D307AEAFF61D7 /* RealmCollection.swift */; };
+               2001B9F27B5C4F512389BD0397B960C0 /* FIRLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 1139D92D5DD588DED3347176B562CE39 /* FIRLogger.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               2073B5144EE9AFA37CDFD692DEB706FB /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B16201B3D91EF42BC6478D973A85F4E3 /* Security.framework */; };
+               207B542DB4417E34853F931AC63A5CAC /* Andada-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 265D21A8AF0C73915C5E3808A10562F6 /* Andada-BoldItalic.otf */; };
+               209B7AD282510AC70A483C033EB3CA1E /* FRBook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77D4DB8CCBD4568AF20BA0C53858FE2C /* FRBook.swift */; };
+               20FEB969513418DFAC5090C858D10516 /* Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FF7EDC5386C9DB98B823B474CDDF650 /* Kingfisher.swift */; };
+               2214D9EF048C093D94CEFDC849C2743E /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 172A7ABC432D07D75B2B5BA8C0BA4592 /* QuartzCore.framework */; };
+               2246385DA62FFD8D78968FA2CD4FB056 /* RLMResults_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 59F29FF04A45EA9F6FF92C7F6BAB4B12 /* RLMResults_Private.h */; };
+               22D2A945A41753AE917AB2BFE8723E17 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E3E3FD2AA61E951D9A15E12FEB6A59F /* Queue.swift */; };
+               22F05ED1E0EB10DBCDB9B5CC1D0C2698 /* object_schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 30619A0389B050792D52E48CA7FB5016 /* object_schema.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               23088C73D99E659C6AF3D218CF278E90 /* GPBWellKnownTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = A725C1F81E58C098054A2B656E85AF76 /* GPBWellKnownTypes.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               248945FF49A7072DC1EDB438BDCC5D64 /* sync_permission.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DAF1B68A9CAAA2CBE0B167CB2DB3976B /* sync_permission.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               24D95C24CFFEA030873E55B2E7136CD8 /* FIRVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = A66F9D57725E8CDA7C62CB465AA36456 /* FIRVersion.m */; };
+               24FEB632227140B54BAD67E1062D6633 /* QuoteImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B49CE581DA5B45AF17857662EB6AE539 /* QuoteImage.swift */; };
+               2579ED6A1AE7C63170724ADB5A714717 /* GPBDescriptor_PackagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 664B0758A8B043DFA4F69B51BA065D60 /* GPBDescriptor_PackagePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               2598B4D94CB3B0490D8620CDCA6CE774 /* Lato-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B85E290DB3BDCF8A7288FE23314B642E /* Lato-BoldItalic.ttf */; };
+               25A69A0D9CB1E51FF9CD48D00869C361 /* FIRMessagingRmqManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C1E4306D36A4D225A7D2A80EE05DEADF /* FIRMessagingRmqManager.m */; };
+               25A81E5381E3BA30B223DD15B5216C88 /* FIRMessagingRemoteNotificationsProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8721E6220CCCF76B0303EB98C5388FA4 /* FIRMessagingRemoteNotificationsProxy.m */; };
+               27319AAC3B3C7588918B3CDB17FAA981 /* SortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FC57F395225CF90BC8F25F5C08777D /* SortDescriptor.swift */; };
+               2833252EE9A62335D18C1AE8C312AC80 /* FIRApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 553FFB49824A1E787E4D0F386F6DE765 /* FIRApp.m */; };
+               28F39A8DB19B7AD0D4C6B21D1B4EE037 /* RLMSyncConfiguration_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = ABFF357E3BABBE6991EFE7D5FDB24E1A /* RLMSyncConfiguration_Private.h */; };
+               2931E0DFBDA4552EE9982EF8A836701C /* RLMSyncUtil.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 390D860093219F412EFBA3562CA65AAE /* RLMSyncUtil.h */; };
+               2A484CD6CAFD0E0BF0065A806191BF10 /* RLMSyncCredentials.m in Sources */ = {isa = PBXBuildFile; fileRef = 858870B7EB30D724E5204DDD159ABB0B /* RLMSyncCredentials.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               2ABBA3ADA9361D1A334126BB3638CD3F /* Any.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = EB7911A826F61129EE80FDDE264EF222 /* Any.pbobjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               2B117AE5D1F324D55E3A62D0ACADB0A4 /* RLMRealmConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = ECE59B0188CE015680FFF31522E8A852 /* RLMRealmConfiguration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               2B84E33935819EE11FB321CBE6926130 /* RLMObjectBase_Dynamic.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = C544F4546E0587724503A71F8C8A0730 /* RLMObjectBase_Dynamic.h */; };
+               2BAC30A9FB2B3D8AF2A4878093EF2E83 /* Lora-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7961A53CC6789E3BF3F511B917EB410E /* Lora-Bold.ttf */; };
+               2C0B55F2EFCFCE6A903D5F9C7F9E2FC7 /* OAuthSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD231A8FF3DB1782497120FA6AEBB1F /* OAuthSwift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               2C79F094EE51D57289867AD834F53ABE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               2CDB4D166E4CBF5A3A2E868ED2530022 /* aestab.c in Sources */ = {isa = PBXBuildFile; fileRef = 79E033869BAC941730E6D735B4343813 /* aestab.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               2D826FE4A1A56AF3E2611FEB5E7C91EE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               2E05889FB32F6C8AB33AE8854A5D9CA8 /* FIRMessagingPubSubRegistrar.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A658FBE00AD5575FAEB350259C6C0C6 /* FIRMessagingPubSubRegistrar.m */; };
+               2E1DF3B5FC2960D31B8C5023885CBE83 /* GPBDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 735555A726916552BAAAE6D9696116EC /* GPBDescriptor.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               2F0489083E3FA29C9358EB2E926A0CBA /* RLMCollection.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 398EF8E6348C58E6A461B252064C6BA6 /* RLMCollection.h */; };
+               2F84C0A84F2B5A0E9E3073417CA651C3 /* ImageTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A4F86F985E98A40E7E014CEF0B3C3C /* ImageTransition.swift */; };
+               318FF8801C5C975BDBF82EB2A5E4CEBF /* Lora-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AE0A609553E6424094E5B32BF5FC4BBD /* Lora-Italic.ttf */; };
+               31E46BAA929925BA7B082ADC8151EFCE /* RLMSyncUtil_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 45D79C1899AC277B26DD3524D5AC9919 /* RLMSyncUtil_Private.h */; };
+               31F9103A1AA2692C0D131E1D8A063B92 /* RLMListBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8F6A6DC559F6B50404824FF2BC108753 /* RLMListBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               3268873EC9356C7FBD6634B8F2D8A39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               32AD75DDBF58131E8A09D268EC722BF5 /* Any.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 507E13A307CB3F11C321AD5B742C1C07 /* Any.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               32C34770A17BC1A4A41B37EB50210025 /* pwd2key.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BCBFCAFBBD2EB2F3E726450815158E5 /* pwd2key.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               32FD28CC21723B433DB5C00FA50CCAA7 /* GPBProtocolBuffers.h in Headers */ = {isa = PBXBuildFile; fileRef = CA67844C5A4BD05A959C0E1A6F8CE505 /* GPBProtocolBuffers.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               331A23585335B80070D936162CEE0537 /* Highlight+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B6147E3ED6E10C6C9C37BB8A5B2379A /* Highlight+Helper.swift */; };
+               33288BB59843FAAD4A2330C75D48B80F /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB55573C83483EB32EA80704AA7F376 /* Utils.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               33F9CED1A15F442B598833EA4A03F272 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 064177D2B62F30947132CA37F78CABBC /* Application.swift */; };
+               3475139C8A2AD695042C57414E50B17C /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B037AD884094D78946E98AECCBDBCB3 /* SystemConfiguration.framework */; };
+               34AEF9EE16EAEAF7FA0E66A48982F0C8 /* UIButton+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2198B0E49174CFB22F300B5737A7ACD0 /* UIButton+Kingfisher.swift */; };
+               3552218D2E421ECA1909B93DE2FB959A /* pb_common.c in Sources */ = {isa = PBXBuildFile; fileRef = E8A06D0E7033B7449F302ACF32EBB05E /* pb_common.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc -fno-objc-arc -fno-objc-arc"; }; };
+               3554E5702FFEA0C2FC74B19EEC7D6325 /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19AB99E9F6D28349E358F6A347A20B0 /* Indicator.swift */; };
+               35EA02810BD8E554255704189C611C77 /* FIRAnalyticsConfiguration+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F5216060EC5386917B636135207D13B /* FIRAnalyticsConfiguration+Internal.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               36FF8853CB34A9297AFAA8F5F7456324 /* TaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FE1DC4CFC4F73D99D647A77014F550D /* TaskDelegate.swift */; };
+               3722F219D124C3525591F1262DB6B054 /* RLMClassInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC045719CEBEE0DAEFC813B823C1F006 /* RLMClassInfo.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               378A2C2BCCC9C97B40BACC18B6247967 /* RLMObjectSchema.h in Headers */ = {isa = PBXBuildFile; fileRef = 565A58C3DEC5EF04039C13B055C22CE9 /* RLMObjectSchema.h */; };
+               388CDEE9BC70517AEF5184E807928E39 /* Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDAF8E81AE4DDFBC78DDEC4C39A073C9 /* Tools.swift */; };
+               38A4CE8C383FDFD2649583533D64D074 /* RLMThreadSafeReference.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18DD8C06F1B3E9FD17466D5018391BF9 /* RLMThreadSafeReference.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               38A9418DB1E08F249F0C9FF9BF9A13FD /* RLMSyncSessionRefreshHandle.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1E36AA751EC8381B882AC6FDA504FBBB /* RLMSyncSessionRefreshHandle.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               3906566AEC9254BF6566E5DF3BBFD4CD /* brg_endian.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A66F3D39C1729EE4090DC644FBA6F65 /* brg_endian.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               39530C3F489E2997186FFA7089C8F8AC /* FirebaseCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 48A23DC4D40077329084C4E6B9086403 /* FirebaseCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               39613AD5AF4DA0C13CE4B4AFC463B90F /* FieldMask.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E3805379AC3A9301D26DE62077DF340 /* FieldMask.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               3A5246361D0B799BAB50FC33B33A0143 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               3C385BD23D7DE86023C31305720EC106 /* RLMRealm.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D6A3C039A370F8C8DA839EF41DC6D43 /* RLMRealm.h */; };
+               3C456892E267BA54104D3E201ED78146 /* RLMMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = DD11C12E8B8A3662D6DE8C3D9CC3DE31 /* RLMMigration.h */; };
+               3C75E6E5423044F2030C703BF880C506 /* FRTocReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A6B7317D2AA66425340B4A6E3A73F5A /* FRTocReference.swift */; };
+               3DB1DE26BB4BE2B4F99DC851F0EDEF54 /* fileenc.c in Sources */ = {isa = PBXBuildFile; fileRef = FA66C7BF6C3F1584F68147CED33971E9 /* fileenc.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               3E02E8903767E2C1DF6802E1A90D4CA9 /* FIRMessagingUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = BE7C83907BD9FDBF71121B4369E85D4D /* FIRMessagingUtilities.m */; };
+               3EB63319904299429B2565B7D6D7F984 /* RLMSyncPermissionResults.mm in Sources */ = {isa = PBXBuildFile; fileRef = B2D261BF1562798AA9C2BED6293A727E /* RLMSyncPermissionResults.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               3EC722DCBCD5A8C482260EB19E80F42B /* MenuItemKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F3C0A81FF167D0550DDCA852853434AE /* MenuItemKit.framework */; };
+               3EE16703619890CD8BA87B53FC36D952 /* FIRErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 76C8AA2F24D4A7499FBFEC89207A32B8 /* FIRErrors.m */; };
+               3F7D0723B053EFF5E23BADFF2017C8CA /* NSError+FIRMessaging.h in Headers */ = {isa = PBXBuildFile; fileRef = 800828347609EC68238C01C399EE9BAB /* NSError+FIRMessaging.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               3FB245715D8F1A01546D39C6C9D98AB9 /* RLMThreadSafeReference.h in Headers */ = {isa = PBXBuildFile; fileRef = 196A114E523544DED122A0BBBD405C1F /* RLMThreadSafeReference.h */; };
+               3FD076818FABD54684FBD4BA813ADD51 /* RLMRealmConfiguration_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 19103A10E8E96743A56E682ED1DAC70D /* RLMRealmConfiguration_Private.h */; };
+               410D36DE05F759C2B105AC77C46B4E0B /* RLMArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E26DF3327D2EE24CED40AA07E5CC179 /* RLMArray.h */; };
+               4121DDEF0418D39AB3F5EAE215013F25 /* FIRMessagingSyncMessageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F50C72731047DE9CC0AA2AA8A9570C7D /* FIRMessagingSyncMessageManager.m */; };
+               41403EA071B1A174F010A55A10A7A992 /* SideMenuTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 949A6E53FE16E2D6C8BCFE91DF8285B6 /* SideMenuTransition.swift */; };
+               414C72E011AB7CD7FEE2FF1941C9A0FE /* RLMThreadSafeReference.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 196A114E523544DED122A0BBBD405C1F /* RLMThreadSafeReference.h */; };
+               415845C60B06A5ABAA75E5C0D23158E8 /* Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3110559B9CEE5BA9F86BBB0E5EFE87F7 /* Highlight.swift */; };
+               4159995BC3A7E764C041646E13FEF961 /* AEXML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D46C127210F7863A4A22D9F761D51E5E /* AEXML.framework */; };
+               41A7890A42E1D0E0D7AF47E053E98F64 /* FIRMessagingContextManagerService.m in Sources */ = {isa = PBXBuildFile; fileRef = D0F97794D9CDF7090BB151567CC375CE /* FIRMessagingContextManagerService.m */; };
+               41D22E6F4C3E24D8FE690DE6D644AC3E /* ImagePrefetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8F217E75535D2BDB07F64DF2D98ADE2 /* ImagePrefetcher.swift */; };
+               427799CB422851F585C02C74820C0463 /* pb_common.h in Headers */ = {isa = PBXBuildFile; fileRef = 34414B1475EAA53DE09EE78DBCF4F362 /* pb_common.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               42D98477CB528F62B9B18917B609A720 /* RLMOptionalBase.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = B495C794563F95CB715EBF0C78EDBFE7 /* RLMOptionalBase.h */; };
+               4337162E4FCBB327692532D2AADB24AB /* OAuthSwiftResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1798AF9275969301B5346C1DF5410CFE /* OAuthSwiftResponse.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               435678410A8C2F628EC64B22316A27CA /* RLMArray.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 8E26DF3327D2EE24CED40AA07E5CC179 /* RLMArray.h */; };
+               4461B138968C3C131E90EDE2484F1963 /* Lato-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 270BBF6FE1627D659D64D320D8AC3E14 /* Lato-Bold.ttf */; };
+               44FA9AF3247A4A007AC516966031ABC0 /* RLMObjectBase.h in Headers */ = {isa = PBXBuildFile; fileRef = AAE3C1B13216CF41A8A27FC654EAB454 /* RLMObjectBase.h */; };
+               4516203E5F7963FA3BF40C6841E23A38 /* ZFDragableModalTransition.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDDF76EEC0C6394E55456C0935E846C1 /* ZFDragableModalTransition.framework */; };
+               453378DA661AD7FCBA79D1BFA59302EB /* FormatIndicatedCacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F2399A1BF621F8F87173F1D95B405A3 /* FormatIndicatedCacheSerializer.swift */; };
+               4537962FCACF5D81EAB89DA49599982C /* ioapi_buf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CC80ED2F8C74EAA722AF3F92251516C /* ioapi_buf.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               4548EDD8058F8F1C3CDBF04943E718FF /* crypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E5D4A81CC4B4E8549AF794D55B53EA2 /* crypt.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               4561B0FCA3C8DF58989077063351B895 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 172A7ABC432D07D75B2B5BA8C0BA4592 /* QuartzCore.framework */; };
+               45628F4ADA33649827094165E1EF6130 /* SwiftVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72A41C016F25201385C2676F0DA9C35B /* SwiftVersion.swift */; };
+               460853DB66417DD46127928A9C654AE3 /* RLMRealm_Dynamic.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = E00C401BE147D47DFBB42AA69755AF0D /* RLMRealm_Dynamic.h */; };
+               469DDB5B5F2165785D04941163FF7CEB /* FIRMessaging_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A118E1890C7A2E6094454B06E7D1CA6 /* FIRMessaging_Private.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               47524C3AFAC5EC486F2ABC0E146EF760 /* FolioReaderChapterList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 318DC2541285D47723C03D0A990A8731 /* FolioReaderChapterList.swift */; };
+               475636CA83561EFF3E62F8D3E34DAEE4 /* GPBMessage_PackagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 77801061E8A612FE9356272D868D0287 /* GPBMessage_PackagePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               47800CD44B95F7CD27C4EFE24537F816 /* RLMRealmConfiguration+Sync.mm in Sources */ = {isa = PBXBuildFile; fileRef = 88A4ECC72B8822083B261FCBB3FF852B /* RLMRealmConfiguration+Sync.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               47B8810DB4416A9E5CA39F27C1193706 /* AlamofireActivityLogger-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 996EA22BABCBE1C3BAD96548D6A604EA /* AlamofireActivityLogger-dummy.m */; };
+               47F644862D2F3FA7ACAC9AC409E11C83 /* pb.h in Headers */ = {isa = PBXBuildFile; fileRef = CCDB7486E72F0C100C4E4BB6FEE50AD9 /* pb.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               487D10219F92875CBCD79DFE096C45A8 /* FIRConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 27E4181D0238606C2DB16B0708782E6F /* FIRConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               48D9D155785BA85911BED51563E4AF1D /* GULNSData+zlib.m in Sources */ = {isa = PBXBuildFile; fileRef = 398D45D2D76BE97410B68392E6D3A6B8 /* GULNSData+zlib.m */; };
+               498191589F06997E6260BC116976A172 /* ThreadSafeReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D24FB6B80FD2575D5E65E4D136E8F3 /* ThreadSafeReference.swift */; };
+               4A3474D8A0D6C404774479927440FEE7 /* JSQWebViewController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 99A354380AB7A447785FC47FD513732A /* JSQWebViewController.framework */; };
+               4A3EA2B64857488A7D376B2912A3D1A1 /* FIRBundleUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = EB82AFCF317326DBE0583F7DBA1C4456 /* FIRBundleUtil.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               4AD9953777DFE36AEE9544EC06B42152 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               4BD3B1D588017EA43F3E98563319DC19 /* MBProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 35746DF215D495B2F6B254F11A4B932C /* MBProgressHUD.m */; };
+               4C92164BAF0CAF480EE22B78A7DB03CD /* Wrappers.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F2F483A8EC81272E3D12F5FEAC0445C7 /* Wrappers.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               4D742248C548884F45E3FDA82657EB39 /* Lato-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F4F1D6257BCD207C943C18F8BE4FAB8B /* Lato-Regular.ttf */; };
+               4D7671FDCF69EFE3ED3D2784829916BC /* Bridge.js in Resources */ = {isa = PBXBuildFile; fileRef = 7948622C522A6B89E5AB5041C4F1C30F /* Bridge.js */; };
+               4D9EFB38E1597E064355A122BB351917 /* FolioReaderFontsMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98BD4D3148F63139C8DAA1AD6929A2C9 /* FolioReaderFontsMenu.swift */; };
+               4DA0448649E8F979A300306BF59CF9C7 /* RLMSyncPermission.mm in Sources */ = {isa = PBXBuildFile; fileRef = D981664DF64C9BF8EA0C1391E3BCDC0B /* RLMSyncPermission.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               4E1A913EFB404FB11524718FF0298EFE /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05F6A7144D612A1B7814FB4DB19F20D0 /* Alamofire.swift */; };
+               4E1E754909BF2487C287C6A3043A71A4 /* ioapi.c in Sources */ = {isa = PBXBuildFile; fileRef = E59FF5C1E7B5535FD956BD1CE743AF56 /* ioapi.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               4F9863537BC7DA16B2F5D24FB8332853 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88AE1EEFE43FF13884C8AB2A06138B81 /* CoreGraphics.framework */; };
+               4FF5E89C1F4715E5676C25F96E3EEE85 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB79B171CC6F891A99776C0356406A11 /* Event.swift */; };
+               500C8EDA60C07B0F127C7FC385E17D38 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6939449CF2795C11704E929B05036FEA /* Notifications.swift */; };
+               502C51559F5C58BF7CEEEB1CF9FDC045 /* GPBDictionary_PackagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4874EE6FBA94E2552B91BABF2A599E39 /* GPBDictionary_PackagePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               504D5C61A998F589CA3907F70BEF92B1 /* RLMSchema.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5DECCEDCC3CF34ACB64710FFB19496B1 /* RLMSchema.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               50B382E4D134FA9389118ABBA08386AE /* GULSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F3C6DB088252560431D0D373867A04 /* GULSwizzler.m */; };
+               50D9B09473B537797907C8FCDFC93CDF /* RLMSyncPermission.h in Headers */ = {isa = PBXBuildFile; fileRef = 9F56E9572F18DCBB5E446C23D65E1213 /* RLMSyncPermission.h */; };
+               512005B87BEE5FA25BE5714A1CF6D29D /* FolioReaderContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B05C0825F95873D124EF95EAEF8DFA /* FolioReaderContainer.swift */; };
+               517608EEF7DF8E8FA0BCB4EF84BCCD8E /* MZUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94B7E924BB49EFDE5E1CF30DB2934504 /* MZUtility.swift */; };
+               518FD0AB83B763CF76F044064E9995F4 /* RLMArray.mm in Sources */ = {isa = PBXBuildFile; fileRef = 111BC4AB5DFDA30B3AF9451ACE9A8D00 /* RLMArray.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               5204D65C96DAD52AEB781EAF7298504E /* SourceContext.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = DEA90EC1C9E0AAD475B6E5C849B0DFE7 /* SourceContext.pbobjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               52237C35642089F77DD4D723CEB25737 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813E11E3BB26542B146AEA87E7EF84B0 /* Response.swift */; };
+               528A475990402BB9755D688F7E7F57AC /* results_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEF9F5FA8A61F170B0136C7055E746E0 /* results_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               53FC18A271CAD411EF3E1BEA44FF30EE /* RealmConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 888A59D2EDB25ABCA1679CB435837211 /* RealmConfiguration.swift */; };
+               54143F95ED39F0BF32782D9D7F9AAFD3 /* UIApplication+OAuthSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DB7F3C979E6A543E2E6DB68E1B2B57E /* UIApplication+OAuthSwift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               546962FBBD986ECF27FB9CE1690B820B /* Timestamp.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 2CBAD617EBEDCB28ABE5FE59AFF568AC /* Timestamp.pbobjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               54EC154AEE5C8B1E2DE1D39EB0071F30 /* Int+OAuthSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A67C7CD84356516F1F7D3986E9C09C6 /* Int+OAuthSwift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               556338F7D37DA2D8F7BD73F00E4507A6 /* FIRMessagingDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 644342C27115C1D613C4C9D164A46647 /* FIRMessagingDefines.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               5592A36E78743E345801FA826E4E9EEF /* KeychainWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97B70D1D75462F234CC7CB3B3BD41F38 /* KeychainWrapper.swift */; };
+               55C1439CC2A316DC58C336349A7E01E4 /* Locale+HttpAcceptLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A0830A90DAC8805601A6BFBF4EB2B4 /* Locale+HttpAcceptLanguage.swift */; };
+               577709021CF910800C4DD1402A862998 /* NSError+RLMSync.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 4C5FC54772307BC323BD252586733ADA /* NSError+RLMSync.h */; };
+               579E4BADCC61A81905CD4F9422740700 /* GPBRootObject_PackagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E620905A80A469191BFE9DB5F9240E1 /* GPBRootObject_PackagePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               57DB75CC5BF875F06FAAD1BC8BC92DE4 /* GULNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = DC2E8829C4E43BC76E7158A0E3E701AF /* GULNetwork.m */; };
+               57EB381CF010DA8ED4D9C4F9C40A0F4A /* MZDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B703AD37258C65E00D1C7CBE59684A89 /* MZDownloadManager.swift */; };
+               58A9719584AFA2D108D9E5C585A79329 /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84E14A5C3089E4CBFF33B08E907A754 /* Validation.swift */; };
+               58EB7C8152CA11D8013F636778407728 /* GPBMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 920321EC36450F9B7B43C072420F78D7 /* GPBMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               5A02E2515DCBFAD6FF7D7E3BA545EE57 /* FirebaseMessaging-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 20CB9934A5EA1F3987C1D0B98512E6C2 /* FirebaseMessaging-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               5A35ADDBCBFEED97AF50A9AE2E4FD577 /* crypt.c in Sources */ = {isa = PBXBuildFile; fileRef = 9E4FA5283B6F750E4FDB42A32E3D2DA3 /* crypt.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               5BFDB162AB870CB831D499EDA6D25C8E /* RLMObjectBase_Dynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = C544F4546E0587724503A71F8C8A0730 /* RLMObjectBase_Dynamic.h */; };
+               5C24A34DD99DDD7316EA7AB3DC0CE6E8 /* RLMPredicateUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 28AEF58ACB93749F0DEF7933EDEBF6EE /* RLMPredicateUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               5C83031B7233347A62498786C3616AB2 /* Duration.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 3300795729EA0A168E19390ED6E7867E /* Duration.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               5CF30E2EF4AB65F79529D73AA5964434 /* SideMenuManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0FB00C4DF6F35808D341ED5C7A18C79 /* SideMenuManager.swift */; };
+               5D75DA564BFADC9A873E57E1ECF4E92E /* SideMenu-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C509D32CE877D802F9188A1C63C0EAE3 /* SideMenu-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               5DA946929FEBC12359A58ECA949E7D98 /* GPBArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D124571A3976EA459CE4987C55A9192 /* GPBArray.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               5EDFAFC0BC5DA207670220C792609991 /* MemoryQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14D40CBD84A69593DDFFE433389950C2 /* MemoryQueue.swift */; };
+               5EE5FED83B90A606A763CF1114D1D6FB /* ResponseSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9A7C04F88EE32E59BDA90F0BFE6D21 /* ResponseSerialization.swift */; };
+               5FE2BF6881AFD13E4429F377BB153387 /* RLMSwiftSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ECF0996C30AA6093268F60DE4366E45 /* RLMSwiftSupport.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               601CCF982A0CE8CD05B3FA83FD2B6229 /* Lora-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 57300EE01B9AFBB08B6C2FCC7AF87CAB /* Lora-Regular.ttf */; };
+               602C1949653A4615A5DD6AC91B824BBC /* OAuth1Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C59F12F85CDAAB121A1677774DF2C7F /* OAuth1Swift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               615B4F07638913112ACF0FDFA305BB36 /* Data+OAuthSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A0764E9029258456100401AF3A1D31 /* Data+OAuthSwift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               631598B30FF0A9F6FFFBF73C921D8DE0 /* Realm.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 144443F4C5352AA38AC82F621455EA3C /* Realm.h */; };
+               633315B0A4E810C6350A9F2B6E8C1DEE /* SwiftKeychainWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = DDFAC3CB75A6486957AF5FE89066B9C5 /* SwiftKeychainWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               63918BD6F8F5B260907AFDF0C74DB214 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A1472ABD1E4C2EDD04CA4BEC0521713 /* WebKit.framework */; };
+               6435822D7351527ABE3F446D157348F4 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F837D39A9B1D03254A4B178F9C9DA0C /* Extensions.swift */; };
+               648B5903884066417E7C3B899A1C4034 /* RLMSyncManager.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = BA32998B725316B590F1094FCEDC7A57 /* RLMSyncManager.h */; };
+               651DC312EDA04DF49A00D67D20A982BF /* ioapi_buf.h in Headers */ = {isa = PBXBuildFile; fileRef = 46EA2FB5D744DD40EDB4C7F7E064E37C /* ioapi_buf.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               656F9722496C3797B612201ADCB5ACA5 /* Struct.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = B11AB6EE95088046C66066C1A9DAD036 /* Struct.pbobjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               65AAC1AE61E8BB8694844AC913343969 /* sync_user.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 368FE0998EDC34C2BF80A3D2F7898574 /* sync_user.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               662F1759FE8C2ECF8911CF2AFA446AA0 /* GPBArray_PackagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = E927E8565EF0555119B9A543D4AF8643 /* GPBArray_PackagePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               66354F1A710D61367F5401B5D88E5CFC /* LoggeableRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32203867C907AC35E7B64C374D54195 /* LoggeableRequest.swift */; };
+               66CCE448DAB752E36445FD69CD761C93 /* KingfisherManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32096158C38CC91501302DDD6AF488D /* KingfisherManager.swift */; };
+               6778CC3C9A4FD26056E56D16B24F42B7 /* collection_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3DA7423D7A46C6EADE51876FC656AB6 /* collection_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               67B266C0ECD04AF124C2A47B05E01978 /* RLMObjectSchema_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = EFD854B5B21344EA040B49EA6725FC00 /* RLMObjectSchema_Private.h */; };
+               67B656A4361A03B54BEBA7C91F5628A4 /* Visitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C3231D8AF28863899D26FC24910D46 /* Visitor.swift */; };
+               681CBE150BEC5038D0CFB5E0C5013FA4 /* FIROptionsInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = A5D04CCAC2902D10865E0D2EF5DADE45 /* FIROptionsInternal.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               689A238CC3488CA2A44C7DAE4C71C427 /* zip.h in Headers */ = {isa = PBXBuildFile; fileRef = FD6794D516FA6960417F7EB0CAB5E35F /* zip.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               694817727BEB0AB642739E85783BE7DD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               6A4FCEA27B851AF3E4C7097A953303CD /* SSZipArchive.h in Headers */ = {isa = PBXBuildFile; fileRef = 44B61755C9439AAE0D2773B101A3E1EF /* SSZipArchive.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               6A5E2674F275B1EA573D8673A84DA984 /* RLMCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 398EF8E6348C58E6A461B252064C6BA6 /* RLMCollection.h */; };
+               6A6B8055DEE857F21D04A3FF7485A44E /* OAuthSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B4840E7D2EB9EB8417A1C9645FEDAAAE /* OAuthSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               6A8A6842F5FA4FF78BE3A1B2279232AB /* ioapi_mem.c in Sources */ = {isa = PBXBuildFile; fileRef = 320E6B71AC99F49980ACD3104FAF01B2 /* ioapi_mem.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               6B6450B5AE2DFD18D9DFB338F0F79BD5 /* binding_callback_thread_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADE4B9714FB02E4C58524BAA3E5D262E /* binding_callback_thread_observer.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               6B6C8C8779D1F44D865AE7ED4B4D8678 /* zip.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E60177A5CFD27A0866D68EFFD8094F5 /* zip.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               6B6E266F5393BC0F2B550420990DDF37 /* MatomoUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951EF2BE6965114A5D8E84809EAC6424 /* MatomoUserDefaults.swift */; };
+               6BAC2CBC9089C7C89D29B4DDFEC59A0F /* RLMCollection_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2189955C32F1B821C22A320B350D4E9D /* RLMCollection_Private.h */; };
+               6BE3EFF731C1AEB200C4DE0DDB3584EC /* GPBWellKnownTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F3368CC7921022310DA5AC855FBF373 /* GPBWellKnownTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               6BEA14EC335E07C7063CD1383C0C443C /* ServerTrustPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEC20068DA07AB47A0BC6045AB5A15A9 /* ServerTrustPolicy.swift */; };
+               6C1020CB1C2A89A8741875967A9B7787 /* FontBlaster-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 942DDF3E267A719FF596B56B855D26D9 /* FontBlaster-dummy.m */; };
+               6C36E4CB9562801D310E890FE8DF5107 /* Realm-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DD123C500606EE058F44458705F9DE9 /* Realm-dummy.m */; };
+               6D1C369DD8369F361E9A2E2A123C87DE /* uuid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 89B9846A28B16483D7E8716E91BD0781 /* uuid.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               6D4F02F9947B695D3074491BB4D917ED /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FDD2185138D081B59430FFD466ADEA1 /* Document.swift */; };
+               6E2E50D2FB366F4E22FB175E61DE50B0 /* FolioReaderCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AC354FE6FEB3854FB790A1543E37D7 /* FolioReaderCenter.swift */; };
+               6ED5B5CE6E6697D6E87170B8F7E20CF8 /* AEXML-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 50FD250AF3DF50EFB3BFDE0C1A66DB27 /* AEXML-dummy.m */; };
+               6F70AF82C5E93D1D29187C6FE141BAF3 /* RLMProperty_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 16E69380BDC8BE32A32CE50AC558292F /* RLMProperty_Private.h */; };
+               6F78BFAAAD958F7B294EAD931C21C3C1 /* NotificationCenter+OAuthSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = B365D8630E824F91C12C169371386488 /* NotificationCenter+OAuthSwift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               6FE1CD1571CA5065913B86D47689F3B3 /* MZDownloadManager-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C46E57D513084D4F163AE3274C8ACEE0 /* MZDownloadManager-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               7068E8A7DDC1424EE8F24BC77E8746F4 /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C14036937A2DCAE7AF38A487E9A5AD /* SessionManager.swift */; };
+               70D404723BA91BA1EBD8442587F7E4C0 /* RLMObject_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A934BE8A55D992D204B3B9A14611E10 /* RLMObject_Private.h */; };
+               70DFDE03BE15B88F7288274A986812B8 /* SSZipArchive.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 767EF6727F4D919F160041B1B4BD5D24 /* SSZipArchive.framework */; };
+               70E1335E582FB824E10AAC28B7C70C5C /* RLMRealm_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = E48289A832C7D72E990BA4AD40B16CF4 /* RLMRealm_Private.h */; };
+               7163EBADC5D6FA8EEE2348A5C5CB53D1 /* sync_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A4A319D825BD520943E481653FBDB97 /* sync_file.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               72431300C9DACB7D5CA00D5A85114050 /* Swizzlings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9DB620986320D1403CEEBA251CB09C2 /* Swizzlings.swift */; };
+               727804B0F19D9A317560D81BCC3631E6 /* ZFModalTransitionAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = E6E81ACB0B42B77DE2FFAAD41DC5D97B /* ZFModalTransitionAnimator.m */; };
+               72EC8F56CE889E7BB0C09C7FEB1CA475 /* FIRMessagingCodedInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 914C23ACBDFC7C74C0EF765A53C0EC7C /* FIRMessagingCodedInputStream.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               7309FB2B42EDCA453F804DDEA6981FB3 /* RLMCollection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 23C00816B8A572DD6069F377FC98681A /* RLMCollection.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               737FA14AA54CEA27C6970DD23735F412 /* RLMSyncConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = DF16170DE63470802D26A252D0E24CE8 /* RLMSyncConfiguration.h */; };
+               73A70DB94B07194B5AD5B24EFDCF403D /* FIRMessagingDelayedMessageQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = E819F8A139413C7ACFA07F82BF5F5CA9 /* FIRMessagingDelayedMessageQueue.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               74991D9F2A55044D75545D00A70C8CA9 /* Kingfisher.h in Headers */ = {isa = PBXBuildFile; fileRef = 38ED9F6020F6E95DF135E9564D06DB85 /* Kingfisher.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               74F8FEEBD2D51A5315C36399189B65E8 /* GULUserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C0F994B8858C9DEB29A4E169084A475 /* GULUserDefaults.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               759BF3C710068C3C216586677E493F12 /* RLMOptionalBase.h in Headers */ = {isa = PBXBuildFile; fileRef = B495C794563F95CB715EBF0C78EDBFE7 /* RLMOptionalBase.h */; };
+               75A1E4900C0AA7CE6E2454C64AA169E2 /* ImageView+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0AECA8112FB6A3BFDFA9BC9BC8AC765 /* ImageView+Kingfisher.swift */; };
+               75AA6AFBEA5C650C1A3B0A23C96E4B1F /* FIRMessagingVersionUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A71836910F0F4E2CBA0CD1D3D5940EF /* FIRMessagingVersionUtilities.m */; };
+               76A9C1EC901BEA3FE51873831EEC2D1E /* SSZipCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = B3F61ADE7C91CF96278DB665AE571F4E /* SSZipCommon.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               772237CA61C25004F79D55E6F04ED19C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05984A0DCE357F35A45231C58BADFD71 /* UIKit.framework */; };
+               77315EF5B7DDA235E02F549AB9D66E57 /* FIRMessagingReceiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 8835399917D54555633265E36FF8665D /* FIRMessagingReceiver.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               77ED8AAE6516726FEBCC37BFE9FAE0A7 /* GULNetworkURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 147E69F1ED7DFD212ABB16B8DF662DA5 /* GULNetworkURLSession.m */; };
+               7876BC2321617C237B93692E6DCC9F45 /* SwiftKeychainWrapper-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 239CBA52FEF031788D07DBF21BE742E4 /* SwiftKeychainWrapper-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               79206EADC95965C170D359EAE615FF28 /* FolioReaderKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 41E4C9647DF00F5A8397812EB95B4B77 /* FolioReaderKit-dummy.m */; };
+               7953040EF657ED5CD6E5F235400E59ED /* minishared.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FD1E1F5EF445099E14F78112E4DE5BD /* minishared.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               796177DE2762F24DAC16A709FD954838 /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91EF582D4746813F0EBB183BC4373C05 /* ParameterEncoding.swift */; };
+               799EEAF62D2BC7F7681F9AF0324FFB7C /* RLMSyncUtil_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 45D79C1899AC277B26DD3524D5AC9919 /* RLMSyncUtil_Private.h */; };
+               7A332BCDBB3A90B34304EB2BF8D9712B /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F2AFDD06DDE1D92AA1028FB0C8C109 /* Property.swift */; };
+               7AB46D076300E2093CB5077FF2E2ED81 /* sync_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FDD0294627FC460FFE79962ABD58D781 /* sync_session.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               7AB6088B4276502E47E9681F93DC28D4 /* RLMObjectSchema_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EFD854B5B21344EA040B49EA6725FC00 /* RLMObjectSchema_Private.h */; };
+               7B17CD91C88596B5F2D19D16CB28A4FD /* Dictionary+OAuthSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 680BC3B5B1C18CB129B3F626B32364F8 /* Dictionary+OAuthSwift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               7B92B58352349AB061E2DC676BE670CC /* RLMAnalytics.mm in Sources */ = {isa = PBXBuildFile; fileRef = 381F31B952F0996552B45EB064A7DF08 /* RLMAnalytics.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               7C0BD3F2B8DE0865A8E0A48922385AE6 /* GPBRootObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 30B4F0F35B2E6FB1AB726ECD8CA57734 /* GPBRootObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               7C5EBA165C3E36BDE6EFD25BC86C9251 /* placeholder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F7CD333CED7797EA7D39F18163187E6C /* placeholder.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               7C871C890E18F233E79E9743EA839B08 /* object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1171DB362D96B024E18F290CEC25F65 /* object.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               7D318B1096AB60265B397B5032AA6225 /* ObjectiveCSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB63B8D80EDAD314AAD2D198C7B0C90 /* ObjectiveCSupport.swift */; };
+               7D3D05CCBCF7BE685384C79A19C5D36D /* FolioReaderQuoteShare.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FE04C47410787B608EB2E6AE39AE374 /* FolioReaderQuoteShare.swift */; };
+               7D6F755661B4FD496E74E2A707A210B0 /* FIRMessagingRmq2PersistentStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 93B635E4D5E3A40A7208AF4D15B52428 /* FIRMessagingRmq2PersistentStore.m */; };
+               7D986FCE9489A0759134A022FD2B6A82 /* FirebaseMessaging.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F9995DB177F960FAC776F60EE05C851 /* FirebaseMessaging.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               7DA68975AD1D00BA8808AB41A54082C3 /* RLMOptionalBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC9CFDB3F3B5693890E17EDBD113E565 /* RLMOptionalBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               7DE1F186E56EBAA07D2F4961E78438F3 /* FIRMessagingLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 4259A6F91094C5C3FE957FE5285966CD /* FIRMessagingLogger.m */; };
+               7DF4208F0ED7849BF881FF06D9F0F476 /* Andada-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 9261FF50BC82FC7986B85E0D8CE24D61 /* Andada-Regular.otf */; };
+               7E1BC12B4DFD06232C695FBC3E61D448 /* FIRMessagingPubSub.h in Headers */ = {isa = PBXBuildFile; fileRef = FEC060F5B023A25663DA3A7FF0A8A1A6 /* FIRMessagingPubSub.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               7E2E5F263C3FF1944756B17250BCFE0E /* RLMListBase.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 5A064D124C92CFA11CDFB748E3A1F23E /* RLMListBase.h */; };
+               7EA1C26030A5AA6D6CA0E107EC89171D /* SMSegmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210CA0599041DC7C7A13E77AD4BAA706 /* SMSegmentView.swift */; };
+               7EECB9CF279E71AA725186BAE7CB7E8C /* RLMSyncUser.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 1482EE96CD4BCC0E2EDBBA3ED70FDCBD /* RLMSyncUser.h */; };
+               7F17AC1ED6D7AED732EDAEB7C0B4103C /* Andada-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8EAB79E1A81BA370C6AF53D9EAE85AA /* Andada-Bold.otf */; };
+               7F1E5BEFFDDCC19F2FCC3B791CB4DF8C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 829AD1D8E7F6EE418C950F905263EADD /* Images.xcassets */; };
+               7F1ECD77604532CE1DE5EB04A8A69863 /* FIRMessagingRmq2PersistentStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EA756190D5253EAE03798BEA1646E0C1 /* FIRMessagingRmq2PersistentStore.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               7F4789035CE0FD046F49F8C8B0B03075 /* RLMObjectSchema.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 565A58C3DEC5EF04039C13B055C22CE9 /* RLMObjectSchema.h */; };
+               804163B1F6F5F7B66E6AC4EAB5BAAB37 /* GoogleUtilities-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B6795E15B518CF390D339237ABBC9032 /* GoogleUtilities-dummy.m */; };
+               80B122FBC94F932F73B3CB08A8176E8C /* GULLoggerCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BAB9A55E1DAE6C7836391C9CDC942B6 /* GULLoggerCodes.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               80C3AEB085EC7C9FF503B92EEBB0C4E2 /* Timestamp.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = D7F8073C1B181564744EF259B9F8739C /* Timestamp.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               810C5913A0776118199562DACAAC40EB /* hmac.c in Sources */ = {isa = PBXBuildFile; fileRef = E541CBE59D00CC3AB220D84C5F2B0398 /* hmac.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               81408383F052E56545F0465BD28DCC6B /* Toast-Swift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C649BF15B07EDFC3A64FEB2AAA77C29 /* Toast-Swift-dummy.m */; };
+               82291B80DF30DC3BF5F84BE780B7A794 /* RealmSwift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 21214BE8206ACC637CA1C38C1FD26273 /* RealmSwift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               826FF04A17F730BF8D251D79233F3C4D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               833B4550B26D2A945E08C978BB7BD505 /* String+MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 014EFC1AE04ADF977639E4046BE9008C /* String+MD5.swift */; };
+               83AE053633BE46D1C7AECB85BC90373B /* GtalkExtensions.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 371FFB7916EE7697D6F76BA98C2717AA /* GtalkExtensions.pbobjc.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               84395DA299C5B7051B049D2D1A1BE8A6 /* GULLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 43FB1CC94D2C1CA721B8517B02BB5EC3 /* GULLogger.m */; };
+               844063DFB5DFA2E4495E173CFFDD8419 /* GULAppDelegateSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = DDE5567F4018883FFB851237B4CB839C /* GULAppDelegateSwizzler.m */; };
+               86D305B61E79828EFBC40467AD34CE3A /* RLMSyncSession.h in Headers */ = {isa = PBXBuildFile; fileRef = AF2F39EAC2946E050E52E2C675208EAB /* RLMSyncSession.h */; };
+               86DD792B02A8F758BB5088B53C00B405 /* RLMUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4370FE81CEE77016401F7AAAD0158F4A /* RLMUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               86E19D48949A95AD016D27AB7BB8AE41 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA80AF01EB6F8C828B12CA686720BCF5 /* Logger.swift */; };
+               87523532AD81389088F389D9ABF1A953 /* RLMObjectStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 67D2BFFD100F5D39305BA3FC2802DB4A /* RLMObjectStore.h */; };
+               8785F990581DF6C8B51B3BDAC6A87334 /* brg_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 163DF539BC15EE2749223B588A4D9978 /* brg_types.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               87EA88C8DEEFDE135776F01CD31DE4FD /* FIRMessagingDelayedMessageQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 07900388E4AD52051EE289F5ED2EEDF4 /* FIRMessagingDelayedMessageQueue.m */; };
+               8829CEFEC09F22535860AA11F5542425 /* ObjectSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB132E5EB9BAC1B61BECF12A10C2BF5 /* ObjectSchema.swift */; };
+               88CA03D951F6E22D0FAE5EC52BF1310D /* RLMObjectBase_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = E0EBEE00C0127E3C90E732DB5F01CF5D /* RLMObjectBase_Private.h */; };
+               88CE4EE02A104D4BD65DE024ACF209F7 /* KingfisherOptionsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B99ABE547EBBB6C2B8F7FF04E1C02378 /* KingfisherOptionsInfo.swift */; };
+               892547A1AD5152F3DCF8C20F0A607F80 /* GULLoggerLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = 1408A9D036A6990304A3BEF6A655CBDA /* GULLoggerLevel.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               896BDB57CD0867624ADF5017511D5780 /* sync_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C756083B788404F7E5739A71EF0218 /* sync_manager.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               897468434F0001FFC94F66C27E4F39D9 /* PageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09BBC999192646D11C33C3FD05C523F8 /* PageViewController.swift */; };
+               897E6F31468ABE5B9C8EF68994C54833 /* FIRErrorCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 544A23E1053CDBF91F0B3731EDA4FFC4 /* FIRErrorCode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               8989C8A69A11C1A52BE62DA21BDF8ABA /* GPBUnknownField.h in Headers */ = {isa = PBXBuildFile; fileRef = FFF62627BCA2758AD2D6ACBEC9926E8F /* GPBUnknownField.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               89B5EBF2995898023F8443B2E6071559 /* RLMSyncSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = 359CF8CC37A4F97F6D3086A95EE10DA0 /* RLMSyncSession.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               89BEA56583B63F9F07B07094A9A687D1 /* MenuItemKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B52F39B542A1D04B86E6DAE81129EB7C /* MenuItemKit-dummy.m */; };
+               8A09F85B24B1F26EA9E0FBBCCDC497C0 /* FIRMessagingConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 42ECF2075F0721267E036F3A8F90A0D0 /* FIRMessagingConnection.m */; };
+               8A30EAA74A0996419BD207F92DFD652B /* FolioReaderPlayerMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E40312A6196B0B687CFF43F843C806 /* FolioReaderPlayerMenu.swift */; };
+               8A4277DAFEEE4F1120E4854E20FB1C50 /* RLMListBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A064D124C92CFA11CDFB748E3A1F23E /* RLMListBase.h */; };
+               8A4EF2DACFFEFCF78D724E02C536794B /* FIRAppAssociationRegistration.h in Headers */ = {isa = PBXBuildFile; fileRef = E586F0F684FC8FE07B23490EB240D074 /* FIRAppAssociationRegistration.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               8ACC7142BE1924CC48F5ECB3CAFC1EF3 /* aes_ni.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C822CD35CD43E275C564128C723F24C /* aes_ni.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               8BA29AE08FB5640473F27C9244782CC5 /* FirebaseMessaging.h in Headers */ = {isa = PBXBuildFile; fileRef = 95086548DA4949A5831665FD287ACD99 /* FirebaseMessaging.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               8BBEF850C762FA864916060EC8AB2821 /* GPBCodedInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 87970034DF91CE05199D6AAA2E708D72 /* GPBCodedInputStream.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               8BF9337F999D245FB064B98432DB252E /* FIRMessagingVersionUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = B641B98C9FE0F76E5A9246DD05AEF31C /* FIRMessagingVersionUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               8C0D10DA3122D7BD7AAEB35E502C9BF4 /* GPBMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D77B5098FA436FB6E1221AB6EDF22370 /* GPBMessage.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               8CBBE676E43BBBCE90BA4D320D972208 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07F73D4373DB447405CB3388AB42D8A0 /* Error.swift */; };
+               8D7C6BD5D7B2671FFC800C600A255BFA /* MZDownloadManager-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F9AF84291BB835371B31C38FB32B164 /* MZDownloadManager-dummy.m */; };
+               8E10EE937C903154C94EF756B76D9DF5 /* FIRMessagingClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 994AF878471D422AC2866C7E5A3733AE /* FIRMessagingClient.m */; };
+               8E82FE260753B343D76FDA58389A72CD /* FRSmilElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F03D2A8CC72E729DA124F2004020658 /* FRSmilElement.swift */; };
+               8ECD93126F045EBB7633AB23EE84F1B0 /* RLMSyncCredentials.h in Headers */ = {isa = PBXBuildFile; fileRef = FCE7E03A64E471CB3D6F8F1475E092A1 /* RLMSyncCredentials.h */; };
+               8F857D1268C9E85BF2103CAEAC35EDCD /* MenuItemKit.h in Headers */ = {isa = PBXBuildFile; fileRef = FA98BF272CF91084C53F3CD50D3B18C6 /* MenuItemKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               900B34291E7F49B1A52BCE9DDCE6E6F3 /* FontBlaster-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 126192276E659647B019680598D32B29 /* FontBlaster-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               90523ACEFEF8D56C35F5CF41CB70EE58 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4300E3895B62156BB316982523FC8D06 /* ImageCache.swift */; };
+               90966D0CEAA5B97157C5CACC36671C23 /* collection_notifications.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6F01E4CF285A102DB8F3FFC4F10EEE2C /* collection_notifications.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               9131856F764756E2A2B806107EFBE608 /* GULNetworkConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 7EC37761CA1DCE7138823E75B1E22949 /* GULNetworkConstants.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               91716377663C5129353AE65DDC9EAC55 /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72C53A710A523025D5E9074631788CD9 /* Device.swift */; };
+               91A13910AA34A5D9021EFB5AF76E3807 /* FIRMessagingPacketQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D81BFE71A07B939C010F22763CBED031 /* FIRMessagingPacketQueue.m */; };
+               92167B5CE3EF41711C5347D710306CF4 /* GPBWireFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = C5EAE55F8524706A50D1E64B2067946C /* GPBWireFormat.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               92256B318D82A1668DB0944579F7A568 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               9233126AB5F69C247A408E26A9B7942C /* Objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = C631E06F6A57B0AE8546402CFFB399D1 /* Objc.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               927823650C468EA5082D7B5BFA2A6D42 /* aeskey.c in Sources */ = {isa = PBXBuildFile; fileRef = BEB3597A302BDA172DD6558B46724016 /* aeskey.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               92B608263232B47001557148A8874F1D /* list_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F1FB8E4F26A7220132808171D2B97800 /* list_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               941351D8B8F0D0D877A2C7E5067EBBE3 /* Type.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F9331D2AE04D7359BA18D23BDA96D7A /* Type.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               942DBC0B89C8BAE0D5CEC47CFD84406B /* NSDictionary+FIRMessaging.m in Sources */ = {isa = PBXBuildFile; fileRef = B5625DF9366F18F9E5B0D1CF875407AD /* NSDictionary+FIRMessaging.m */; };
+               9453DB0ED8774AE633437A11A474D93E /* GPBUnknownField.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C3D986619B47384323E22FC783B359B /* GPBUnknownField.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               9496D8AAC021E30241C522A57E5D7C58 /* RLMPlatform.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = ECC142A8EA3AE58507D64C2E6BF510CD /* RLMPlatform.h */; };
+               94E1292824D6FC94E97E2D9C175D1C3E /* LinkingObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49A8496480916B38FC60F790D50CAF75 /* LinkingObjects.swift */; };
+               951D6869853F4D02D5DBF66847D45173 /* RLMSyncManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = AF31C2EDFE5591ADC92DB32F0BC409BC /* RLMSyncManager_Private.h */; };
+               957C09275A801195985E591D420848F1 /* RLMSyncPermission.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 9F56E9572F18DCBB5E446C23D65E1213 /* RLMSyncPermission.h */; };
+               9598DD3F30A87385E507352646B3A1E2 /* FIRMessagingTopicOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BDDA12CFFFDAF42893E0356DEFD50B9 /* FIRMessagingTopicOperation.m */; };
+               95998AD316485C5728F1B7ED02C5640C /* GULAppEnvironmentUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = AA835BF7AFA43E2BF03FA593F7AD83CE /* GULAppEnvironmentUtil.m */; };
+               95A48A974C9115D1C848C6FAB5CE5FB8 /* OAuthSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3045AE10FB372AE8C59AF5E27B74F273 /* OAuthSwift-dummy.m */; };
+               95AB33523EE2DA5C145714D7279E8D27 /* CacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DA6E2DC0B34EB311EB4E1A5DD25EC45 /* CacheSerializer.swift */; };
+               965DACF3DC02857ECBE66C5CBA3DA5D4 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA5A1028F01FEC16A4E71E8C1F5E062 /* Request.swift */; };
+               969201902C32F7A20B1803BEDE928791 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B037AD884094D78946E98AECCBDBCB3 /* SystemConfiguration.framework */; };
+               9716EF4B9C2B31231060FFC1DEB40DA3 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD8344B4C8CA5CDCD06CDB95C9779E9E /* Parser.swift */; };
+               975A2D21AE6EB81D5349AF850728E5D7 /* GULNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 145E95AE5F85319E80B8B12CC8A05570 /* GULNetwork.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               97665278CC3323C5745205C9245831C2 /* GPBRuntimeTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B125D2F070E0A07B9BCA5BAA19580AF /* GPBRuntimeTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               97BE0A5C72137771761CECCC14345FBF /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4894DE102973633A89B8B1A928B2F3F /* Toast.swift */; };
+               97DEC66B2525E0B840E4796D007CF196 /* FolioReaderSharingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED4E426129BC7417B7E88791A14B5E6 /* FolioReaderSharingProvider.swift */; };
+               97EB2E795C2BE40106D734B5C0AEBDB3 /* ImageDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FBF1EAFC3C8941E114238A69DC823D2 /* ImageDownloader.swift */; };
+               980D1B333A71D6250A7737912581D4D9 /* GPBDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 22DBCD1AD7AE653008499BE79CEE9E27 /* GPBDictionary.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               9842D46A74A89E0ED7809E92EC434290 /* OAuthSwiftURLHandlerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49AB6C96EADE159493F9CF27E33B2452 /* OAuthSwiftURLHandlerType.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               98466C008D41A93B78FE27FFC40890B6 /* GPBUnknownFieldSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D93CE20821B7BD08A6D6ED2CE388920 /* GPBUnknownFieldSet.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               984847FC535429EFA3945922B0E17A0F /* RLMRealmConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 11A9CEA39CE194E1A1C87939E94FA086 /* RLMRealmConfiguration.h */; };
+               98686D23553D62431BB3E7B33470129C /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C52956025F0F8DD25F68011683ABBE9 /* Logger.swift */; };
+               9912AE753145BC77732BADA9E0069532 /* GPBUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 98BE3D9CD9277E245270D62A28AD5DC6 /* GPBUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               99718A98B98F71465754F17EF9F1D6CF /* FolioReaderKit.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C47C18DAE8DE892F90FDDBF5ADB969 /* FolioReaderKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               99D878A13E43241F161775020BD80F32 /* keychain_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FB591C76B0F358BC96ACBDAF812A8552 /* keychain_helper.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               9A5080678DC0D65FEA4F469C75975FE5 /* RLMObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = 705DCC263710805CED0A0D347B897A45 /* RLMObject.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               9BA5CDC177BFB299696081BF56DBEE02 /* unzip.h in Headers */ = {isa = PBXBuildFile; fileRef = AC93D8CD26E177BF4425613E26F84E62 /* unzip.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               9BD83CDE9B986272ADEDC8724052287D /* FRSpine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9570CA07B3D1379C659F3B95E6764364 /* FRSpine.swift */; };
+               9BF96E6F904DDCF6EDECBBC840C2F314 /* FIRMessagingCheckinService.m in Sources */ = {isa = PBXBuildFile; fileRef = 271E61CB4DD108196A89495C96CA3D61 /* FIRMessagingCheckinService.m */; };
+               9CA7916A2098BA1EE30155DA105317D3 /* Protobuf-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = EC83E7C64CD71374E5BBA059F4BBA0BF /* Protobuf-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               9CD281C3381240CCF5B1CBA25FC9A7C5 /* MatomoTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184161E1B57664D8505D6B2744800293 /* MatomoTracker.swift */; };
+               9D2D4F83EB0072F2F9976666F90412D0 /* NSError+OAuthSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA785A1C830431915F33A46AD84FEF7 /* NSError+OAuthSwift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               9DCECCE3655A709508C929D3770C7D97 /* FIRMessagingReceiver.m in Sources */ = {isa = PBXBuildFile; fileRef = 92B419D2322B119DB7175E65FF9B76F3 /* FIRMessagingReceiver.m */; };
+               9E250206EB49756E42E7066A99E87A29 /* RLMProperty.mm in Sources */ = {isa = PBXBuildFile; fileRef = 325E20A67E5E9F463834C45CE511FA12 /* RLMProperty.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               9E6D778183C74562D0631A3CB1279157 /* RLMNetworkClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7E7474ABDCF1D1C9A8212C18E3BCFB2B /* RLMNetworkClient.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               9E75B30A41B50CFD5BFC89EC65D0AAC9 /* Type.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 874F2D847F616BB0F3DA0EFB4ACD0FD2 /* Type.pbobjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               9EC2BB6DB27F86658A9451AB726B018B /* aes_ni.c in Sources */ = {isa = PBXBuildFile; fileRef = 7C031C39DEA92D368607CCB505525724 /* aes_ni.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               9EC8A08B215141017FAF89FA92738E80 /* GULReachabilityChecker+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 2747F0DF00234AE9135F29A3EF8186E0 /* GULReachabilityChecker+Internal.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               9ECACEAD09A3D0231033532D62799534 /* object_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1E62823E12CC340D00EEB586EEF177F9 /* object_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               9EDFA1C34D4853B76A78B8E40B89C775 /* RequestModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 268FB6F347F0C0C1834F10B2DF31A8A9 /* RequestModifier.swift */; };
+               A168479F8F7841A22AAD62F7A029A0DE /* Raleway-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0260176D84540F033791D6C38116526E /* Raleway-Regular.ttf */; };
+               A19B968EFB027AF79BEA846262E6EF2C /* SMSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989BDF769D8240DA767C777DF3E091BA /* SMSegment.swift */; };
+               A1F5532D985161407C5FC08C8905166B /* SSZipArchive-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 23A4583BE5CEDDE176D8A3C11916D33E /* SSZipArchive-dummy.m */; };
+               A20B89DAA8EF5142AF1F9D1F6D9351A4 /* GPBCodedOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 08B415F2B9935EB42007587E2BF3BFF3 /* GPBCodedOutputStream.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               A32E0A2404ACF53852037EB065812C57 /* UISideMenuNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952F782A447D10CA2ADAA9DFD3016A9A /* UISideMenuNavigationController.swift */; };
+               A347846A8F6FA3E180C7431CA5440B58 /* RLMResults_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 59F29FF04A45EA9F6FF92C7F6BAB4B12 /* RLMResults_Private.h */; };
+               A38C4C09C9050193B9C36EF2E20069E0 /* FolioReaderHighlightList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 949BE471587E597B7F1E52BF803F5EA0 /* FolioReaderHighlightList.swift */; };
+               A3A4F4A1704A233C74813BC038C89AAB /* RLMObjectStore.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 67D2BFFD100F5D39305BA3FC2802DB4A /* RLMObjectStore.h */; };
+               A3E489E4AD745C39082A645D9B4E51F9 /* RLMSyncConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = EE3822E4428D213B2F2D9A7BEB84C33B /* RLMSyncConfiguration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               A407454449431173E8D9BF7DD73EC859 /* GPBCodedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = B2AD5B1877FE90020B8EC5A5A9468CFD /* GPBCodedInputStream.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               A420636229F2CF99350F76899CEDCAAE /* Duration.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 0DBAFFED7A2B4724B0A96F2E74FDB1E6 /* Duration.pbobjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               A47FF3D3C35B24F874E8B4A1FF9BE25F /* FIRMessagingTopicsCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = CC32893E8F8BEBF85F395C456E4DC02E /* FIRMessagingTopicsCommon.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               A52BEE1EFE79023CBB820877AC7BA489 /* FIRCoreConfigurable.h in Headers */ = {isa = PBXBuildFile; fileRef = 77E15D3937F6AFDAD39719014F24AF7A /* FIRCoreConfigurable.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               A5D44A77060CAA13F1151DBC7E42E586 /* FIRDependency.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D57FCAFAEC5BD45B0A651B90B820F7 /* FIRDependency.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               A6190300A689C5F3E9116E39BA2985B7 /* FIRComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = E57A9782169E3DFBF458AD63149C5AFB /* FIRComponent.m */; };
+               A688C76313AAB612FE7F283088ADDD7A /* RLMMigration.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = DD11C12E8B8A3662D6DE8C3D9CC3DE31 /* RLMMigration.h */; };
+               A7A4A80FE4D52D1492141FB82CED7679 /* HMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DD1F2E48435B86FC4B95841449B2727 /* HMAC.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               A7E31F629D025F3099B8FC93D241170E /* RLMRealm+Sync.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = CFE9AB50F57A50B5C79F92AD4CAC7208 /* RLMRealm+Sync.h */; };
+               A815CCD462141D7937E2BD32224F8516 /* GPBExtensionRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E3BE0CB2177D65A373AA72656755163 /* GPBExtensionRegistry.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               A83EBA98974578B84655FD73AED9963F /* Pods-WolneLektury-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B451004D639471DB5A62688F187D31F0 /* Pods-WolneLektury-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               A872241FBEBAFD4FFCAC9E39E5F34549 /* primitive_list_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 177C7DCAC0D7CB576C44F872CFF50370 /* primitive_list_notifier.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               A8906F6003D20F1B2E9C5BFEF7AE9C1F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               A8D220DDEC5421EF632699031556C390 /* GULNetworkMessageCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 5016398FE6D4E791E29F725CF2C48D1F /* GULNetworkMessageCode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               A94B61C667CDF4664E423BF403D6C9CD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               A95BA997891CD85177E8DC406B14D429 /* FIRComponentType.h in Headers */ = {isa = PBXBuildFile; fileRef = F5CD30385E94D3E58B07F43DE2CF3F9B /* FIRComponentType.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               A985909029B8F65FB62C9277C5AE8FAC /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24BECF804ACC1DFA1AEF908133E8CDF9 /* Object.swift */; };
+               A9DB4DA63493082FBE08873CDD30608B /* FIRMessagingCodedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 75E719D436BFA4E227332190F0A114CC /* FIRMessagingCodedInputStream.m */; };
+               A9FB27A766998F6AC8D31D811F9068F4 /* RLMProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = 998DC89C6539EB5EE837C792F9D36764 /* RLMProperty.h */; };
+               AA6A216DB6FF225A0EBDB574A195E6B7 /* RLMOptionalBase.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = B495C794563F95CB715EBF0C78EDBFE7 /* RLMOptionalBase.h */; };
+               AAA6A3680C85C31B477E4C906DE9815A /* nanopb-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = EA3A59300A145FB0537DB2E9829405FE /* nanopb-dummy.m */; };
+               AAFB81C326930D69B9D3D346EBC919FE /* FIRComponentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 5267DB065F6BF93D0021FB08DF8245BC /* FIRComponentType.m */; };
+               AB0522654C4F3AD21BAB960A6E2B556E /* prng.c in Sources */ = {isa = PBXBuildFile; fileRef = A5D9C7E3D96C2EBCE4D34719C6211F2E /* prng.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               AB64E51C9C137EAA03865FF2904E9DD5 /* RLMRealmUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = EBBA27AA678EB1363869D44237655330 /* RLMRealmUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               ABE291CCCFE48E189213631A3DC03721 /* NSError+RLMSync.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C5FC54772307BC323BD252586733ADA /* NSError+RLMSync.h */; };
+               AC84818A2D77A2919122284BE2BE7B08 /* aestab.h in Headers */ = {isa = PBXBuildFile; fileRef = 022810A628AB345511375F43564EA066 /* aestab.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               ACE60E955FBC54769746FA2C87C6C79C /* FRSmils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0814C5C3D6056C7C77038075C0FAA4DF /* FRSmils.swift */; };
+               ACEF3F20D625C95223D1ED8AC7476E84 /* FIRLoggerLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = B6BCD5898DBE90DAD2D5258B776CE839 /* FIRLoggerLevel.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD0CB2E82475505A45C0E13A0FB2628A /* FolioReaderAudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC7E4A5F9F3490D492D52132DB76FC3 /* FolioReaderAudioPlayer.swift */; };
+               AD2E5F4E82A54D5CD24E4FA34A7D8FFC /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B5C11A1670ED8BEE826D1FADC42D7C7 /* Options.swift */; };
+               AE0A293D64D5C8C9BC4A508331231083 /* FieldMask.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AA2497F29326836BF937B466101FCB1 /* FieldMask.pbobjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AE1CA95A3B55780E3E65385B5A6ED1FD /* unzip.c in Sources */ = {isa = PBXBuildFile; fileRef = 295B0C54BAE1989B99BA0920EA7C603C /* unzip.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               AE633B3532903E2EF04B5D7BA480C3C7 /* Kingfisher-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 51AB21DAE8AE316862E8F8DC498D8765 /* Kingfisher-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AE6574015BBD1352327E7ED052444702 /* FIRMessagingInternalUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C5B9BB923D2153C5695489BFCB3FE6A /* FIRMessagingInternalUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               AE734ACC00A8B3AAFD766ED6EA46E8F3 /* RLMSyncSession.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = AF2F39EAC2946E050E52E2C675208EAB /* RLMSyncSession.h */; };
+               AE83443661BB982C102FEA9241A8689E /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3E1C11CC8048486D77496DD0A2226A8B /* realm_coordinator.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               AEDA45FBD321C4357FD8EF98EF751E71 /* FIRComponentContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 71C22AA5D5F284DC9B78BFAF42A83C57 /* FIRComponentContainer.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               AFACFB7AD394A77CA94FBA11ACAE171A /* FIRMessagingTopicOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = FE7332CEBB79FA1E661973254DDB64BC /* FIRMessagingTopicOperation.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               B03A8EB3C2C4DDF017566DC0BAF341AE /* RLMObject.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 101DAC3BA33CBEB734526D4617775FA0 /* RLMObject.h */; };
+               B102D9296D1A1CE12DDFDA38F7ADBC1F /* FirebaseCore-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FC3A34A8538E5CC949053777424628AD /* FirebaseCore-dummy.m */; };
+               B1D0744B9405A528508A49B4DE06A8DB /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5B948DB0251166DBE25C3040F48E8B /* Filter.swift */; };
+               B1FB342273BF07C38E607FB0F7C9EFE8 /* RLMRealm_Dynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = E00C401BE147D47DFBB42AA69755AF0D /* RLMRealm_Dynamic.h */; };
+               B239F6D91EC58C8046497C0909CB2E78 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3039DE7A044D5D519CE558DF7522DD75 /* Box.swift */; };
+               B2EFCB251998FC9F2F1D168E53BD2111 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC637EB57FE64DC55E93BA536D067809 /* CFNetwork.framework */; };
+               B3BA00FDD104AD94BA87E2F72D1A72B9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               B3F4C7A3BAEC0A616DBB463397C77A4F /* GULReachabilityChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 40F16EED8D382D5170EEEF0B6ABAB383 /* GULReachabilityChecker.m */; };
+               B424F524BBBE34E685129945993809A8 /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = D35917412D73B9AC787F462EBB9FB9F2 /* Timeline.swift */; };
+               B4A64FEECB4C4615A17AEC2B4F058611 /* GULNetworkLoggerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 422B10EE9CCDEDA3886C8B1E3F5FC8C1 /* GULNetworkLoggerProtocol.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               B54493F39F42776CD05ECED24CD1761F /* Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D3597C80CBA96B3BFF1BA9CA8BBE7B /* Migration.swift */; };
+               B572324693A1AC994775445E0DD5A97C /* FIRMessagingLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E393C39CB6940A61FA1A4A213758B92 /* FIRMessagingLogger.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               B6329FFFCBA250E12B34D86881D4F168 /* GULMutableDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B850B52C6C12544F2E2F5CD3FE6B3E2 /* GULMutableDictionary.m */; };
+               B6697D7D32BBDC73A32DBEE6C94F5B20 /* aesopt.h in Headers */ = {isa = PBXBuildFile; fileRef = 24DDFB7ADF9F5BB4277956C0D45E3124 /* aesopt.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               B6A3F8383A59C25ED224AEBE6879F5B0 /* GPBExtensionInternals.h in Headers */ = {isa = PBXBuildFile; fileRef = 1632A30520BF40ED6C45A578D6411917 /* GPBExtensionInternals.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               B7278B647DD4BBEDC59EE9D0715DBAC3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               B7653C4FA703BD3F06D8044CB8F0438A /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C94C12B9600AFD613B21A25CCFE5EB1C /* Realm.framework */; };
+               B77705737566AE83ED7E448923D7FA60 /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD7DCAEBC8CE3C15E7460B40AC02A51 /* NetworkReachabilityManager.swift */; };
+               B79BE1F4BADB104866BB7829F473E02F /* URL+OAuthSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB01793375BE6CD1AF6B3C2F9EF5824A /* URL+OAuthSwift.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               B85DA5827B8802F9BB6F592B8897947E /* Wrappers.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 0829B70301B60B62E908B075914CABF5 /* Wrappers.pbobjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               B8B48D5BA99EDC2EA08EAE87BF84A5AB /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3D1E8C327C132322D396C80318931C /* List.swift */; };
+               B8B9C790E3BF944F6186C4C06CA699C6 /* FIRConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F65B7D4BDC23758E135571100B9ADD3 /* FIRConfiguration.m */; };
+               B90DDFC5A8728D00CDC60C2A0245A909 /* MediaType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93FA1B8771A7779F02E65DEC3E502D16 /* MediaType.swift */; };
+               B964C5F1F8293F421DE36D227AEDCA0B /* GULAppDelegateSwizzler.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F944B2EC591C1D211C295341CADE77 /* GULAppDelegateSwizzler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               B9FAC68D54CD7F85CEC7A9A90821A1D3 /* FIRLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = B54964F717A975AE9680DA086D24374B /* FIRLogger.m */; };
+               B9FD06D9FD2F42D434B290CE9203A006 /* RLMProperty_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 16E69380BDC8BE32A32CE50AC558292F /* RLMProperty_Private.h */; };
+               BA600C2097F18291FEAFD5F98322C4C4 /* SSZipArchive-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 566B9BE756758F1C246084E3A26FE6F6 /* SSZipArchive-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               BA9FF7E126E6510C1A93D8702F28AF18 /* NSDictionary+FIRMessaging.h in Headers */ = {isa = PBXBuildFile; fileRef = 55F481A6CA9DC577BC74D7F5D53EDF8B /* NSDictionary+FIRMessaging.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               BBE6B85E960BF7927A44E2329DE05A5F /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC1492CEB8D133CC73739E31913B7B80 /* Image.swift */; };
+               BC64C18FC824FEBD2E3D105B134601ED /* RLMConstants.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 61EE9421F804DDD1641EC8DBF05D66FB /* RLMConstants.h */; };
+               BCD6A1F3C955437467963A3DB7E4DE31 /* FIRMessagingSecureSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B53B8C53CD29268FAB913C8A28911B7 /* FIRMessagingSecureSocket.m */; };
+               BCD985518761B18FBEC44ABD5B5DFF16 /* RealmSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 636D155F9DC5CDD62606CFA7F4189E51 /* RealmSwift.framework */; };
+               BD82F14A4F355DBC495185EB37E86665 /* GPBCodedInputStream_PackagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DA0DAE3F773587A21A814757E4F02240 /* GPBCodedInputStream_PackagePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               BDC078DD5D057A5D018843EE06CA53C8 /* GPBCodedOutputStream_PackagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C996AC58C57E04E25603A8E527EFDD9 /* GPBCodedOutputStream_PackagePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               BE4BA1EDE444A770F834605F4B65348E /* AFError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60BD8D4AEB998E3927430AB2F3519F08 /* AFError.swift */; };
+               BE9F2DA9E442EAD0FC82FB9D04476E88 /* FIRApp.h in Headers */ = {isa = PBXBuildFile; fileRef = 913FAA89ACC1B00F7CB063D2904C54F4 /* FIRApp.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               BEC19DF99BBD1445348712D8E45B8618 /* RLMCollection_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 2189955C32F1B821C22A320B350D4E9D /* RLMCollection_Private.h */; };
+               BEEB7138D830B9414158F024CF836B2E /* OAuthSwiftClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951F8277D8CC7B918EC3F93FB9E672B7 /* OAuthSwiftClient.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               BF8CA5C730F2DFD3748A63C1B29A51B9 /* GPBExtensionInternals.m in Sources */ = {isa = PBXBuildFile; fileRef = E3C8D78F1AD702A005F2E4E7F4DC718D /* GPBExtensionInternals.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               C087908BE81A71BAD372326D557C9528 /* GULAppDelegateSwizzler_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = A499040F440DBB0BA31258B194703264 /* GULAppDelegateSwizzler_Private.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               C0AC33AF5FE2D94A5D4A28B5BD190A74 /* NSError+RLMSync.m in Sources */ = {isa = PBXBuildFile; fileRef = CE6AE3ED1C3D1C2BFE3264A07CACD6E2 /* NSError+RLMSync.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               C0E6233E2F0518B8FBFE8A17B699DA6D /* FIRMessagingSecureSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 5535738AED780D0B2515527F81E80523 /* FIRMessagingSecureSocket.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               C10A68AE9667B0B614FA15298E366820 /* partial_sync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CCE4359E18EB2A62E3B89616E1DF99AD /* partial_sync.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               C13F71F78AFBA66B73CE80D60A27BAC7 /* RLMObservation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5468DF52D9F560728DF9925C2F0D210A /* RLMObservation.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               C15902B0ABEE0E2E8B4AD7A0E03F70E9 /* FontBlaster.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94BC0645BAAC1EB760B6B157A87213E0 /* FontBlaster.framework */; };
+               C19B4A16F77AEF3F8DA734F25B8625FE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               C27D8715EE3472F13F5D9C02B083EFF6 /* RLMArray_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 135AAE7A08E11EC8D750570317E36028 /* RLMArray_Private.h */; };
+               C31470E92B1A691AC3D3D55A127DCBF0 /* MBProgressHUD-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 29C8800C3B241A2D1C57FB57C1A07B4F /* MBProgressHUD-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C40D0F4780A38FCD6991E37D72982D6B /* RLMObject_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 5A934BE8A55D992D204B3B9A14611E10 /* RLMObject_Private.h */; };
+               C417DA5E48DE74B3E2C6F38BD6E8AA82 /* OAuthSwiftHTTPRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C66C0B1401F75949F378B18E584E000 /* OAuthSwiftHTTPRequest.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               C46B7BDE25252D8EB990DEB9FE7CD98A /* pb_encode.c in Sources */ = {isa = PBXBuildFile; fileRef = 475E47404BD3273B96027E83999E404E /* pb_encode.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc -fno-objc-arc"; }; };
+               C47E0890DBC816176E3D01D4FAE56798 /* Empty.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = A6A011D6F31E757D458A431970A9F122 /* Empty.pbobjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C4AFF48A2EBFCA39EDA56971CCD1179C /* Realm.swift in Sources */ = {isa = PBXBuildFile; fileRef = A08B99A4BBBE9911D2FBF593D5865F00 /* Realm.swift */; };
+               C4E789001269543FE1654D7BA0CFA4E0 /* FIRMessagingPersistentSyncMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = FE0FC737191323EFE3F2E1F6EB1608C2 /* FIRMessagingPersistentSyncMessage.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               C5541A4DD5B0E7BFF0898FBBE784250C /* SSZipArchive.m in Sources */ = {isa = PBXBuildFile; fileRef = 673243EDEFCAEB87BF4D213D5731B809 /* SSZipArchive.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               C591500094347AAEEB0DA12A7555B0F7 /* Style.css in Resources */ = {isa = PBXBuildFile; fileRef = F1F2F74DBD2E4458AD6B2F16D85CBDD9 /* Style.css */; };
+               C599CE94C59B4740EC39FD0115B3EC6E /* OAuthSwiftMultipartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C1441259AE0384F9D916435E4E1934 /* OAuthSwiftMultipartData.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               C6AAA96E9F95646F5B19AA8CA8C82D96 /* pb_encode.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E803E5235D95B461EE5B6994A8C102A /* pb_encode.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               C7802895FAE85F99BB7C2116BE4D8DA0 /* FRMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB2A0E8B68A46C4DC9F646C4806F1CF /* FRMetadata.swift */; };
+               C7A51C3E97E628F007074B337D44E4DB /* RLMQueryUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07F8C4C696AED2F05397BDC937C97823 /* RLMQueryUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               C7A62A3BF5D85833A2C6FC4AF01CA9C8 /* ScrollScrubber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9413A640B6F54FA4706173107E9234C /* ScrollScrubber.swift */; };
+               C83872BAE9422D1BA0845AB635CD8303 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               C8603807EDCC2B3CD7AE6556E7C96017 /* RLMSchema_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 9E3C47D73094DB09E1D3465A7E11ED30 /* RLMSchema_Private.h */; };
+               C898AFA88F75E2E9F703FF35249C7095 /* FIRComponentContainerInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 38D0FD7E483E650C35611A41941D7F0C /* FIRComponentContainerInternal.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               C9538BAC268AB8AAB4336A4E62B2EA1F /* Kingfisher-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = CA1A1481F0FEAD0729D79CC5B96CC428 /* Kingfisher-dummy.m */; };
+               C97607EA1341DCD74DDDA94986512302 /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9470A90522247E16FD92E96415CCBE86 /* Session.swift */; };
+               C9B4ADD0D0F5552A71C1DE332431350E /* FIRComponentContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C570FE262AD0188836ABD1A74A469B8 /* FIRComponentContainer.m */; };
+               C9D626FE1D508CDCA87F0825540D1873 /* Element.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9757E13C4DE3D7D5E6DAEA3955E6FAD5 /* Element.swift */; };
+               CA69265D91583F5EEE1925B6D303E8B0 /* OAuthWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8C5B26C86483D9A133E4500AD5F3E5 /* OAuthWebViewController.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               CA702089270A891BB374FB47432A1DC7 /* RLMSchema.h in Headers */ = {isa = PBXBuildFile; fileRef = CA7CF0757872C51EE58D200C9E26B2FC /* RLMSchema.h */; };
+               CAB79888713AD1AE289DDC24177894E1 /* FIRMessaging.h in Headers */ = {isa = PBXBuildFile; fileRef = FD7782331BF962C501ACCF35CB93A3A2 /* FIRMessaging.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               CB0EF305E7FB417EE9205AF4EC10F40F /* FIRMMessageCode.h in Headers */ = {isa = PBXBuildFile; fileRef = E79B9239EB89FA4AD74571B09810D57E /* FIRMMessageCode.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               CB22520C2BDF53338EE25CC38CED3CB7 /* prng.h in Headers */ = {isa = PBXBuildFile; fileRef = 767CA3F1E7E65948D78C5DF3A4C074E0 /* prng.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               CB43E93AE5026D07BDBE67663E82FF63 /* GULOriginalIMPConvenienceMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 0414DBE31282AC394C122F66BCD5F0B9 /* GULOriginalIMPConvenienceMacros.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               CBD17AF9CA87FB377F4EE1E1BFEB9B81 /* FIRMessagingPubSubRegistrar.h in Headers */ = {isa = PBXBuildFile; fileRef = DA4991241E87AAE84DFB7BA0D724913D /* FIRMessagingPubSubRegistrar.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               CCEDEC445844E6B637DA2A026CB8D854 /* RLMRealmConfiguration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 19103A10E8E96743A56E682ED1DAC70D /* RLMRealmConfiguration_Private.h */; };
+               CD49C523E23DD6F03A083B4BC59D068F /* Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A09C91D35E07AA4192A517BE98B0501 /* Sync.swift */; };
+               CD51EFD7426F2291BB5CE539312A9D45 /* FREpubParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121EEBF8A77EDC98AEA7B8D83937FA0D /* FREpubParser.swift */; };
+               CD9DD3D0C66EC7909572BA6A53B6CDA8 /* network_reachability_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6EC16E5C569E109186D6849D847BCA48 /* network_reachability_observer.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               CDA8F5CBBFF8A44C3882A01BE169F7CB /* HADiscreteSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842A4F7CFA4D598C49D85FD16BDA6863 /* HADiscreteSlider.swift */; };
+               CDC451CD2717CF465EE0088C5F814225 /* Raleway-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 74A53A7DF4AC60A93A1B0E51F7868167 /* Raleway-Bold.ttf */; };
+               CDF3A8A9ED7AAFB711EB8F0AF31DCB9F /* RLMRealm_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = E48289A832C7D72E990BA4AD40B16CF4 /* RLMRealm_Private.h */; };
+               CE4C5F49DDFEE03B141FDDF9BF9C340D /* minishared.c in Sources */ = {isa = PBXBuildFile; fileRef = B118C522E1E15B2BC5C5465B7A8BF1F8 /* minishared.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               CE7BCC6FB4611BA106CD07FDCDF7F9A4 /* URLSessionDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = E82022B2EF9283B342396105A0A70952 /* URLSessionDispatcher.swift */; };
+               CEC7604E8FF1DCF8EBEBB4EAA4F8DE4B /* Raleway-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CB9A13DABF37C8B1C19A7D255823CAA4 /* Raleway-BoldItalic.ttf */; };
+               CEE84B4BDE2D34D3101CB002E747B6D3 /* FIRMessagingContextManagerService.h in Headers */ = {isa = PBXBuildFile; fileRef = 981A5B8EBD0C0B1A640FF08231E6B5B6 /* FIRMessagingContextManagerService.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               CFE77EB211CD332A018770078DB1FCA0 /* FIRAppInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = C0BAC9516D62801AB56D27E95F7F3D9F /* FIRAppInternal.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               D03AA0169D259F96B95D13552F8701F0 /* RLMSchema.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = CA7CF0757872C51EE58D200C9E26B2FC /* RLMSchema.h */; };
+               D10786C81EC7B694E61F78F87A02F5E2 /* GPBArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 9652C4208B2FE01685B9A06DA74AFC47 /* GPBArray.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               D143A48E638AFDBD788EBFDC833E04BC /* pb_decode.h in Headers */ = {isa = PBXBuildFile; fileRef = 963111857C649092C623F85E0357C427 /* pb_decode.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               D1915B79A59D228E362C21F07F18D75E /* JSQWebViewController-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FD7307754A9D3291AA278FE0AC2A59 /* JSQWebViewController-dummy.m */; };
+               D19792C2E4A14796A74B63E03A109642 /* Lato-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C2A1A3A5F1D030B843050DAD43A6C29B /* Lato-Italic.ttf */; };
+               D1DA4EAD01D3C287A9B6E73CCFFFFB26 /* RLMJSONModels.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B6D9C42264CD0C6BBBAA16AC38FBAB /* RLMJSONModels.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               D21800D777F3316095AA453225ECA626 /* GtalkCore.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = A2349254CD9C9408722F71F6AD779F05 /* GtalkCore.pbobjc.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               D240323F38901478EBC30FDD350A51EA /* RLMResults.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6FE6126BA6653AAFFCEBCBBFFF6468D1 /* RLMResults.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               D2487D8620743518E0D0D2A012B33B15 /* thread_safe_reference.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB849E98A82D4590D3BC88C7262C3BA0 /* thread_safe_reference.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               D274C2230E53457776F9D4EF567A0E65 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               D283C8EEA6FAE9D42AAE690FD8BC78E8 /* shared_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AA32ED0B55D90CA66D7AEF4ACF95BF9 /* shared_realm.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               D291D5CE2E27C37824F88C5BD10C5A32 /* RLMConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 61EE9421F804DDD1641EC8DBF05D66FB /* RLMConstants.h */; };
+               D358E4455630F01D3214D1680ECDD20F /* FolioReaderPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 867D7359F1B0B74054D95F921EE9719D /* FolioReaderPage.swift */; };
+               D3F4FCD6B069F0717222E2EAED5B4FE9 /* GtalkExtensions.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D6B8DEAD7FD8714CF97837608E65A53 /* GtalkExtensions.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               D41B930CB10DFF51D639B68CB5B89863 /* aescrypt.c in Sources */ = {isa = PBXBuildFile; fileRef = 3E49B5DCB4074C65C833C7A0589864F5 /* aescrypt.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               D47B14DC6C9B7712015208A12B200E3C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               D47CB6A901F1D27ED2467977207577DB /* FIRMessagingUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C1DA1A18B834B12A4D0AB03D6158BAA /* FIRMessagingUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               D490E0B6EECF37004DAF724C6770E284 /* FirebaseCore-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C242E0FBAFC647C7747ADD8DB8933A5D /* FirebaseCore-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               D4C3899574E9D5DF5E5DA52310560BCC /* Alamofire-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B1A74A1C0913D69BECC8DD4A1244E42 /* Alamofire-dummy.m */; };
+               D4E749A4E02E3A68B9378913D6B9E85D /* FIRMessagingConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 52994FB9A90DB506C3E6F452B690FB0A /* FIRMessagingConnection.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               D4FA61942176C432BC1D447F3697DEBA /* RLMObjectStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4021B8B58240DC8FF47070F9C04D0B84 /* RLMObjectStore.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               D50366A52954A24888BCD71108E1B8D9 /* FIRMessagingConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AAE711C897BC13E226DA166153F2CC1A /* FIRMessagingConstants.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               D538B82C7E65F7CE463D4C6D7C9AED2E /* GPBUnknownFieldSet_PackagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = E1370966B1A42FC061491DDB4307FD36 /* GPBUnknownFieldSet_PackagePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               D566C419398A952D6E73627A9039F46C /* Results.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78F09E028FCE691991655050A5C01557 /* Results.swift */; };
+               D632058640983C63303E6A4EB12EF66B /* RLMSyncManager.h in Headers */ = {isa = PBXBuildFile; fileRef = BA32998B725316B590F1094FCEDC7A57 /* RLMSyncManager.h */; };
+               D640EF5F80D46D58145341D4707E106E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               D6AC96954C61A1609B0E1F9BF0E55E6E /* Dispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287E8DD99129AC55E28F8626306CAF6D /* Dispatcher.swift */; };
+               D71D4288624808FE415F3F69ABB7E1FA /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A6814BEFF03CE726BEBE631A1023F3D /* Schema.swift */; };
+               D724088A363665433487170B58995146 /* GULNetworkURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 102F3288391D25B4A3D40836298C7188 /* GULNetworkURLSession.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               D734ED9789332A91D374B481156D0DCF /* RLMSyncUtil.mm in Sources */ = {isa = PBXBuildFile; fileRef = 903326FCEFF6EA67E56D2AC3651DA53D /* RLMSyncUtil.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               D79A33A19224F27D9A01AB3FCF59F799 /* aes.h in Headers */ = {isa = PBXBuildFile; fileRef = 75B1B63C2A759AECF71AAE0B4E974E6E /* aes.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               D7B2B69DB1CA1AE2B42BCD295B6D9307 /* GULReachabilityMessageCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 24C6C6D138272A472EEE390DE844A4A8 /* GULReachabilityMessageCode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               D7DC434934FDD5A7EE1D36F9BF5E4AC7 /* UIMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D4B08A2F7A3B6D01CC2E61B621DBDD6 /* UIMenuItem.swift */; };
+               D8163C149C04D8EEAF514FF8D2793A06 /* Api.pbobjc.h in Headers */ = {isa = PBXBuildFile; fileRef = D70294FBB42ACF7801395BF1CDD4A314 /* Api.pbobjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               D891EAFB106227CE19C8445C28509D2A /* GULAppEnvironmentUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 78DDB9DF0FB83A9E64F5C81FD75182BA /* GULAppEnvironmentUtil.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               D8EC5B74B9B5DC842F4179D19E6DE6D4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               D966EB561A0A6A10BAF414F0B952E95E /* MBProgressHUD.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E4511CE1127E4BD217BDCDA408D6412 /* MBProgressHUD.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               D96BF0B05F160D6D21A774579CAA72CB /* GPBRootObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 471BFA1649D5CA3AE3965F662D1DE691 /* GPBRootObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               D9A35C4A7153B8C94CD1DA7727DB566A /* FontBlaster.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DD99B05DEEA0C7E3366E61EF5345DE0 /* FontBlaster.swift */; };
+               D9BFA451C350D31DBD48E928ADCC234F /* Api.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D18DABC2A2093AA4FF972DC35EE001 /* Api.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               DA4875D773DC5D1CF32C56806F67F4FB /* RealmSwift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3692888732B2C535E1B29F8055892B60 /* RealmSwift-dummy.m */; };
+               DA53200FF1D2861EDDAD1BF8C253852F /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A7FC9C89D6223A32109126F8DA0598B /* Optional.swift */; };
+               DA64C1471E8233CCDD71183C58913EC7 /* RLMConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 559E68E63A2FF675E25D5268714196AC /* RLMConstants.m */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               DAC3F0C65BA82526594477C6DDF06745 /* ImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86C2A7E9D5A20FF0BB6F96663456AAB /* ImageProcessor.swift */; };
+               DBD0B446E327AA7EAFA659468ADF856E /* FIRMessagingPersistentSyncMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = F1338B52ECE802CF0757825F0E908475 /* FIRMessagingPersistentSyncMessage.m */; };
+               DBD9E450420ADDB6A7C45F79160D252E /* GULNSData+zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 551760570F42A57DBE4540EE1D5683B3 /* GULNSData+zlib.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               DBE6E2E4D205545E7988CFA5057C31D6 /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E5BA415740BBF222E187A28324E007 /* DispatchQueue+Alamofire.swift */; };
+               DC1E73AC9A04AF660FED52360A603DAB /* RLMSyncUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 390D860093219F412EFBA3562CA65AAE /* RLMSyncUtil.h */; };
+               DE496EDB682975EE3F22A6DD7CC44A10 /* list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8BB3550CF8402CA38A8968A24619F96D /* list.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               DEDDEC971B9B7FB43037345DAFA68D6A /* GULUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C3508A7D1CAF406C759727D4E8B2E3 /* GULUserDefaults.m */; };
+               DEF220928DA65E9CD067D6EF494F787F /* MenuItemKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 85F4B8052CF32CEC52ADCF95F476CFF4 /* MenuItemKit-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               E01DAC754BB51E53AF0EE5403EAA021C /* MatomoTracker-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E24CCC6A27E2E018BF41A40B41A424AA /* MatomoTracker-dummy.m */; };
+               E0F1AF0D2445D107E839AE6F807D6EDF /* JSQWebViewController-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = FBE95E61CEA92DCA57F93939717FD876 /* JSQWebViewController-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               E108C4C6258317867DB7BD4F6FE51EDD /* GULReachabilityChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F1711FCB677F0183149FEE396E5512E /* GULReachabilityChecker.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E1331724A7D92526AA8714A62C353932 /* pb_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = 4F6C31FBB1F555C570AF8298A3C1B669 /* pb_decode.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc -fno-objc-arc"; }; };
+               E190452FD228F46A54C15440A5C12DE1 /* RLMRealm.mm in Sources */ = {isa = PBXBuildFile; fileRef = C1BE0B9E0494E6BB92AF3675D929C48B /* RLMRealm.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               E19B8C59AFACEE8F7AF63071889282EE /* FIRMessagingCheckinService.h in Headers */ = {isa = PBXBuildFile; fileRef = 27B24667E9710453A768281EEBD94D07 /* FIRMessagingCheckinService.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               E202978465C0C0AB115A56D6965C97FA /* RLMMigration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9407CB8E023E9A5CA6CBCCDBFE4232D0 /* RLMMigration_Private.h */; };
+               E238641231BA89B9BAB557ADE131BE5F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */; };
+               E23DC013FF59BE5790CE675EE9D7325A /* GPBExtensionRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = BB067AB097CD608D10755D6B04142C5B /* GPBExtensionRegistry.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               E27007C4C2C6290EB464FBC03B32474B /* SideMenu-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F23F4421C11D6FB3D4D8BE7097CA0541 /* SideMenu-dummy.m */; };
+               E290FCB16106E8AB43988955873CCDA6 /* GoogleUtilities-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B8A71DDD2334CACD70DFCA4D4C49E068 /* GoogleUtilities-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               E2EACE492AB8CF01D7B72C648D7315B0 /* RLMSyncManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 00FD76608109F812520449E0094FE8EB /* RLMSyncManager.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               E383C89D956C843A4426D609BE15537F /* GPBUnknownField_PackagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 142D4F8576AA189AB61C610C1D4C95E9 /* GPBUnknownField_PackagePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               E38B78B6CA84105DF5F8AE572177CD0D /* FolioReaderConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83DAE8A53744DA268CA2420E716533B /* FolioReaderConfig.swift */; };
+               E3AFFD602D64FB33BAE28D0D73B08670 /* hmac.h in Headers */ = {isa = PBXBuildFile; fileRef = 547372D2B91EA430CBD2723BC70C75CF /* hmac.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               E5DA0C270AB43B80C91B45C5DDFAB219 /* MatomoTracker-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 68048DCB850F7DD9097DC6465BE6708C /* MatomoTracker-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               E61F4B0E717EC8F97F20D19FDEC69A90 /* FIRMessagingRegistrar.h in Headers */ = {isa = PBXBuildFile; fileRef = CDA1CF0F7DD23253D2FF8DEBD395CA56 /* FIRMessagingRegistrar.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               E6BE8C1DE57A430B0698E913A0BF93B9 /* object_store.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71184DA28D4C35AD9B685754D612FA27 /* object_store.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               E720FBEA160BA1B68930341F884C5BEE /* RLMRealmConfiguration+Sync.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 0A8D24EBF9A9A7024F37D1F9967496CD /* RLMRealmConfiguration+Sync.h */; };
+               E74E5BDBEE3EA42EBC71661137B43AE2 /* Aliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11464C69FD2AF6A1A4FB214E7DA90908 /* Aliases.swift */; };
+               E7DABCBC335268A1E1578F43EAC5F638 /* FIRMessagingPubSub.m in Sources */ = {isa = PBXBuildFile; fileRef = 080E328D3807B3217C5C18DF8C4A3EEE /* FIRMessagingPubSub.m */; };
+               E86A87B5954FA9592F1678360DD07C50 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C94C12B9600AFD613B21A25CCFE5EB1C /* Realm.framework */; };
+               E947A20F257442D9C5B6A5C587D42DD7 /* Struct.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = B90FE0A11962A6DA9BF00E6837D6D97F /* Struct.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               E9684D581ED545368F64191A32C00051 /* RLMResults.h in Headers */ = {isa = PBXBuildFile; fileRef = BC05BD18E546C120599B8E7ECBDC263A /* RLMResults.h */; };
+               E9A2A0E1987B6254968A155DCE98E201 /* Lora-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AE2928B25227E5D294693BA171652B40 /* Lora-BoldItalic.ttf */; };
+               E9F698ABDB9ED20BE2B51E54AFF20FC9 /* sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = 790BCAA2E56A258EC49B7088FF78B747 /* sha1.c */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; };
+               EA3377CCC9EEE507451F5024222E6377 /* FRResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D67678FC98B9B6656DC8B4F02C5BC5 /* FRResources.swift */; };
+               EA9BE16ECCC17CC9AD847C52D1B64671 /* AnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA3BEC324F13823959A0F7DB8B7CC6C /* AnimatedImageView.swift */; };
+               EACA38D04DFBDC1A7F70704D022BD6A9 /* sha1.h in Headers */ = {isa = PBXBuildFile; fileRef = E5EB8F009021F403129D7F6C9B3B5988 /* sha1.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               EAD92FB7BCD3E150505ED55F0EEE694D /* KeychainItemAccessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27FD5DDB5405AF442504B5810F7F8C09 /* KeychainItemAccessibility.swift */; };
+               ECFDA76C66868DCFD35BECF99B0068B0 /* RLMAccessor.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D39D9675E518588A8F01F21E57FADD /* RLMAccessor.h */; };
+               ED3A0EC643AA230DD128771C2EB56C1E /* FIRMessagingRegistrar.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B85F70E0A1A55686844764DFE3D8D51 /* FIRMessagingRegistrar.m */; };
+               ED5C2982003937B649783B5CF9E97AA4 /* RLMRealm+Sync.h in Headers */ = {isa = PBXBuildFile; fileRef = CFE9AB50F57A50B5C79F92AD4CAC7208 /* RLMRealm+Sync.h */; };
+               ED9967754FA36BC9BE4F248E94F0134B /* OAuthSwiftError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53C308E0551021233B7D7464DFB9CF6F /* OAuthSwiftError.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               EDF41351299C7A250C2E94A35CA06810 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD162B8C0A7547B8B57A0CAA8D18DC3B /* Alamofire.framework */; };
+               EEB4A455551D7B917E1AD4B0A1A964FF /* MBProgressHUD-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DBAF510F0AE8270FAC4746FF6B5DE61 /* MBProgressHUD-dummy.m */; };
+               EF1461221681BCA12A4147900A704727 /* Alamofire-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 27D30FBF66A9DE692B39A8C5F09C89BC /* Alamofire-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               EF2EFAD6BC067A166C63BF83028D294E /* RLMResults.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = BC05BD18E546C120599B8E7ECBDC263A /* RLMResults.h */; };
+               EFFCF738F52D8982F374F0DFF6E31CF5 /* RLMAccessor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 99E4C1135178406ACD7B58D040D578B8 /* RLMAccessor.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               F052772D824CBEAEB28C0BE7F824F366 /* RLMProperty.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 998DC89C6539EB5EE837C792F9D36764 /* RLMProperty.h */; };
+               F09C1A81CC96798491316A5068BD49C5 /* CustomDimension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6B9472B42A9508F283EE68CC1DD4873 /* CustomDimension.swift */; };
+               F0C034AE9C789504898FFDA80C4C99BD /* RLMObjectBase.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = AAE3C1B13216CF41A8A27FC654EAB454 /* RLMObjectBase.h */; };
+               F0E90BDEF941B43FBEB3469F9BD2056F /* OAuthSwiftCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08CFF3FAFCD900564C2FBD18D08004D /* OAuthSwiftCredential.swift */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               F11CF9D9D0DB80F5A8E73049D5E7ADC5 /* GPBDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 09C9AA3CFC585729EE4EDE5BD86C0BE8 /* GPBDictionary.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               F13360DB4A30B9E2C1FEB874F4617D3A /* RLMSyncUser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0CEE0C6AFC79099D001A7A47F9DF3629 /* RLMSyncUser.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               F17B1AEAC49DEE53F3853118676FD7DD /* FIRVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = BEED1B7F7E8BEE0FECD996A3C14F1965 /* FIRVersion.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F1F9E107CF3F2DAEC683203BAC21039F /* GPBUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B2FB4434FC33598F34B81CC4C2BBA4A6 /* GPBUtilities.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               F2A3EDC371140C9BA569CB5F6B39B3DF /* CustomVariable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 498C0C39E4B4B2835310F4E961B0F195 /* CustomVariable.swift */; };
+               F3118053FC15EA6080A8AAA4CFE5E945 /* FIRMessaging.m in Sources */ = {isa = PBXBuildFile; fileRef = E14F77E50CAD9E11C3549CBA1979F1DF /* FIRMessaging.m */; };
+               F31FD3E2A79A4C5136B1F174A5CB7A48 /* RLMMigration_Private.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = 9407CB8E023E9A5CA6CBCCDBFE4232D0 /* RLMMigration_Private.h */; };
+               F3C06FC5B46FDBF8B73BDAAFD440507C /* AlamofireActivityLogger-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 002E42C157E2F539BD6615AA794C87F5 /* AlamofireActivityLogger-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               F43F62FAE6AE801555299C9F462842C4 /* ioapi_mem.h in Headers */ = {isa = PBXBuildFile; fileRef = 882EF9DA2512BFAF5A2DB148555DE38D /* ioapi_mem.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               F4706798CA79248896257375715BC101 /* GPBUtilities_PackagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E347A569BFF34387F66BD101D00CE61 /* GPBUtilities_PackagePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               F48ED2E945391BD5132F07ED5BA09577 /* FIRMessagingDataMessageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D4217B57C3B931D356222A9AD5E757B1 /* FIRMessagingDataMessageManager.m */; };
+               F50701D8682CF06AF6F13E8478406BD6 /* RLMSchema_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E3C47D73094DB09E1D3465A7E11ED30 /* RLMSchema_Private.h */; };
+               F51562DC0E2E13ADBC885981E5F6920F /* FolioReaderUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE1B1CA98EB9A6E3AAA0EB5E93C94CE0 /* FolioReaderUserDefaults.swift */; };
+               F51893EBAA6334E89ECE1A62511CE6D0 /* FirebaseMessaging-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F5D3488048887EE9B928BA33C0AF05B8 /* FirebaseMessaging-dummy.m */; };
+               F51906E25AAFD6E62327B7397574F998 /* RLMObjectBase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 028B9D423259932362B562EE3C671242 /* RLMObjectBase.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               F5338B4BBCB9220717CA96D6417D7BC2 /* GULMutableDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 56308D4A7C4CA17AC58C82130DDC2718 /* GULMutableDictionary.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F53F400DEC55E2AC6DAC1158DF988C26 /* GPBUnknownFieldSet.m in Sources */ = {isa = PBXBuildFile; fileRef = E676199B28E86FF57CC135787DDEFCD4 /* GPBUnknownFieldSet.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+               F600D3CAA39166B8C8B59C55DADF57F1 /* FIRComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 77F70CB6DF1D0FD7256121A7906A25E0 /* FIRComponent.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F64B055D96831705211AAFD9F061C9F1 /* FIROptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 52CCC25F1B42510727D9EDF134CB3132 /* FIROptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               F667936752E8742E44668CC80FC31720 /* ThreadHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34524258955E5E82BF9892F8A42C5CCA /* ThreadHelper.swift */; };
+               F7179C9B307A6E8075252263B2C8F5B3 /* RLMSyncConfiguration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = ABFF357E3BABBE6991EFE7D5FDB24E1A /* RLMSyncConfiguration_Private.h */; };
+               F844B6B5A00BB0C228F4B0AB50D7EA26 /* NSError+FIRMessaging.m in Sources */ = {isa = PBXBuildFile; fileRef = A6C3A06B414DEEC08D882C5AA7655DC3 /* NSError+FIRMessaging.m */; };
+               F8FDCEEAC87A1436D24A40738C945B23 /* RLMObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 101DAC3BA33CBEB734526D4617775FA0 /* RLMObject.h */; };
+               F9DD5C7C2F99839007CBFEDD8561423D /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581B420D10729B6EFD3B20CB081FC1D6 /* Util.swift */; };
+               F9E97C597DF42C783D99D7ADA0B2BB4F /* FIRMessagingConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD3E8F081279D07705FBE2734A30CC /* FIRMessagingConstants.m */; };
+               F9EA61D484CC15FDDAB0D8C0D26D7949 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FDF85370176057B4C572AFDD0DC5AAF /* Result.swift */; };
+               FA5FE8862C97307E7516046C7C926F91 /* ZFDragableModalTransition-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = DAED7224D7EC25824E0386956459E7A9 /* ZFDragableModalTransition-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               FB66877DD3A50E6892F267071FDD497C /* RLMMigration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 597E534C745A644235BCAB1CB8A229CA /* RLMMigration.mm */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               FC2133A0E00C03A5358618F49F4C5469 /* GPBWireFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = E49A610A798652E72449FC283CB8E58F /* GPBWireFormat.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               FD32EC5A94EE80217BE8F428956897D7 /* Internals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 751FCB64C83107FC0D3415A048D97D1A /* Internals.swift */; };
+               FD38D7B6ED90C1C09EFD48DAAFCDE5D2 /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42071D61BA4308E0EBA1DB720C8DA29A /* external_commit_helper.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               FDD30714805DCA7A56F6D140C73C119D /* FIRAppAssociationRegistration.m in Sources */ = {isa = PBXBuildFile; fileRef = BF4FAD5E6B95D0DCD4CF2873B7A5F657 /* FIRAppAssociationRegistration.m */; };
+               FDD804AF0A8DCB4D4FB54C527B1222A0 /* RLMAccessor.h in Copy . Private Headers */ = {isa = PBXBuildFile; fileRef = D4D39D9675E518588A8F01F21E57FADD /* RLMAccessor.h */; };
+               FE8499CC84B668222CEFB2B0EB2173C1 /* schema.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3D679A454CA39A6DB40C5D58EF59566 /* schema.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               FEB7295281E330F595EFC112E8CBAF4E /* FIRMessagingPendingTopicsList.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BA66D357F5E6460D64AEC0F89C5ABBB /* FIRMessagingPendingTopicsList.h */; settings = {ATTRIBUTES = (Project, ); }; };
+               FF02FC2CC4A9D5A7ACCF8B16C8310E87 /* RLMRealm.h in Copy . Public Headers */ = {isa = PBXBuildFile; fileRef = 6D6A3C039A370F8C8DA839EF41DC6D43 /* RLMRealm.h */; };
+               FF33EDC46088ED783E2B1BA415AC84A4 /* collection_change_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DE0FC609758763CD20EA7151D99DF94 /* collection_change_builder.cpp */; settings = {COMPILER_FLAGS = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"3.1.1\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"; }; };
+               FF9C7BC64DB23D2CED48197DE67F0335 /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7E7BD134D837DB6672EBC1FDA1F10DD /* MultipartFormData.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+               02316B1E1E26FD5C54880A8C8B0FF05D /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = DEB0D4FCD7FECFEDBCB372932FCB07CB;
+                       remoteInfo = MZDownloadManager;
+               };
+               05E26F3DCAECB2BD02C47C15ECC3E1FC /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 2B2CE33B441C5D4BD9A5A04D3087E5C8;
+                       remoteInfo = GoogleUtilities;
+               };
+               0B89BA46392931BB20153DE364AE349D /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 87C645C5CBE6078EBEE1659ABEEF780D;
+                       remoteInfo = JSQWebViewController;
+               };
+               1D71CB61901BB7470E1880F4F547C2DC /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 13218BC4FEB9FA3C157F802AB364DCBB;
+                       remoteInfo = Realm;
+               };
+               22D890E341A029066109E3A4DA62C0A9 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = A15CD3220CB655EF65975142EDBD8C6E;
+                       remoteInfo = Kingfisher;
+               };
+               2476160EA5824AAAFA83863389371F85 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 76948DED43ECFC74BB41AA7F11DA2200;
+                       remoteInfo = RealmSwift;
+               };
+               311F8D431BE2BC22FB49F1A2BAFC0C39 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = E6D2D23AF894E245CA516BC67EBF8703;
+                       remoteInfo = "Toast-Swift";
+               };
+               35A78108E1664F82A14159857CFF0FCA /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 4338000AC6DBAF783F338F055DDE55C6;
+                       remoteInfo = FirebaseCore;
+               };
+               42DAA0DF2C3A059786C66B4FD98F10DE /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 673DBE5653C51962C057371B5CA8A2B4;
+                       remoteInfo = MenuItemKit;
+               };
+               4F07FC1E60C35CAFAF5F6BE7A3414EAF /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 76948DED43ECFC74BB41AA7F11DA2200;
+                       remoteInfo = RealmSwift;
+               };
+               4F6AC847EF22FC528E647D4C456AA3E1 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = E76458C58C9140B6A16D60547E68E80C;
+                       remoteInfo = Alamofire;
+               };
+               53BF719B7B3E5EE951AB21D67921A2C5 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = A938E07DF5A500E5C4FA1C9FD1886EB2;
+                       remoteInfo = MatomoTracker;
+               };
+               5BD2C6D6D4FA5ACC09607C98CE853C43 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = CE310247D34682517FA45BE1C20210CF;
+                       remoteInfo = SSZipArchive;
+               };
+               6FC94F22C84D5CC00531C6AD243FA2A0 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = A0243AC4D5FF53ED067BF1609A6898A0;
+                       remoteInfo = FontBlaster;
+               };
+               727B317CFE7E8D41021C5F5A9E47190D /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 87C645C5CBE6078EBEE1659ABEEF780D;
+                       remoteInfo = JSQWebViewController;
+               };
+               7B48C4FE112E5187B0430861D683250E /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 13218BC4FEB9FA3C157F802AB364DCBB;
+                       remoteInfo = Realm;
+               };
+               812D50BE3323CD5C876E5FA6665E9005 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = D449FC7BC81F775BC2E4BB53D77B64D6;
+                       remoteInfo = ZFDragableModalTransition;
+               };
+               8FF4D201921D8BB020C76ED5C65EB196 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 4338000AC6DBAF783F338F055DDE55C6;
+                       remoteInfo = FirebaseCore;
+               };
+               91CB6F1E706029F6E6FEA6183E1C4550 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 8CD09A1EC484C4C08A7FBF85CA871914;
+                       remoteInfo = SwiftKeychainWrapper;
+               };
+               94AB6C677FF17F93C4CD3C8DA4F3D3E1 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = AD408BAB384B6DDA88A50A16FB158B66;
+                       remoteInfo = MBProgressHUD;
+               };
+               B3D4DAC5C40401E39DB4AC6895B267ED /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = E76458C58C9140B6A16D60547E68E80C;
+                       remoteInfo = Alamofire;
+               };
+               B62A90D776EFF517673A4D36F5B715EA /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 939DA4FAAB94FC5EE0B3FCF4BAA2C49E;
+                       remoteInfo = AEXML;
+               };
+               BF529E2F449B4C20A7581E433B6448CD /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 8364BB258018143B83798DE31C13546D;
+                       remoteInfo = nanopb;
+               };
+               C09F454687D19EF80188E947AA045D53 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 1EB42C6E589197DB247315875A9AE141;
+                       remoteInfo = FolioReaderKit;
+               };
+               C2506ECA219D0DA3E086096D720CAC9B /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = CE310247D34682517FA45BE1C20210CF;
+                       remoteInfo = SSZipArchive;
+               };
+               C26CF6553971B4CD56C7A64AE9D460D6 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 13218BC4FEB9FA3C157F802AB364DCBB;
+                       remoteInfo = Realm;
+               };
+               C8D096F97B29ABA3CEDE27707C64A8C2 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = EF83FCC10E7453ACCB801B656501E2F6;
+                       remoteInfo = OAuthSwift;
+               };
+               C9CEA78207D0D29B7DD3FA50EF6E85E2 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 71E4E70C7BAC6389783D3A3C8EB1F910;
+                       remoteInfo = Protobuf;
+               };
+               D4C953138F7589A9A33210FD8AB5A2C9 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 2B2CE33B441C5D4BD9A5A04D3087E5C8;
+                       remoteInfo = GoogleUtilities;
+               };
+               D4F3648965C515FF27BEBF3F3B70E429 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = D449FC7BC81F775BC2E4BB53D77B64D6;
+                       remoteInfo = ZFDragableModalTransition;
+               };
+               D936712DA2BB3899876B825056FE1F0E /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 673DBE5653C51962C057371B5CA8A2B4;
+                       remoteInfo = MenuItemKit;
+               };
+               DD198968AE4E005ED8EA9E9601356F3B /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = C8C2C570FCCD7CC9014E895FBBEEF1FC;
+                       remoteInfo = SideMenu;
+               };
+               E23933E264AC64CFAB19F86C52879EE3 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 2B2CE33B441C5D4BD9A5A04D3087E5C8;
+                       remoteInfo = GoogleUtilities;
+               };
+               E3928A04DE3CDEE92EAA19264BE55509 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 71E4E70C7BAC6389783D3A3C8EB1F910;
+                       remoteInfo = Protobuf;
+               };
+               EC6740D2628F020744661F197771AF80 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 0DD17D85AC06742489BCAFB83E51FDB0;
+                       remoteInfo = FirebaseMessaging;
+               };
+               F0CC6EFFDE3094DC5D73F1DA00D20FE1 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = A0243AC4D5FF53ED067BF1609A6898A0;
+                       remoteInfo = FontBlaster;
+               };
+               F9B3EE8B7D9C78A8BD9C8D385A77BC2A /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 939DA4FAAB94FC5EE0B3FCF4BAA2C49E;
+                       remoteInfo = AEXML;
+               };
+               FB569D0C72CF34C76C7A57CDF99D4C5F /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 8D956160DB7545834B961CB5E8DBBD1B;
+                       remoteInfo = AlamofireActivityLogger;
+               };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+               2E644E33F37AD2BCDAAF587E57D43722 /* Copy . Private Headers */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = "$(PRIVATE_HEADERS_FOLDER_PATH)/.";
+                       dstSubfolderSpec = 16;
+                       files = (
+                               FDD804AF0A8DCB4D4FB54C527B1222A0 /* RLMAccessor.h in Copy . Private Headers */,
+                               C27D8715EE3472F13F5D9C02B083EFF6 /* RLMArray_Private.h in Copy . Private Headers */,
+                               BEC19DF99BBD1445348712D8E45B8618 /* RLMCollection_Private.h in Copy . Private Headers */,
+                               0CCFE5CDD79323DE83640752C7962817 /* RLMListBase.h in Copy . Private Headers */,
+                               F31FD3E2A79A4C5136B1F174A5CB7A48 /* RLMMigration_Private.h in Copy . Private Headers */,
+                               C40D0F4780A38FCD6991E37D72982D6B /* RLMObject_Private.h in Copy . Private Headers */,
+                               17CFC5DE8EA6C5ADA822748DA2B65969 /* RLMObjectBase_Private.h in Copy . Private Headers */,
+                               67B266C0ECD04AF124C2A47B05E01978 /* RLMObjectSchema_Private.h in Copy . Private Headers */,
+                               A3A4F4A1704A233C74813BC038C89AAB /* RLMObjectStore.h in Copy . Private Headers */,
+                               42D98477CB528F62B9B18917B609A720 /* RLMOptionalBase.h in Copy . Private Headers */,
+                               B9FD06D9FD2F42D434B290CE9203A006 /* RLMProperty_Private.h in Copy . Private Headers */,
+                               70E1335E582FB824E10AAC28B7C70C5C /* RLMRealm_Private.h in Copy . Private Headers */,
+                               3FD076818FABD54684FBD4BA813ADD51 /* RLMRealmConfiguration_Private.h in Copy . Private Headers */,
+                               2246385DA62FFD8D78968FA2CD4FB056 /* RLMResults_Private.h in Copy . Private Headers */,
+                               C8603807EDCC2B3CD7AE6556E7C96017 /* RLMSchema_Private.h in Copy . Private Headers */,
+                               28F39A8DB19B7AD0D4C6B21D1B4EE037 /* RLMSyncConfiguration_Private.h in Copy . Private Headers */,
+                               131AE323F3D51BA77288DF6E07362B5B /* RLMSyncManager_Private.h in Copy . Private Headers */,
+                               31E46BAA929925BA7B082ADC8151EFCE /* RLMSyncUtil_Private.h in Copy . Private Headers */,
+                       );
+                       name = "Copy . Private Headers";
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               50CA721690027D834A9C9705A285FB24 /* Copy . Public Headers */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = "$(PUBLIC_HEADERS_FOLDER_PATH)/.";
+                       dstSubfolderSpec = 16;
+                       files = (
+                               577709021CF910800C4DD1402A862998 /* NSError+RLMSync.h in Copy . Public Headers */,
+                               631598B30FF0A9F6FFFBF73C921D8DE0 /* Realm.h in Copy . Public Headers */,
+                               435678410A8C2F628EC64B22316A27CA /* RLMArray.h in Copy . Public Headers */,
+                               2F0489083E3FA29C9358EB2E926A0CBA /* RLMCollection.h in Copy . Public Headers */,
+                               BC64C18FC824FEBD2E3D105B134601ED /* RLMConstants.h in Copy . Public Headers */,
+                               7E2E5F263C3FF1944756B17250BCFE0E /* RLMListBase.h in Copy . Public Headers */,
+                               A688C76313AAB612FE7F283088ADDD7A /* RLMMigration.h in Copy . Public Headers */,
+                               B03A8EB3C2C4DDF017566DC0BAF341AE /* RLMObject.h in Copy . Public Headers */,
+                               F0C034AE9C789504898FFDA80C4C99BD /* RLMObjectBase.h in Copy . Public Headers */,
+                               2B84E33935819EE11FB321CBE6926130 /* RLMObjectBase_Dynamic.h in Copy . Public Headers */,
+                               7F4789035CE0FD046F49F8C8B0B03075 /* RLMObjectSchema.h in Copy . Public Headers */,
+                               AA6A216DB6FF225A0EBDB574A195E6B7 /* RLMOptionalBase.h in Copy . Public Headers */,
+                               9496D8AAC021E30241C522A57E5D7C58 /* RLMPlatform.h in Copy . Public Headers */,
+                               F052772D824CBEAEB28C0BE7F824F366 /* RLMProperty.h in Copy . Public Headers */,
+                               A7E31F629D025F3099B8FC93D241170E /* RLMRealm+Sync.h in Copy . Public Headers */,
+                               FF02FC2CC4A9D5A7ACCF8B16C8310E87 /* RLMRealm.h in Copy . Public Headers */,
+                               460853DB66417DD46127928A9C654AE3 /* RLMRealm_Dynamic.h in Copy . Public Headers */,
+                               E720FBEA160BA1B68930341F884C5BEE /* RLMRealmConfiguration+Sync.h in Copy . Public Headers */,
+                               08D673E9336055FF08C6F1BB6B8719EB /* RLMRealmConfiguration.h in Copy . Public Headers */,
+                               EF2EFAD6BC067A166C63BF83028D294E /* RLMResults.h in Copy . Public Headers */,
+                               D03AA0169D259F96B95D13552F8701F0 /* RLMSchema.h in Copy . Public Headers */,
+                               19EFB114884F3F91E618EBAF871D978E /* RLMSyncConfiguration.h in Copy . Public Headers */,
+                               180ED06F9C965B69A63B39639903903D /* RLMSyncCredentials.h in Copy . Public Headers */,
+                               648B5903884066417E7C3B899A1C4034 /* RLMSyncManager.h in Copy . Public Headers */,
+                               957C09275A801195985E591D420848F1 /* RLMSyncPermission.h in Copy . Public Headers */,
+                               AE734ACC00A8B3AAFD766ED6EA46E8F3 /* RLMSyncSession.h in Copy . Public Headers */,
+                               7EECB9CF279E71AA725186BAE7CB7E8C /* RLMSyncUser.h in Copy . Public Headers */,
+                               2931E0DFBDA4552EE9982EF8A836701C /* RLMSyncUtil.h in Copy . Public Headers */,
+                               414C72E011AB7CD7FEE2FF1941C9A0FE /* RLMThreadSafeReference.h in Copy . Public Headers */,
+                       );
+                       name = "Copy . Public Headers";
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+               002E42C157E2F539BD6615AA794C87F5 /* AlamofireActivityLogger-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "AlamofireActivityLogger-umbrella.h"; sourceTree = "<group>"; };
+               006988DA32BDAA5630C125654D9C44A9 /* Alamofire-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-prefix.pch"; sourceTree = "<group>"; };
+               00D3597C80CBA96B3BFF1BA9CA8BBE7B /* Migration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Migration.swift; path = RealmSwift/Migration.swift; sourceTree = "<group>"; };
+               00FD76608109F812520449E0094FE8EB /* RLMSyncManager.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncManager.mm; path = Realm/RLMSyncManager.mm; sourceTree = "<group>"; };
+               014EFC1AE04ADF977639E4046BE9008C /* String+MD5.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "String+MD5.swift"; path = "Sources/String+MD5.swift"; sourceTree = "<group>"; };
+               01C14036937A2DCAE7AF38A487E9A5AD /* SessionManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionManager.swift; path = Source/SessionManager.swift; sourceTree = "<group>"; };
+               022810A628AB345511375F43564EA066 /* aestab.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = aestab.h; path = SSZipArchive/minizip/aes/aestab.h; sourceTree = "<group>"; };
+               0260176D84540F033791D6C38116526E /* Raleway-Regular.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Raleway-Regular.ttf"; path = "Source/Resources/Fonts/Raleway/Raleway-Regular.ttf"; sourceTree = "<group>"; };
+               028B9D423259932362B562EE3C671242 /* RLMObjectBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectBase.mm; path = Realm/RLMObjectBase.mm; sourceTree = "<group>"; };
+               02AC354FE6FEB3854FB790A1543E37D7 /* FolioReaderCenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderCenter.swift; path = Source/FolioReaderCenter.swift; sourceTree = "<group>"; };
+               02FC51FFCD3F7704A972B5801BCF548E /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               03DF232DE3B9FC2B3ABA654016B16028 /* FirebaseCore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseCore.xcconfig; sourceTree = "<group>"; };
+               0414DBE31282AC394C122F66BCD5F0B9 /* GULOriginalIMPConvenienceMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULOriginalIMPConvenienceMacros.h; path = GoogleUtilities/MethodSwizzler/Private/GULOriginalIMPConvenienceMacros.h; sourceTree = "<group>"; };
+               05984A0DCE357F35A45231C58BADFD71 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
+               05F6A7144D612A1B7814FB4DB19F20D0 /* Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Alamofire.swift; path = Source/Alamofire.swift; sourceTree = "<group>"; };
+               064177D2B62F30947132CA37F78CABBC /* Application.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Application.swift; path = MatomoTracker/Application.swift; sourceTree = "<group>"; };
+               06967CA663F790072A7AFB661576619B /* JSQWebViewController.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = JSQWebViewController.xcconfig; sourceTree = "<group>"; };
+               06FC57F395225CF90BC8F25F5C08777D /* SortDescriptor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SortDescriptor.swift; path = RealmSwift/SortDescriptor.swift; sourceTree = "<group>"; };
+               07900388E4AD52051EE289F5ED2EEDF4 /* FIRMessagingDelayedMessageQueue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingDelayedMessageQueue.m; path = Firebase/Messaging/FIRMessagingDelayedMessageQueue.m; sourceTree = "<group>"; };
+               0792F8241E343E3F566D735EF2DEF482 /* Placeholder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Placeholder.swift; path = Sources/Placeholder.swift; sourceTree = "<group>"; };
+               07F73D4373DB447405CB3388AB42D8A0 /* Error.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Error.swift; path = Sources/Error.swift; sourceTree = "<group>"; };
+               07F8C4C696AED2F05397BDC937C97823 /* RLMQueryUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMQueryUtil.mm; path = Realm/RLMQueryUtil.mm; sourceTree = "<group>"; };
+               080E328D3807B3217C5C18DF8C4A3EEE /* FIRMessagingPubSub.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingPubSub.m; path = Firebase/Messaging/FIRMessagingPubSub.m; sourceTree = "<group>"; };
+               0814C5C3D6056C7C77038075C0FAA4DF /* FRSmils.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FRSmils.swift; path = Source/EPUBCore/FRSmils.swift; sourceTree = "<group>"; };
+               0829B70301B60B62E908B075914CABF5 /* Wrappers.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Wrappers.pbobjc.h; path = objectivec/google/protobuf/Wrappers.pbobjc.h; sourceTree = "<group>"; };
+               0858841A9D93BD6323F873933150A081 /* RLMManagedArray.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMManagedArray.mm; path = Realm/RLMManagedArray.mm; sourceTree = "<group>"; };
+               08B415F2B9935EB42007587E2BF3BFF3 /* GPBCodedOutputStream.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBCodedOutputStream.m; path = objectivec/GPBCodedOutputStream.m; sourceTree = "<group>"; };
+               08E6C7A1FFCA6B21341142FFDC8761EE /* OAuthSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = OAuthSwift.framework; path = OAuthSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               08FD7307754A9D3291AA278FE0AC2A59 /* JSQWebViewController-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "JSQWebViewController-dummy.m"; sourceTree = "<group>"; };
+               09BBC999192646D11C33C3FD05C523F8 /* PageViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PageViewController.swift; path = Source/PageViewController.swift; sourceTree = "<group>"; };
+               09C9AA3CFC585729EE4EDE5BD86C0BE8 /* GPBDictionary.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBDictionary.m; path = objectivec/GPBDictionary.m; sourceTree = "<group>"; };
+               0A71836910F0F4E2CBA0CD1D3D5940EF /* FIRMessagingVersionUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingVersionUtilities.m; path = Firebase/Messaging/FIRMessagingVersionUtilities.m; sourceTree = "<group>"; };
+               0A8D24EBF9A9A7024F37D1F9967496CD /* RLMRealmConfiguration+Sync.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "RLMRealmConfiguration+Sync.h"; path = "include/RLMRealmConfiguration+Sync.h"; sourceTree = "<group>"; };
+               0AD231A8FF3DB1782497120FA6AEBB1F /* OAuthSwift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuthSwift.swift; path = Sources/OAuthSwift.swift; sourceTree = "<group>"; };
+               0B31CAB3A5CA2AA601A8A6B0DBA8C713 /* system_configuration.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = system_configuration.cpp; path = Realm/ObjectStore/src/sync/impl/apple/system_configuration.cpp; sourceTree = "<group>"; };
+               0B5C11A1670ED8BEE826D1FADC42D7C7 /* Options.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Options.swift; path = Sources/Options.swift; sourceTree = "<group>"; };
+               0CEE0C6AFC79099D001A7A47F9DF3629 /* RLMSyncUser.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncUser.mm; path = Realm/RLMSyncUser.mm; sourceTree = "<group>"; };
+               0DBAF510F0AE8270FAC4746FF6B5DE61 /* MBProgressHUD-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MBProgressHUD-dummy.m"; sourceTree = "<group>"; };
+               0DBAFFED7A2B4724B0A96F2E74FDB1E6 /* Duration.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Duration.pbobjc.h; path = objectivec/google/protobuf/Duration.pbobjc.h; sourceTree = "<group>"; };
+               0DD1F2E48435B86FC4B95841449B2727 /* HMAC.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HMAC.swift; path = Sources/HMAC.swift; sourceTree = "<group>"; };
+               0E996F2DB7B3E51D9BD5BD8819F76842 /* index_set.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = index_set.cpp; path = Realm/ObjectStore/src/index_set.cpp; sourceTree = "<group>"; };
+               0ECF0996C30AA6093268F60DE4366E45 /* RLMSwiftSupport.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSwiftSupport.m; path = Realm/RLMSwiftSupport.m; sourceTree = "<group>"; };
+               0F03D2A8CC72E729DA124F2004020658 /* FRSmilElement.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FRSmilElement.swift; path = Source/EPUBCore/FRSmilElement.swift; sourceTree = "<group>"; };
+               101DAC3BA33CBEB734526D4617775FA0 /* RLMObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObject.h; path = include/RLMObject.h; sourceTree = "<group>"; };
+               102F3288391D25B4A3D40836298C7188 /* GULNetworkURLSession.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkURLSession.h; path = GoogleUtilities/Network/Private/GULNetworkURLSession.h; sourceTree = "<group>"; };
+               10EEF27CE0A2D40194B5166A61763970 /* FIRMessagingPacketQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingPacketQueue.h; path = Firebase/Messaging/FIRMessagingPacketQueue.h; sourceTree = "<group>"; };
+               111BC4AB5DFDA30B3AF9451ACE9A8D00 /* RLMArray.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMArray.mm; path = Realm/RLMArray.mm; sourceTree = "<group>"; };
+               1139D92D5DD588DED3347176B562CE39 /* FIRLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLogger.h; path = Firebase/Core/Private/FIRLogger.h; sourceTree = "<group>"; };
+               11464C69FD2AF6A1A4FB214E7DA90908 /* Aliases.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Aliases.swift; path = RealmSwift/Aliases.swift; sourceTree = "<group>"; };
+               11A9CEA39CE194E1A1C87939E94FA086 /* RLMRealmConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealmConfiguration.h; path = include/RLMRealmConfiguration.h; sourceTree = "<group>"; };
+               121EEBF8A77EDC98AEA7B8D83937FA0D /* FREpubParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FREpubParser.swift; path = Source/EPUBCore/FREpubParser.swift; sourceTree = "<group>"; };
+               126192276E659647B019680598D32B29 /* FontBlaster-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FontBlaster-umbrella.h"; sourceTree = "<group>"; };
+               12CDC0657349E8207CA037288EA17AFE /* Protobuf-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Protobuf-prefix.pch"; sourceTree = "<group>"; };
+               133EC299B9E30E151C3D7E0091490188 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               1355BFFA13307D543E931E754A0C400B /* GoogleUtilities.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = GoogleUtilities.modulemap; sourceTree = "<group>"; };
+               135AAE7A08E11EC8D750570317E36028 /* RLMArray_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMArray_Private.h; path = include/RLMArray_Private.h; sourceTree = "<group>"; };
+               1408A9D036A6990304A3BEF6A655CBDA /* GULLoggerLevel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULLoggerLevel.h; path = GoogleUtilities/Logger/Public/GULLoggerLevel.h; sourceTree = "<group>"; };
+               142D4F8576AA189AB61C610C1D4C95E9 /* GPBUnknownField_PackagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBUnknownField_PackagePrivate.h; path = objectivec/GPBUnknownField_PackagePrivate.h; sourceTree = "<group>"; };
+               144443F4C5352AA38AC82F621455EA3C /* Realm.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Realm.h; path = include/Realm.h; sourceTree = "<group>"; };
+               145E95AE5F85319E80B8B12CC8A05570 /* GULNetwork.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetwork.h; path = GoogleUtilities/Network/Private/GULNetwork.h; sourceTree = "<group>"; };
+               147E69F1ED7DFD212ABB16B8DF662DA5 /* GULNetworkURLSession.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetworkURLSession.m; path = GoogleUtilities/Network/GULNetworkURLSession.m; sourceTree = "<group>"; };
+               1482EE96CD4BCC0E2EDBBA3ED70FDCBD /* RLMSyncUser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUser.h; path = include/RLMSyncUser.h; sourceTree = "<group>"; };
+               14D40CBD84A69593DDFFE433389950C2 /* MemoryQueue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MemoryQueue.swift; path = MatomoTracker/MemoryQueue.swift; sourceTree = "<group>"; };
+               154EB52C148A3AC43AAFFEBEB198039F /* SourceContext.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SourceContext.pbobjc.m; path = objectivec/google/protobuf/SourceContext.pbobjc.m; sourceTree = "<group>"; };
+               1632A30520BF40ED6C45A578D6411917 /* GPBExtensionInternals.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBExtensionInternals.h; path = objectivec/GPBExtensionInternals.h; sourceTree = "<group>"; };
+               163DF539BC15EE2749223B588A4D9978 /* brg_types.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = brg_types.h; path = SSZipArchive/minizip/aes/brg_types.h; sourceTree = "<group>"; };
+               164FD2FA81136FE310077B45A814E345 /* Protobuf-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Protobuf-dummy.m"; sourceTree = "<group>"; };
+               165E1BA5EC237CD62B66F0F55037F269 /* SwiftKeychainWrapper-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftKeychainWrapper-dummy.m"; sourceTree = "<group>"; };
+               16C3508A7D1CAF406C759727D4E8B2E3 /* GULUserDefaults.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULUserDefaults.m; path = GoogleUtilities/UserDefaults/GULUserDefaults.m; sourceTree = "<group>"; };
+               16E69380BDC8BE32A32CE50AC558292F /* RLMProperty_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMProperty_Private.h; path = include/RLMProperty_Private.h; sourceTree = "<group>"; };
+               172A7ABC432D07D75B2B5BA8C0BA4592 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
+               17476BC599C0EE4947789CF3EC5E1024 /* AlamofireActivityLogger.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = AlamofireActivityLogger.xcconfig; sourceTree = "<group>"; };
+               177C7DCAC0D7CB576C44F872CFF50370 /* primitive_list_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = primitive_list_notifier.cpp; path = Realm/ObjectStore/src/impl/primitive_list_notifier.cpp; sourceTree = "<group>"; };
+               1798AF9275969301B5346C1DF5410CFE /* OAuthSwiftResponse.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuthSwiftResponse.swift; path = Sources/OAuthSwiftResponse.swift; sourceTree = "<group>"; };
+               17C756083B788404F7E5739A71EF0218 /* sync_manager.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_manager.cpp; path = Realm/ObjectStore/src/sync/sync_manager.cpp; sourceTree = "<group>"; };
+               17E87A50423EE2843AB0EB08528F52EC /* AlamofireActivityLogger.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = AlamofireActivityLogger.framework; path = AlamofireActivityLogger.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               17F3C6DB088252560431D0D373867A04 /* GULSwizzler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULSwizzler.m; path = GoogleUtilities/MethodSwizzler/GULSwizzler.m; sourceTree = "<group>"; };
+               181C2DE81F42B9BF86D03575F19C0494 /* sync_metadata.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_metadata.cpp; path = Realm/ObjectStore/src/sync/impl/sync_metadata.cpp; sourceTree = "<group>"; };
+               184161E1B57664D8505D6B2744800293 /* MatomoTracker.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MatomoTracker.swift; path = MatomoTracker/MatomoTracker.swift; sourceTree = "<group>"; };
+               18DD8C06F1B3E9FD17466D5018391BF9 /* RLMThreadSafeReference.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMThreadSafeReference.mm; path = Realm/RLMThreadSafeReference.mm; sourceTree = "<group>"; };
+               19103A10E8E96743A56E682ED1DAC70D /* RLMRealmConfiguration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealmConfiguration_Private.h; path = include/RLMRealmConfiguration_Private.h; sourceTree = "<group>"; };
+               191B12415ABB5A507EEBB049752E30AD /* FontBlaster.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FontBlaster.xcconfig; sourceTree = "<group>"; };
+               196A114E523544DED122A0BBBD405C1F /* RLMThreadSafeReference.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMThreadSafeReference.h; path = include/RLMThreadSafeReference.h; sourceTree = "<group>"; };
+               1A1472ABD1E4C2EDD04CA4BEC0521713 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
+               1A4A319D825BD520943E481653FBDB97 /* sync_file.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_file.cpp; path = Realm/ObjectStore/src/sync/impl/sync_file.cpp; sourceTree = "<group>"; };
+               1A6B7317D2AA66425340B4A6E3A73F5A /* FRTocReference.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FRTocReference.swift; path = Source/EPUBCore/FRTocReference.swift; sourceTree = "<group>"; };
+               1AA32ED0B55D90CA66D7AEF4ACF95BF9 /* shared_realm.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = shared_realm.cpp; path = Realm/ObjectStore/src/shared_realm.cpp; sourceTree = "<group>"; };
+               1AEFAE3B5379808E09A395D84018890C /* MenuItemKit.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MenuItemKit.xcconfig; sourceTree = "<group>"; };
+               1B1EC0DCBE67D03787183294CA3BCA28 /* MZDownloadManager-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MZDownloadManager-prefix.pch"; sourceTree = "<group>"; };
+               1B850B52C6C12544F2E2F5CD3FE6B3E2 /* GULMutableDictionary.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULMutableDictionary.m; path = GoogleUtilities/Network/GULMutableDictionary.m; sourceTree = "<group>"; };
+               1B85F70E0A1A55686844764DFE3D8D51 /* FIRMessagingRegistrar.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingRegistrar.m; path = Firebase/Messaging/FIRMessagingRegistrar.m; sourceTree = "<group>"; };
+               1CB73575EACC7B667464A81741CD40B9 /* GPBDescriptor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBDescriptor.h; path = objectivec/GPBDescriptor.h; sourceTree = "<group>"; };
+               1D6B8DEAD7FD8714CF97837608E65A53 /* GtalkExtensions.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GtalkExtensions.pbobjc.m; path = Firebase/Messaging/Protos/GtalkExtensions.pbobjc.m; sourceTree = "<group>"; };
+               1DC773D985A5ABEEB648555055E8329E /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               1DE0FC609758763CD20EA7151D99DF94 /* collection_change_builder.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_change_builder.cpp; path = Realm/ObjectStore/src/impl/collection_change_builder.cpp; sourceTree = "<group>"; };
+               1E36AA751EC8381B882AC6FDA504FBBB /* RLMSyncSessionRefreshHandle.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncSessionRefreshHandle.mm; path = Realm/RLMSyncSessionRefreshHandle.mm; sourceTree = "<group>"; };
+               1E620905A80A469191BFE9DB5F9240E1 /* GPBRootObject_PackagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBRootObject_PackagePrivate.h; path = objectivec/GPBRootObject_PackagePrivate.h; sourceTree = "<group>"; };
+               1E62823E12CC340D00EEB586EEF177F9 /* object_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_notifier.cpp; path = Realm/ObjectStore/src/impl/object_notifier.cpp; sourceTree = "<group>"; };
+               1F1711FCB677F0183149FEE396E5512E /* GULReachabilityChecker.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULReachabilityChecker.h; path = GoogleUtilities/Reachability/Private/GULReachabilityChecker.h; sourceTree = "<group>"; };
+               1F3368CC7921022310DA5AC855FBF373 /* GPBWellKnownTypes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBWellKnownTypes.h; path = objectivec/GPBWellKnownTypes.h; sourceTree = "<group>"; };
+               1F9C0BE9D000422B577A97E3A9C3994C /* Fabric.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Fabric.h; path = iOS/Fabric.framework/Headers/Fabric.h; sourceTree = "<group>"; };
+               1FB2A0E8B68A46C4DC9F646C4806F1CF /* FRMetadata.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FRMetadata.swift; path = Source/EPUBCore/FRMetadata.swift; sourceTree = "<group>"; };
+               1FD1E1F5EF445099E14F78112E4DE5BD /* minishared.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = minishared.h; path = SSZipArchive/minizip/minishared.h; sourceTree = "<group>"; };
+               1FDD2185138D081B59430FFD466ADEA1 /* Document.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Document.swift; path = Sources/Document.swift; sourceTree = "<group>"; };
+               20CB9934A5EA1F3987C1D0B98512E6C2 /* FirebaseMessaging-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FirebaseMessaging-umbrella.h"; sourceTree = "<group>"; };
+               210CA0599041DC7C7A13E77AD4BAA706 /* SMSegmentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SMSegmentView.swift; path = Vendor/SMSegmentView/SMSegmentView.swift; sourceTree = "<group>"; };
+               21214BE8206ACC637CA1C38C1FD26273 /* RealmSwift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RealmSwift-umbrella.h"; sourceTree = "<group>"; };
+               214BEC95D55426D9CEF7A8F37517E601 /* FirebaseInstanceID.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseInstanceID.framework; path = Frameworks/FirebaseInstanceID.framework; sourceTree = "<group>"; };
+               21537D1611785AD9AF8C2D2C8014E8C7 /* FontBlaster.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = FontBlaster.modulemap; sourceTree = "<group>"; };
+               2189955C32F1B821C22A320B350D4E9D /* RLMCollection_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMCollection_Private.h; path = include/RLMCollection_Private.h; sourceTree = "<group>"; };
+               2198B0E49174CFB22F300B5737A7ACD0 /* UIButton+Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIButton+Kingfisher.swift"; path = "Sources/UIButton+Kingfisher.swift"; sourceTree = "<group>"; };
+               21D67678FC98B9B6656DC8B4F02C5BC5 /* FRResources.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FRResources.swift; path = Source/EPUBCore/FRResources.swift; sourceTree = "<group>"; };
+               22DBCD1AD7AE653008499BE79CEE9E27 /* GPBDictionary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBDictionary.h; path = objectivec/GPBDictionary.h; sourceTree = "<group>"; };
+               23841AC0A870055682FE7FBEE6920AE3 /* FIRMessagingSyncMessageManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingSyncMessageManager.h; path = Firebase/Messaging/FIRMessagingSyncMessageManager.h; sourceTree = "<group>"; };
+               239CBA52FEF031788D07DBF21BE742E4 /* SwiftKeychainWrapper-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftKeychainWrapper-umbrella.h"; sourceTree = "<group>"; };
+               23A4583BE5CEDDE176D8A3C11916D33E /* SSZipArchive-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SSZipArchive-dummy.m"; sourceTree = "<group>"; };
+               23C00816B8A572DD6069F377FC98681A /* RLMCollection.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMCollection.mm; path = Realm/RLMCollection.mm; sourceTree = "<group>"; };
+               24BECF804ACC1DFA1AEF908133E8CDF9 /* Object.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Object.swift; path = RealmSwift/Object.swift; sourceTree = "<group>"; };
+               24C6C6D138272A472EEE390DE844A4A8 /* GULReachabilityMessageCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULReachabilityMessageCode.h; path = GoogleUtilities/Reachability/Private/GULReachabilityMessageCode.h; sourceTree = "<group>"; };
+               24DDFB7ADF9F5BB4277956C0D45E3124 /* aesopt.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = aesopt.h; path = SSZipArchive/minizip/aes/aesopt.h; sourceTree = "<group>"; };
+               260BE158E1193A24E66F1F038B243149 /* FIRMessagingRemoteNotificationsProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingRemoteNotificationsProxy.h; path = Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.h; sourceTree = "<group>"; };
+               26216404833D73818C5EFEC7E16DA183 /* AEXML.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = AEXML.framework; path = AEXML.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               265D21A8AF0C73915C5E3808A10562F6 /* Andada-BoldItalic.otf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Andada-BoldItalic.otf"; path = "Source/Resources/Fonts/Andada/Andada-BoldItalic.otf"; sourceTree = "<group>"; };
+               268FB6F347F0C0C1834F10B2DF31A8A9 /* RequestModifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestModifier.swift; path = Sources/RequestModifier.swift; sourceTree = "<group>"; };
+               27041CCB67B1C89587A0048ED6CA3D37 /* JSQWebViewController-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "JSQWebViewController-prefix.pch"; sourceTree = "<group>"; };
+               270BBF6FE1627D659D64D320D8AC3E14 /* Lato-Bold.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Lato-Bold.ttf"; path = "Source/Resources/Fonts/Lato/Lato-Bold.ttf"; sourceTree = "<group>"; };
+               271E61CB4DD108196A89495C96CA3D61 /* FIRMessagingCheckinService.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingCheckinService.m; path = Firebase/Messaging/FIRMessagingCheckinService.m; sourceTree = "<group>"; };
+               2747F0DF00234AE9135F29A3EF8186E0 /* GULReachabilityChecker+Internal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "GULReachabilityChecker+Internal.h"; path = "GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h"; sourceTree = "<group>"; };
+               27B24667E9710453A768281EEBD94D07 /* FIRMessagingCheckinService.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingCheckinService.h; path = Firebase/Messaging/FIRMessagingCheckinService.h; sourceTree = "<group>"; };
+               27D24FB6B80FD2575D5E65E4D136E8F3 /* ThreadSafeReference.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ThreadSafeReference.swift; path = RealmSwift/ThreadSafeReference.swift; sourceTree = "<group>"; };
+               27D30FBF66A9DE692B39A8C5F09C89BC /* Alamofire-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-umbrella.h"; sourceTree = "<group>"; };
+               27E4181D0238606C2DB16B0708782E6F /* FIRConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRConfiguration.h; path = Firebase/Core/Public/FIRConfiguration.h; sourceTree = "<group>"; };
+               27FD5DDB5405AF442504B5810F7F8C09 /* KeychainItemAccessibility.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeychainItemAccessibility.swift; path = SwiftKeychainWrapper/KeychainItemAccessibility.swift; sourceTree = "<group>"; };
+               287E8DD99129AC55E28F8626306CAF6D /* Dispatcher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Dispatcher.swift; path = MatomoTracker/Dispatcher.swift; sourceTree = "<group>"; };
+               28AEF58ACB93749F0DEF7933EDEBF6EE /* RLMPredicateUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMPredicateUtil.mm; path = Realm/RLMPredicateUtil.mm; sourceTree = "<group>"; };
+               292A755469D2B57D1C34F30F13C1F167 /* FontBlaster-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FontBlaster-prefix.pch"; sourceTree = "<group>"; };
+               295B0C54BAE1989B99BA0920EA7C603C /* unzip.c */ = {isa = PBXFileReference; includeInIndex = 1; name = unzip.c; path = SSZipArchive/minizip/unzip.c; sourceTree = "<group>"; };
+               29C8800C3B241A2D1C57FB57C1A07B4F /* MBProgressHUD-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MBProgressHUD-umbrella.h"; sourceTree = "<group>"; };
+               29EECBD20B32BF000A7D4FB4F05DF3F9 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               29F9073DE228E0B9F78EE26B5AD02AF0 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               2A77D96CD73CC56CAADE88A26867210D /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               2BA66D357F5E6460D64AEC0F89C5ABBB /* FIRMessagingPendingTopicsList.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingPendingTopicsList.h; path = Firebase/Messaging/FIRMessagingPendingTopicsList.h; sourceTree = "<group>"; };
+               2BD7DCAEBC8CE3C15E7460B40AC02A51 /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkReachabilityManager.swift; path = Source/NetworkReachabilityManager.swift; sourceTree = "<group>"; };
+               2C3D986619B47384323E22FC783B359B /* GPBUnknownField.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBUnknownField.m; path = objectivec/GPBUnknownField.m; sourceTree = "<group>"; };
+               2C570FE262AD0188836ABD1A74A469B8 /* FIRComponentContainer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponentContainer.m; path = Firebase/Core/FIRComponentContainer.m; sourceTree = "<group>"; };
+               2C649BF15B07EDFC3A64FEB2AAA77C29 /* Toast-Swift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Toast-Swift-dummy.m"; sourceTree = "<group>"; };
+               2CBAD617EBEDCB28ABE5FE59AFF568AC /* Timestamp.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Timestamp.pbobjc.h; path = objectivec/google/protobuf/Timestamp.pbobjc.h; sourceTree = "<group>"; };
+               2D5D254748958FD22A32E3D5D3B9997D /* Crashlytics.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Crashlytics.h; path = iOS/Crashlytics.framework/Headers/Crashlytics.h; sourceTree = "<group>"; };
+               2E3E3FD2AA61E951D9A15E12FEB6A59F /* Queue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Queue.swift; path = MatomoTracker/Queue.swift; sourceTree = "<group>"; };
+               2E60177A5CFD27A0866D68EFFD8094F5 /* zip.c */ = {isa = PBXFileReference; includeInIndex = 1; name = zip.c; path = SSZipArchive/minizip/zip.c; sourceTree = "<group>"; };
+               2E6F3CBA4A6A0429B6813DB5778F3D40 /* Toast_Swift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Toast_Swift.framework; path = "Toast-Swift.framework"; sourceTree = BUILT_PRODUCTS_DIR; };
+               2EA3BEC324F13823959A0F7DB8B7CC6C /* AnimatedImageView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AnimatedImageView.swift; path = Sources/AnimatedImageView.swift; sourceTree = "<group>"; };
+               2F837D39A9B1D03254A4B178F9C9DA0C /* Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Extensions.swift; path = Source/Extensions.swift; sourceTree = "<group>"; };
+               2F9331D2AE04D7359BA18D23BDA96D7A /* Type.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Type.pbobjc.m; path = objectivec/google/protobuf/Type.pbobjc.m; sourceTree = "<group>"; };
+               2FBF1EAFC3C8941E114238A69DC823D2 /* ImageDownloader.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageDownloader.swift; path = Sources/ImageDownloader.swift; sourceTree = "<group>"; };
+               2FDF85370176057B4C572AFDD0DC5AAF /* Result.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Result.swift; path = Source/Result.swift; sourceTree = "<group>"; };
+               3039DE7A044D5D519CE558DF7522DD75 /* Box.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Box.swift; path = Sources/Box.swift; sourceTree = "<group>"; };
+               3045AE10FB372AE8C59AF5E27B74F273 /* OAuthSwift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "OAuthSwift-dummy.m"; sourceTree = "<group>"; };
+               30619A0389B050792D52E48CA7FB5016 /* object_schema.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_schema.cpp; path = Realm/ObjectStore/src/object_schema.cpp; sourceTree = "<group>"; };
+               30B4F0F35B2E6FB1AB726ECD8CA57734 /* GPBRootObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBRootObject.h; path = objectivec/GPBRootObject.h; sourceTree = "<group>"; };
+               3110559B9CEE5BA9F86BBB0E5EFE87F7 /* Highlight.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Highlight.swift; path = Source/Models/Highlight.swift; sourceTree = "<group>"; };
+               318DC2541285D47723C03D0A990A8731 /* FolioReaderChapterList.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderChapterList.swift; path = Source/FolioReaderChapterList.swift; sourceTree = "<group>"; };
+               31C89ED169005A2C1DBDF20457DCAE15 /* Toast-Swift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Toast-Swift-umbrella.h"; sourceTree = "<group>"; };
+               320E6B71AC99F49980ACD3104FAF01B2 /* ioapi_mem.c */ = {isa = PBXFileReference; includeInIndex = 1; name = ioapi_mem.c; path = SSZipArchive/minizip/ioapi_mem.c; sourceTree = "<group>"; };
+               325E20A67E5E9F463834C45CE511FA12 /* RLMProperty.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMProperty.mm; path = Realm/RLMProperty.mm; sourceTree = "<group>"; };
+               32898A025087808B3DAD1D234840702F /* transact_log_handler.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = transact_log_handler.cpp; path = Realm/ObjectStore/src/impl/transact_log_handler.cpp; sourceTree = "<group>"; };
+               3300795729EA0A168E19390ED6E7867E /* Duration.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Duration.pbobjc.m; path = objectivec/google/protobuf/Duration.pbobjc.m; sourceTree = "<group>"; };
+               3304A45FA8673B2DACEE5AAA8C75E0FD /* GPBBootstrap.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBBootstrap.h; path = objectivec/GPBBootstrap.h; sourceTree = "<group>"; };
+               333C849D07817FE374DB8EE976C7AD2C /* MenuItemKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MenuItemKit.modulemap; sourceTree = "<group>"; };
+               33F94CA67ECA556757ACE1D3C592B279 /* CLSAttributes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CLSAttributes.h; path = iOS/Crashlytics.framework/Headers/CLSAttributes.h; sourceTree = "<group>"; };
+               34414B1475EAA53DE09EE78DBCF4F362 /* pb_common.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb_common.h; sourceTree = "<group>"; };
+               34524258955E5E82BF9892F8A42C5CCA /* ThreadHelper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ThreadHelper.swift; path = Sources/ThreadHelper.swift; sourceTree = "<group>"; };
+               3541A4F6170F57F3159B5235DF1E20D6 /* CLSReport.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CLSReport.h; path = iOS/Crashlytics.framework/Headers/CLSReport.h; sourceTree = "<group>"; };
+               35746DF215D495B2F6B254F11A4B932C /* MBProgressHUD.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = MBProgressHUD.m; sourceTree = "<group>"; };
+               359CF8CC37A4F97F6D3086A95EE10DA0 /* RLMSyncSession.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncSession.mm; path = Realm/RLMSyncSession.mm; sourceTree = "<group>"; };
+               368FE0998EDC34C2BF80A3D2F7898574 /* sync_user.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_user.cpp; path = Realm/ObjectStore/src/sync/sync_user.cpp; sourceTree = "<group>"; };
+               3692888732B2C535E1B29F8055892B60 /* RealmSwift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RealmSwift-dummy.m"; sourceTree = "<group>"; };
+               371FFB7916EE7697D6F76BA98C2717AA /* GtalkExtensions.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GtalkExtensions.pbobjc.h; path = Firebase/Messaging/Protos/GtalkExtensions.pbobjc.h; sourceTree = "<group>"; };
+               37215B06C349AEA05F2D6634E784DCC0 /* ZFDragableModalTransition.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = ZFDragableModalTransition.framework; path = ZFDragableModalTransition.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               381F31B952F0996552B45EB064A7DF08 /* RLMAnalytics.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMAnalytics.mm; path = Realm/RLMAnalytics.mm; sourceTree = "<group>"; };
+               382852248FE8C625CF0186C9D1FFCC34 /* GPBCodedOutputStream.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBCodedOutputStream.h; path = objectivec/GPBCodedOutputStream.h; sourceTree = "<group>"; };
+               38D0FD7E483E650C35611A41941D7F0C /* FIRComponentContainerInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentContainerInternal.h; path = Firebase/Core/Private/FIRComponentContainerInternal.h; sourceTree = "<group>"; };
+               38ED9F6020F6E95DF135E9564D06DB85 /* Kingfisher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Kingfisher.h; path = Sources/Kingfisher.h; sourceTree = "<group>"; };
+               390D860093219F412EFBA3562CA65AAE /* RLMSyncUtil.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUtil.h; path = include/RLMSyncUtil.h; sourceTree = "<group>"; };
+               398D45D2D76BE97410B68392E6D3A6B8 /* GULNSData+zlib.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "GULNSData+zlib.m"; path = "GoogleUtilities/NSData+zlib/GULNSData+zlib.m"; sourceTree = "<group>"; };
+               398EF8E6348C58E6A461B252064C6BA6 /* RLMCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMCollection.h; path = include/RLMCollection.h; sourceTree = "<group>"; };
+               3A67C7CD84356516F1F7D3986E9C09C6 /* Int+OAuthSwift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Int+OAuthSwift.swift"; path = "Sources/Int+OAuthSwift.swift"; sourceTree = "<group>"; };
+               3AF9BC4C9A3CD19279B980ED20FBAD39 /* Protobuf.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Protobuf.framework; path = Protobuf.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               3BCBFCAFBBD2EB2F3E726450815158E5 /* pwd2key.c */ = {isa = PBXFileReference; includeInIndex = 1; name = pwd2key.c; path = SSZipArchive/minizip/aes/pwd2key.c; sourceTree = "<group>"; };
+               3C292CFC75EC0FD46345F9ED0D5374DA /* AlamofireActivityLogger-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "AlamofireActivityLogger-prefix.pch"; sourceTree = "<group>"; };
+               3C5B9BB923D2153C5695489BFCB3FE6A /* FIRMessagingInternalUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingInternalUtilities.h; path = Firebase/Messaging/InternalHeaders/FIRMessagingInternalUtilities.h; sourceTree = "<group>"; };
+               3C7E52BB0C02BDC584CD76DF51B1B4A9 /* AEXML-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "AEXML-prefix.pch"; sourceTree = "<group>"; };
+               3D6410D66A868469BEB52C0E969B7A21 /* RLMRealm+Sync.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "RLMRealm+Sync.mm"; path = "Realm/RLMRealm+Sync.mm"; sourceTree = "<group>"; };
+               3D9516D4522CD7C42A017229D056F0A4 /* ZFModalTransitionAnimator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ZFModalTransitionAnimator.h; path = Classes/ZFModalTransitionAnimator.h; sourceTree = "<group>"; };
+               3DA6E2DC0B34EB311EB4E1A5DD25EC45 /* CacheSerializer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CacheSerializer.swift; path = Sources/CacheSerializer.swift; sourceTree = "<group>"; };
+               3DD99B05DEEA0C7E3366E61EF5345DE0 /* FontBlaster.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontBlaster.swift; path = Sources/FontBlaster.swift; sourceTree = "<group>"; };
+               3E1C11CC8048486D77496DD0A2226A8B /* realm_coordinator.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = realm_coordinator.cpp; path = Realm/ObjectStore/src/impl/realm_coordinator.cpp; sourceTree = "<group>"; };
+               3E49B5DCB4074C65C833C7A0589864F5 /* aescrypt.c */ = {isa = PBXFileReference; includeInIndex = 1; name = aescrypt.c; path = SSZipArchive/minizip/aes/aescrypt.c; sourceTree = "<group>"; };
+               3E9295BAC2870D8B5DCFE535DDDAEA03 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               3ED4E426129BC7417B7E88791A14B5E6 /* FolioReaderSharingProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderSharingProvider.swift; path = Source/FolioReaderSharingProvider.swift; sourceTree = "<group>"; };
+               3F0C4378148A65453A4D307AEAFF61D7 /* RealmCollection.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RealmCollection.swift; path = RealmSwift/RealmCollection.swift; sourceTree = "<group>"; };
+               3F9AF84291BB835371B31C38FB32B164 /* MZDownloadManager-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MZDownloadManager-dummy.m"; sourceTree = "<group>"; };
+               3FDE4083CA57C224C3F4D3D0A234E8FA /* ZFDragableModalTransition.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = ZFDragableModalTransition.modulemap; sourceTree = "<group>"; };
+               3FE04C47410787B608EB2E6AE39AE374 /* FolioReaderQuoteShare.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderQuoteShare.swift; path = Source/FolioReaderQuoteShare.swift; sourceTree = "<group>"; };
+               3FF7EDC5386C9DB98B823B474CDDF650 /* Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Kingfisher.swift; path = Sources/Kingfisher.swift; sourceTree = "<group>"; };
+               4021B8B58240DC8FF47070F9C04D0B84 /* RLMObjectStore.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectStore.mm; path = Realm/RLMObjectStore.mm; sourceTree = "<group>"; };
+               405DF96346BB3ABC89A8AEEF6B9B995F /* RealmSwift.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RealmSwift.xcconfig; sourceTree = "<group>"; };
+               40A2A80064952DE4D4507D13F705D066 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               40F16EED8D382D5170EEEF0B6ABAB383 /* GULReachabilityChecker.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULReachabilityChecker.m; path = GoogleUtilities/Reachability/GULReachabilityChecker.m; sourceTree = "<group>"; };
+               4125B8ABC42EE8FDD40F2E6DF0DB3D20 /* FIRComponentRegistrant.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentRegistrant.h; path = Firebase/Core/Private/FIRComponentRegistrant.h; sourceTree = "<group>"; };
+               41A2C193ABFAE8C48F5DD4FE7D3A1CA6 /* FolioReaderChapterListCell.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderChapterListCell.swift; path = Source/FolioReaderChapterListCell.swift; sourceTree = "<group>"; };
+               41E4C9647DF00F5A8397812EB95B4B77 /* FolioReaderKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FolioReaderKit-dummy.m"; sourceTree = "<group>"; };
+               42071D61BA4308E0EBA1DB720C8DA29A /* external_commit_helper.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = external_commit_helper.cpp; path = Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp; sourceTree = "<group>"; };
+               422B10EE9CCDEDA3886C8B1E3F5FC8C1 /* GULNetworkLoggerProtocol.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkLoggerProtocol.h; path = GoogleUtilities/Network/Private/GULNetworkLoggerProtocol.h; sourceTree = "<group>"; };
+               4259A6F91094C5C3FE957FE5285966CD /* FIRMessagingLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingLogger.m; path = Firebase/Messaging/FIRMessagingLogger.m; sourceTree = "<group>"; };
+               42ECF2075F0721267E036F3A8F90A0D0 /* FIRMessagingConnection.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingConnection.m; path = Firebase/Messaging/FIRMessagingConnection.m; sourceTree = "<group>"; };
+               4300E3895B62156BB316982523FC8D06 /* ImageCache.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = Sources/ImageCache.swift; sourceTree = "<group>"; };
+               4370FE81CEE77016401F7AAAD0158F4A /* RLMUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMUtil.mm; path = Realm/RLMUtil.mm; sourceTree = "<group>"; };
+               439526BFFA7B883CDBF438B9EA23C647 /* MZDownloadModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MZDownloadModel.swift; path = MZDownloadManager/Classes/MZDownloadModel.swift; sourceTree = "<group>"; };
+               439ED54E64F4037372B80993935BB837 /* AEXML.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = AEXML.xcconfig; sourceTree = "<group>"; };
+               43CDCABD9284457D43B646E90B874944 /* MBProgressHUD.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MBProgressHUD.xcconfig; sourceTree = "<group>"; };
+               43FB1CC94D2C1CA721B8517B02BB5EC3 /* GULLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULLogger.m; path = GoogleUtilities/Logger/GULLogger.m; sourceTree = "<group>"; };
+               44B61755C9439AAE0D2773B101A3E1EF /* SSZipArchive.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SSZipArchive.h; path = SSZipArchive/SSZipArchive.h; sourceTree = "<group>"; };
+               457F370BE978B9A7B711AB40080ED382 /* FirebaseMessaging.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseMessaging.framework; path = FirebaseMessaging.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               45D79C1899AC277B26DD3524D5AC9919 /* RLMSyncUtil_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncUtil_Private.h; path = include/RLMSyncUtil_Private.h; sourceTree = "<group>"; };
+               45DFA3160A343E4AA095D885BBD9483B /* FIRMessagingRmqManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingRmqManager.h; path = Firebase/Messaging/FIRMessagingRmqManager.h; sourceTree = "<group>"; };
+               46EA2FB5D744DD40EDB4C7F7E064E37C /* ioapi_buf.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ioapi_buf.h; path = SSZipArchive/minizip/ioapi_buf.h; sourceTree = "<group>"; };
+               46FC785119A479FD291D52486083C3F8 /* RLMObjectSchema.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObjectSchema.mm; path = Realm/RLMObjectSchema.mm; sourceTree = "<group>"; };
+               471BFA1649D5CA3AE3965F662D1DE691 /* GPBRootObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBRootObject.m; path = objectivec/GPBRootObject.m; sourceTree = "<group>"; };
+               475E47404BD3273B96027E83999E404E /* pb_encode.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_encode.c; sourceTree = "<group>"; };
+               47C92578FFB7251147AE7AD4DAF28E6F /* FIRAnalyticsConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAnalyticsConfiguration.h; path = Firebase/Core/Public/FIRAnalyticsConfiguration.h; sourceTree = "<group>"; };
+               4874EE6FBA94E2552B91BABF2A599E39 /* GPBDictionary_PackagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBDictionary_PackagePrivate.h; path = objectivec/GPBDictionary_PackagePrivate.h; sourceTree = "<group>"; };
+               48A23DC4D40077329084C4E6B9086403 /* FirebaseCore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FirebaseCore.h; path = Firebase/Core/Public/FirebaseCore.h; sourceTree = "<group>"; };
+               498C0C39E4B4B2835310F4E961B0F195 /* CustomVariable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CustomVariable.swift; path = MatomoTracker/CustomVariable.swift; sourceTree = "<group>"; };
+               49A8496480916B38FC60F790D50CAF75 /* LinkingObjects.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LinkingObjects.swift; path = RealmSwift/LinkingObjects.swift; sourceTree = "<group>"; };
+               49AB6C96EADE159493F9CF27E33B2452 /* OAuthSwiftURLHandlerType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuthSwiftURLHandlerType.swift; path = Sources/OAuthSwiftURLHandlerType.swift; sourceTree = "<group>"; };
+               4A118E1890C7A2E6094454B06E7D1CA6 /* FIRMessaging_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessaging_Private.h; path = Firebase/Messaging/FIRMessaging_Private.h; sourceTree = "<group>"; };
+               4B125D2F070E0A07B9BCA5BAA19580AF /* GPBRuntimeTypes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBRuntimeTypes.h; path = objectivec/GPBRuntimeTypes.h; sourceTree = "<group>"; };
+               4C59F12F85CDAAB121A1677774DF2C7F /* OAuth1Swift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuth1Swift.swift; path = Sources/OAuth1Swift.swift; sourceTree = "<group>"; };
+               4C5FC54772307BC323BD252586733ADA /* NSError+RLMSync.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSError+RLMSync.h"; path = "include/NSError+RLMSync.h"; sourceTree = "<group>"; };
+               4C822CD35CD43E275C564128C723F24C /* aes_ni.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = aes_ni.h; path = SSZipArchive/minizip/aes/aes_ni.h; sourceTree = "<group>"; };
+               4CCB952DC44D32026263D1EB9F1D19CE /* Empty.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Empty.pbobjc.m; path = objectivec/google/protobuf/Empty.pbobjc.m; sourceTree = "<group>"; };
+               4D4B08A2F7A3B6D01CC2E61B621DBDD6 /* UIMenuItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIMenuItem.swift; path = MenuItemKit/UIMenuItem.swift; sourceTree = "<group>"; };
+               4EBDFD83C94D7DEEEA33A42478FC9305 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               4F5216060EC5386917B636135207D13B /* FIRAnalyticsConfiguration+Internal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRAnalyticsConfiguration+Internal.h"; path = "Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h"; sourceTree = "<group>"; };
+               4F6C31FBB1F555C570AF8298A3C1B669 /* pb_decode.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_decode.c; sourceTree = "<group>"; };
+               4F731C61972BF7BC92E5CF84BC1752EA /* MZDownloadManager.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MZDownloadManager.modulemap; sourceTree = "<group>"; };
+               5016398FE6D4E791E29F725CF2C48D1F /* GULNetworkMessageCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkMessageCode.h; path = GoogleUtilities/Network/Private/GULNetworkMessageCode.h; sourceTree = "<group>"; };
+               507E13A307CB3F11C321AD5B742C1C07 /* Any.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Any.pbobjc.m; path = objectivec/google/protobuf/Any.pbobjc.m; sourceTree = "<group>"; };
+               50A1477AE40664A579ED50B1D7233B7B /* MenuItemKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MenuItemKit-prefix.pch"; sourceTree = "<group>"; };
+               50FD250AF3DF50EFB3BFDE0C1A66DB27 /* AEXML-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "AEXML-dummy.m"; sourceTree = "<group>"; };
+               518860BA42EF7435BFDD67CC13858D49 /* GoogleUtilities.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleUtilities.xcconfig; sourceTree = "<group>"; };
+               5192103CC06271B7D3368A703ACDCCCB /* FIRMessagingPendingTopicsList.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingPendingTopicsList.m; path = Firebase/Messaging/FIRMessagingPendingTopicsList.m; sourceTree = "<group>"; };
+               51AB21DAE8AE316862E8F8DC498D8765 /* Kingfisher-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Kingfisher-umbrella.h"; sourceTree = "<group>"; };
+               5247E5300F4595C93CFD5EFEB52D7B63 /* FolioReaderKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = FolioReaderKit.modulemap; sourceTree = "<group>"; };
+               5267DB065F6BF93D0021FB08DF8245BC /* FIRComponentType.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponentType.m; path = Firebase/Core/FIRComponentType.m; sourceTree = "<group>"; };
+               52994FB9A90DB506C3E6F452B690FB0A /* FIRMessagingConnection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingConnection.h; path = Firebase/Messaging/FIRMessagingConnection.h; sourceTree = "<group>"; };
+               52C57B4C09979E16295F7D9EF93E8588 /* FirebaseMessaging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseMessaging.xcconfig; sourceTree = "<group>"; };
+               52CCC25F1B42510727D9EDF134CB3132 /* FIROptions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIROptions.h; path = Firebase/Core/Public/FIROptions.h; sourceTree = "<group>"; };
+               53C308E0551021233B7D7464DFB9CF6F /* OAuthSwiftError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuthSwiftError.swift; path = Sources/OAuthSwiftError.swift; sourceTree = "<group>"; };
+               544A23E1053CDBF91F0B3731EDA4FFC4 /* FIRErrorCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRErrorCode.h; path = Firebase/Core/Private/FIRErrorCode.h; sourceTree = "<group>"; };
+               5468DF52D9F560728DF9925C2F0D210A /* RLMObservation.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObservation.mm; path = Realm/RLMObservation.mm; sourceTree = "<group>"; };
+               547372D2B91EA430CBD2723BC70C75CF /* hmac.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = hmac.h; path = SSZipArchive/minizip/aes/hmac.h; sourceTree = "<group>"; };
+               551760570F42A57DBE4540EE1D5683B3 /* GULNSData+zlib.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "GULNSData+zlib.h"; path = "GoogleUtilities/NSData+zlib/GULNSData+zlib.h"; sourceTree = "<group>"; };
+               5535738AED780D0B2515527F81E80523 /* FIRMessagingSecureSocket.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingSecureSocket.h; path = Firebase/Messaging/FIRMessagingSecureSocket.h; sourceTree = "<group>"; };
+               553FFB49824A1E787E4D0F386F6DE765 /* FIRApp.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRApp.m; path = Firebase/Core/FIRApp.m; sourceTree = "<group>"; };
+               557649FFA165485E24A1271D2638F344 /* FIRBundleUtil.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRBundleUtil.m; path = Firebase/Core/FIRBundleUtil.m; sourceTree = "<group>"; };
+               559E68E63A2FF675E25D5268714196AC /* RLMConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMConstants.m; path = Realm/RLMConstants.m; sourceTree = "<group>"; };
+               55DA96677843C9CF1DB923922BBF68F3 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               55F481A6CA9DC577BC74D7F5D53EDF8B /* NSDictionary+FIRMessaging.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+FIRMessaging.h"; path = "Firebase/Messaging/NSDictionary+FIRMessaging.h"; sourceTree = "<group>"; };
+               56308D4A7C4CA17AC58C82130DDC2718 /* GULMutableDictionary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULMutableDictionary.h; path = GoogleUtilities/Network/Private/GULMutableDictionary.h; sourceTree = "<group>"; };
+               565A58C3DEC5EF04039C13B055C22CE9 /* RLMObjectSchema.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectSchema.h; path = include/RLMObjectSchema.h; sourceTree = "<group>"; };
+               566B9BE756758F1C246084E3A26FE6F6 /* SSZipArchive-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SSZipArchive-umbrella.h"; sourceTree = "<group>"; };
+               56D082DC4F23AECFD551D310E0D3C41C /* FIRErrors.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRErrors.h; path = Firebase/Core/Private/FIRErrors.h; sourceTree = "<group>"; };
+               57001FF2A9A8C5351AFE3F8078C847D6 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Fabric.framework; path = iOS/Fabric.framework; sourceTree = "<group>"; };
+               57300EE01B9AFBB08B6C2FCC7AF87CAB /* Lora-Regular.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Lora-Regular.ttf"; path = "Source/Resources/Fonts/Lora/Lora-Regular.ttf"; sourceTree = "<group>"; };
+               581B420D10729B6EFD3B20CB081FC1D6 /* Util.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Util.swift; path = RealmSwift/Util.swift; sourceTree = "<group>"; };
+               587033B39A1B751CBDCBBB6F1F3E3668 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               59706CD0DE8A5C55FBF1750747E35736 /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Source/SessionDelegate.swift; sourceTree = "<group>"; };
+               597E534C745A644235BCAB1CB8A229CA /* RLMMigration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMMigration.mm; path = Realm/RLMMigration.mm; sourceTree = "<group>"; };
+               59F29FF04A45EA9F6FF92C7F6BAB4B12 /* RLMResults_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMResults_Private.h; path = include/RLMResults_Private.h; sourceTree = "<group>"; };
+               5A064D124C92CFA11CDFB748E3A1F23E /* RLMListBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMListBase.h; path = include/RLMListBase.h; sourceTree = "<group>"; };
+               5A934BE8A55D992D204B3B9A14611E10 /* RLMObject_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObject_Private.h; path = include/RLMObject_Private.h; sourceTree = "<group>"; };
+               5B53B8C53CD29268FAB913C8A28911B7 /* FIRMessagingSecureSocket.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingSecureSocket.m; path = Firebase/Messaging/FIRMessagingSecureSocket.m; sourceTree = "<group>"; };
+               5BA7792CBD42BEB1923EA0ADB15A10C7 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               5C52956025F0F8DD25F68011683ABBE9 /* Logger.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Logger.swift; path = MatomoTracker/Logger.swift; sourceTree = "<group>"; };
+               5CA5A1028F01FEC16A4E71E8C1F5E062 /* Request.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Request.swift; path = Source/Request.swift; sourceTree = "<group>"; };
+               5DD123C500606EE058F44458705F9DE9 /* Realm-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Realm-dummy.m"; sourceTree = "<group>"; };
+               5DECCEDCC3CF34ACB64710FFB19496B1 /* RLMSchema.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSchema.mm; path = Realm/RLMSchema.mm; sourceTree = "<group>"; };
+               5E9A697406390F3FC0AD51D2C04D46A0 /* Collection+OAuthSwift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Collection+OAuthSwift.swift"; path = "Sources/Collection+OAuthSwift.swift"; sourceTree = "<group>"; };
+               60BD8D4AEB998E3927430AB2F3519F08 /* AFError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AFError.swift; path = Source/AFError.swift; sourceTree = "<group>"; };
+               60BF42A0CA4751A6D288CDE5D876CA40 /* Realm.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Realm.framework; path = Realm.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               619880A6749DFFFD64BB91B2E35CD611 /* GULSwizzler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULSwizzler.h; path = GoogleUtilities/MethodSwizzler/Private/GULSwizzler.h; sourceTree = "<group>"; };
+               61A0764E9029258456100401AF3A1D31 /* Data+OAuthSwift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Data+OAuthSwift.swift"; path = "Sources/Data+OAuthSwift.swift"; sourceTree = "<group>"; };
+               61D0B52E0F670DE2CB3F0AA75F6ECF4F /* FolioReaderKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FolioReaderKit-umbrella.h"; sourceTree = "<group>"; };
+               61EE9421F804DDD1641EC8DBF05D66FB /* RLMConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMConstants.h; path = include/RLMConstants.h; sourceTree = "<group>"; };
+               62D18DABC2A2093AA4FF972DC35EE001 /* Api.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Api.pbobjc.m; path = objectivec/google/protobuf/Api.pbobjc.m; sourceTree = "<group>"; };
+               62F944B2EC591C1D211C295341CADE77 /* GULAppDelegateSwizzler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppDelegateSwizzler.h; path = GoogleUtilities/AppDelegateSwizzler/Private/GULAppDelegateSwizzler.h; sourceTree = "<group>"; };
+               636D155F9DC5CDD62606CFA7F4189E51 /* RealmSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RealmSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               6394969E53E997D80E253AA0E193E08E /* Alamofire.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Alamofire.modulemap; sourceTree = "<group>"; };
+               644342C27115C1D613C4C9D164A46647 /* FIRMessagingDefines.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingDefines.h; path = Firebase/Messaging/FIRMessagingDefines.h; sourceTree = "<group>"; };
+               64DD69F5766B0BC4B9B88FD6ABC36472 /* AEXML.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = AEXML.modulemap; sourceTree = "<group>"; };
+               655356583D31A214437E878EB5DB8BF0 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               664B0758A8B043DFA4F69B51BA065D60 /* GPBDescriptor_PackagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBDescriptor_PackagePrivate.h; path = objectivec/GPBDescriptor_PackagePrivate.h; sourceTree = "<group>"; };
+               67189D34B36DE89CF42B0389F09FB517 /* Raleway-Italic.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Raleway-Italic.ttf"; path = "Source/Resources/Fonts/Raleway/Raleway-Italic.ttf"; sourceTree = "<group>"; };
+               673243EDEFCAEB87BF4D213D5731B809 /* SSZipArchive.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = SSZipArchive.m; path = SSZipArchive/SSZipArchive.m; sourceTree = "<group>"; };
+               67D2BFFD100F5D39305BA3FC2802DB4A /* RLMObjectStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectStore.h; path = include/RLMObjectStore.h; sourceTree = "<group>"; };
+               67F2AFDD06DDE1D92AA1028FB0C8C109 /* Property.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Property.swift; path = RealmSwift/Property.swift; sourceTree = "<group>"; };
+               68048DCB850F7DD9097DC6465BE6708C /* MatomoTracker-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MatomoTracker-umbrella.h"; sourceTree = "<group>"; };
+               680BC3B5B1C18CB129B3F626B32364F8 /* Dictionary+OAuthSwift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Dictionary+OAuthSwift.swift"; path = "Sources/Dictionary+OAuthSwift.swift"; sourceTree = "<group>"; };
+               6939449CF2795C11704E929B05036FEA /* Notifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Notifications.swift; path = Source/Notifications.swift; sourceTree = "<group>"; };
+               6A09C91D35E07AA4192A517BE98B0501 /* Sync.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Sync.swift; path = RealmSwift/Sync.swift; sourceTree = "<group>"; };
+               6A43C662AC0818E1DA41050DA811BA97 /* SwiftKeychainWrapper.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftKeychainWrapper.xcconfig; sourceTree = "<group>"; };
+               6B9A7C04F88EE32E59BDA90F0BFE6D21 /* ResponseSerialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResponseSerialization.swift; path = Source/ResponseSerialization.swift; sourceTree = "<group>"; };
+               6D6A3C039A370F8C8DA839EF41DC6D43 /* RLMRealm.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm.h; path = include/RLMRealm.h; sourceTree = "<group>"; };
+               6DC6F0E58FABA2B29D2CFE63A7F0DFB3 /* nanopb-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "nanopb-umbrella.h"; sourceTree = "<group>"; };
+               6E393C39CB6940A61FA1A4A213758B92 /* FIRMessagingLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingLogger.h; path = Firebase/Messaging/FIRMessagingLogger.h; sourceTree = "<group>"; };
+               6E5D4A81CC4B4E8549AF794D55B53EA2 /* crypt.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = crypt.h; path = SSZipArchive/minizip/crypt.h; sourceTree = "<group>"; };
+               6EB55573C83483EB32EA80704AA7F376 /* Utils.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = Sources/Utils.swift; sourceTree = "<group>"; };
+               6EC16E5C569E109186D6849D847BCA48 /* network_reachability_observer.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = network_reachability_observer.cpp; path = Realm/ObjectStore/src/sync/impl/apple/network_reachability_observer.cpp; sourceTree = "<group>"; };
+               6EEBA2C8A6B1441F5049FD44C2E246B2 /* FolioReaderWebView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderWebView.swift; path = Source/FolioReaderWebView.swift; sourceTree = "<group>"; };
+               6EEE490DFA86C4AA2298366F0472518F /* nanopb.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = nanopb.modulemap; sourceTree = "<group>"; };
+               6F01E4CF285A102DB8F3FFC4F10EEE2C /* collection_notifications.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_notifications.cpp; path = Realm/ObjectStore/src/collection_notifications.cpp; sourceTree = "<group>"; };
+               6FE6126BA6653AAFFCEBCBBFFF6468D1 /* RLMResults.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMResults.mm; path = Realm/RLMResults.mm; sourceTree = "<group>"; };
+               705DCC263710805CED0A0D347B897A45 /* RLMObject.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMObject.mm; path = Realm/RLMObject.mm; sourceTree = "<group>"; };
+               71184DA28D4C35AD9B685754D612FA27 /* object_store.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object_store.cpp; path = Realm/ObjectStore/src/object_store.cpp; sourceTree = "<group>"; };
+               71C22AA5D5F284DC9B78BFAF42A83C57 /* FIRComponentContainer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentContainer.h; path = Firebase/Core/Private/FIRComponentContainer.h; sourceTree = "<group>"; };
+               71E1A97221D2058BC3406E9A3A784E36 /* FirebaseAnalytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseAnalytics.framework; path = Frameworks/FirebaseAnalytics.framework; sourceTree = "<group>"; };
+               71E40312A6196B0B687CFF43F843C806 /* FolioReaderPlayerMenu.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderPlayerMenu.swift; path = Source/FolioReaderPlayerMenu.swift; sourceTree = "<group>"; };
+               72A41C016F25201385C2676F0DA9C35B /* SwiftVersion.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftVersion.swift; path = RealmSwift/SwiftVersion.swift; sourceTree = "<group>"; };
+               72C53A710A523025D5E9074631788CD9 /* Device.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Device.swift; path = MatomoTracker/Device.swift; sourceTree = "<group>"; };
+               735555A726916552BAAAE6D9696116EC /* GPBDescriptor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBDescriptor.m; path = objectivec/GPBDescriptor.m; sourceTree = "<group>"; };
+               73A38D551097D8B8470056E0B48FD703 /* MenuItemKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MenuItemKit.framework; path = MenuItemKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               73E5BA415740BBF222E187A28324E007 /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Alamofire.swift"; path = "Source/DispatchQueue+Alamofire.swift"; sourceTree = "<group>"; };
+               74A53A7DF4AC60A93A1B0E51F7868167 /* Raleway-Bold.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Raleway-Bold.ttf"; path = "Source/Resources/Fonts/Raleway/Raleway-Bold.ttf"; sourceTree = "<group>"; };
+               74D4394D9CF114521A46CA900125128B /* Pods-WolneLektury.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-WolneLektury.modulemap"; sourceTree = "<group>"; };
+               751FCB64C83107FC0D3415A048D97D1A /* Internals.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Internals.swift; path = MenuItemKit/Internals.swift; sourceTree = "<group>"; };
+               75473FEF44F28C44015117474F2BD735 /* results.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = results.cpp; path = Realm/ObjectStore/src/results.cpp; sourceTree = "<group>"; };
+               75B1B63C2A759AECF71AAE0B4E974E6E /* aes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = aes.h; path = SSZipArchive/minizip/aes/aes.h; sourceTree = "<group>"; };
+               75C33EBDEC91BF786E5D6B619847EBE7 /* Answers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Answers.h; path = iOS/Crashlytics.framework/Headers/Answers.h; sourceTree = "<group>"; };
+               75E719D436BFA4E227332190F0A114CC /* FIRMessagingCodedInputStream.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingCodedInputStream.m; path = Firebase/Messaging/FIRMessagingCodedInputStream.m; sourceTree = "<group>"; };
+               767CA3F1E7E65948D78C5DF3A4C074E0 /* prng.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = prng.h; path = SSZipArchive/minizip/aes/prng.h; sourceTree = "<group>"; };
+               767EF6727F4D919F160041B1B4BD5D24 /* SSZipArchive.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SSZipArchive.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               76C8AA2F24D4A7499FBFEC89207A32B8 /* FIRErrors.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRErrors.m; path = Firebase/Core/FIRErrors.m; sourceTree = "<group>"; };
+               77801061E8A612FE9356272D868D0287 /* GPBMessage_PackagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBMessage_PackagePrivate.h; path = objectivec/GPBMessage_PackagePrivate.h; sourceTree = "<group>"; };
+               77D4DB8CCBD4568AF20BA0C53858FE2C /* FRBook.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FRBook.swift; path = Source/EPUBCore/FRBook.swift; sourceTree = "<group>"; };
+               77E15D3937F6AFDAD39719014F24AF7A /* FIRCoreConfigurable.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRCoreConfigurable.h; path = Firebase/Core/Private/FIRCoreConfigurable.h; sourceTree = "<group>"; };
+               77F70CB6DF1D0FD7256121A7906A25E0 /* FIRComponent.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponent.h; path = Firebase/Core/Private/FIRComponent.h; sourceTree = "<group>"; };
+               788487EAB3CAF7711050C108550F7F71 /* CLSStackFrame.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CLSStackFrame.h; path = iOS/Crashlytics.framework/Headers/CLSStackFrame.h; sourceTree = "<group>"; };
+               78DDB9DF0FB83A9E64F5C81FD75182BA /* GULAppEnvironmentUtil.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppEnvironmentUtil.h; path = GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h; sourceTree = "<group>"; };
+               78F09E028FCE691991655050A5C01557 /* Results.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Results.swift; path = RealmSwift/Results.swift; sourceTree = "<group>"; };
+               790BCAA2E56A258EC49B7088FF78B747 /* sha1.c */ = {isa = PBXFileReference; includeInIndex = 1; name = sha1.c; path = SSZipArchive/minizip/aes/sha1.c; sourceTree = "<group>"; };
+               7948622C522A6B89E5AB5041C4F1C30F /* Bridge.js */ = {isa = PBXFileReference; includeInIndex = 1; name = Bridge.js; path = Source/Resources/Bridge.js; sourceTree = "<group>"; };
+               7961A53CC6789E3BF3F511B917EB410E /* Lora-Bold.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Lora-Bold.ttf"; path = "Source/Resources/Fonts/Lora/Lora-Bold.ttf"; sourceTree = "<group>"; };
+               79E033869BAC941730E6D735B4343813 /* aestab.c */ = {isa = PBXFileReference; includeInIndex = 1; name = aestab.c; path = SSZipArchive/minizip/aes/aestab.c; sourceTree = "<group>"; };
+               7A658FBE00AD5575FAEB350259C6C0C6 /* FIRMessagingPubSubRegistrar.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingPubSubRegistrar.m; path = Firebase/Messaging/FIRMessagingPubSubRegistrar.m; sourceTree = "<group>"; };
+               7AA2497F29326836BF937B466101FCB1 /* FieldMask.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FieldMask.pbobjc.h; path = objectivec/google/protobuf/FieldMask.pbobjc.h; sourceTree = "<group>"; };
+               7ABD3E8F081279D07705FBE2734A30CC /* FIRMessagingConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingConstants.m; path = Firebase/Messaging/FIRMessagingConstants.m; sourceTree = "<group>"; };
+               7B037AD884094D78946E98AECCBDBCB3 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; };
+               7C031C39DEA92D368607CCB505525724 /* aes_ni.c */ = {isa = PBXFileReference; includeInIndex = 1; name = aes_ni.c; path = SSZipArchive/minizip/aes/aes_ni.c; sourceTree = "<group>"; };
+               7C0F994B8858C9DEB29A4E169084A475 /* GULUserDefaults.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULUserDefaults.h; path = GoogleUtilities/UserDefaults/Private/GULUserDefaults.h; sourceTree = "<group>"; };
+               7C66C0B1401F75949F378B18E584E000 /* OAuthSwiftHTTPRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuthSwiftHTTPRequest.swift; path = Sources/OAuthSwiftHTTPRequest.swift; sourceTree = "<group>"; };
+               7D124571A3976EA459CE4987C55A9192 /* GPBArray.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBArray.h; path = objectivec/GPBArray.h; sourceTree = "<group>"; };
+               7D42DEFD122E76DA6A7E235F66B755A0 /* FIRMessagingDataMessageManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingDataMessageManager.h; path = Firebase/Messaging/FIRMessagingDataMessageManager.h; sourceTree = "<group>"; };
+               7DB7F3C979E6A543E2E6DB68E1B2B57E /* UIApplication+OAuthSwift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIApplication+OAuthSwift.swift"; path = "Sources/UIApplication+OAuthSwift.swift"; sourceTree = "<group>"; };
+               7E1E4AAE9C884BBB11A62AB5632E491D /* GoogleUtilities-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GoogleUtilities-prefix.pch"; sourceTree = "<group>"; };
+               7E200356C1953B2F03FD9BCED858D6A3 /* UITableViewVibrantCell.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UITableViewVibrantCell.swift; path = Pod/Classes/UITableViewVibrantCell.swift; sourceTree = "<group>"; };
+               7E3BE0CB2177D65A373AA72656755163 /* GPBExtensionRegistry.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBExtensionRegistry.m; path = objectivec/GPBExtensionRegistry.m; sourceTree = "<group>"; };
+               7E7474ABDCF1D1C9A8212C18E3BCFB2B /* RLMNetworkClient.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMNetworkClient.mm; path = Realm/RLMNetworkClient.mm; sourceTree = "<group>"; };
+               7E803E5235D95B461EE5B6994A8C102A /* pb_encode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb_encode.h; sourceTree = "<group>"; };
+               7EC37761CA1DCE7138823E75B1E22949 /* GULNetworkConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkConstants.h; path = GoogleUtilities/Network/Private/GULNetworkConstants.h; sourceTree = "<group>"; };
+               7ED838A709B4B90C48DC5866242DA115 /* librealmcore-ios.a */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = archive.ar; name = "librealmcore-ios.a"; path = "core/librealmcore-ios.a"; sourceTree = "<group>"; };
+               7EDB45F1273F71C411A0545C4CE445E3 /* FolioReaderKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderKit.swift; path = Source/FolioReaderKit.swift; sourceTree = "<group>"; };
+               7EF265DF52FA8325B0C347AAB6C2F966 /* FirebaseCore.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = FirebaseCore.modulemap; sourceTree = "<group>"; };
+               7F9995DB177F960FAC776F60EE05C851 /* FirebaseMessaging.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FirebaseMessaging.h; path = Firebase/Messaging/Public/FirebaseMessaging.h; sourceTree = "<group>"; };
+               7FE1DC4CFC4F73D99D647A77014F550D /* TaskDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TaskDelegate.swift; path = Source/TaskDelegate.swift; sourceTree = "<group>"; };
+               800828347609EC68238C01C399EE9BAB /* NSError+FIRMessaging.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSError+FIRMessaging.h"; path = "Firebase/Messaging/NSError+FIRMessaging.h"; sourceTree = "<group>"; };
+               80A169221157FA4EC5B1B57B2E943A32 /* Toast-Swift.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Toast-Swift.xcconfig"; sourceTree = "<group>"; };
+               813E11E3BB26542B146AEA87E7EF84B0 /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Source/Response.swift; sourceTree = "<group>"; };
+               8180AFDD65D23809C369C6C0D0AE2006 /* MZDownloadManager.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MZDownloadManager.framework; path = MZDownloadManager.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               829AD1D8E7F6EE418C950F905263EADD /* Images.xcassets */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Source/Resources/Images.xcassets; sourceTree = "<group>"; };
+               82DC2D8E239E4638D8DB80C8A06D83BB /* ioapi.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ioapi.h; path = SSZipArchive/minizip/ioapi.h; sourceTree = "<group>"; };
+               832216D1A72BB1036524B4C72D8DD3AD /* Kingfisher.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Kingfisher.xcconfig; sourceTree = "<group>"; };
+               842A4F7CFA4D598C49D85FD16BDA6863 /* HADiscreteSlider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HADiscreteSlider.swift; path = Vendor/HAControls/HADiscreteSlider.swift; sourceTree = "<group>"; };
+               855D4A41CB229BD53497542B6A0C0045 /* Realm.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Realm.modulemap; sourceTree = "<group>"; };
+               858870B7EB30D724E5204DDD159ABB0B /* RLMSyncCredentials.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMSyncCredentials.m; path = Realm/RLMSyncCredentials.m; sourceTree = "<group>"; };
+               85F4B8052CF32CEC52ADCF95F476CFF4 /* MenuItemKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MenuItemKit-umbrella.h"; sourceTree = "<group>"; };
+               85F84124A6BFAFBBE1B88A9C531344EE /* sync_config.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_config.cpp; path = Realm/ObjectStore/src/sync/sync_config.cpp; sourceTree = "<group>"; };
+               867A9A2B86FF919DE6428765E5138E5E /* FolioReaderKit.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FolioReaderKit.xcconfig; sourceTree = "<group>"; };
+               867D7359F1B0B74054D95F921EE9719D /* FolioReaderPage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderPage.swift; path = Source/FolioReaderPage.swift; sourceTree = "<group>"; };
+               86D80C24B863D4F395E5574A6A6AF46E /* Pods-WolneLektury-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-WolneLektury-resources.sh"; sourceTree = "<group>"; };
+               8721E6220CCCF76B0303EB98C5388FA4 /* FIRMessagingRemoteNotificationsProxy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingRemoteNotificationsProxy.m; path = Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m; sourceTree = "<group>"; };
+               874F2D847F616BB0F3DA0EFB4ACD0FD2 /* Type.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Type.pbobjc.h; path = objectivec/google/protobuf/Type.pbobjc.h; sourceTree = "<group>"; };
+               87970034DF91CE05199D6AAA2E708D72 /* GPBCodedInputStream.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBCodedInputStream.h; path = objectivec/GPBCodedInputStream.h; sourceTree = "<group>"; };
+               8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
+               882EF9DA2512BFAF5A2DB148555DE38D /* ioapi_mem.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ioapi_mem.h; path = SSZipArchive/minizip/ioapi_mem.h; sourceTree = "<group>"; };
+               8835399917D54555633265E36FF8665D /* FIRMessagingReceiver.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingReceiver.h; path = Firebase/Messaging/FIRMessagingReceiver.h; sourceTree = "<group>"; };
+               88442290CE172CD09060A11A2945E6A8 /* Firebase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Firebase.h; path = CoreOnly/Sources/Firebase.h; sourceTree = "<group>"; };
+               888A59D2EDB25ABCA1679CB435837211 /* RealmConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RealmConfiguration.swift; path = RealmSwift/RealmConfiguration.swift; sourceTree = "<group>"; };
+               88A4ECC72B8822083B261FCBB3FF852B /* RLMRealmConfiguration+Sync.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "RLMRealmConfiguration+Sync.mm"; path = "Realm/RLMRealmConfiguration+Sync.mm"; sourceTree = "<group>"; };
+               88AE1EEFE43FF13884C8AB2A06138B81 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
+               88B6D9C42264CD0C6BBBAA16AC38FBAB /* RLMJSONModels.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = RLMJSONModels.m; path = Realm/RLMJSONModels.m; sourceTree = "<group>"; };
+               89079A7F371E0726D388AB0BF9142BC1 /* OAuthSwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = OAuthSwift.modulemap; sourceTree = "<group>"; };
+               89B9846A28B16483D7E8716E91BD0781 /* uuid.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = uuid.cpp; path = Realm/ObjectStore/src/util/uuid.cpp; sourceTree = "<group>"; };
+               89E56BEB4CD676A8D94F51F9C6E01ED1 /* Pods-WolneLektury-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-WolneLektury-acknowledgements.markdown"; sourceTree = "<group>"; };
+               8A66F3D39C1729EE4090DC644FBA6F65 /* brg_endian.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = brg_endian.h; path = SSZipArchive/minizip/aes/brg_endian.h; sourceTree = "<group>"; };
+               8A7FC9C89D6223A32109126F8DA0598B /* Optional.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Optional.swift; path = RealmSwift/Optional.swift; sourceTree = "<group>"; };
+               8A8A815B8D521F8CDEB202EE76039FB7 /* RealmSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = RealmSwift.framework; path = RealmSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               8AB132E5EB9BAC1B61BECF12A10C2BF5 /* ObjectSchema.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObjectSchema.swift; path = RealmSwift/ObjectSchema.swift; sourceTree = "<group>"; };
+               8BA27C2B8AAF78AFF56284BFAEABBBEF /* GoogleUtilities.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = GoogleUtilities.framework; path = GoogleUtilities.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               8BB3550CF8402CA38A8968A24619F96D /* list.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = list.cpp; path = Realm/ObjectStore/src/list.cpp; sourceTree = "<group>"; };
+               8C1DA1A18B834B12A4D0AB03D6158BAA /* FIRMessagingUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingUtilities.h; path = Firebase/Messaging/FIRMessagingUtilities.h; sourceTree = "<group>"; };
+               8C880512EDE126F43FCD1A23D069173B /* Pods-WolneLektury-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-WolneLektury-acknowledgements.plist"; sourceTree = "<group>"; };
+               8CB63B8D80EDAD314AAD2D198C7B0C90 /* ObjectiveCSupport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ObjectiveCSupport.swift; path = RealmSwift/ObjectiveCSupport.swift; sourceTree = "<group>"; };
+               8CC80ED2F8C74EAA722AF3F92251516C /* ioapi_buf.c */ = {isa = PBXFileReference; includeInIndex = 1; name = ioapi_buf.c; path = SSZipArchive/minizip/ioapi_buf.c; sourceTree = "<group>"; };
+               8D93CE20821B7BD08A6D6ED2CE388920 /* GPBUnknownFieldSet.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBUnknownFieldSet.h; path = objectivec/GPBUnknownFieldSet.h; sourceTree = "<group>"; };
+               8DA785A1C830431915F33A46AD84FEF7 /* NSError+OAuthSwift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSError+OAuthSwift.swift"; path = "Sources/NSError+OAuthSwift.swift"; sourceTree = "<group>"; };
+               8E26DF3327D2EE24CED40AA07E5CC179 /* RLMArray.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMArray.h; path = include/RLMArray.h; sourceTree = "<group>"; };
+               8E3805379AC3A9301D26DE62077DF340 /* FieldMask.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FieldMask.pbobjc.m; path = objectivec/google/protobuf/FieldMask.pbobjc.m; sourceTree = "<group>"; };
+               8E4511CE1127E4BD217BDCDA408D6412 /* MBProgressHUD.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = MBProgressHUD.h; sourceTree = "<group>"; };
+               8E53544433E2DD770130AA1E2E2DCF4D /* SSZipArchive.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SSZipArchive.xcconfig; sourceTree = "<group>"; };
+               8F2399A1BF621F8F87173F1D95B405A3 /* FormatIndicatedCacheSerializer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FormatIndicatedCacheSerializer.swift; path = Sources/FormatIndicatedCacheSerializer.swift; sourceTree = "<group>"; };
+               8F6A6DC559F6B50404824FF2BC108753 /* RLMListBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMListBase.mm; path = Realm/RLMListBase.mm; sourceTree = "<group>"; };
+               903326FCEFF6EA67E56D2AC3651DA53D /* RLMSyncUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncUtil.mm; path = Realm/RLMSyncUtil.mm; sourceTree = "<group>"; };
+               9063F3DE5317FC929388EA67A8F5C368 /* MBProgressHUD-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MBProgressHUD-prefix.pch"; sourceTree = "<group>"; };
+               913FAA89ACC1B00F7CB063D2904C54F4 /* FIRApp.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRApp.h; path = Firebase/Core/Public/FIRApp.h; sourceTree = "<group>"; };
+               914C23ACBDFC7C74C0EF765A53C0EC7C /* FIRMessagingCodedInputStream.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingCodedInputStream.h; path = Firebase/Messaging/FIRMessagingCodedInputStream.h; sourceTree = "<group>"; };
+               91EF582D4746813F0EBB183BC4373C05 /* ParameterEncoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoding.swift; path = Source/ParameterEncoding.swift; sourceTree = "<group>"; };
+               920321EC36450F9B7B43C072420F78D7 /* GPBMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBMessage.h; path = objectivec/GPBMessage.h; sourceTree = "<group>"; };
+               9261FF50BC82FC7986B85E0D8CE24D61 /* Andada-Regular.otf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Andada-Regular.otf"; path = "Source/Resources/Fonts/Andada/Andada-Regular.otf"; sourceTree = "<group>"; };
+               92B419D2322B119DB7175E65FF9B76F3 /* FIRMessagingReceiver.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingReceiver.m; path = Firebase/Messaging/FIRMessagingReceiver.m; sourceTree = "<group>"; };
+               92BF3A91822F9A6AA5AC338BDF21C78E /* JSQWebViewController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = JSQWebViewController.framework; path = JSQWebViewController.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
+               93B635E4D5E3A40A7208AF4D15B52428 /* FIRMessagingRmq2PersistentStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingRmq2PersistentStore.m; path = Firebase/Messaging/FIRMessagingRmq2PersistentStore.m; sourceTree = "<group>"; };
+               93FA1B8771A7779F02E65DEC3E502D16 /* MediaType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MediaType.swift; path = Source/EPUBCore/MediaType.swift; sourceTree = "<group>"; };
+               9407CB8E023E9A5CA6CBCCDBFE4232D0 /* RLMMigration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMMigration_Private.h; path = include/RLMMigration_Private.h; sourceTree = "<group>"; };
+               942DDF3E267A719FF596B56B855D26D9 /* FontBlaster-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FontBlaster-dummy.m"; sourceTree = "<group>"; };
+               9470A90522247E16FD92E96415CCBE86 /* Session.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Session.swift; path = MatomoTracker/Session.swift; sourceTree = "<group>"; };
+               949A6E53FE16E2D6C8BCFE91DF8285B6 /* SideMenuTransition.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SideMenuTransition.swift; path = Pod/Classes/SideMenuTransition.swift; sourceTree = "<group>"; };
+               949BE471587E597B7F1E52BF803F5EA0 /* FolioReaderHighlightList.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderHighlightList.swift; path = Source/FolioReaderHighlightList.swift; sourceTree = "<group>"; };
+               94B7E924BB49EFDE5E1CF30DB2934504 /* MZUtility.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MZUtility.swift; path = MZDownloadManager/Classes/MZUtility.swift; sourceTree = "<group>"; };
+               94BC0645BAAC1EB760B6B157A87213E0 /* FontBlaster.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FontBlaster.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               95086548DA4949A5831665FD287ACD99 /* FirebaseMessaging.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FirebaseMessaging.h; path = Firebase/Messaging/FirebaseMessaging.h; sourceTree = "<group>"; };
+               951EF2BE6965114A5D8E84809EAC6424 /* MatomoUserDefaults.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MatomoUserDefaults.swift; path = MatomoTracker/MatomoUserDefaults.swift; sourceTree = "<group>"; };
+               951F8277D8CC7B918EC3F93FB9E672B7 /* OAuthSwiftClient.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuthSwiftClient.swift; path = Sources/OAuthSwiftClient.swift; sourceTree = "<group>"; };
+               952F782A447D10CA2ADAA9DFD3016A9A /* UISideMenuNavigationController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UISideMenuNavigationController.swift; path = Pod/Classes/UISideMenuNavigationController.swift; sourceTree = "<group>"; };
+               9570CA07B3D1379C659F3B95E6764364 /* FRSpine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FRSpine.swift; path = Source/EPUBCore/FRSpine.swift; sourceTree = "<group>"; };
+               959CC486954AB25818AE8D824A60C530 /* FRResource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FRResource.swift; path = Source/EPUBCore/FRResource.swift; sourceTree = "<group>"; };
+               95D533AD2585411926C7E5E8AC9C4CB0 /* FolioReaderKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FolioReaderKit-prefix.pch"; sourceTree = "<group>"; };
+               95E96EB83DCAB6611B1B9CE36203FD71 /* SwiftKeychainWrapper.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftKeychainWrapper.framework; path = SwiftKeychainWrapper.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               963111857C649092C623F85E0357C427 /* pb_decode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb_decode.h; sourceTree = "<group>"; };
+               9652C4208B2FE01685B9A06DA74AFC47 /* GPBArray.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBArray.m; path = objectivec/GPBArray.m; sourceTree = "<group>"; };
+               972B088C9B63AE08FC00845F18AAD308 /* ImageModifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageModifier.swift; path = Sources/ImageModifier.swift; sourceTree = "<group>"; };
+               9757E13C4DE3D7D5E6DAEA3955E6FAD5 /* Element.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Element.swift; path = Sources/Element.swift; sourceTree = "<group>"; };
+               97B70D1D75462F234CC7CB3B3BD41F38 /* KeychainWrapper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeychainWrapper.swift; path = SwiftKeychainWrapper/KeychainWrapper.swift; sourceTree = "<group>"; };
+               981A5B8EBD0C0B1A640FF08231E6B5B6 /* FIRMessagingContextManagerService.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingContextManagerService.h; path = Firebase/Messaging/FIRMessagingContextManagerService.h; sourceTree = "<group>"; };
+               989BDF769D8240DA767C777DF3E091BA /* SMSegment.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SMSegment.swift; path = Vendor/SMSegmentView/SMSegment.swift; sourceTree = "<group>"; };
+               98BD4D3148F63139C8DAA1AD6929A2C9 /* FolioReaderFontsMenu.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderFontsMenu.swift; path = Source/FolioReaderFontsMenu.swift; sourceTree = "<group>"; };
+               98BE3D9CD9277E245270D62A28AD5DC6 /* GPBUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBUtilities.h; path = objectivec/GPBUtilities.h; sourceTree = "<group>"; };
+               994AF878471D422AC2866C7E5A3733AE /* FIRMessagingClient.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingClient.m; path = Firebase/Messaging/FIRMessagingClient.m; sourceTree = "<group>"; };
+               996EA22BABCBE1C3BAD96548D6A604EA /* AlamofireActivityLogger-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "AlamofireActivityLogger-dummy.m"; sourceTree = "<group>"; };
+               998DC89C6539EB5EE837C792F9D36764 /* RLMProperty.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMProperty.h; path = include/RLMProperty.h; sourceTree = "<group>"; };
+               99A354380AB7A447785FC47FD513732A /* JSQWebViewController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JSQWebViewController.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               99E4C1135178406ACD7B58D040D578B8 /* RLMAccessor.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMAccessor.mm; path = Realm/RLMAccessor.mm; sourceTree = "<group>"; };
+               9A28197B26F0C810272E9A84A7487DD3 /* FIRMessagingClient.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingClient.h; path = Firebase/Messaging/FIRMessagingClient.h; sourceTree = "<group>"; };
+               9A52F85BB96ED2C9CB95229AEA2579A0 /* MZDownloadManager.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MZDownloadManager.xcconfig; sourceTree = "<group>"; };
+               9A6814BEFF03CE726BEBE631A1023F3D /* Schema.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Schema.swift; path = RealmSwift/Schema.swift; sourceTree = "<group>"; };
+               9AF454025C49412F40A5D08FF3EFD576 /* FolioReaderKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FolioReaderKit.framework; path = FolioReaderKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               9B1A74A1C0913D69BECC8DD4A1244E42 /* Alamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Alamofire-dummy.m"; sourceTree = "<group>"; };
+               9B1E7F0536D4F2862E841EA8D47CC44E /* FIRAnalyticsConfiguration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRAnalyticsConfiguration.m; path = Firebase/Core/FIRAnalyticsConfiguration.m; sourceTree = "<group>"; };
+               9B6147E3ED6E10C6C9C37BB8A5B2379A /* Highlight+Helper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Highlight+Helper.swift"; path = "Source/Models/Highlight+Helper.swift"; sourceTree = "<group>"; };
+               9BAB9A55E1DAE6C7836391C9CDC942B6 /* GULLoggerCodes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULLoggerCodes.h; path = GoogleUtilities/Common/GULLoggerCodes.h; sourceTree = "<group>"; };
+               9BDDA12CFFFDAF42893E0356DEFD50B9 /* FIRMessagingTopicOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingTopicOperation.m; path = Firebase/Messaging/FIRMessagingTopicOperation.m; sourceTree = "<group>"; };
+               9BE6F0CE8E94835C26F17B16EAFC5775 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               9C8782BA532A6DC13F487B719C37E76D /* RealmSwift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RealmSwift-prefix.pch"; sourceTree = "<group>"; };
+               9C996AC58C57E04E25603A8E527EFDD9 /* GPBCodedOutputStream_PackagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBCodedOutputStream_PackagePrivate.h; path = objectivec/GPBCodedOutputStream_PackagePrivate.h; sourceTree = "<group>"; };
+               9D3AC472F71CAA627B1A729C76C47E40 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Kingfisher.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               9D4BD4F234C5733679D2D1FAC1B99FE4 /* Pods-WolneLektury.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-WolneLektury.release.xcconfig"; sourceTree = "<group>"; };
+               9E347A569BFF34387F66BD101D00CE61 /* GPBUtilities_PackagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBUtilities_PackagePrivate.h; path = objectivec/GPBUtilities_PackagePrivate.h; sourceTree = "<group>"; };
+               9E3C47D73094DB09E1D3465A7E11ED30 /* RLMSchema_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSchema_Private.h; path = include/RLMSchema_Private.h; sourceTree = "<group>"; };
+               9E4FA5283B6F750E4FDB42A32E3D2DA3 /* crypt.c */ = {isa = PBXFileReference; includeInIndex = 1; name = crypt.c; path = SSZipArchive/minizip/crypt.c; sourceTree = "<group>"; };
+               9E8C5B26C86483D9A133E4500AD5F3E5 /* OAuthWebViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuthWebViewController.swift; path = Sources/OAuthWebViewController.swift; sourceTree = "<group>"; };
+               9F56E9572F18DCBB5E446C23D65E1213 /* RLMSyncPermission.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncPermission.h; path = include/RLMSyncPermission.h; sourceTree = "<group>"; };
+               9F65B7D4BDC23758E135571100B9ADD3 /* FIRConfiguration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRConfiguration.m; path = Firebase/Core/FIRConfiguration.m; sourceTree = "<group>"; };
+               9F903DCFBA56EDFAE70249F698E2B8A3 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               A03D3817FB78E6E3C9263752C05307DC /* RealmSwift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = RealmSwift.modulemap; sourceTree = "<group>"; };
+               A08B99A4BBBE9911D2FBF593D5865F00 /* Realm.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Realm.swift; path = RealmSwift/Realm.swift; sourceTree = "<group>"; };
+               A2349254CD9C9408722F71F6AD779F05 /* GtalkCore.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GtalkCore.pbobjc.h; path = Firebase/Messaging/Protos/GtalkCore.pbobjc.h; sourceTree = "<group>"; };
+               A24FF4F622BC306700C4E27CB55217B9 /* Pods-WolneLektury.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-WolneLektury.debug.xcconfig"; sourceTree = "<group>"; };
+               A499040F440DBB0BA31258B194703264 /* GULAppDelegateSwizzler_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppDelegateSwizzler_Private.h; path = GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h; sourceTree = "<group>"; };
+               A5D04CCAC2902D10865E0D2EF5DADE45 /* FIROptionsInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIROptionsInternal.h; path = Firebase/Core/Private/FIROptionsInternal.h; sourceTree = "<group>"; };
+               A5D9C7E3D96C2EBCE4D34719C6211F2E /* prng.c */ = {isa = PBXFileReference; includeInIndex = 1; name = prng.c; path = SSZipArchive/minizip/aes/prng.c; sourceTree = "<group>"; };
+               A66F9D57725E8CDA7C62CB465AA36456 /* FIRVersion.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRVersion.m; path = Firebase/Core/FIRVersion.m; sourceTree = "<group>"; };
+               A6705B2C75B45917B97EA5C687CE5BB8 /* nanopb-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "nanopb-prefix.pch"; sourceTree = "<group>"; };
+               A6A011D6F31E757D458A431970A9F122 /* Empty.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Empty.pbobjc.h; path = objectivec/google/protobuf/Empty.pbobjc.h; sourceTree = "<group>"; };
+               A6C3A06B414DEEC08D882C5AA7655DC3 /* NSError+FIRMessaging.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSError+FIRMessaging.m"; path = "Firebase/Messaging/NSError+FIRMessaging.m"; sourceTree = "<group>"; };
+               A725C1F81E58C098054A2B656E85AF76 /* GPBWellKnownTypes.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBWellKnownTypes.m; path = objectivec/GPBWellKnownTypes.m; sourceTree = "<group>"; };
+               A744EE68CA6B35FFFDEB557843FE5AF7 /* MatomoTracker.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MatomoTracker.modulemap; sourceTree = "<group>"; };
+               A8EAB79E1A81BA370C6AF53D9EAE85AA /* Andada-Bold.otf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Andada-Bold.otf"; path = "Source/Resources/Fonts/Andada/Andada-Bold.otf"; sourceTree = "<group>"; };
+               A9EE7F7299E48AE9BAE6A21FC4E45526 /* Pods_WolneLektury.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_WolneLektury.framework; path = "Pods-WolneLektury.framework"; sourceTree = BUILT_PRODUCTS_DIR; };
+               AA835BF7AFA43E2BF03FA593F7AD83CE /* GULAppEnvironmentUtil.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULAppEnvironmentUtil.m; path = GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m; sourceTree = "<group>"; };
+               AAE3C1B13216CF41A8A27FC654EAB454 /* RLMObjectBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase.h; path = include/RLMObjectBase.h; sourceTree = "<group>"; };
+               AAE711C897BC13E226DA166153F2CC1A /* FIRMessagingConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingConstants.h; path = Firebase/Messaging/FIRMessagingConstants.h; sourceTree = "<group>"; };
+               AB031B75B0BA0CBC90F67922A73A4C5D /* OAuthSwift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "OAuthSwift-prefix.pch"; sourceTree = "<group>"; };
+               AB30FE116FBBE62941E0322FB3B9496C /* MainThread.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MainThread.swift; path = MatomoTracker/MainThread.swift; sourceTree = "<group>"; };
+               ABFF357E3BABBE6991EFE7D5FDB24E1A /* RLMSyncConfiguration_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncConfiguration_Private.h; path = include/RLMSyncConfiguration_Private.h; sourceTree = "<group>"; };
+               AC93D8CD26E177BF4425613E26F84E62 /* unzip.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = unzip.h; path = SSZipArchive/minizip/unzip.h; sourceTree = "<group>"; };
+               AC9CFDB3F3B5693890E17EDBD113E565 /* RLMOptionalBase.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMOptionalBase.mm; path = Realm/RLMOptionalBase.mm; sourceTree = "<group>"; };
+               AD8344B4C8CA5CDCD06CDB95C9779E9E /* Parser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Parser.swift; path = Sources/Parser.swift; sourceTree = "<group>"; };
+               ADE4B9714FB02E4C58524BAA3E5D262E /* binding_callback_thread_observer.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = binding_callback_thread_observer.cpp; path = Realm/ObjectStore/src/binding_callback_thread_observer.cpp; sourceTree = "<group>"; };
+               ADE94AD745E9D666DE8E2CEDAC68333D /* SSZipArchive.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SSZipArchive.framework; path = SSZipArchive.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               ADE9A188FDEC484B9CDF695ECE7959A8 /* MBProgressHUD.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MBProgressHUD.modulemap; sourceTree = "<group>"; };
+               AE0A609553E6424094E5B32BF5FC4BBD /* Lora-Italic.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Lora-Italic.ttf"; path = "Source/Resources/Fonts/Lora/Lora-Italic.ttf"; sourceTree = "<group>"; };
+               AE1B1CA98EB9A6E3AAA0EB5E93C94CE0 /* FolioReaderUserDefaults.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderUserDefaults.swift; path = Source/FolioReaderUserDefaults.swift; sourceTree = "<group>"; };
+               AE2928B25227E5D294693BA171652B40 /* Lora-BoldItalic.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Lora-BoldItalic.ttf"; path = "Source/Resources/Fonts/Lora/Lora-BoldItalic.ttf"; sourceTree = "<group>"; };
+               AEC20068DA07AB47A0BC6045AB5A15A9 /* ServerTrustPolicy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServerTrustPolicy.swift; path = Source/ServerTrustPolicy.swift; sourceTree = "<group>"; };
+               AF2F39EAC2946E050E52E2C675208EAB /* RLMSyncSession.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncSession.h; path = include/RLMSyncSession.h; sourceTree = "<group>"; };
+               AF31C2EDFE5591ADC92DB32F0BC409BC /* RLMSyncManager_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncManager_Private.h; path = include/RLMSyncManager_Private.h; sourceTree = "<group>"; };
+               AF61D0766107FF7823B5B06DFB3221D3 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               B056E5B697BF12D5A943B914A8944339 /* MatomoTracker.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MatomoTracker.xcconfig; sourceTree = "<group>"; };
+               B118C522E1E15B2BC5C5465B7A8BF1F8 /* minishared.c */ = {isa = PBXFileReference; includeInIndex = 1; name = minishared.c; path = SSZipArchive/minizip/minishared.c; sourceTree = "<group>"; };
+               B11AB6EE95088046C66066C1A9DAD036 /* Struct.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Struct.pbobjc.h; path = objectivec/google/protobuf/Struct.pbobjc.h; sourceTree = "<group>"; };
+               B16201B3D91EF42BC6478D973A85F4E3 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
+               B19AB99E9F6D28349E358F6A347A20B0 /* Indicator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Indicator.swift; path = Sources/Indicator.swift; sourceTree = "<group>"; };
+               B2AD5B1877FE90020B8EC5A5A9468CFD /* GPBCodedInputStream.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBCodedInputStream.m; path = objectivec/GPBCodedInputStream.m; sourceTree = "<group>"; };
+               B2D261BF1562798AA9C2BED6293A727E /* RLMSyncPermissionResults.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncPermissionResults.mm; path = Realm/RLMSyncPermissionResults.mm; sourceTree = "<group>"; };
+               B2FB4434FC33598F34B81CC4C2BBA4A6 /* GPBUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBUtilities.m; path = objectivec/GPBUtilities.m; sourceTree = "<group>"; };
+               B365D8630E824F91C12C169371386488 /* NotificationCenter+OAuthSwift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NotificationCenter+OAuthSwift.swift"; path = "Sources/NotificationCenter+OAuthSwift.swift"; sourceTree = "<group>"; };
+               B3F61ADE7C91CF96278DB665AE571F4E /* SSZipCommon.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SSZipCommon.h; path = SSZipArchive/SSZipCommon.h; sourceTree = "<group>"; };
+               B451004D639471DB5A62688F187D31F0 /* Pods-WolneLektury-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-WolneLektury-umbrella.h"; sourceTree = "<group>"; };
+               B4840E7D2EB9EB8417A1C9645FEDAAAE /* OAuthSwift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "OAuthSwift-umbrella.h"; sourceTree = "<group>"; };
+               B4894DE102973633A89B8B1A928B2F3F /* Toast.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Toast.swift; path = Toast/Toast.swift; sourceTree = "<group>"; };
+               B495C794563F95CB715EBF0C78EDBFE7 /* RLMOptionalBase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMOptionalBase.h; path = include/RLMOptionalBase.h; sourceTree = "<group>"; };
+               B49CE581DA5B45AF17857662EB6AE539 /* QuoteImage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = QuoteImage.swift; path = Source/QuoteImage.swift; sourceTree = "<group>"; };
+               B4CBDCB5B5D048C5CCB38F2A12D07EA0 /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Crashlytics.framework; path = iOS/Crashlytics.framework; sourceTree = "<group>"; };
+               B51E1C8BEE26A23D7600FD8BD5C9FCF2 /* SideMenu.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SideMenu.xcconfig; sourceTree = "<group>"; };
+               B52F39B542A1D04B86E6DAE81129EB7C /* MenuItemKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MenuItemKit-dummy.m"; sourceTree = "<group>"; };
+               B54964F717A975AE9680DA086D24374B /* FIRLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRLogger.m; path = Firebase/Core/FIRLogger.m; sourceTree = "<group>"; };
+               B5625DF9366F18F9E5B0D1CF875407AD /* NSDictionary+FIRMessaging.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+FIRMessaging.m"; path = "Firebase/Messaging/NSDictionary+FIRMessaging.m"; sourceTree = "<group>"; };
+               B5ECAEB329C348903C27484F1697B006 /* Toast-Swift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Toast-Swift.modulemap"; sourceTree = "<group>"; };
+               B641B98C9FE0F76E5A9246DD05AEF31C /* FIRMessagingVersionUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingVersionUtilities.h; path = Firebase/Messaging/FIRMessagingVersionUtilities.h; sourceTree = "<group>"; };
+               B6795E15B518CF390D339237ABBC9032 /* GoogleUtilities-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "GoogleUtilities-dummy.m"; sourceTree = "<group>"; };
+               B6A4879596BEAAC18CA41AD231043F17 /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Alamofire.framework; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               B6BCD5898DBE90DAD2D5258B776CE839 /* FIRLoggerLevel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLoggerLevel.h; path = Firebase/Core/Public/FIRLoggerLevel.h; sourceTree = "<group>"; };
+               B700720A592F03E24DAF4D98F45F06F5 /* SSZipArchive-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SSZipArchive-prefix.pch"; sourceTree = "<group>"; };
+               B703AD37258C65E00D1C7CBE59684A89 /* MZDownloadManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MZDownloadManager.swift; path = MZDownloadManager/Classes/MZDownloadManager.swift; sourceTree = "<group>"; };
+               B85E290DB3BDCF8A7288FE23314B642E /* Lato-BoldItalic.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Lato-BoldItalic.ttf"; path = "Source/Resources/Fonts/Lato/Lato-BoldItalic.ttf"; sourceTree = "<group>"; };
+               B8746C7F6910CD010EF2C8342D472881 /* MBProgressHUD.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MBProgressHUD.framework; path = MBProgressHUD.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               B896CF8A24113A3F12663506E53F6EAF /* EventSerializer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = EventSerializer.swift; path = MatomoTracker/EventSerializer.swift; sourceTree = "<group>"; };
+               B8A71DDD2334CACD70DFCA4D4C49E068 /* GoogleUtilities-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GoogleUtilities-umbrella.h"; sourceTree = "<group>"; };
+               B90FE0A11962A6DA9BF00E6837D6D97F /* Struct.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Struct.pbobjc.m; path = objectivec/google/protobuf/Struct.pbobjc.m; sourceTree = "<group>"; };
+               B91A3C531E1F1C9F2523B12FBC6B31F5 /* GtalkCore.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GtalkCore.pbobjc.m; path = Firebase/Messaging/Protos/GtalkCore.pbobjc.m; sourceTree = "<group>"; };
+               B99ABE547EBBB6C2B8F7FF04E1C02378 /* KingfisherOptionsInfo.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KingfisherOptionsInfo.swift; path = Sources/KingfisherOptionsInfo.swift; sourceTree = "<group>"; };
+               BA32998B725316B590F1094FCEDC7A57 /* RLMSyncManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncManager.h; path = include/RLMSyncManager.h; sourceTree = "<group>"; };
+               BB067AB097CD608D10755D6B04142C5B /* GPBExtensionRegistry.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBExtensionRegistry.h; path = objectivec/GPBExtensionRegistry.h; sourceTree = "<group>"; };
+               BB48738A79C0712C66A87BA10D8F71B0 /* nanopb.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = nanopb.framework; path = nanopb.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               BB849E98A82D4590D3BC88C7262C3BA0 /* thread_safe_reference.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = thread_safe_reference.cpp; path = Realm/ObjectStore/src/thread_safe_reference.cpp; sourceTree = "<group>"; };
+               BC05BD18E546C120599B8E7ECBDC263A /* RLMResults.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMResults.h; path = include/RLMResults.h; sourceTree = "<group>"; };
+               BD0C0339F7D29150F3C16CE5C6A047BA /* FIRAnalyticsConnector.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FIRAnalyticsConnector.framework; path = Frameworks/FIRAnalyticsConnector.framework; sourceTree = "<group>"; };
+               BDF51D99BBE2062A17F2CC6D14953BE3 /* Error.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Error.swift; path = RealmSwift/Error.swift; sourceTree = "<group>"; };
+               BE5B948DB0251166DBE25C3040F48E8B /* Filter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Filter.swift; path = Sources/Filter.swift; sourceTree = "<group>"; };
+               BE7C83907BD9FDBF71121B4369E85D4D /* FIRMessagingUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingUtilities.m; path = Firebase/Messaging/FIRMessagingUtilities.m; sourceTree = "<group>"; };
+               BEB3597A302BDA172DD6558B46724016 /* aeskey.c */ = {isa = PBXFileReference; includeInIndex = 1; name = aeskey.c; path = SSZipArchive/minizip/aes/aeskey.c; sourceTree = "<group>"; };
+               BEED1B7F7E8BEE0FECD996A3C14F1965 /* FIRVersion.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRVersion.h; path = Firebase/Core/Private/FIRVersion.h; sourceTree = "<group>"; };
+               BEF9F5FA8A61F170B0136C7055E746E0 /* results_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = results_notifier.cpp; path = Realm/ObjectStore/src/impl/results_notifier.cpp; sourceTree = "<group>"; };
+               BF4FAD5E6B95D0DCD4CF2873B7A5F657 /* FIRAppAssociationRegistration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRAppAssociationRegistration.m; path = Firebase/Core/FIRAppAssociationRegistration.m; sourceTree = "<group>"; };
+               BFC7E4A5F9F3490D492D52132DB76FC3 /* FolioReaderAudioPlayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderAudioPlayer.swift; path = Source/FolioReaderAudioPlayer.swift; sourceTree = "<group>"; };
+               C0AECA8112FB6A3BFDFA9BC9BC8AC765 /* ImageView+Kingfisher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "ImageView+Kingfisher.swift"; path = "Sources/ImageView+Kingfisher.swift"; sourceTree = "<group>"; };
+               C0BAC9516D62801AB56D27E95F7F3D9F /* FIRAppInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAppInternal.h; path = Firebase/Core/Private/FIRAppInternal.h; sourceTree = "<group>"; };
+               C1171DB362D96B024E18F290CEC25F65 /* object.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = object.cpp; path = Realm/ObjectStore/src/object.cpp; sourceTree = "<group>"; };
+               C1BE0B9E0494E6BB92AF3675D929C48B /* RLMRealm.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealm.mm; path = Realm/RLMRealm.mm; sourceTree = "<group>"; };
+               C1E4306D36A4D225A7D2A80EE05DEADF /* FIRMessagingRmqManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingRmqManager.m; path = Firebase/Messaging/FIRMessagingRmqManager.m; sourceTree = "<group>"; };
+               C242E0FBAFC647C7747ADD8DB8933A5D /* FirebaseCore-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FirebaseCore-umbrella.h"; sourceTree = "<group>"; };
+               C264DCAB575FD671A804AD57D7710977 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               C28470CDCC9315FCDA60C6472439C9EE /* pwd2key.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = pwd2key.h; path = SSZipArchive/minizip/aes/pwd2key.h; sourceTree = "<group>"; };
+               C2A1A3A5F1D030B843050DAD43A6C29B /* Lato-Italic.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Lato-Italic.ttf"; path = "Source/Resources/Fonts/Lato/Lato-Italic.ttf"; sourceTree = "<group>"; };
+               C32096158C38CC91501302DDD6AF488D /* KingfisherManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KingfisherManager.swift; path = Sources/KingfisherManager.swift; sourceTree = "<group>"; };
+               C32203867C907AC35E7B64C374D54195 /* LoggeableRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LoggeableRequest.swift; path = alamofire_activity_logger/ActivityLogger/LoggeableRequest.swift; sourceTree = "<group>"; };
+               C440878C8B1131EE071571ECF86D6B26 /* Andada-Italic.otf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Andada-Italic.otf"; path = "Source/Resources/Fonts/Andada/Andada-Italic.otf"; sourceTree = "<group>"; };
+               C46E57D513084D4F163AE3274C8ACEE0 /* MZDownloadManager-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MZDownloadManager-umbrella.h"; sourceTree = "<group>"; };
+               C4A0830A90DAC8805601A6BFBF4EB2B4 /* Locale+HttpAcceptLanguage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Locale+HttpAcceptLanguage.swift"; path = "MatomoTracker/Locale+HttpAcceptLanguage.swift"; sourceTree = "<group>"; };
+               C509D32CE877D802F9188A1C63C0EAE3 /* SideMenu-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SideMenu-umbrella.h"; sourceTree = "<group>"; };
+               C544F4546E0587724503A71F8C8A0730 /* RLMObjectBase_Dynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase_Dynamic.h; path = include/RLMObjectBase_Dynamic.h; sourceTree = "<group>"; };
+               C5B05C0825F95873D124EF95EAEF8DFA /* FolioReaderContainer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderContainer.swift; path = Source/FolioReaderContainer.swift; sourceTree = "<group>"; };
+               C5EAE55F8524706A50D1E64B2067946C /* GPBWireFormat.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBWireFormat.m; path = objectivec/GPBWireFormat.m; sourceTree = "<group>"; };
+               C5ECEF2B1DFCF0C01450C80D3BD0610F /* nanopb.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = nanopb.xcconfig; sourceTree = "<group>"; };
+               C631E06F6A57B0AE8546402CFFB399D1 /* Objc.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Objc.swift; path = Sources/Objc.swift; sourceTree = "<group>"; };
+               C67BBEF0CB8A371D0C8647130CD00979 /* RLMUpdateChecker.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMUpdateChecker.mm; path = Realm/RLMUpdateChecker.mm; sourceTree = "<group>"; };
+               C79142150002194346CF89C5F4514BB4 /* Resource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Resource.swift; path = Sources/Resource.swift; sourceTree = "<group>"; };
+               C7BD9F4CEDFB5189A20C7BDA0DD11C47 /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = weak_realm_notifier.cpp; path = Realm/ObjectStore/src/impl/weak_realm_notifier.cpp; sourceTree = "<group>"; };
+               C7E7BD134D837DB6672EBC1FDA1F10DD /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Source/MultipartFormData.swift; sourceTree = "<group>"; };
+               C7E9B4F5787DCAAC9B794757C5709BC4 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               C7EFF3E888638C1BF7E636B5D3F02372 /* SHA1.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SHA1.swift; path = Sources/SHA1.swift; sourceTree = "<group>"; };
+               C8C1441259AE0384F9D916435E4E1934 /* OAuthSwiftMultipartData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuthSwiftMultipartData.swift; path = Sources/OAuthSwiftMultipartData.swift; sourceTree = "<group>"; };
+               C9413A640B6F54FA4706173107E9234C /* ScrollScrubber.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScrollScrubber.swift; path = Source/ScrollScrubber.swift; sourceTree = "<group>"; };
+               C94C12B9600AFD613B21A25CCFE5EB1C /* Realm.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Realm.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               CA0D42E5ED3F3A6EA13743141521AB33 /* SideMenu-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SideMenu-prefix.pch"; sourceTree = "<group>"; };
+               CA1A1481F0FEAD0729D79CC5B96CC428 /* Kingfisher-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Kingfisher-dummy.m"; sourceTree = "<group>"; };
+               CA67844C5A4BD05A959C0E1A6F8CE505 /* GPBProtocolBuffers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBProtocolBuffers.h; path = objectivec/GPBProtocolBuffers.h; sourceTree = "<group>"; };
+               CA7CF0757872C51EE58D200C9E26B2FC /* RLMSchema.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSchema.h; path = include/RLMSchema.h; sourceTree = "<group>"; };
+               CB9A13DABF37C8B1C19A7D255823CAA4 /* Raleway-BoldItalic.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Raleway-BoldItalic.ttf"; path = "Source/Resources/Fonts/Raleway/Raleway-BoldItalic.ttf"; sourceTree = "<group>"; };
+               CC036F1EA6EB67C4A2C91AB3FCDEBC4C /* ZipArchive.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ZipArchive.h; path = SSZipArchive/ZipArchive.h; sourceTree = "<group>"; };
+               CC045719CEBEE0DAEFC813B823C1F006 /* RLMClassInfo.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMClassInfo.mm; path = Realm/RLMClassInfo.mm; sourceTree = "<group>"; };
+               CC32893E8F8BEBF85F395C456E4DC02E /* FIRMessagingTopicsCommon.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingTopicsCommon.h; path = Firebase/Messaging/FIRMessagingTopicsCommon.h; sourceTree = "<group>"; };
+               CCC7E5E20F306AB7DABDF3C6630C2AEF /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               CCDB7486E72F0C100C4E4BB6FEE50AD9 /* pb.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pb.h; sourceTree = "<group>"; };
+               CCE4359E18EB2A62E3B89616E1DF99AD /* partial_sync.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = partial_sync.cpp; path = Realm/ObjectStore/src/sync/partial_sync.cpp; sourceTree = "<group>"; };
+               CD162B8C0A7547B8B57A0CAA8D18DC3B /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               CDA1CF0F7DD23253D2FF8DEBD395CA56 /* FIRMessagingRegistrar.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingRegistrar.h; path = Firebase/Messaging/FIRMessagingRegistrar.h; sourceTree = "<group>"; };
+               CDBC7D8D526EF3A5D16C73935E37B18C /* FirebaseMessaging.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = FirebaseMessaging.modulemap; sourceTree = "<group>"; };
+               CDDF76EEC0C6394E55456C0935E846C1 /* ZFDragableModalTransition.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ZFDragableModalTransition.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               CE3134DC7AF610DBF7C87E8EDCEC2BED /* FIROptions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIROptions.m; path = Firebase/Core/FIROptions.m; sourceTree = "<group>"; };
+               CE6AE3ED1C3D1C2BFE3264A07CACD6E2 /* NSError+RLMSync.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSError+RLMSync.m"; path = "Realm/NSError+RLMSync.m"; sourceTree = "<group>"; };
+               CE8CAA6373F642196F0C634C6207FFDF /* GULLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULLogger.h; path = GoogleUtilities/Logger/Private/GULLogger.h; sourceTree = "<group>"; };
+               CF6A19A1959A02404C04F8950A83B596 /* Pods-WolneLektury-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-WolneLektury-dummy.m"; sourceTree = "<group>"; };
+               CFE9AB50F57A50B5C79F92AD4CAC7208 /* RLMRealm+Sync.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "RLMRealm+Sync.h"; path = "include/RLMRealm+Sync.h"; sourceTree = "<group>"; };
+               D063D80E2E978A2BE32BFE9214B50DBA /* FolioReaderPageIndicator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderPageIndicator.swift; path = Source/FolioReaderPageIndicator.swift; sourceTree = "<group>"; };
+               D08CFF3FAFCD900564C2FBD18D08004D /* OAuthSwiftCredential.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuthSwiftCredential.swift; path = Sources/OAuthSwiftCredential.swift; sourceTree = "<group>"; };
+               D0B097B839F15EE609BCC436E5608221 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               D0F97794D9CDF7090BB151567CC375CE /* FIRMessagingContextManagerService.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingContextManagerService.m; path = Firebase/Messaging/FIRMessagingContextManagerService.m; sourceTree = "<group>"; };
+               D35917412D73B9AC787F462EBB9FB9F2 /* Timeline.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Timeline.swift; path = Source/Timeline.swift; sourceTree = "<group>"; };
+               D4217B57C3B931D356222A9AD5E757B1 /* FIRMessagingDataMessageManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingDataMessageManager.m; path = Firebase/Messaging/FIRMessagingDataMessageManager.m; sourceTree = "<group>"; };
+               D43D637F253AC03F17C1986897E46ACC /* Protobuf.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Protobuf.modulemap; sourceTree = "<group>"; };
+               D46C127210F7863A4A22D9F761D51E5E /* AEXML.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AEXML.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               D489E4F14592AD2E12AB3FE975D754FD /* MatomoTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MatomoTracker.framework; path = MatomoTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               D4D39D9675E518588A8F01F21E57FADD /* RLMAccessor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMAccessor.h; path = include/RLMAccessor.h; sourceTree = "<group>"; };
+               D4D57FCAFAEC5BD45B0A651B90B820F7 /* FIRDependency.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRDependency.h; path = Firebase/Core/Private/FIRDependency.h; sourceTree = "<group>"; };
+               D5C47C18DAE8DE892F90FDDBF5ADB969 /* FolioReaderKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FolioReaderKit.h; path = Source/FolioReaderKit.h; sourceTree = "<group>"; };
+               D656437C48D246F2B717E827BC5622B4 /* SideMenu.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SideMenu.modulemap; sourceTree = "<group>"; };
+               D6C3231D8AF28863899D26FC24910D46 /* Visitor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Visitor.swift; path = MatomoTracker/Visitor.swift; sourceTree = "<group>"; };
+               D70294FBB42ACF7801395BF1CDD4A314 /* Api.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Api.pbobjc.h; path = objectivec/google/protobuf/Api.pbobjc.h; sourceTree = "<group>"; };
+               D77B5098FA436FB6E1221AB6EDF22370 /* GPBMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBMessage.m; path = objectivec/GPBMessage.m; sourceTree = "<group>"; };
+               D795F064F1050FA6050617BB0936FBD1 /* Protobuf.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Protobuf.xcconfig; sourceTree = "<group>"; };
+               D7F8073C1B181564744EF259B9F8739C /* Timestamp.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Timestamp.pbobjc.m; path = objectivec/google/protobuf/Timestamp.pbobjc.m; sourceTree = "<group>"; };
+               D81BFE71A07B939C010F22763CBED031 /* FIRMessagingPacketQueue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingPacketQueue.m; path = Firebase/Messaging/FIRMessagingPacketQueue.m; sourceTree = "<group>"; };
+               D83DAE8A53744DA268CA2420E716533B /* FolioReaderConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FolioReaderConfig.swift; path = Source/FolioReaderConfig.swift; sourceTree = "<group>"; };
+               D981664DF64C9BF8EA0C1391E3BCDC0B /* RLMSyncPermission.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncPermission.mm; path = Realm/RLMSyncPermission.mm; sourceTree = "<group>"; };
+               D9DB620986320D1403CEEBA251CB09C2 /* Swizzlings.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Swizzlings.swift; path = MenuItemKit/Swizzlings.swift; sourceTree = "<group>"; };
+               DA0B520E2EE0857DCD4B89E33AA8F06E /* WebViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = WebViewController.swift; path = Source/WebViewController.swift; sourceTree = "<group>"; };
+               DA0DAE3F773587A21A814757E4F02240 /* GPBCodedInputStream_PackagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBCodedInputStream_PackagePrivate.h; path = objectivec/GPBCodedInputStream_PackagePrivate.h; sourceTree = "<group>"; };
+               DA4991241E87AAE84DFB7BA0D724913D /* FIRMessagingPubSubRegistrar.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingPubSubRegistrar.h; path = Firebase/Messaging/FIRMessagingPubSubRegistrar.h; sourceTree = "<group>"; };
+               DAED7224D7EC25824E0386956459E7A9 /* ZFDragableModalTransition-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ZFDragableModalTransition-umbrella.h"; sourceTree = "<group>"; };
+               DAF1B68A9CAAA2CBE0B167CB2DB3976B /* sync_permission.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_permission.cpp; path = Realm/ObjectStore/src/sync/sync_permission.cpp; sourceTree = "<group>"; };
+               DC09E3FDEEA4F2DCC4E5C5E2947C1282 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               DC2E8829C4E43BC76E7158A0E3E701AF /* GULNetwork.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetwork.m; path = GoogleUtilities/Network/GULNetwork.m; sourceTree = "<group>"; };
+               DCD69015A6C555B93460E69425DB1406 /* Alamofire.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.xcconfig; sourceTree = "<group>"; };
+               DD11C12E8B8A3662D6DE8C3D9CC3DE31 /* RLMMigration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMMigration.h; path = include/RLMMigration.h; sourceTree = "<group>"; };
+               DDE5567F4018883FFB851237B4CB839C /* GULAppDelegateSwizzler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULAppDelegateSwizzler.m; path = GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m; sourceTree = "<group>"; };
+               DDFAC3CB75A6486957AF5FE89066B9C5 /* SwiftKeychainWrapper.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SwiftKeychainWrapper.h; path = SwiftKeychainWrapper/SwiftKeychainWrapper.h; sourceTree = "<group>"; };
+               DE2432A0214596A77E0715BF9EDA6217 /* FontBlaster.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FontBlaster.framework; path = FontBlaster.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               DE3D1E8C327C132322D396C80318931C /* List.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = List.swift; path = RealmSwift/List.swift; sourceTree = "<group>"; };
+               DEA6D6B4545E008C5EAD0934BFDA40CD /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               DEA90EC1C9E0AAD475B6E5C849B0DFE7 /* SourceContext.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SourceContext.pbobjc.h; path = objectivec/google/protobuf/SourceContext.pbobjc.h; sourceTree = "<group>"; };
+               DEE0C47DB821716AA0D71317CBEF8D48 /* SSZipArchive.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SSZipArchive.modulemap; sourceTree = "<group>"; };
+               DF16170DE63470802D26A252D0E24CE8 /* RLMSyncConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncConfiguration.h; path = include/RLMSyncConfiguration.h; sourceTree = "<group>"; };
+               DF1FEC972A61EF84B2A3BE043B7FE763 /* Kingfisher.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Kingfisher.modulemap; sourceTree = "<group>"; };
+               E00C401BE147D47DFBB42AA69755AF0D /* RLMRealm_Dynamic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm_Dynamic.h; path = include/RLMRealm_Dynamic.h; sourceTree = "<group>"; };
+               E0EBEE00C0127E3C90E732DB5F01CF5D /* RLMObjectBase_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectBase_Private.h; path = include/RLMObjectBase_Private.h; sourceTree = "<group>"; };
+               E1370966B1A42FC061491DDB4307FD36 /* GPBUnknownFieldSet_PackagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBUnknownFieldSet_PackagePrivate.h; path = objectivec/GPBUnknownFieldSet_PackagePrivate.h; sourceTree = "<group>"; };
+               E14F77E50CAD9E11C3549CBA1979F1DF /* FIRMessaging.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessaging.m; path = Firebase/Messaging/FIRMessaging.m; sourceTree = "<group>"; };
+               E24CCC6A27E2E018BF41A40B41A424AA /* MatomoTracker-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MatomoTracker-dummy.m"; sourceTree = "<group>"; };
+               E252500B89B041B9B232AAAF467C2CD0 /* MatomoTracker-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MatomoTracker-prefix.pch"; sourceTree = "<group>"; };
+               E2A4F86F985E98A40E7E014CEF0B3C3C /* ImageTransition.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageTransition.swift; path = Sources/ImageTransition.swift; sourceTree = "<group>"; };
+               E3C8D78F1AD702A005F2E4E7F4DC718D /* GPBExtensionInternals.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBExtensionInternals.m; path = objectivec/GPBExtensionInternals.m; sourceTree = "<group>"; };
+               E3D679A454CA39A6DB40C5D58EF59566 /* schema.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = schema.cpp; path = Realm/ObjectStore/src/schema.cpp; sourceTree = "<group>"; };
+               E3DA7423D7A46C6EADE51876FC656AB6 /* collection_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = collection_notifier.cpp; path = Realm/ObjectStore/src/impl/collection_notifier.cpp; sourceTree = "<group>"; };
+               E48289A832C7D72E990BA4AD40B16CF4 /* RLMRealm_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMRealm_Private.h; path = include/RLMRealm_Private.h; sourceTree = "<group>"; };
+               E49A610A798652E72449FC283CB8E58F /* GPBWireFormat.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBWireFormat.h; path = objectivec/GPBWireFormat.h; sourceTree = "<group>"; };
+               E541CBE59D00CC3AB220D84C5F2B0398 /* hmac.c */ = {isa = PBXFileReference; includeInIndex = 1; name = hmac.c; path = SSZipArchive/minizip/aes/hmac.c; sourceTree = "<group>"; };
+               E57A9782169E3DFBF458AD63149C5AFB /* FIRComponent.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponent.m; path = Firebase/Core/FIRComponent.m; sourceTree = "<group>"; };
+               E586F0F684FC8FE07B23490EB240D074 /* FIRAppAssociationRegistration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAppAssociationRegistration.h; path = Firebase/Core/Private/FIRAppAssociationRegistration.h; sourceTree = "<group>"; };
+               E59FF5C1E7B5535FD956BD1CE743AF56 /* ioapi.c */ = {isa = PBXFileReference; includeInIndex = 1; name = ioapi.c; path = SSZipArchive/minizip/ioapi.c; sourceTree = "<group>"; };
+               E5EB8F009021F403129D7F6C9B3B5988 /* sha1.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = sha1.h; path = SSZipArchive/minizip/aes/sha1.h; sourceTree = "<group>"; };
+               E651C8E4427AFF98E7D6A6CE3091862F /* ANSCompatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ANSCompatibility.h; path = iOS/Crashlytics.framework/Headers/ANSCompatibility.h; sourceTree = "<group>"; };
+               E676199B28E86FF57CC135787DDEFCD4 /* GPBUnknownFieldSet.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GPBUnknownFieldSet.m; path = objectivec/GPBUnknownFieldSet.m; sourceTree = "<group>"; };
+               E6B9472B42A9508F283EE68CC1DD4873 /* CustomDimension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CustomDimension.swift; path = MatomoTracker/CustomDimension.swift; sourceTree = "<group>"; };
+               E6E81ACB0B42B77DE2FFAAD41DC5D97B /* ZFModalTransitionAnimator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = ZFModalTransitionAnimator.m; path = Classes/ZFModalTransitionAnimator.m; sourceTree = "<group>"; };
+               E6F1BACD1BE358CC3FB4627C58C15B63 /* GULNetworkConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetworkConstants.m; path = GoogleUtilities/Network/GULNetworkConstants.m; sourceTree = "<group>"; };
+               E79B9239EB89FA4AD74571B09810D57E /* FIRMMessageCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMMessageCode.h; path = Firebase/Messaging/FIRMMessageCode.h; sourceTree = "<group>"; };
+               E819F8A139413C7ACFA07F82BF5F5CA9 /* FIRMessagingDelayedMessageQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingDelayedMessageQueue.h; path = Firebase/Messaging/FIRMessagingDelayedMessageQueue.h; sourceTree = "<group>"; };
+               E82022B2EF9283B342396105A0A70952 /* URLSessionDispatcher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLSessionDispatcher.swift; path = MatomoTracker/URLSessionDispatcher.swift; sourceTree = "<group>"; };
+               E84E14A5C3089E4CBFF33B08E907A754 /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Validation.swift; sourceTree = "<group>"; };
+               E86C2A7E9D5A20FF0BB6F96663456AAB /* ImageProcessor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageProcessor.swift; path = Sources/ImageProcessor.swift; sourceTree = "<group>"; };
+               E8A06D0E7033B7449F302ACF32EBB05E /* pb_common.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_common.c; sourceTree = "<group>"; };
+               E8F217E75535D2BDB07F64DF2D98ADE2 /* ImagePrefetcher.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImagePrefetcher.swift; path = Sources/ImagePrefetcher.swift; sourceTree = "<group>"; };
+               E905CCE024EFD711D72190AFB7BE740E /* ZFDragableModalTransition-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ZFDragableModalTransition-prefix.pch"; sourceTree = "<group>"; };
+               E927E8565EF0555119B9A543D4AF8643 /* GPBArray_PackagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBArray_PackagePrivate.h; path = objectivec/GPBArray_PackagePrivate.h; sourceTree = "<group>"; };
+               E9333496134B3CBFAAEAAFA463CF2680 /* FABAttributes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FABAttributes.h; path = iOS/Fabric.framework/Headers/FABAttributes.h; sourceTree = "<group>"; };
+               E97010326B67089745CE74477A2868F3 /* Swizzlings.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Swizzlings.m; path = MenuItemKit/Swizzlings.m; sourceTree = "<group>"; };
+               E9DDBAC1EE1AE3E21B4E4711D2D2D090 /* Kingfisher-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Kingfisher-prefix.pch"; sourceTree = "<group>"; };
+               EA03DD8446D3AEC84BD974CE2A7C9A1B /* JSQWebViewController.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = JSQWebViewController.modulemap; sourceTree = "<group>"; };
+               EA3A59300A145FB0537DB2E9829405FE /* nanopb-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "nanopb-dummy.m"; sourceTree = "<group>"; };
+               EA756190D5253EAE03798BEA1646E0C1 /* FIRMessagingRmq2PersistentStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingRmq2PersistentStore.h; path = Firebase/Messaging/FIRMessagingRmq2PersistentStore.h; sourceTree = "<group>"; };
+               EB01793375BE6CD1AF6B3C2F9EF5824A /* URL+OAuthSwift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URL+OAuthSwift.swift"; path = "Sources/URL+OAuthSwift.swift"; sourceTree = "<group>"; };
+               EB7911A826F61129EE80FDDE264EF222 /* Any.pbobjc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Any.pbobjc.h; path = objectivec/google/protobuf/Any.pbobjc.h; sourceTree = "<group>"; };
+               EB79B171CC6F891A99776C0356406A11 /* Event.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Event.swift; path = MatomoTracker/Event.swift; sourceTree = "<group>"; };
+               EB82AFCF317326DBE0583F7DBA1C4456 /* FIRBundleUtil.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRBundleUtil.h; path = Firebase/Core/Private/FIRBundleUtil.h; sourceTree = "<group>"; };
+               EBBA27AA678EB1363869D44237655330 /* RLMRealmUtil.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealmUtil.mm; path = Realm/RLMRealmUtil.mm; sourceTree = "<group>"; };
+               EC3A799A30601BBE93164EDFEE22F371 /* OAuth2Swift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OAuth2Swift.swift; path = Sources/OAuth2Swift.swift; sourceTree = "<group>"; };
+               EC83E7C64CD71374E5BBA059F4BBA0BF /* Protobuf-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Protobuf-umbrella.h"; sourceTree = "<group>"; };
+               ECC142A8EA3AE58507D64C2E6BF510CD /* RLMPlatform.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMPlatform.h; path = include/RLMPlatform.h; sourceTree = "<group>"; };
+               ECE59B0188CE015680FFF31522E8A852 /* RLMRealmConfiguration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMRealmConfiguration.mm; path = Realm/RLMRealmConfiguration.mm; sourceTree = "<group>"; };
+               ED302E755753DDD0A6ED0FDF3C5FCB18 /* fileenc.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = fileenc.h; path = SSZipArchive/minizip/aes/fileenc.h; sourceTree = "<group>"; };
+               EDA7D2FF7FC1178C591E6CB913FDAFD1 /* Pods-WolneLektury-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-WolneLektury-frameworks.sh"; sourceTree = "<group>"; };
+               EE3822E4428D213B2F2D9A7BEB84C33B /* RLMSyncConfiguration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = RLMSyncConfiguration.mm; path = Realm/RLMSyncConfiguration.mm; sourceTree = "<group>"; };
+               EFBD1D154E2BDAF6A560BD281251E63B /* String+OAuthSwift.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "String+OAuthSwift.swift"; path = "Sources/String+OAuthSwift.swift"; sourceTree = "<group>"; };
+               EFD854B5B21344EA040B49EA6725FC00 /* RLMObjectSchema_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMObjectSchema_Private.h; path = include/RLMObjectSchema_Private.h; sourceTree = "<group>"; };
+               EFF74401ABE190D825E20B007B43E406 /* FirebaseCoreDiagnostics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseCoreDiagnostics.framework; path = Frameworks/FirebaseCoreDiagnostics.framework; sourceTree = "<group>"; };
+               F0FB00C4DF6F35808D341ED5C7A18C79 /* SideMenuManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SideMenuManager.swift; path = Pod/Classes/SideMenuManager.swift; sourceTree = "<group>"; };
+               F1338B52ECE802CF0757825F0E908475 /* FIRMessagingPersistentSyncMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingPersistentSyncMessage.m; path = Firebase/Messaging/FIRMessagingPersistentSyncMessage.m; sourceTree = "<group>"; };
+               F1F2F74DBD2E4458AD6B2F16D85CBDD9 /* Style.css */ = {isa = PBXFileReference; includeInIndex = 1; name = Style.css; path = Source/Resources/Style.css; sourceTree = "<group>"; };
+               F1FB8E4F26A7220132808171D2B97800 /* list_notifier.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = list_notifier.cpp; path = Realm/ObjectStore/src/impl/list_notifier.cpp; sourceTree = "<group>"; };
+               F20D4BCFB8DB98002A6891B8201F765D /* SwiftKeychainWrapper.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftKeychainWrapper.modulemap; sourceTree = "<group>"; };
+               F23F4421C11D6FB3D4D8BE7097CA0541 /* SideMenu-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SideMenu-dummy.m"; sourceTree = "<group>"; };
+               F2D84C6D27A7D7F7981ADF55AE61C3DA /* GPBProtocolBuffers_RuntimeSupport.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBProtocolBuffers_RuntimeSupport.h; path = objectivec/GPBProtocolBuffers_RuntimeSupport.h; sourceTree = "<group>"; };
+               F2F483A8EC81272E3D12F5FEAC0445C7 /* Wrappers.pbobjc.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Wrappers.pbobjc.m; path = objectivec/google/protobuf/Wrappers.pbobjc.m; sourceTree = "<group>"; };
+               F30A057D3DA981112B03FE6E5B956E6E /* Toast-Swift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Toast-Swift-prefix.pch"; sourceTree = "<group>"; };
+               F31594F98BBECEB6EC59E4DA97410A6D /* OAuthSwift.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = OAuthSwift.xcconfig; sourceTree = "<group>"; };
+               F3BDC06DD13BFE0A5C49CF6C90378D22 /* FIRDependency.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRDependency.m; path = Firebase/Core/FIRDependency.m; sourceTree = "<group>"; };
+               F3C0A81FF167D0550DDCA852853434AE /* MenuItemKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MenuItemKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               F40F2B300FA1483A110F22D05E0470B3 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               F43729E28A11711576AB478FBB69B931 /* SwiftKeychainWrapper-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftKeychainWrapper-prefix.pch"; sourceTree = "<group>"; };
+               F49AA6EB751F7512499D46DE90D3EAE6 /* FirebaseCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseCore.framework; path = FirebaseCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               F4D421C457DF7CD3454C132F83B10591 /* Realm.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Realm.xcconfig; sourceTree = "<group>"; };
+               F4F1D6257BCD207C943C18F8BE4FAB8B /* Lato-Regular.ttf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Lato-Regular.ttf"; path = "Source/Resources/Fonts/Lato/Lato-Regular.ttf"; sourceTree = "<group>"; };
+               F50C72731047DE9CC0AA2AA8A9570C7D /* FIRMessagingSyncMessageManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRMessagingSyncMessageManager.m; path = Firebase/Messaging/FIRMessagingSyncMessageManager.m; sourceTree = "<group>"; };
+               F5CD30385E94D3E58B07F43DE2CF3F9B /* FIRComponentType.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentType.h; path = Firebase/Core/Private/FIRComponentType.h; sourceTree = "<group>"; };
+               F5D3488048887EE9B928BA33C0AF05B8 /* FirebaseMessaging-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FirebaseMessaging-dummy.m"; sourceTree = "<group>"; };
+               F60C46D0A0F154124C98A12300EC6CC3 /* CLSLogging.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CLSLogging.h; path = iOS/Crashlytics.framework/Headers/CLSLogging.h; sourceTree = "<group>"; };
+               F68F301F91E5C3A1F8902F7DF484A725 /* GoogleAppMeasurement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleAppMeasurement.framework; path = Frameworks/GoogleAppMeasurement.framework; sourceTree = "<group>"; };
+               F7CD333CED7797EA7D39F18163187E6C /* placeholder.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = placeholder.cpp; path = Realm/ObjectStore/src/placeholder.cpp; sourceTree = "<group>"; };
+               F9188B8734549A2FE3A1E20B11E68AA9 /* AEXML-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "AEXML-umbrella.h"; sourceTree = "<group>"; };
+               F9CDED3C6CB31F72B4C9F0B29CB4D813 /* ZFDragableModalTransition.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ZFDragableModalTransition.xcconfig; sourceTree = "<group>"; };
+               F9ED9601D0552304E103D84E3E96D6B3 /* ZFDragableModalTransition-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "ZFDragableModalTransition-dummy.m"; sourceTree = "<group>"; };
+               FA66C7BF6C3F1584F68147CED33971E9 /* fileenc.c */ = {isa = PBXFileReference; includeInIndex = 1; name = fileenc.c; path = SSZipArchive/minizip/aes/fileenc.c; sourceTree = "<group>"; };
+               FA80AF01EB6F8C828B12CA686720BCF5 /* Logger.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Logger.swift; path = alamofire_activity_logger/ActivityLogger/Logger.swift; sourceTree = "<group>"; };
+               FA98BF272CF91084C53F3CD50D3B18C6 /* MenuItemKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MenuItemKit.h; path = MenuItemKit/MenuItemKit.h; sourceTree = "<group>"; };
+               FB591C76B0F358BC96ACBDAF812A8552 /* keychain_helper.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = keychain_helper.cpp; path = Realm/ObjectStore/src/impl/apple/keychain_helper.cpp; sourceTree = "<group>"; };
+               FBE95E61CEA92DCA57F93939717FD876 /* JSQWebViewController-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "JSQWebViewController-umbrella.h"; sourceTree = "<group>"; };
+               FC1492CEB8D133CC73739E31913B7B80 /* Image.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Image.swift; path = Sources/Image.swift; sourceTree = "<group>"; };
+               FC3A34A8538E5CC949053777424628AD /* FirebaseCore-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FirebaseCore-dummy.m"; sourceTree = "<group>"; };
+               FC637EB57FE64DC55E93BA536D067809 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
+               FCE7E03A64E471CB3D6F8F1475E092A1 /* RLMSyncCredentials.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = RLMSyncCredentials.h; path = include/RLMSyncCredentials.h; sourceTree = "<group>"; };
+               FD6794D516FA6960417F7EB0CAB5E35F /* zip.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = zip.h; path = SSZipArchive/minizip/zip.h; sourceTree = "<group>"; };
+               FD7782331BF962C501ACCF35CB93A3A2 /* FIRMessaging.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessaging.h; path = Firebase/Messaging/Public/FIRMessaging.h; sourceTree = "<group>"; };
+               FD7874495EC4F3D74193F038627D03DB /* Realm-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Realm-prefix.pch"; sourceTree = "<group>"; };
+               FDAF8E81AE4DDFBC78DDEC4C39A073C9 /* Tools.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Tools.swift; path = alamofire_activity_logger/ActivityLogger/Tools.swift; sourceTree = "<group>"; };
+               FDD0294627FC460FFE79962ABD58D781 /* sync_session.cpp */ = {isa = PBXFileReference; includeInIndex = 1; name = sync_session.cpp; path = Realm/ObjectStore/src/sync/sync_session.cpp; sourceTree = "<group>"; };
+               FE0FC737191323EFE3F2E1F6EB1608C2 /* FIRMessagingPersistentSyncMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingPersistentSyncMessage.h; path = Firebase/Messaging/FIRMessagingPersistentSyncMessage.h; sourceTree = "<group>"; };
+               FE4F111F51BF9B184A5BB08ACC653779 /* AlamofireActivityLogger.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = AlamofireActivityLogger.modulemap; sourceTree = "<group>"; };
+               FE7332CEBB79FA1E661973254DDB64BC /* FIRMessagingTopicOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingTopicOperation.h; path = Firebase/Messaging/FIRMessagingTopicOperation.h; sourceTree = "<group>"; };
+               FEC060F5B023A25663DA3A7FF0A8A1A6 /* FIRMessagingPubSub.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRMessagingPubSub.h; path = Firebase/Messaging/FIRMessagingPubSub.h; sourceTree = "<group>"; };
+               FF3E6E1A3AAB0EAF989465DAEDBB72B3 /* SideMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SideMenu.framework; path = SideMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               FFF62627BCA2758AD2D6ACBEC9926E8F /* GPBUnknownField.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GPBUnknownField.h; path = objectivec/GPBUnknownField.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               0CF52E852695FAAC81E05083B79F4F61 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               0946606FD8E76D9FAE68288CF5DCF45C /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               149BFCC3118409FF931C35DAFFD80226 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               4159995BC3A7E764C041646E13FEF961 /* AEXML.framework in Frameworks */,
+                               C15902B0ABEE0E2E8B4AD7A0E03F70E9 /* FontBlaster.framework in Frameworks */,
+                               3268873EC9356C7FBD6634B8F2D8A39A /* Foundation.framework in Frameworks */,
+                               4A3474D8A0D6C404774479927440FEE7 /* JSQWebViewController.framework in Frameworks */,
+                               3EC722DCBCD5A8C482260EB19E80F42B /* MenuItemKit.framework in Frameworks */,
+                               B7653C4FA703BD3F06D8044CB8F0438A /* Realm.framework in Frameworks */,
+                               BCD985518761B18FBEC44ABD5B5DFF16 /* RealmSwift.framework in Frameworks */,
+                               70DFDE03BE15B88F7288274A986812B8 /* SSZipArchive.framework in Frameworks */,
+                               4516203E5F7963FA3BF40C6841E23A38 /* ZFDragableModalTransition.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               16D444A7CCF2B0694ACECBADD81414CB /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               06D4500B35FA21D2D87816AF3949002C /* Foundation.framework in Frameworks */,
+                               131730EA4B99E067FFA174A75E5DF9A4 /* UIKit.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               2F448EC1059CA90FE162E2864E1804FD /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               D47B14DC6C9B7712015208A12B200E3C /* Foundation.framework in Frameworks */,
+                               772237CA61C25004F79D55E6F04ED19C /* UIKit.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               35A398F6CC37A8D8E469498B848E286F /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               4F9863537BC7DA16B2F5D24FB8332853 /* CoreGraphics.framework in Frameworks */,
+                               A8906F6003D20F1B2E9C5BFEF7AE9C1F /* Foundation.framework in Frameworks */,
+                               2214D9EF048C093D94CEFDC849C2743E /* QuartzCore.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               38F36558E979BB5F4A18013938C7D8B7 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               B7278B647DD4BBEDC59EE9D0715DBAC3 /* Foundation.framework in Frameworks */,
+                               4561B0FCA3C8DF58989077063351B895 /* QuartzCore.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               3BA345FDCB39F16EE60953B6F18492E0 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               1388AF87C19F63DA28CC1EC3C26C882C /* Foundation.framework in Frameworks */,
+                               2073B5144EE9AFA37CDFD692DEB706FB /* Security.framework in Frameworks */,
+                               969201902C32F7A20B1803BEDE928791 /* SystemConfiguration.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               3D375098AF4E80A3884E2EBE5C64940D /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               2C79F094EE51D57289867AD834F53ABE /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               3EFA960CA4B047B45EFF91F11347F88D /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               1FC3F893E33BAEE1200C9435CDE0AE42 /* Foundation.framework in Frameworks */,
+                               3475139C8A2AD695042C57414E50B17C /* SystemConfiguration.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               4F5E6400BFA73BDC27205E23C09D84F9 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               C83872BAE9422D1BA0845AB635CD8303 /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               55C36F7CC1AF497CD89C8420A717D06F /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               B2EFCB251998FC9F2F1D168E53BD2111 /* CFNetwork.framework in Frameworks */,
+                               D274C2230E53457776F9D4EF567A0E65 /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6E8AF668A2161F7D6F680F721DB65D2D /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               D8EC5B74B9B5DC842F4179D19E6DE6D4 /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6E984081A095D70D07D998B48BC38F15 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               2D826FE4A1A56AF3E2611FEB5E7C91EE /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               76FED3C0A0FC13C147C353C828762C65 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               EDF41351299C7A250C2E94A35CA06810 /* Alamofire.framework in Frameworks */,
+                               92256B318D82A1668DB0944579F7A568 /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               82FED27C7ED0BF5001015F002AE62C27 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               A94B61C667CDF4664E423BF403D6C9CD /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               8F3E0087AE718FE6739991305CB4B11A /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               3A5246361D0B799BAB50FC33B33A0143 /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               AC5DE671039B379159BBC2DF39C825DF /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               B3BA00FDD104AD94BA87E2F72D1A72B9 /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               BEFF92BF85B66920AF555957E6A0E222 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               4AD9953777DFE36AEE9544EC06B42152 /* Foundation.framework in Frameworks */,
+                               63918BD6F8F5B260907AFDF0C74DB214 /* WebKit.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               C4ABFEE97A320BC0D912D90ED1C0311F /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E238641231BA89B9BAB557ADE131BE5F /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               D87A98D96FAB24DA408B8541DBB20CD2 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               013F0A749F2617A13C9A9A78A8777AF0 /* Foundation.framework in Frameworks */,
+                               E86A87B5954FA9592F1678360DD07C50 /* Realm.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               E411FE86C35FE60BCDED88381E3A1DE4 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               D640EF5F80D46D58145341D4707E106E /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               ED62D79EE4F9879BFB576933C7A797DA /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               1878441306907D7660D5DABA997E5407 /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F14DC5876305DE082BDBCD988CE23864 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               826FF04A17F730BF8D251D79233F3C4D /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F4E6020736A4B63C32D7AAF0B8C3674A /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               694817727BEB0AB642739E85783BE7DD /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               FF98F5D38366A576835533C3EDA5286E /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               C19B4A16F77AEF3F8DA734F25B8625FE /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               03B5776FE77607AC63F475248CF94DCA /* Resources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               A8EAB79E1A81BA370C6AF53D9EAE85AA /* Andada-Bold.otf */,
+                               265D21A8AF0C73915C5E3808A10562F6 /* Andada-BoldItalic.otf */,
+                               C440878C8B1131EE071571ECF86D6B26 /* Andada-Italic.otf */,
+                               9261FF50BC82FC7986B85E0D8CE24D61 /* Andada-Regular.otf */,
+                               7948622C522A6B89E5AB5041C4F1C30F /* Bridge.js */,
+                               829AD1D8E7F6EE418C950F905263EADD /* Images.xcassets */,
+                               270BBF6FE1627D659D64D320D8AC3E14 /* Lato-Bold.ttf */,
+                               B85E290DB3BDCF8A7288FE23314B642E /* Lato-BoldItalic.ttf */,
+                               C2A1A3A5F1D030B843050DAD43A6C29B /* Lato-Italic.ttf */,
+                               F4F1D6257BCD207C943C18F8BE4FAB8B /* Lato-Regular.ttf */,
+                               7961A53CC6789E3BF3F511B917EB410E /* Lora-Bold.ttf */,
+                               AE2928B25227E5D294693BA171652B40 /* Lora-BoldItalic.ttf */,
+                               AE0A609553E6424094E5B32BF5FC4BBD /* Lora-Italic.ttf */,
+                               57300EE01B9AFBB08B6C2FCC7AF87CAB /* Lora-Regular.ttf */,
+                               74A53A7DF4AC60A93A1B0E51F7868167 /* Raleway-Bold.ttf */,
+                               CB9A13DABF37C8B1C19A7D255823CAA4 /* Raleway-BoldItalic.ttf */,
+                               67189D34B36DE89CF42B0389F09FB517 /* Raleway-Italic.ttf */,
+                               0260176D84540F033791D6C38116526E /* Raleway-Regular.ttf */,
+                               F1F2F74DBD2E4458AD6B2F16D85CBDD9 /* Style.css */,
+                       );
+                       name = Resources;
+                       sourceTree = "<group>";
+               };
+               095BC404560B09B0DFF4F822380B5121 /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B4CBDCB5B5D048C5CCB38F2A12D07EA0 /* Crashlytics.framework */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               0C11E2D9B92B0E5F9ECEEE03522EEE0A /* Environment */ = {
+                       isa = PBXGroup;
+                       children = (
+                               78DDB9DF0FB83A9E64F5C81FD75182BA /* GULAppEnvironmentUtil.h */,
+                               AA835BF7AFA43E2BF03FA593F7AD83CE /* GULAppEnvironmentUtil.m */,
+                       );
+                       name = Environment;
+                       sourceTree = "<group>";
+               };
+               0CBF6F1EBF3CF47B338B91E23D5D651F /* Toast-Swift */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B4894DE102973633A89B8B1A928B2F3F /* Toast.swift */,
+                               8D8172F242BB23F774B2DC1E5D5922A8 /* Support Files */,
+                       );
+                       name = "Toast-Swift";
+                       path = "Toast-Swift";
+                       sourceTree = "<group>";
+               };
+               0E063F7D2362ADAB01028DA9EFAF5D95 /* Headers */ = {
+                       isa = PBXGroup;
+                       children = (
+                               4C5FC54772307BC323BD252586733ADA /* NSError+RLMSync.h */,
+                               144443F4C5352AA38AC82F621455EA3C /* Realm.h */,
+                               8E26DF3327D2EE24CED40AA07E5CC179 /* RLMArray.h */,
+                               398EF8E6348C58E6A461B252064C6BA6 /* RLMCollection.h */,
+                               61EE9421F804DDD1641EC8DBF05D66FB /* RLMConstants.h */,
+                               DD11C12E8B8A3662D6DE8C3D9CC3DE31 /* RLMMigration.h */,
+                               101DAC3BA33CBEB734526D4617775FA0 /* RLMObject.h */,
+                               AAE3C1B13216CF41A8A27FC654EAB454 /* RLMObjectBase.h */,
+                               C544F4546E0587724503A71F8C8A0730 /* RLMObjectBase_Dynamic.h */,
+                               565A58C3DEC5EF04039C13B055C22CE9 /* RLMObjectSchema.h */,
+                               ECC142A8EA3AE58507D64C2E6BF510CD /* RLMPlatform.h */,
+                               998DC89C6539EB5EE837C792F9D36764 /* RLMProperty.h */,
+                               6D6A3C039A370F8C8DA839EF41DC6D43 /* RLMRealm.h */,
+                               CFE9AB50F57A50B5C79F92AD4CAC7208 /* RLMRealm+Sync.h */,
+                               E00C401BE147D47DFBB42AA69755AF0D /* RLMRealm_Dynamic.h */,
+                               11A9CEA39CE194E1A1C87939E94FA086 /* RLMRealmConfiguration.h */,
+                               0A8D24EBF9A9A7024F37D1F9967496CD /* RLMRealmConfiguration+Sync.h */,
+                               BC05BD18E546C120599B8E7ECBDC263A /* RLMResults.h */,
+                               CA7CF0757872C51EE58D200C9E26B2FC /* RLMSchema.h */,
+                               DF16170DE63470802D26A252D0E24CE8 /* RLMSyncConfiguration.h */,
+                               FCE7E03A64E471CB3D6F8F1475E092A1 /* RLMSyncCredentials.h */,
+                               BA32998B725316B590F1094FCEDC7A57 /* RLMSyncManager.h */,
+                               9F56E9572F18DCBB5E446C23D65E1213 /* RLMSyncPermission.h */,
+                               AF2F39EAC2946E050E52E2C675208EAB /* RLMSyncSession.h */,
+                               1482EE96CD4BCC0E2EDBBA3ED70FDCBD /* RLMSyncUser.h */,
+                               390D860093219F412EFBA3562CA65AAE /* RLMSyncUtil.h */,
+                               196A114E523544DED122A0BBBD405C1F /* RLMThreadSafeReference.h */,
+                       );
+                       name = Headers;
+                       sourceTree = "<group>";
+               };
+               10791832B9481862D47906950191849D /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               CDBC7D8D526EF3A5D16C73935E37B18C /* FirebaseMessaging.modulemap */,
+                               52C57B4C09979E16295F7D9EF93E8588 /* FirebaseMessaging.xcconfig */,
+                               F5D3488048887EE9B928BA33C0AF05B8 /* FirebaseMessaging-dummy.m */,
+                               20CB9934A5EA1F3987C1D0B98512E6C2 /* FirebaseMessaging-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/FirebaseMessaging";
+                       sourceTree = "<group>";
+               };
+               17E2D254002A8E4F7B785313B5136C1C /* AEXML */ = {
+                       isa = PBXGroup;
+                       children = (
+                               1FDD2185138D081B59430FFD466ADEA1 /* Document.swift */,
+                               9757E13C4DE3D7D5E6DAEA3955E6FAD5 /* Element.swift */,
+                               07F73D4373DB447405CB3388AB42D8A0 /* Error.swift */,
+                               0B5C11A1670ED8BEE826D1FADC42D7C7 /* Options.swift */,
+                               AD8344B4C8CA5CDCD06CDB95C9779E9E /* Parser.swift */,
+                               8760F6C8233DAA9F8A90F12152F6C8C2 /* Support Files */,
+                       );
+                       name = AEXML;
+                       path = AEXML;
+                       sourceTree = "<group>";
+               };
+               1ACC2FD8D5329635D25C32B32902A24A /* SwiftKeychainWrapper */ = {
+                       isa = PBXGroup;
+                       children = (
+                               27FD5DDB5405AF442504B5810F7F8C09 /* KeychainItemAccessibility.swift */,
+                               97B70D1D75462F234CC7CB3B3BD41F38 /* KeychainWrapper.swift */,
+                               DDFAC3CB75A6486957AF5FE89066B9C5 /* SwiftKeychainWrapper.h */,
+                               D3FD5E629E54511182B7DA412962263A /* Support Files */,
+                       );
+                       name = SwiftKeychainWrapper;
+                       path = SwiftKeychainWrapper;
+                       sourceTree = "<group>";
+               };
+               1D8803F9B7CE40673FE85FE8C3819E1D /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               DEA6D6B4545E008C5EAD0934BFDA40CD /* Info.plist */,
+                               DF1FEC972A61EF84B2A3BE043B7FE763 /* Kingfisher.modulemap */,
+                               832216D1A72BB1036524B4C72D8DD3AD /* Kingfisher.xcconfig */,
+                               CA1A1481F0FEAD0729D79CC5B96CC428 /* Kingfisher-dummy.m */,
+                               E9DDBAC1EE1AE3E21B4E4711D2D2D090 /* Kingfisher-prefix.pch */,
+                               51AB21DAE8AE316862E8F8DC498D8765 /* Kingfisher-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/Kingfisher";
+                       sourceTree = "<group>";
+               };
+               24C9CC72D22E539B5C7EBA41EEACC900 /* Kingfisher */ = {
+                       isa = PBXGroup;
+                       children = (
+                               2EA3BEC324F13823959A0F7DB8B7CC6C /* AnimatedImageView.swift */,
+                               3039DE7A044D5D519CE558DF7522DD75 /* Box.swift */,
+                               3DA6E2DC0B34EB311EB4E1A5DD25EC45 /* CacheSerializer.swift */,
+                               BE5B948DB0251166DBE25C3040F48E8B /* Filter.swift */,
+                               8F2399A1BF621F8F87173F1D95B405A3 /* FormatIndicatedCacheSerializer.swift */,
+                               FC1492CEB8D133CC73739E31913B7B80 /* Image.swift */,
+                               4300E3895B62156BB316982523FC8D06 /* ImageCache.swift */,
+                               2FBF1EAFC3C8941E114238A69DC823D2 /* ImageDownloader.swift */,
+                               972B088C9B63AE08FC00845F18AAD308 /* ImageModifier.swift */,
+                               E8F217E75535D2BDB07F64DF2D98ADE2 /* ImagePrefetcher.swift */,
+                               E86C2A7E9D5A20FF0BB6F96663456AAB /* ImageProcessor.swift */,
+                               E2A4F86F985E98A40E7E014CEF0B3C3C /* ImageTransition.swift */,
+                               C0AECA8112FB6A3BFDFA9BC9BC8AC765 /* ImageView+Kingfisher.swift */,
+                               B19AB99E9F6D28349E358F6A347A20B0 /* Indicator.swift */,
+                               38ED9F6020F6E95DF135E9564D06DB85 /* Kingfisher.h */,
+                               3FF7EDC5386C9DB98B823B474CDDF650 /* Kingfisher.swift */,
+                               C32096158C38CC91501302DDD6AF488D /* KingfisherManager.swift */,
+                               B99ABE547EBBB6C2B8F7FF04E1C02378 /* KingfisherOptionsInfo.swift */,
+                               0792F8241E343E3F566D735EF2DEF482 /* Placeholder.swift */,
+                               268FB6F347F0C0C1834F10B2DF31A8A9 /* RequestModifier.swift */,
+                               C79142150002194346CF89C5F4514BB4 /* Resource.swift */,
+                               014EFC1AE04ADF977639E4046BE9008C /* String+MD5.swift */,
+                               34524258955E5E82BF9892F8A42C5CCA /* ThreadHelper.swift */,
+                               2198B0E49174CFB22F300B5737A7ACD0 /* UIButton+Kingfisher.swift */,
+                               1D8803F9B7CE40673FE85FE8C3819E1D /* Support Files */,
+                       );
+                       name = Kingfisher;
+                       path = Kingfisher;
+                       sourceTree = "<group>";
+               };
+               27FA4F5EDA23BFA25823B5AF80FC7EDF /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               4EBDFD83C94D7DEEEA33A42478FC9305 /* Info.plist */,
+                               D656437C48D246F2B717E827BC5622B4 /* SideMenu.modulemap */,
+                               B51E1C8BEE26A23D7600FD8BD5C9FCF2 /* SideMenu.xcconfig */,
+                               F23F4421C11D6FB3D4D8BE7097CA0541 /* SideMenu-dummy.m */,
+                               CA0D42E5ED3F3A6EA13743141521AB33 /* SideMenu-prefix.pch */,
+                               C509D32CE877D802F9188A1C63C0EAE3 /* SideMenu-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/SideMenu";
+                       sourceTree = "<group>";
+               };
+               2C83513224506E7FFBF1EBC2C5A1FA19 /* encode */ = {
+                       isa = PBXGroup;
+                       children = (
+                       );
+                       name = encode;
+                       sourceTree = "<group>";
+               };
+               2D1EE3668B57DF2E7FB3DA16C233D52D /* Alamofire */ = {
+                       isa = PBXGroup;
+                       children = (
+                               60BD8D4AEB998E3927430AB2F3519F08 /* AFError.swift */,
+                               05F6A7144D612A1B7814FB4DB19F20D0 /* Alamofire.swift */,
+                               73E5BA415740BBF222E187A28324E007 /* DispatchQueue+Alamofire.swift */,
+                               C7E7BD134D837DB6672EBC1FDA1F10DD /* MultipartFormData.swift */,
+                               2BD7DCAEBC8CE3C15E7460B40AC02A51 /* NetworkReachabilityManager.swift */,
+                               6939449CF2795C11704E929B05036FEA /* Notifications.swift */,
+                               91EF582D4746813F0EBB183BC4373C05 /* ParameterEncoding.swift */,
+                               5CA5A1028F01FEC16A4E71E8C1F5E062 /* Request.swift */,
+                               813E11E3BB26542B146AEA87E7EF84B0 /* Response.swift */,
+                               6B9A7C04F88EE32E59BDA90F0BFE6D21 /* ResponseSerialization.swift */,
+                               2FDF85370176057B4C572AFDD0DC5AAF /* Result.swift */,
+                               AEC20068DA07AB47A0BC6045AB5A15A9 /* ServerTrustPolicy.swift */,
+                               59706CD0DE8A5C55FBF1750747E35736 /* SessionDelegate.swift */,
+                               01C14036937A2DCAE7AF38A487E9A5AD /* SessionManager.swift */,
+                               7FE1DC4CFC4F73D99D647A77014F550D /* TaskDelegate.swift */,
+                               D35917412D73B9AC787F462EBB9FB9F2 /* Timeline.swift */,
+                               E84E14A5C3089E4CBFF33B08E907A754 /* Validation.swift */,
+                               90875D8FC731EED009452DD40005716E /* Support Files */,
+                       );
+                       name = Alamofire;
+                       path = Alamofire;
+                       sourceTree = "<group>";
+               };
+               3525020F0C9C4886278AF73393EA986C /* Pods */ = {
+                       isa = PBXGroup;
+                       children = (
+                               17E2D254002A8E4F7B785313B5136C1C /* AEXML */,
+                               2D1EE3668B57DF2E7FB3DA16C233D52D /* Alamofire */,
+                               CB92378B611BD5AA7C0E9DC3A01C8F58 /* AlamofireActivityLogger */,
+                               B226072ED93BCD88AD84D3C69BD872F4 /* Crashlytics */,
+                               BF03C3BD0CDD865DB1F8CE177BA17CD2 /* Fabric */,
+                               5BE09486CF78E925037ECA67ED218506 /* Firebase */,
+                               537E60F95619BEF0BC8010E2C1B1CF6E /* FirebaseAnalytics */,
+                               6CC135C2BE55F065813888C8E6BBDB78 /* FirebaseCore */,
+                               8D702CEF8E49867D36C4FE20038C7204 /* FirebaseInstanceID */,
+                               7525D290334C997F8E9E2D2A08C7E4F2 /* FirebaseMessaging */,
+                               47CCDE4258F3CBA119658E314D544854 /* FolioReaderKit */,
+                               4BBE196B3ED5EA5E7E721C2E79C36A5F /* FontBlaster */,
+                               6E01492863C6603A2FAC30ED1E006A4C /* GoogleAppMeasurement */,
+                               6198E260E4150A4FE046511ABBA94557 /* GoogleUtilities */,
+                               D8D0F08FCCAD2A06E2AA0A071F512720 /* JSQWebViewController */,
+                               24C9CC72D22E539B5C7EBA41EEACC900 /* Kingfisher */,
+                               AD9EB60CAAD16802B01659350ABD7D55 /* MatomoTracker */,
+                               7BD483F9820EA71B96782CED3C5B0D70 /* MBProgressHUD */,
+                               BB03BCC9653FF7E2D14F24690FD296B9 /* MenuItemKit */,
+                               AC2243DEDD09BEA6BCD1D891FD9BEE17 /* MZDownloadManager */,
+                               9C2EE7EB6ED15058458BB69651F41FBB /* nanopb */,
+                               FBE2510A7746B57C173EB74BDDA3A968 /* OAuthSwift */,
+                               A6F534F3F3142215ECB0662A0F672BDA /* Protobuf */,
+                               C66B3A79A9DE6F8D697700F91DAF4EB3 /* Realm */,
+                               90DD5F7BEA3C3A4C99643966F336A587 /* RealmSwift */,
+                               7FCF73539F770DAAA5EDEC7049D9A53E /* SideMenu */,
+                               51A02E06C4EEC57349F516EC92C0ABE5 /* SSZipArchive */,
+                               1ACC2FD8D5329635D25C32B32902A24A /* SwiftKeychainWrapper */,
+                               0CBF6F1EBF3CF47B338B91E23D5D651F /* Toast-Swift */,
+                               9DBFDAC85DFB0472300EC6068543E40A /* ZFDragableModalTransition */,
+                       );
+                       name = Pods;
+                       sourceTree = "<group>";
+               };
+               36266B7D46E52E23201D38174A471174 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               587033B39A1B751CBDCBBB6F1F3E3668 /* Info.plist */,
+                               855D4A41CB229BD53497542B6A0C0045 /* Realm.modulemap */,
+                               F4D421C457DF7CD3454C132F83B10591 /* Realm.xcconfig */,
+                               5DD123C500606EE058F44458705F9DE9 /* Realm-dummy.m */,
+                               FD7874495EC4F3D74193F038627D03DB /* Realm-prefix.pch */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/Realm";
+                       sourceTree = "<group>";
+               };
+               3858B7293812A87863A2968E595A7C97 /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               57001FF2A9A8C5351AFE3F8078C847D6 /* Fabric.framework */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               391BCB73939AC0FB909AA799B32593B8 /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               7ED838A709B4B90C48DC5866242DA115 /* librealmcore-ios.a */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               441618E90A47E6B4A47B17F612C27BE4 /* Core */ = {
+                       isa = PBXGroup;
+                       children = (
+                               064177D2B62F30947132CA37F78CABBC /* Application.swift */,
+                               E6B9472B42A9508F283EE68CC1DD4873 /* CustomDimension.swift */,
+                               498C0C39E4B4B2835310F4E961B0F195 /* CustomVariable.swift */,
+                               72C53A710A523025D5E9074631788CD9 /* Device.swift */,
+                               287E8DD99129AC55E28F8626306CAF6D /* Dispatcher.swift */,
+                               EB79B171CC6F891A99776C0356406A11 /* Event.swift */,
+                               B896CF8A24113A3F12663506E53F6EAF /* EventSerializer.swift */,
+                               C4A0830A90DAC8805601A6BFBF4EB2B4 /* Locale+HttpAcceptLanguage.swift */,
+                               5C52956025F0F8DD25F68011683ABBE9 /* Logger.swift */,
+                               AB30FE116FBBE62941E0322FB3B9496C /* MainThread.swift */,
+                               184161E1B57664D8505D6B2744800293 /* MatomoTracker.swift */,
+                               951EF2BE6965114A5D8E84809EAC6424 /* MatomoUserDefaults.swift */,
+                               14D40CBD84A69593DDFFE433389950C2 /* MemoryQueue.swift */,
+                               2E3E3FD2AA61E951D9A15E12FEB6A59F /* Queue.swift */,
+                               9470A90522247E16FD92E96415CCBE86 /* Session.swift */,
+                               E82022B2EF9283B342396105A0A70952 /* URLSessionDispatcher.swift */,
+                               D6C3231D8AF28863899D26FC24910D46 /* Visitor.swift */,
+                       );
+                       name = Core;
+                       sourceTree = "<group>";
+               };
+               47CCDE4258F3CBA119658E314D544854 /* FolioReaderKit */ = {
+                       isa = PBXGroup;
+                       children = (
+                               2F837D39A9B1D03254A4B178F9C9DA0C /* Extensions.swift */,
+                               BFC7E4A5F9F3490D492D52132DB76FC3 /* FolioReaderAudioPlayer.swift */,
+                               02AC354FE6FEB3854FB790A1543E37D7 /* FolioReaderCenter.swift */,
+                               318DC2541285D47723C03D0A990A8731 /* FolioReaderChapterList.swift */,
+                               41A2C193ABFAE8C48F5DD4FE7D3A1CA6 /* FolioReaderChapterListCell.swift */,
+                               D83DAE8A53744DA268CA2420E716533B /* FolioReaderConfig.swift */,
+                               C5B05C0825F95873D124EF95EAEF8DFA /* FolioReaderContainer.swift */,
+                               98BD4D3148F63139C8DAA1AD6929A2C9 /* FolioReaderFontsMenu.swift */,
+                               949BE471587E597B7F1E52BF803F5EA0 /* FolioReaderHighlightList.swift */,
+                               D5C47C18DAE8DE892F90FDDBF5ADB969 /* FolioReaderKit.h */,
+                               7EDB45F1273F71C411A0545C4CE445E3 /* FolioReaderKit.swift */,
+                               867D7359F1B0B74054D95F921EE9719D /* FolioReaderPage.swift */,
+                               D063D80E2E978A2BE32BFE9214B50DBA /* FolioReaderPageIndicator.swift */,
+                               71E40312A6196B0B687CFF43F843C806 /* FolioReaderPlayerMenu.swift */,
+                               3FE04C47410787B608EB2E6AE39AE374 /* FolioReaderQuoteShare.swift */,
+                               3ED4E426129BC7417B7E88791A14B5E6 /* FolioReaderSharingProvider.swift */,
+                               AE1B1CA98EB9A6E3AAA0EB5E93C94CE0 /* FolioReaderUserDefaults.swift */,
+                               6EEBA2C8A6B1441F5049FD44C2E246B2 /* FolioReaderWebView.swift */,
+                               77D4DB8CCBD4568AF20BA0C53858FE2C /* FRBook.swift */,
+                               121EEBF8A77EDC98AEA7B8D83937FA0D /* FREpubParser.swift */,
+                               1FB2A0E8B68A46C4DC9F646C4806F1CF /* FRMetadata.swift */,
+                               959CC486954AB25818AE8D824A60C530 /* FRResource.swift */,
+                               21D67678FC98B9B6656DC8B4F02C5BC5 /* FRResources.swift */,
+                               0F03D2A8CC72E729DA124F2004020658 /* FRSmilElement.swift */,
+                               0814C5C3D6056C7C77038075C0FAA4DF /* FRSmils.swift */,
+                               9570CA07B3D1379C659F3B95E6764364 /* FRSpine.swift */,
+                               1A6B7317D2AA66425340B4A6E3A73F5A /* FRTocReference.swift */,
+                               842A4F7CFA4D598C49D85FD16BDA6863 /* HADiscreteSlider.swift */,
+                               3110559B9CEE5BA9F86BBB0E5EFE87F7 /* Highlight.swift */,
+                               9B6147E3ED6E10C6C9C37BB8A5B2379A /* Highlight+Helper.swift */,
+                               93FA1B8771A7779F02E65DEC3E502D16 /* MediaType.swift */,
+                               09BBC999192646D11C33C3FD05C523F8 /* PageViewController.swift */,
+                               B49CE581DA5B45AF17857662EB6AE539 /* QuoteImage.swift */,
+                               C9413A640B6F54FA4706173107E9234C /* ScrollScrubber.swift */,
+                               989BDF769D8240DA767C777DF3E091BA /* SMSegment.swift */,
+                               210CA0599041DC7C7A13E77AD4BAA706 /* SMSegmentView.swift */,
+                               03B5776FE77607AC63F475248CF94DCA /* Resources */,
+                               9AC14799E35FF2DDB6EAE4BAF02612AE /* Support Files */,
+                       );
+                       name = FolioReaderKit;
+                       path = FolioReaderKit;
+                       sourceTree = "<group>";
+               };
+               492FAF4014AB0C47243F2590249A7CD3 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               55DA96677843C9CF1DB923922BBF68F3 /* Info.plist */,
+                               4F731C61972BF7BC92E5CF84BC1752EA /* MZDownloadManager.modulemap */,
+                               9A52F85BB96ED2C9CB95229AEA2579A0 /* MZDownloadManager.xcconfig */,
+                               3F9AF84291BB835371B31C38FB32B164 /* MZDownloadManager-dummy.m */,
+                               1B1EC0DCBE67D03787183294CA3BCA28 /* MZDownloadManager-prefix.pch */,
+                               C46E57D513084D4F163AE3274C8ACEE0 /* MZDownloadManager-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/MZDownloadManager";
+                       sourceTree = "<group>";
+               };
+               4BBE196B3ED5EA5E7E721C2E79C36A5F /* FontBlaster */ = {
+                       isa = PBXGroup;
+                       children = (
+                               3DD99B05DEEA0C7E3366E61EF5345DE0 /* FontBlaster.swift */,
+                               735777F6FD536A8F3A8205091EA02B20 /* Support Files */,
+                       );
+                       name = FontBlaster;
+                       path = FontBlaster;
+                       sourceTree = "<group>";
+               };
+               4BF630CEE5770AD6975E58B778B441D1 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               CCC7E5E20F306AB7DABDF3C6630C2AEF /* Info.plist */,
+                               6EEE490DFA86C4AA2298366F0472518F /* nanopb.modulemap */,
+                               C5ECEF2B1DFCF0C01450C80D3BD0610F /* nanopb.xcconfig */,
+                               EA3A59300A145FB0537DB2E9829405FE /* nanopb-dummy.m */,
+                               A6705B2C75B45917B97EA5C687CE5BB8 /* nanopb-prefix.pch */,
+                               6DC6F0E58FABA2B29D2CFE63A7F0DFB3 /* nanopb-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/nanopb";
+                       sourceTree = "<group>";
+               };
+               4DC94AA82575A6D073C93B205EC6A6D9 /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               214BEC95D55426D9CEF7A8F37517E601 /* FirebaseInstanceID.framework */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               50AF9A16FBEA3B2286BB71DC38F8622D /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               9F903DCFBA56EDFAE70249F698E2B8A3 /* Info.plist */,
+                               333C849D07817FE374DB8EE976C7AD2C /* MenuItemKit.modulemap */,
+                               1AEFAE3B5379808E09A395D84018890C /* MenuItemKit.xcconfig */,
+                               B52F39B542A1D04B86E6DAE81129EB7C /* MenuItemKit-dummy.m */,
+                               50A1477AE40664A579ED50B1D7233B7B /* MenuItemKit-prefix.pch */,
+                               85F4B8052CF32CEC52ADCF95F476CFF4 /* MenuItemKit-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/MenuItemKit";
+                       sourceTree = "<group>";
+               };
+               511A16D779FB63E7B779D6B47DBE9C0A /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               655356583D31A214437E878EB5DB8BF0 /* Info.plist */,
+                               EA03DD8446D3AEC84BD974CE2A7C9A1B /* JSQWebViewController.modulemap */,
+                               06967CA663F790072A7AFB661576619B /* JSQWebViewController.xcconfig */,
+                               08FD7307754A9D3291AA278FE0AC2A59 /* JSQWebViewController-dummy.m */,
+                               27041CCB67B1C89587A0048ED6CA3D37 /* JSQWebViewController-prefix.pch */,
+                               FBE95E61CEA92DCA57F93939717FD876 /* JSQWebViewController-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/JSQWebViewController";
+                       sourceTree = "<group>";
+               };
+               51A02E06C4EEC57349F516EC92C0ABE5 /* SSZipArchive */ = {
+                       isa = PBXGroup;
+                       children = (
+                               75B1B63C2A759AECF71AAE0B4E974E6E /* aes.h */,
+                               7C031C39DEA92D368607CCB505525724 /* aes_ni.c */,
+                               4C822CD35CD43E275C564128C723F24C /* aes_ni.h */,
+                               3E49B5DCB4074C65C833C7A0589864F5 /* aescrypt.c */,
+                               BEB3597A302BDA172DD6558B46724016 /* aeskey.c */,
+                               24DDFB7ADF9F5BB4277956C0D45E3124 /* aesopt.h */,
+                               79E033869BAC941730E6D735B4343813 /* aestab.c */,
+                               022810A628AB345511375F43564EA066 /* aestab.h */,
+                               8A66F3D39C1729EE4090DC644FBA6F65 /* brg_endian.h */,
+                               163DF539BC15EE2749223B588A4D9978 /* brg_types.h */,
+                               9E4FA5283B6F750E4FDB42A32E3D2DA3 /* crypt.c */,
+                               6E5D4A81CC4B4E8549AF794D55B53EA2 /* crypt.h */,
+                               FA66C7BF6C3F1584F68147CED33971E9 /* fileenc.c */,
+                               ED302E755753DDD0A6ED0FDF3C5FCB18 /* fileenc.h */,
+                               E541CBE59D00CC3AB220D84C5F2B0398 /* hmac.c */,
+                               547372D2B91EA430CBD2723BC70C75CF /* hmac.h */,
+                               E59FF5C1E7B5535FD956BD1CE743AF56 /* ioapi.c */,
+                               82DC2D8E239E4638D8DB80C8A06D83BB /* ioapi.h */,
+                               8CC80ED2F8C74EAA722AF3F92251516C /* ioapi_buf.c */,
+                               46EA2FB5D744DD40EDB4C7F7E064E37C /* ioapi_buf.h */,
+                               320E6B71AC99F49980ACD3104FAF01B2 /* ioapi_mem.c */,
+                               882EF9DA2512BFAF5A2DB148555DE38D /* ioapi_mem.h */,
+                               B118C522E1E15B2BC5C5465B7A8BF1F8 /* minishared.c */,
+                               1FD1E1F5EF445099E14F78112E4DE5BD /* minishared.h */,
+                               A5D9C7E3D96C2EBCE4D34719C6211F2E /* prng.c */,
+                               767CA3F1E7E65948D78C5DF3A4C074E0 /* prng.h */,
+                               3BCBFCAFBBD2EB2F3E726450815158E5 /* pwd2key.c */,
+                               C28470CDCC9315FCDA60C6472439C9EE /* pwd2key.h */,
+                               790BCAA2E56A258EC49B7088FF78B747 /* sha1.c */,
+                               E5EB8F009021F403129D7F6C9B3B5988 /* sha1.h */,
+                               44B61755C9439AAE0D2773B101A3E1EF /* SSZipArchive.h */,
+                               673243EDEFCAEB87BF4D213D5731B809 /* SSZipArchive.m */,
+                               B3F61ADE7C91CF96278DB665AE571F4E /* SSZipCommon.h */,
+                               295B0C54BAE1989B99BA0920EA7C603C /* unzip.c */,
+                               AC93D8CD26E177BF4425613E26F84E62 /* unzip.h */,
+                               2E60177A5CFD27A0866D68EFFD8094F5 /* zip.c */,
+                               FD6794D516FA6960417F7EB0CAB5E35F /* zip.h */,
+                               CC036F1EA6EB67C4A2C91AB3FCDEBC4C /* ZipArchive.h */,
+                               E417A556BC11CA3A56E97BCCD79C5EF6 /* Support Files */,
+                       );
+                       name = SSZipArchive;
+                       path = SSZipArchive;
+                       sourceTree = "<group>";
+               };
+               52D6AE34C4E0F34026B6742B61C0471A /* Targets Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               BEE4530588D33886305C1708066743D3 /* Pods-WolneLektury */,
+                       );
+                       name = "Targets Support Files";
+                       sourceTree = "<group>";
+               };
+               537E60F95619BEF0BC8010E2C1B1CF6E /* FirebaseAnalytics */ = {
+                       isa = PBXGroup;
+                       children = (
+                               DF98B25F46BC615A236546E0E1DEC357 /* Frameworks */,
+                       );
+                       name = FirebaseAnalytics;
+                       path = FirebaseAnalytics;
+                       sourceTree = "<group>";
+               };
+               581A0F9429BAC7DDBF34C6D0FF9F3159 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               133EC299B9E30E151C3D7E0091490188 /* Info.plist */,
+                               D43D637F253AC03F17C1986897E46ACC /* Protobuf.modulemap */,
+                               D795F064F1050FA6050617BB0936FBD1 /* Protobuf.xcconfig */,
+                               164FD2FA81136FE310077B45A814E345 /* Protobuf-dummy.m */,
+                               12CDC0657349E8207CA037288EA17AFE /* Protobuf-prefix.pch */,
+                               EC83E7C64CD71374E5BBA059F4BBA0BF /* Protobuf-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/Protobuf";
+                       sourceTree = "<group>";
+               };
+               5BE09486CF78E925037ECA67ED218506 /* Firebase */ = {
+                       isa = PBXGroup;
+                       children = (
+                               FB58DCB5D77B17D0C61761EC063D9422 /* CoreOnly */,
+                       );
+                       name = Firebase;
+                       path = Firebase;
+                       sourceTree = "<group>";
+               };
+               5D2F77AD0FAFA81241681AD55997FB7C /* UserDefaults */ = {
+                       isa = PBXGroup;
+                       children = (
+                               7C0F994B8858C9DEB29A4E169084A475 /* GULUserDefaults.h */,
+                               16C3508A7D1CAF406C759727D4E8B2E3 /* GULUserDefaults.m */,
+                       );
+                       name = UserDefaults;
+                       sourceTree = "<group>";
+               };
+               6198E260E4150A4FE046511ABBA94557 /* GoogleUtilities */ = {
+                       isa = PBXGroup;
+                       children = (
+                               DEF103B18FC482F01A3E6F613BDA306F /* AppDelegateSwizzler */,
+                               0C11E2D9B92B0E5F9ECEEE03522EEE0A /* Environment */,
+                               FF381E2221DBCA43D1EE90FBD49A1B93 /* Logger */,
+                               B5A8B19AC5306040D4E551899C399BF8 /* MethodSwizzler */,
+                               96465B22D3460DC947309E5EA8439439 /* Network */,
+                               EBD8349EBF650C21D25FEAD456A93FC7 /* NSData+zlib */,
+                               C8877525A825CE91E95AB3D0E59E87CA /* Reachability */,
+                               B014D292E078790F9F3A628420F22675 /* Support Files */,
+                               5D2F77AD0FAFA81241681AD55997FB7C /* UserDefaults */,
+                       );
+                       name = GoogleUtilities;
+                       path = GoogleUtilities;
+                       sourceTree = "<group>";
+               };
+               6554C4FC4FC2D4754FCA141A9949FAEC /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               D46C127210F7863A4A22D9F761D51E5E /* AEXML.framework */,
+                               CD162B8C0A7547B8B57A0CAA8D18DC3B /* Alamofire.framework */,
+                               94BC0645BAAC1EB760B6B157A87213E0 /* FontBlaster.framework */,
+                               99A354380AB7A447785FC47FD513732A /* JSQWebViewController.framework */,
+                               F3C0A81FF167D0550DDCA852853434AE /* MenuItemKit.framework */,
+                               C94C12B9600AFD613B21A25CCFE5EB1C /* Realm.framework */,
+                               636D155F9DC5CDD62606CFA7F4189E51 /* RealmSwift.framework */,
+                               767EF6727F4D919F160041B1B4BD5D24 /* SSZipArchive.framework */,
+                               CDDF76EEC0C6394E55456C0935E846C1 /* ZFDragableModalTransition.framework */,
+                               F8A182268839C90A31F0F987A088E87B /* iOS */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               6A39F4DC00843FFC458FE4616AD48B9D /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               26216404833D73818C5EFEC7E16DA183 /* AEXML.framework */,
+                               B6A4879596BEAAC18CA41AD231043F17 /* Alamofire.framework */,
+                               17E87A50423EE2843AB0EB08528F52EC /* AlamofireActivityLogger.framework */,
+                               F49AA6EB751F7512499D46DE90D3EAE6 /* FirebaseCore.framework */,
+                               457F370BE978B9A7B711AB40080ED382 /* FirebaseMessaging.framework */,
+                               9AF454025C49412F40A5D08FF3EFD576 /* FolioReaderKit.framework */,
+                               DE2432A0214596A77E0715BF9EDA6217 /* FontBlaster.framework */,
+                               8BA27C2B8AAF78AFF56284BFAEABBBEF /* GoogleUtilities.framework */,
+                               92BF3A91822F9A6AA5AC338BDF21C78E /* JSQWebViewController.framework */,
+                               9D3AC472F71CAA627B1A729C76C47E40 /* Kingfisher.framework */,
+                               D489E4F14592AD2E12AB3FE975D754FD /* MatomoTracker.framework */,
+                               B8746C7F6910CD010EF2C8342D472881 /* MBProgressHUD.framework */,
+                               73A38D551097D8B8470056E0B48FD703 /* MenuItemKit.framework */,
+                               8180AFDD65D23809C369C6C0D0AE2006 /* MZDownloadManager.framework */,
+                               BB48738A79C0712C66A87BA10D8F71B0 /* nanopb.framework */,
+                               08E6C7A1FFCA6B21341142FFDC8761EE /* OAuthSwift.framework */,
+                               A9EE7F7299E48AE9BAE6A21FC4E45526 /* Pods_WolneLektury.framework */,
+                               3AF9BC4C9A3CD19279B980ED20FBAD39 /* Protobuf.framework */,
+                               60BF42A0CA4751A6D288CDE5D876CA40 /* Realm.framework */,
+                               8A8A815B8D521F8CDEB202EE76039FB7 /* RealmSwift.framework */,
+                               FF3E6E1A3AAB0EAF989465DAEDBB72B3 /* SideMenu.framework */,
+                               ADE94AD745E9D666DE8E2CEDAC68333D /* SSZipArchive.framework */,
+                               95E96EB83DCAB6611B1B9CE36203FD71 /* SwiftKeychainWrapper.framework */,
+                               2E6F3CBA4A6A0429B6813DB5778F3D40 /* Toast_Swift.framework */,
+                               37215B06C349AEA05F2D6634E784DCC0 /* ZFDragableModalTransition.framework */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               6CC135C2BE55F065813888C8E6BBDB78 /* FirebaseCore */ = {
+                       isa = PBXGroup;
+                       children = (
+                               47C92578FFB7251147AE7AD4DAF28E6F /* FIRAnalyticsConfiguration.h */,
+                               9B1E7F0536D4F2862E841EA8D47CC44E /* FIRAnalyticsConfiguration.m */,
+                               4F5216060EC5386917B636135207D13B /* FIRAnalyticsConfiguration+Internal.h */,
+                               913FAA89ACC1B00F7CB063D2904C54F4 /* FIRApp.h */,
+                               553FFB49824A1E787E4D0F386F6DE765 /* FIRApp.m */,
+                               E586F0F684FC8FE07B23490EB240D074 /* FIRAppAssociationRegistration.h */,
+                               BF4FAD5E6B95D0DCD4CF2873B7A5F657 /* FIRAppAssociationRegistration.m */,
+                               C0BAC9516D62801AB56D27E95F7F3D9F /* FIRAppInternal.h */,
+                               EB82AFCF317326DBE0583F7DBA1C4456 /* FIRBundleUtil.h */,
+                               557649FFA165485E24A1271D2638F344 /* FIRBundleUtil.m */,
+                               77F70CB6DF1D0FD7256121A7906A25E0 /* FIRComponent.h */,
+                               E57A9782169E3DFBF458AD63149C5AFB /* FIRComponent.m */,
+                               71C22AA5D5F284DC9B78BFAF42A83C57 /* FIRComponentContainer.h */,
+                               2C570FE262AD0188836ABD1A74A469B8 /* FIRComponentContainer.m */,
+                               38D0FD7E483E650C35611A41941D7F0C /* FIRComponentContainerInternal.h */,
+                               4125B8ABC42EE8FDD40F2E6DF0DB3D20 /* FIRComponentRegistrant.h */,
+                               F5CD30385E94D3E58B07F43DE2CF3F9B /* FIRComponentType.h */,
+                               5267DB065F6BF93D0021FB08DF8245BC /* FIRComponentType.m */,
+                               27E4181D0238606C2DB16B0708782E6F /* FIRConfiguration.h */,
+                               9F65B7D4BDC23758E135571100B9ADD3 /* FIRConfiguration.m */,
+                               77E15D3937F6AFDAD39719014F24AF7A /* FIRCoreConfigurable.h */,
+                               D4D57FCAFAEC5BD45B0A651B90B820F7 /* FIRDependency.h */,
+                               F3BDC06DD13BFE0A5C49CF6C90378D22 /* FIRDependency.m */,
+                               48A23DC4D40077329084C4E6B9086403 /* FirebaseCore.h */,
+                               544A23E1053CDBF91F0B3731EDA4FFC4 /* FIRErrorCode.h */,
+                               56D082DC4F23AECFD551D310E0D3C41C /* FIRErrors.h */,
+                               76C8AA2F24D4A7499FBFEC89207A32B8 /* FIRErrors.m */,
+                               1139D92D5DD588DED3347176B562CE39 /* FIRLogger.h */,
+                               B54964F717A975AE9680DA086D24374B /* FIRLogger.m */,
+                               B6BCD5898DBE90DAD2D5258B776CE839 /* FIRLoggerLevel.h */,
+                               52CCC25F1B42510727D9EDF134CB3132 /* FIROptions.h */,
+                               CE3134DC7AF610DBF7C87E8EDCEC2BED /* FIROptions.m */,
+                               A5D04CCAC2902D10865E0D2EF5DADE45 /* FIROptionsInternal.h */,
+                               BEED1B7F7E8BEE0FECD996A3C14F1965 /* FIRVersion.h */,
+                               A66F9D57725E8CDA7C62CB465AA36456 /* FIRVersion.m */,
+                               C24B62D14585D8A2ECBC638D318E87B1 /* Support Files */,
+                       );
+                       name = FirebaseCore;
+                       path = FirebaseCore;
+                       sourceTree = "<group>";
+               };
+               6E01492863C6603A2FAC30ED1E006A4C /* GoogleAppMeasurement */ = {
+                       isa = PBXGroup;
+                       children = (
+                               FADF32E2008DA74E1610650187C443F2 /* Frameworks */,
+                       );
+                       name = GoogleAppMeasurement;
+                       path = GoogleAppMeasurement;
+                       sourceTree = "<group>";
+               };
+               735777F6FD536A8F3A8205091EA02B20 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               21537D1611785AD9AF8C2D2C8014E8C7 /* FontBlaster.modulemap */,
+                               191B12415ABB5A507EEBB049752E30AD /* FontBlaster.xcconfig */,
+                               942DDF3E267A719FF596B56B855D26D9 /* FontBlaster-dummy.m */,
+                               292A755469D2B57D1C34F30F13C1F167 /* FontBlaster-prefix.pch */,
+                               126192276E659647B019680598D32B29 /* FontBlaster-umbrella.h */,
+                               1DC773D985A5ABEEB648555055E8329E /* Info.plist */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/FontBlaster";
+                       sourceTree = "<group>";
+               };
+               7525D290334C997F8E9E2D2A08C7E4F2 /* FirebaseMessaging */ = {
+                       isa = PBXGroup;
+                       children = (
+                               7F9995DB177F960FAC776F60EE05C851 /* FirebaseMessaging.h */,
+                               95086548DA4949A5831665FD287ACD99 /* FirebaseMessaging.h */,
+                               FD7782331BF962C501ACCF35CB93A3A2 /* FIRMessaging.h */,
+                               E14F77E50CAD9E11C3549CBA1979F1DF /* FIRMessaging.m */,
+                               4A118E1890C7A2E6094454B06E7D1CA6 /* FIRMessaging_Private.h */,
+                               27B24667E9710453A768281EEBD94D07 /* FIRMessagingCheckinService.h */,
+                               271E61CB4DD108196A89495C96CA3D61 /* FIRMessagingCheckinService.m */,
+                               9A28197B26F0C810272E9A84A7487DD3 /* FIRMessagingClient.h */,
+                               994AF878471D422AC2866C7E5A3733AE /* FIRMessagingClient.m */,
+                               914C23ACBDFC7C74C0EF765A53C0EC7C /* FIRMessagingCodedInputStream.h */,
+                               75E719D436BFA4E227332190F0A114CC /* FIRMessagingCodedInputStream.m */,
+                               52994FB9A90DB506C3E6F452B690FB0A /* FIRMessagingConnection.h */,
+                               42ECF2075F0721267E036F3A8F90A0D0 /* FIRMessagingConnection.m */,
+                               AAE711C897BC13E226DA166153F2CC1A /* FIRMessagingConstants.h */,
+                               7ABD3E8F081279D07705FBE2734A30CC /* FIRMessagingConstants.m */,
+                               981A5B8EBD0C0B1A640FF08231E6B5B6 /* FIRMessagingContextManagerService.h */,
+                               D0F97794D9CDF7090BB151567CC375CE /* FIRMessagingContextManagerService.m */,
+                               7D42DEFD122E76DA6A7E235F66B755A0 /* FIRMessagingDataMessageManager.h */,
+                               D4217B57C3B931D356222A9AD5E757B1 /* FIRMessagingDataMessageManager.m */,
+                               644342C27115C1D613C4C9D164A46647 /* FIRMessagingDefines.h */,
+                               E819F8A139413C7ACFA07F82BF5F5CA9 /* FIRMessagingDelayedMessageQueue.h */,
+                               07900388E4AD52051EE289F5ED2EEDF4 /* FIRMessagingDelayedMessageQueue.m */,
+                               3C5B9BB923D2153C5695489BFCB3FE6A /* FIRMessagingInternalUtilities.h */,
+                               6E393C39CB6940A61FA1A4A213758B92 /* FIRMessagingLogger.h */,
+                               4259A6F91094C5C3FE957FE5285966CD /* FIRMessagingLogger.m */,
+                               10EEF27CE0A2D40194B5166A61763970 /* FIRMessagingPacketQueue.h */,
+                               D81BFE71A07B939C010F22763CBED031 /* FIRMessagingPacketQueue.m */,
+                               2BA66D357F5E6460D64AEC0F89C5ABBB /* FIRMessagingPendingTopicsList.h */,
+                               5192103CC06271B7D3368A703ACDCCCB /* FIRMessagingPendingTopicsList.m */,
+                               FE0FC737191323EFE3F2E1F6EB1608C2 /* FIRMessagingPersistentSyncMessage.h */,
+                               F1338B52ECE802CF0757825F0E908475 /* FIRMessagingPersistentSyncMessage.m */,
+                               FEC060F5B023A25663DA3A7FF0A8A1A6 /* FIRMessagingPubSub.h */,
+                               080E328D3807B3217C5C18DF8C4A3EEE /* FIRMessagingPubSub.m */,
+                               DA4991241E87AAE84DFB7BA0D724913D /* FIRMessagingPubSubRegistrar.h */,
+                               7A658FBE00AD5575FAEB350259C6C0C6 /* FIRMessagingPubSubRegistrar.m */,
+                               8835399917D54555633265E36FF8665D /* FIRMessagingReceiver.h */,
+                               92B419D2322B119DB7175E65FF9B76F3 /* FIRMessagingReceiver.m */,
+                               CDA1CF0F7DD23253D2FF8DEBD395CA56 /* FIRMessagingRegistrar.h */,
+                               1B85F70E0A1A55686844764DFE3D8D51 /* FIRMessagingRegistrar.m */,
+                               260BE158E1193A24E66F1F038B243149 /* FIRMessagingRemoteNotificationsProxy.h */,
+                               8721E6220CCCF76B0303EB98C5388FA4 /* FIRMessagingRemoteNotificationsProxy.m */,
+                               EA756190D5253EAE03798BEA1646E0C1 /* FIRMessagingRmq2PersistentStore.h */,
+                               93B635E4D5E3A40A7208AF4D15B52428 /* FIRMessagingRmq2PersistentStore.m */,
+                               45DFA3160A343E4AA095D885BBD9483B /* FIRMessagingRmqManager.h */,
+                               C1E4306D36A4D225A7D2A80EE05DEADF /* FIRMessagingRmqManager.m */,
+                               5535738AED780D0B2515527F81E80523 /* FIRMessagingSecureSocket.h */,
+                               5B53B8C53CD29268FAB913C8A28911B7 /* FIRMessagingSecureSocket.m */,
+                               23841AC0A870055682FE7FBEE6920AE3 /* FIRMessagingSyncMessageManager.h */,
+                               F50C72731047DE9CC0AA2AA8A9570C7D /* FIRMessagingSyncMessageManager.m */,
+                               FE7332CEBB79FA1E661973254DDB64BC /* FIRMessagingTopicOperation.h */,
+                               9BDDA12CFFFDAF42893E0356DEFD50B9 /* FIRMessagingTopicOperation.m */,
+                               CC32893E8F8BEBF85F395C456E4DC02E /* FIRMessagingTopicsCommon.h */,
+                               8C1DA1A18B834B12A4D0AB03D6158BAA /* FIRMessagingUtilities.h */,
+                               BE7C83907BD9FDBF71121B4369E85D4D /* FIRMessagingUtilities.m */,
+                               B641B98C9FE0F76E5A9246DD05AEF31C /* FIRMessagingVersionUtilities.h */,
+                               0A71836910F0F4E2CBA0CD1D3D5940EF /* FIRMessagingVersionUtilities.m */,
+                               E79B9239EB89FA4AD74571B09810D57E /* FIRMMessageCode.h */,
+                               A2349254CD9C9408722F71F6AD779F05 /* GtalkCore.pbobjc.h */,
+                               B91A3C531E1F1C9F2523B12FBC6B31F5 /* GtalkCore.pbobjc.m */,
+                               371FFB7916EE7697D6F76BA98C2717AA /* GtalkExtensions.pbobjc.h */,
+                               1D6B8DEAD7FD8714CF97837608E65A53 /* GtalkExtensions.pbobjc.m */,
+                               55F481A6CA9DC577BC74D7F5D53EDF8B /* NSDictionary+FIRMessaging.h */,
+                               B5625DF9366F18F9E5B0D1CF875407AD /* NSDictionary+FIRMessaging.m */,
+                               800828347609EC68238C01C399EE9BAB /* NSError+FIRMessaging.h */,
+                               A6C3A06B414DEEC08D882C5AA7655DC3 /* NSError+FIRMessaging.m */,
+                               10791832B9481862D47906950191849D /* Support Files */,
+                       );
+                       name = FirebaseMessaging;
+                       path = FirebaseMessaging;
+                       sourceTree = "<group>";
+               };
+               7BD483F9820EA71B96782CED3C5B0D70 /* MBProgressHUD */ = {
+                       isa = PBXGroup;
+                       children = (
+                               8E4511CE1127E4BD217BDCDA408D6412 /* MBProgressHUD.h */,
+                               35746DF215D495B2F6B254F11A4B932C /* MBProgressHUD.m */,
+                               92DD4DE3C071A8E5839C8C26E7FC1853 /* Support Files */,
+                       );
+                       name = MBProgressHUD;
+                       path = MBProgressHUD;
+                       sourceTree = "<group>";
+               };
+               7DB346D0F39D3F0E887471402A8071AB = {
+                       isa = PBXGroup;
+                       children = (
+                               93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */,
+                               6554C4FC4FC2D4754FCA141A9949FAEC /* Frameworks */,
+                               3525020F0C9C4886278AF73393EA986C /* Pods */,
+                               6A39F4DC00843FFC458FE4616AD48B9D /* Products */,
+                               52D6AE34C4E0F34026B6742B61C0471A /* Targets Support Files */,
+                       );
+                       sourceTree = "<group>";
+               };
+               7DD75EF37FA296C27559ACF08F17AF64 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               40A2A80064952DE4D4507D13F705D066 /* Info.plist */,
+                               A03D3817FB78E6E3C9263752C05307DC /* RealmSwift.modulemap */,
+                               405DF96346BB3ABC89A8AEEF6B9B995F /* RealmSwift.xcconfig */,
+                               3692888732B2C535E1B29F8055892B60 /* RealmSwift-dummy.m */,
+                               9C8782BA532A6DC13F487B719C37E76D /* RealmSwift-prefix.pch */,
+                               21214BE8206ACC637CA1C38C1FD26273 /* RealmSwift-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/RealmSwift";
+                       sourceTree = "<group>";
+               };
+               7FCF73539F770DAAA5EDEC7049D9A53E /* SideMenu */ = {
+                       isa = PBXGroup;
+                       children = (
+                               F0FB00C4DF6F35808D341ED5C7A18C79 /* SideMenuManager.swift */,
+                               949A6E53FE16E2D6C8BCFE91DF8285B6 /* SideMenuTransition.swift */,
+                               952F782A447D10CA2ADAA9DFD3016A9A /* UISideMenuNavigationController.swift */,
+                               7E200356C1953B2F03FD9BCED858D6A3 /* UITableViewVibrantCell.swift */,
+                               27FA4F5EDA23BFA25823B5AF80FC7EDF /* Support Files */,
+                       );
+                       name = SideMenu;
+                       path = SideMenu;
+                       sourceTree = "<group>";
+               };
+               8760F6C8233DAA9F8A90F12152F6C8C2 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               64DD69F5766B0BC4B9B88FD6ABC36472 /* AEXML.modulemap */,
+                               439ED54E64F4037372B80993935BB837 /* AEXML.xcconfig */,
+                               50FD250AF3DF50EFB3BFDE0C1A66DB27 /* AEXML-dummy.m */,
+                               3C7E52BB0C02BDC584CD76DF51B1B4A9 /* AEXML-prefix.pch */,
+                               F9188B8734549A2FE3A1E20B11E68AA9 /* AEXML-umbrella.h */,
+                               3E9295BAC2870D8B5DCFE535DDDAEA03 /* Info.plist */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/AEXML";
+                       sourceTree = "<group>";
+               };
+               8D702CEF8E49867D36C4FE20038C7204 /* FirebaseInstanceID */ = {
+                       isa = PBXGroup;
+                       children = (
+                               4DC94AA82575A6D073C93B205EC6A6D9 /* Frameworks */,
+                       );
+                       name = FirebaseInstanceID;
+                       path = FirebaseInstanceID;
+                       sourceTree = "<group>";
+               };
+               8D8172F242BB23F774B2DC1E5D5922A8 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               5BA7792CBD42BEB1923EA0ADB15A10C7 /* Info.plist */,
+                               B5ECAEB329C348903C27484F1697B006 /* Toast-Swift.modulemap */,
+                               80A169221157FA4EC5B1B57B2E943A32 /* Toast-Swift.xcconfig */,
+                               2C649BF15B07EDFC3A64FEB2AAA77C29 /* Toast-Swift-dummy.m */,
+                               F30A057D3DA981112B03FE6E5B956E6E /* Toast-Swift-prefix.pch */,
+                               31C89ED169005A2C1DBDF20457DCAE15 /* Toast-Swift-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/Toast-Swift";
+                       sourceTree = "<group>";
+               };
+               90875D8FC731EED009452DD40005716E /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               6394969E53E997D80E253AA0E193E08E /* Alamofire.modulemap */,
+                               DCD69015A6C555B93460E69425DB1406 /* Alamofire.xcconfig */,
+                               9B1A74A1C0913D69BECC8DD4A1244E42 /* Alamofire-dummy.m */,
+                               006988DA32BDAA5630C125654D9C44A9 /* Alamofire-prefix.pch */,
+                               27D30FBF66A9DE692B39A8C5F09C89BC /* Alamofire-umbrella.h */,
+                               02FC51FFCD3F7704A972B5801BCF548E /* Info.plist */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/Alamofire";
+                       sourceTree = "<group>";
+               };
+               90DD5F7BEA3C3A4C99643966F336A587 /* RealmSwift */ = {
+                       isa = PBXGroup;
+                       children = (
+                               11464C69FD2AF6A1A4FB214E7DA90908 /* Aliases.swift */,
+                               BDF51D99BBE2062A17F2CC6D14953BE3 /* Error.swift */,
+                               49A8496480916B38FC60F790D50CAF75 /* LinkingObjects.swift */,
+                               DE3D1E8C327C132322D396C80318931C /* List.swift */,
+                               00D3597C80CBA96B3BFF1BA9CA8BBE7B /* Migration.swift */,
+                               24BECF804ACC1DFA1AEF908133E8CDF9 /* Object.swift */,
+                               8CB63B8D80EDAD314AAD2D198C7B0C90 /* ObjectiveCSupport.swift */,
+                               8AB132E5EB9BAC1B61BECF12A10C2BF5 /* ObjectSchema.swift */,
+                               8A7FC9C89D6223A32109126F8DA0598B /* Optional.swift */,
+                               67F2AFDD06DDE1D92AA1028FB0C8C109 /* Property.swift */,
+                               A08B99A4BBBE9911D2FBF593D5865F00 /* Realm.swift */,
+                               3F0C4378148A65453A4D307AEAFF61D7 /* RealmCollection.swift */,
+                               888A59D2EDB25ABCA1679CB435837211 /* RealmConfiguration.swift */,
+                               78F09E028FCE691991655050A5C01557 /* Results.swift */,
+                               9A6814BEFF03CE726BEBE631A1023F3D /* Schema.swift */,
+                               06FC57F395225CF90BC8F25F5C08777D /* SortDescriptor.swift */,
+                               72A41C016F25201385C2676F0DA9C35B /* SwiftVersion.swift */,
+                               6A09C91D35E07AA4192A517BE98B0501 /* Sync.swift */,
+                               27D24FB6B80FD2575D5E65E4D136E8F3 /* ThreadSafeReference.swift */,
+                               581B420D10729B6EFD3B20CB081FC1D6 /* Util.swift */,
+                               7DD75EF37FA296C27559ACF08F17AF64 /* Support Files */,
+                       );
+                       name = RealmSwift;
+                       path = RealmSwift;
+                       sourceTree = "<group>";
+               };
+               90FE502EEC711DD7F35FE740DF049CA4 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               FE4F111F51BF9B184A5BB08ACC653779 /* AlamofireActivityLogger.modulemap */,
+                               17476BC599C0EE4947789CF3EC5E1024 /* AlamofireActivityLogger.xcconfig */,
+                               996EA22BABCBE1C3BAD96548D6A604EA /* AlamofireActivityLogger-dummy.m */,
+                               3C292CFC75EC0FD46345F9ED0D5374DA /* AlamofireActivityLogger-prefix.pch */,
+                               002E42C157E2F539BD6615AA794C87F5 /* AlamofireActivityLogger-umbrella.h */,
+                               2A77D96CD73CC56CAADE88A26867210D /* Info.plist */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/AlamofireActivityLogger";
+                       sourceTree = "<group>";
+               };
+               92DD4DE3C071A8E5839C8C26E7FC1853 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               DC09E3FDEEA4F2DCC4E5C5E2947C1282 /* Info.plist */,
+                               ADE9A188FDEC484B9CDF695ECE7959A8 /* MBProgressHUD.modulemap */,
+                               43CDCABD9284457D43B646E90B874944 /* MBProgressHUD.xcconfig */,
+                               0DBAF510F0AE8270FAC4746FF6B5DE61 /* MBProgressHUD-dummy.m */,
+                               9063F3DE5317FC929388EA67A8F5C368 /* MBProgressHUD-prefix.pch */,
+                               29C8800C3B241A2D1C57FB57C1A07B4F /* MBProgressHUD-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/MBProgressHUD";
+                       sourceTree = "<group>";
+               };
+               96465B22D3460DC947309E5EA8439439 /* Network */ = {
+                       isa = PBXGroup;
+                       children = (
+                               56308D4A7C4CA17AC58C82130DDC2718 /* GULMutableDictionary.h */,
+                               1B850B52C6C12544F2E2F5CD3FE6B3E2 /* GULMutableDictionary.m */,
+                               145E95AE5F85319E80B8B12CC8A05570 /* GULNetwork.h */,
+                               DC2E8829C4E43BC76E7158A0E3E701AF /* GULNetwork.m */,
+                               7EC37761CA1DCE7138823E75B1E22949 /* GULNetworkConstants.h */,
+                               E6F1BACD1BE358CC3FB4627C58C15B63 /* GULNetworkConstants.m */,
+                               422B10EE9CCDEDA3886C8B1E3F5FC8C1 /* GULNetworkLoggerProtocol.h */,
+                               5016398FE6D4E791E29F725CF2C48D1F /* GULNetworkMessageCode.h */,
+                               102F3288391D25B4A3D40836298C7188 /* GULNetworkURLSession.h */,
+                               147E69F1ED7DFD212ABB16B8DF662DA5 /* GULNetworkURLSession.m */,
+                       );
+                       name = Network;
+                       sourceTree = "<group>";
+               };
+               9AC14799E35FF2DDB6EAE4BAF02612AE /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               5247E5300F4595C93CFD5EFEB52D7B63 /* FolioReaderKit.modulemap */,
+                               867A9A2B86FF919DE6428765E5138E5E /* FolioReaderKit.xcconfig */,
+                               41E4C9647DF00F5A8397812EB95B4B77 /* FolioReaderKit-dummy.m */,
+                               95D533AD2585411926C7E5E8AC9C4CB0 /* FolioReaderKit-prefix.pch */,
+                               61D0B52E0F670DE2CB3F0AA75F6ECF4F /* FolioReaderKit-umbrella.h */,
+                               29EECBD20B32BF000A7D4FB4F05DF3F9 /* Info.plist */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/FolioReaderKit";
+                       sourceTree = "<group>";
+               };
+               9C2EE7EB6ED15058458BB69651F41FBB /* nanopb */ = {
+                       isa = PBXGroup;
+                       children = (
+                               CCDB7486E72F0C100C4E4BB6FEE50AD9 /* pb.h */,
+                               E8A06D0E7033B7449F302ACF32EBB05E /* pb_common.c */,
+                               34414B1475EAA53DE09EE78DBCF4F362 /* pb_common.h */,
+                               4F6C31FBB1F555C570AF8298A3C1B669 /* pb_decode.c */,
+                               963111857C649092C623F85E0357C427 /* pb_decode.h */,
+                               475E47404BD3273B96027E83999E404E /* pb_encode.c */,
+                               7E803E5235D95B461EE5B6994A8C102A /* pb_encode.h */,
+                               CADC124A90EBB5CCB4FF575FB83B0D7E /* decode */,
+                               2C83513224506E7FFBF1EBC2C5A1FA19 /* encode */,
+                               4BF630CEE5770AD6975E58B778B441D1 /* Support Files */,
+                       );
+                       name = nanopb;
+                       path = nanopb;
+                       sourceTree = "<group>";
+               };
+               9DBFDAC85DFB0472300EC6068543E40A /* ZFDragableModalTransition */ = {
+                       isa = PBXGroup;
+                       children = (
+                               3D9516D4522CD7C42A017229D056F0A4 /* ZFModalTransitionAnimator.h */,
+                               E6E81ACB0B42B77DE2FFAAD41DC5D97B /* ZFModalTransitionAnimator.m */,
+                               A7F6B43413B5155DD78ADE71391CE5FB /* Support Files */,
+                       );
+                       name = ZFDragableModalTransition;
+                       path = ZFDragableModalTransition;
+                       sourceTree = "<group>";
+               };
+               A19EDB5C725F1B0055B1AD383A0FE5DE /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               D0B097B839F15EE609BCC436E5608221 /* Info.plist */,
+                               89079A7F371E0726D388AB0BF9142BC1 /* OAuthSwift.modulemap */,
+                               F31594F98BBECEB6EC59E4DA97410A6D /* OAuthSwift.xcconfig */,
+                               3045AE10FB372AE8C59AF5E27B74F273 /* OAuthSwift-dummy.m */,
+                               AB031B75B0BA0CBC90F67922A73A4C5D /* OAuthSwift-prefix.pch */,
+                               B4840E7D2EB9EB8417A1C9645FEDAAAE /* OAuthSwift-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/OAuthSwift";
+                       sourceTree = "<group>";
+               };
+               A6F534F3F3142215ECB0662A0F672BDA /* Protobuf */ = {
+                       isa = PBXGroup;
+                       children = (
+                               EB7911A826F61129EE80FDDE264EF222 /* Any.pbobjc.h */,
+                               507E13A307CB3F11C321AD5B742C1C07 /* Any.pbobjc.m */,
+                               D70294FBB42ACF7801395BF1CDD4A314 /* Api.pbobjc.h */,
+                               62D18DABC2A2093AA4FF972DC35EE001 /* Api.pbobjc.m */,
+                               0DBAFFED7A2B4724B0A96F2E74FDB1E6 /* Duration.pbobjc.h */,
+                               3300795729EA0A168E19390ED6E7867E /* Duration.pbobjc.m */,
+                               A6A011D6F31E757D458A431970A9F122 /* Empty.pbobjc.h */,
+                               4CCB952DC44D32026263D1EB9F1D19CE /* Empty.pbobjc.m */,
+                               7AA2497F29326836BF937B466101FCB1 /* FieldMask.pbobjc.h */,
+                               8E3805379AC3A9301D26DE62077DF340 /* FieldMask.pbobjc.m */,
+                               7D124571A3976EA459CE4987C55A9192 /* GPBArray.h */,
+                               9652C4208B2FE01685B9A06DA74AFC47 /* GPBArray.m */,
+                               E927E8565EF0555119B9A543D4AF8643 /* GPBArray_PackagePrivate.h */,
+                               3304A45FA8673B2DACEE5AAA8C75E0FD /* GPBBootstrap.h */,
+                               87970034DF91CE05199D6AAA2E708D72 /* GPBCodedInputStream.h */,
+                               B2AD5B1877FE90020B8EC5A5A9468CFD /* GPBCodedInputStream.m */,
+                               DA0DAE3F773587A21A814757E4F02240 /* GPBCodedInputStream_PackagePrivate.h */,
+                               382852248FE8C625CF0186C9D1FFCC34 /* GPBCodedOutputStream.h */,
+                               08B415F2B9935EB42007587E2BF3BFF3 /* GPBCodedOutputStream.m */,
+                               9C996AC58C57E04E25603A8E527EFDD9 /* GPBCodedOutputStream_PackagePrivate.h */,
+                               1CB73575EACC7B667464A81741CD40B9 /* GPBDescriptor.h */,
+                               735555A726916552BAAAE6D9696116EC /* GPBDescriptor.m */,
+                               664B0758A8B043DFA4F69B51BA065D60 /* GPBDescriptor_PackagePrivate.h */,
+                               22DBCD1AD7AE653008499BE79CEE9E27 /* GPBDictionary.h */,
+                               09C9AA3CFC585729EE4EDE5BD86C0BE8 /* GPBDictionary.m */,
+                               4874EE6FBA94E2552B91BABF2A599E39 /* GPBDictionary_PackagePrivate.h */,
+                               1632A30520BF40ED6C45A578D6411917 /* GPBExtensionInternals.h */,
+                               E3C8D78F1AD702A005F2E4E7F4DC718D /* GPBExtensionInternals.m */,
+                               BB067AB097CD608D10755D6B04142C5B /* GPBExtensionRegistry.h */,
+                               7E3BE0CB2177D65A373AA72656755163 /* GPBExtensionRegistry.m */,
+                               920321EC36450F9B7B43C072420F78D7 /* GPBMessage.h */,
+                               D77B5098FA436FB6E1221AB6EDF22370 /* GPBMessage.m */,
+                               77801061E8A612FE9356272D868D0287 /* GPBMessage_PackagePrivate.h */,
+                               CA67844C5A4BD05A959C0E1A6F8CE505 /* GPBProtocolBuffers.h */,
+                               F2D84C6D27A7D7F7981ADF55AE61C3DA /* GPBProtocolBuffers_RuntimeSupport.h */,
+                               30B4F0F35B2E6FB1AB726ECD8CA57734 /* GPBRootObject.h */,
+                               471BFA1649D5CA3AE3965F662D1DE691 /* GPBRootObject.m */,
+                               1E620905A80A469191BFE9DB5F9240E1 /* GPBRootObject_PackagePrivate.h */,
+                               4B125D2F070E0A07B9BCA5BAA19580AF /* GPBRuntimeTypes.h */,
+                               FFF62627BCA2758AD2D6ACBEC9926E8F /* GPBUnknownField.h */,
+                               2C3D986619B47384323E22FC783B359B /* GPBUnknownField.m */,
+                               142D4F8576AA189AB61C610C1D4C95E9 /* GPBUnknownField_PackagePrivate.h */,
+                               8D93CE20821B7BD08A6D6ED2CE388920 /* GPBUnknownFieldSet.h */,
+                               E676199B28E86FF57CC135787DDEFCD4 /* GPBUnknownFieldSet.m */,
+                               E1370966B1A42FC061491DDB4307FD36 /* GPBUnknownFieldSet_PackagePrivate.h */,
+                               98BE3D9CD9277E245270D62A28AD5DC6 /* GPBUtilities.h */,
+                               B2FB4434FC33598F34B81CC4C2BBA4A6 /* GPBUtilities.m */,
+                               9E347A569BFF34387F66BD101D00CE61 /* GPBUtilities_PackagePrivate.h */,
+                               1F3368CC7921022310DA5AC855FBF373 /* GPBWellKnownTypes.h */,
+                               A725C1F81E58C098054A2B656E85AF76 /* GPBWellKnownTypes.m */,
+                               E49A610A798652E72449FC283CB8E58F /* GPBWireFormat.h */,
+                               C5EAE55F8524706A50D1E64B2067946C /* GPBWireFormat.m */,
+                               DEA90EC1C9E0AAD475B6E5C849B0DFE7 /* SourceContext.pbobjc.h */,
+                               154EB52C148A3AC43AAFFEBEB198039F /* SourceContext.pbobjc.m */,
+                               B11AB6EE95088046C66066C1A9DAD036 /* Struct.pbobjc.h */,
+                               B90FE0A11962A6DA9BF00E6837D6D97F /* Struct.pbobjc.m */,
+                               2CBAD617EBEDCB28ABE5FE59AFF568AC /* Timestamp.pbobjc.h */,
+                               D7F8073C1B181564744EF259B9F8739C /* Timestamp.pbobjc.m */,
+                               874F2D847F616BB0F3DA0EFB4ACD0FD2 /* Type.pbobjc.h */,
+                               2F9331D2AE04D7359BA18D23BDA96D7A /* Type.pbobjc.m */,
+                               0829B70301B60B62E908B075914CABF5 /* Wrappers.pbobjc.h */,
+                               F2F483A8EC81272E3D12F5FEAC0445C7 /* Wrappers.pbobjc.m */,
+                               581A0F9429BAC7DDBF34C6D0FF9F3159 /* Support Files */,
+                       );
+                       name = Protobuf;
+                       path = Protobuf;
+                       sourceTree = "<group>";
+               };
+               A7F6B43413B5155DD78ADE71391CE5FB /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AF61D0766107FF7823B5B06DFB3221D3 /* Info.plist */,
+                               3FDE4083CA57C224C3F4D3D0A234E8FA /* ZFDragableModalTransition.modulemap */,
+                               F9CDED3C6CB31F72B4C9F0B29CB4D813 /* ZFDragableModalTransition.xcconfig */,
+                               F9ED9601D0552304E103D84E3E96D6B3 /* ZFDragableModalTransition-dummy.m */,
+                               E905CCE024EFD711D72190AFB7BE740E /* ZFDragableModalTransition-prefix.pch */,
+                               DAED7224D7EC25824E0386956459E7A9 /* ZFDragableModalTransition-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/ZFDragableModalTransition";
+                       sourceTree = "<group>";
+               };
+               AC2243DEDD09BEA6BCD1D891FD9BEE17 /* MZDownloadManager */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B703AD37258C65E00D1C7CBE59684A89 /* MZDownloadManager.swift */,
+                               439526BFFA7B883CDBF438B9EA23C647 /* MZDownloadModel.swift */,
+                               94B7E924BB49EFDE5E1CF30DB2934504 /* MZUtility.swift */,
+                               492FAF4014AB0C47243F2590249A7CD3 /* Support Files */,
+                       );
+                       name = MZDownloadManager;
+                       path = MZDownloadManager;
+                       sourceTree = "<group>";
+               };
+               AD9EB60CAAD16802B01659350ABD7D55 /* MatomoTracker */ = {
+                       isa = PBXGroup;
+                       children = (
+                               441618E90A47E6B4A47B17F612C27BE4 /* Core */,
+                               C7E65BFA0325F7E7840AE78964719055 /* Support Files */,
+                       );
+                       name = MatomoTracker;
+                       path = MatomoTracker;
+                       sourceTree = "<group>";
+               };
+               B014D292E078790F9F3A628420F22675 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               1355BFFA13307D543E931E754A0C400B /* GoogleUtilities.modulemap */,
+                               518860BA42EF7435BFDD67CC13858D49 /* GoogleUtilities.xcconfig */,
+                               B6795E15B518CF390D339237ABBC9032 /* GoogleUtilities-dummy.m */,
+                               7E1E4AAE9C884BBB11A62AB5632E491D /* GoogleUtilities-prefix.pch */,
+                               B8A71DDD2334CACD70DFCA4D4C49E068 /* GoogleUtilities-umbrella.h */,
+                               C7E9B4F5787DCAAC9B794757C5709BC4 /* Info.plist */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/GoogleUtilities";
+                       sourceTree = "<group>";
+               };
+               B226072ED93BCD88AD84D3C69BD872F4 /* Crashlytics */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E651C8E4427AFF98E7D6A6CE3091862F /* ANSCompatibility.h */,
+                               75C33EBDEC91BF786E5D6B619847EBE7 /* Answers.h */,
+                               33F94CA67ECA556757ACE1D3C592B279 /* CLSAttributes.h */,
+                               F60C46D0A0F154124C98A12300EC6CC3 /* CLSLogging.h */,
+                               3541A4F6170F57F3159B5235DF1E20D6 /* CLSReport.h */,
+                               788487EAB3CAF7711050C108550F7F71 /* CLSStackFrame.h */,
+                               2D5D254748958FD22A32E3D5D3B9997D /* Crashlytics.h */,
+                               095BC404560B09B0DFF4F822380B5121 /* Frameworks */,
+                       );
+                       name = Crashlytics;
+                       path = Crashlytics;
+                       sourceTree = "<group>";
+               };
+               B5A8B19AC5306040D4E551899C399BF8 /* MethodSwizzler */ = {
+                       isa = PBXGroup;
+                       children = (
+                               0414DBE31282AC394C122F66BCD5F0B9 /* GULOriginalIMPConvenienceMacros.h */,
+                               619880A6749DFFFD64BB91B2E35CD611 /* GULSwizzler.h */,
+                               17F3C6DB088252560431D0D373867A04 /* GULSwizzler.m */,
+                       );
+                       name = MethodSwizzler;
+                       sourceTree = "<group>";
+               };
+               BB03BCC9653FF7E2D14F24690FD296B9 /* MenuItemKit */ = {
+                       isa = PBXGroup;
+                       children = (
+                               751FCB64C83107FC0D3415A048D97D1A /* Internals.swift */,
+                               FA98BF272CF91084C53F3CD50D3B18C6 /* MenuItemKit.h */,
+                               E97010326B67089745CE74477A2868F3 /* Swizzlings.m */,
+                               D9DB620986320D1403CEEBA251CB09C2 /* Swizzlings.swift */,
+                               4D4B08A2F7A3B6D01CC2E61B621DBDD6 /* UIMenuItem.swift */,
+                               50AF9A16FBEA3B2286BB71DC38F8622D /* Support Files */,
+                       );
+                       name = MenuItemKit;
+                       path = MenuItemKit;
+                       sourceTree = "<group>";
+               };
+               BEE4530588D33886305C1708066743D3 /* Pods-WolneLektury */ = {
+                       isa = PBXGroup;
+                       children = (
+                               C264DCAB575FD671A804AD57D7710977 /* Info.plist */,
+                               74D4394D9CF114521A46CA900125128B /* Pods-WolneLektury.modulemap */,
+                               89E56BEB4CD676A8D94F51F9C6E01ED1 /* Pods-WolneLektury-acknowledgements.markdown */,
+                               8C880512EDE126F43FCD1A23D069173B /* Pods-WolneLektury-acknowledgements.plist */,
+                               CF6A19A1959A02404C04F8950A83B596 /* Pods-WolneLektury-dummy.m */,
+                               EDA7D2FF7FC1178C591E6CB913FDAFD1 /* Pods-WolneLektury-frameworks.sh */,
+                               86D80C24B863D4F395E5574A6A6AF46E /* Pods-WolneLektury-resources.sh */,
+                               B451004D639471DB5A62688F187D31F0 /* Pods-WolneLektury-umbrella.h */,
+                               A24FF4F622BC306700C4E27CB55217B9 /* Pods-WolneLektury.debug.xcconfig */,
+                               9D4BD4F234C5733679D2D1FAC1B99FE4 /* Pods-WolneLektury.release.xcconfig */,
+                       );
+                       name = "Pods-WolneLektury";
+                       path = "Target Support Files/Pods-WolneLektury";
+                       sourceTree = "<group>";
+               };
+               BF03C3BD0CDD865DB1F8CE177BA17CD2 /* Fabric */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E9333496134B3CBFAAEAAFA463CF2680 /* FABAttributes.h */,
+                               1F9C0BE9D000422B577A97E3A9C3994C /* Fabric.h */,
+                               3858B7293812A87863A2968E595A7C97 /* Frameworks */,
+                       );
+                       name = Fabric;
+                       path = Fabric;
+                       sourceTree = "<group>";
+               };
+               C24B62D14585D8A2ECBC638D318E87B1 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               7EF265DF52FA8325B0C347AAB6C2F966 /* FirebaseCore.modulemap */,
+                               03DF232DE3B9FC2B3ABA654016B16028 /* FirebaseCore.xcconfig */,
+                               FC3A34A8538E5CC949053777424628AD /* FirebaseCore-dummy.m */,
+                               C242E0FBAFC647C7747ADD8DB8933A5D /* FirebaseCore-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/FirebaseCore";
+                       sourceTree = "<group>";
+               };
+               C66B3A79A9DE6F8D697700F91DAF4EB3 /* Realm */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADE4B9714FB02E4C58524BAA3E5D262E /* binding_callback_thread_observer.cpp */,
+                               1DE0FC609758763CD20EA7151D99DF94 /* collection_change_builder.cpp */,
+                               6F01E4CF285A102DB8F3FFC4F10EEE2C /* collection_notifications.cpp */,
+                               E3DA7423D7A46C6EADE51876FC656AB6 /* collection_notifier.cpp */,
+                               42071D61BA4308E0EBA1DB720C8DA29A /* external_commit_helper.cpp */,
+                               0E996F2DB7B3E51D9BD5BD8819F76842 /* index_set.cpp */,
+                               FB591C76B0F358BC96ACBDAF812A8552 /* keychain_helper.cpp */,
+                               8BB3550CF8402CA38A8968A24619F96D /* list.cpp */,
+                               F1FB8E4F26A7220132808171D2B97800 /* list_notifier.cpp */,
+                               6EC16E5C569E109186D6849D847BCA48 /* network_reachability_observer.cpp */,
+                               CE6AE3ED1C3D1C2BFE3264A07CACD6E2 /* NSError+RLMSync.m */,
+                               C1171DB362D96B024E18F290CEC25F65 /* object.cpp */,
+                               1E62823E12CC340D00EEB586EEF177F9 /* object_notifier.cpp */,
+                               30619A0389B050792D52E48CA7FB5016 /* object_schema.cpp */,
+                               71184DA28D4C35AD9B685754D612FA27 /* object_store.cpp */,
+                               CCE4359E18EB2A62E3B89616E1DF99AD /* partial_sync.cpp */,
+                               F7CD333CED7797EA7D39F18163187E6C /* placeholder.cpp */,
+                               177C7DCAC0D7CB576C44F872CFF50370 /* primitive_list_notifier.cpp */,
+                               3E1C11CC8048486D77496DD0A2226A8B /* realm_coordinator.cpp */,
+                               75473FEF44F28C44015117474F2BD735 /* results.cpp */,
+                               BEF9F5FA8A61F170B0136C7055E746E0 /* results_notifier.cpp */,
+                               D4D39D9675E518588A8F01F21E57FADD /* RLMAccessor.h */,
+                               99E4C1135178406ACD7B58D040D578B8 /* RLMAccessor.mm */,
+                               381F31B952F0996552B45EB064A7DF08 /* RLMAnalytics.mm */,
+                               111BC4AB5DFDA30B3AF9451ACE9A8D00 /* RLMArray.mm */,
+                               135AAE7A08E11EC8D750570317E36028 /* RLMArray_Private.h */,
+                               CC045719CEBEE0DAEFC813B823C1F006 /* RLMClassInfo.mm */,
+                               23C00816B8A572DD6069F377FC98681A /* RLMCollection.mm */,
+                               2189955C32F1B821C22A320B350D4E9D /* RLMCollection_Private.h */,
+                               559E68E63A2FF675E25D5268714196AC /* RLMConstants.m */,
+                               88B6D9C42264CD0C6BBBAA16AC38FBAB /* RLMJSONModels.m */,
+                               5A064D124C92CFA11CDFB748E3A1F23E /* RLMListBase.h */,
+                               8F6A6DC559F6B50404824FF2BC108753 /* RLMListBase.mm */,
+                               0858841A9D93BD6323F873933150A081 /* RLMManagedArray.mm */,
+                               597E534C745A644235BCAB1CB8A229CA /* RLMMigration.mm */,
+                               9407CB8E023E9A5CA6CBCCDBFE4232D0 /* RLMMigration_Private.h */,
+                               7E7474ABDCF1D1C9A8212C18E3BCFB2B /* RLMNetworkClient.mm */,
+                               705DCC263710805CED0A0D347B897A45 /* RLMObject.mm */,
+                               5A934BE8A55D992D204B3B9A14611E10 /* RLMObject_Private.h */,
+                               028B9D423259932362B562EE3C671242 /* RLMObjectBase.mm */,
+                               E0EBEE00C0127E3C90E732DB5F01CF5D /* RLMObjectBase_Private.h */,
+                               46FC785119A479FD291D52486083C3F8 /* RLMObjectSchema.mm */,
+                               EFD854B5B21344EA040B49EA6725FC00 /* RLMObjectSchema_Private.h */,
+                               67D2BFFD100F5D39305BA3FC2802DB4A /* RLMObjectStore.h */,
+                               4021B8B58240DC8FF47070F9C04D0B84 /* RLMObjectStore.mm */,
+                               5468DF52D9F560728DF9925C2F0D210A /* RLMObservation.mm */,
+                               B495C794563F95CB715EBF0C78EDBFE7 /* RLMOptionalBase.h */,
+                               AC9CFDB3F3B5693890E17EDBD113E565 /* RLMOptionalBase.mm */,
+                               28AEF58ACB93749F0DEF7933EDEBF6EE /* RLMPredicateUtil.mm */,
+                               325E20A67E5E9F463834C45CE511FA12 /* RLMProperty.mm */,
+                               16E69380BDC8BE32A32CE50AC558292F /* RLMProperty_Private.h */,
+                               07F8C4C696AED2F05397BDC937C97823 /* RLMQueryUtil.mm */,
+                               C1BE0B9E0494E6BB92AF3675D929C48B /* RLMRealm.mm */,
+                               3D6410D66A868469BEB52C0E969B7A21 /* RLMRealm+Sync.mm */,
+                               E48289A832C7D72E990BA4AD40B16CF4 /* RLMRealm_Private.h */,
+                               ECE59B0188CE015680FFF31522E8A852 /* RLMRealmConfiguration.mm */,
+                               88A4ECC72B8822083B261FCBB3FF852B /* RLMRealmConfiguration+Sync.mm */,
+                               19103A10E8E96743A56E682ED1DAC70D /* RLMRealmConfiguration_Private.h */,
+                               EBBA27AA678EB1363869D44237655330 /* RLMRealmUtil.mm */,
+                               6FE6126BA6653AAFFCEBCBBFFF6468D1 /* RLMResults.mm */,
+                               59F29FF04A45EA9F6FF92C7F6BAB4B12 /* RLMResults_Private.h */,
+                               5DECCEDCC3CF34ACB64710FFB19496B1 /* RLMSchema.mm */,
+                               9E3C47D73094DB09E1D3465A7E11ED30 /* RLMSchema_Private.h */,
+                               0ECF0996C30AA6093268F60DE4366E45 /* RLMSwiftSupport.m */,
+                               EE3822E4428D213B2F2D9A7BEB84C33B /* RLMSyncConfiguration.mm */,
+                               ABFF357E3BABBE6991EFE7D5FDB24E1A /* RLMSyncConfiguration_Private.h */,
+                               858870B7EB30D724E5204DDD159ABB0B /* RLMSyncCredentials.m */,
+                               00FD76608109F812520449E0094FE8EB /* RLMSyncManager.mm */,
+                               AF31C2EDFE5591ADC92DB32F0BC409BC /* RLMSyncManager_Private.h */,
+                               D981664DF64C9BF8EA0C1391E3BCDC0B /* RLMSyncPermission.mm */,
+                               B2D261BF1562798AA9C2BED6293A727E /* RLMSyncPermissionResults.mm */,
+                               359CF8CC37A4F97F6D3086A95EE10DA0 /* RLMSyncSession.mm */,
+                               1E36AA751EC8381B882AC6FDA504FBBB /* RLMSyncSessionRefreshHandle.mm */,
+                               0CEE0C6AFC79099D001A7A47F9DF3629 /* RLMSyncUser.mm */,
+                               903326FCEFF6EA67E56D2AC3651DA53D /* RLMSyncUtil.mm */,
+                               45D79C1899AC277B26DD3524D5AC9919 /* RLMSyncUtil_Private.h */,
+                               18DD8C06F1B3E9FD17466D5018391BF9 /* RLMThreadSafeReference.mm */,
+                               C67BBEF0CB8A371D0C8647130CD00979 /* RLMUpdateChecker.mm */,
+                               4370FE81CEE77016401F7AAAD0158F4A /* RLMUtil.mm */,
+                               E3D679A454CA39A6DB40C5D58EF59566 /* schema.cpp */,
+                               1AA32ED0B55D90CA66D7AEF4ACF95BF9 /* shared_realm.cpp */,
+                               85F84124A6BFAFBBE1B88A9C531344EE /* sync_config.cpp */,
+                               1A4A319D825BD520943E481653FBDB97 /* sync_file.cpp */,
+                               17C756083B788404F7E5739A71EF0218 /* sync_manager.cpp */,
+                               181C2DE81F42B9BF86D03575F19C0494 /* sync_metadata.cpp */,
+                               DAF1B68A9CAAA2CBE0B167CB2DB3976B /* sync_permission.cpp */,
+                               FDD0294627FC460FFE79962ABD58D781 /* sync_session.cpp */,
+                               368FE0998EDC34C2BF80A3D2F7898574 /* sync_user.cpp */,
+                               0B31CAB3A5CA2AA601A8A6B0DBA8C713 /* system_configuration.cpp */,
+                               BB849E98A82D4590D3BC88C7262C3BA0 /* thread_safe_reference.cpp */,
+                               32898A025087808B3DAD1D234840702F /* transact_log_handler.cpp */,
+                               89B9846A28B16483D7E8716E91BD0781 /* uuid.cpp */,
+                               C7BD9F4CEDFB5189A20C7BDA0DD11C47 /* weak_realm_notifier.cpp */,
+                               391BCB73939AC0FB909AA799B32593B8 /* Frameworks */,
+                               0E063F7D2362ADAB01028DA9EFAF5D95 /* Headers */,
+                               36266B7D46E52E23201D38174A471174 /* Support Files */,
+                       );
+                       name = Realm;
+                       path = Realm;
+                       sourceTree = "<group>";
+               };
+               C7E65BFA0325F7E7840AE78964719055 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               9BE6F0CE8E94835C26F17B16EAFC5775 /* Info.plist */,
+                               A744EE68CA6B35FFFDEB557843FE5AF7 /* MatomoTracker.modulemap */,
+                               B056E5B697BF12D5A943B914A8944339 /* MatomoTracker.xcconfig */,
+                               E24CCC6A27E2E018BF41A40B41A424AA /* MatomoTracker-dummy.m */,
+                               E252500B89B041B9B232AAAF467C2CD0 /* MatomoTracker-prefix.pch */,
+                               68048DCB850F7DD9097DC6465BE6708C /* MatomoTracker-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/MatomoTracker";
+                       sourceTree = "<group>";
+               };
+               C8877525A825CE91E95AB3D0E59E87CA /* Reachability */ = {
+                       isa = PBXGroup;
+                       children = (
+                               1F1711FCB677F0183149FEE396E5512E /* GULReachabilityChecker.h */,
+                               40F16EED8D382D5170EEEF0B6ABAB383 /* GULReachabilityChecker.m */,
+                               2747F0DF00234AE9135F29A3EF8186E0 /* GULReachabilityChecker+Internal.h */,
+                               24C6C6D138272A472EEE390DE844A4A8 /* GULReachabilityMessageCode.h */,
+                       );
+                       name = Reachability;
+                       sourceTree = "<group>";
+               };
+               CADC124A90EBB5CCB4FF575FB83B0D7E /* decode */ = {
+                       isa = PBXGroup;
+                       children = (
+                       );
+                       name = decode;
+                       sourceTree = "<group>";
+               };
+               CB92378B611BD5AA7C0E9DC3A01C8F58 /* AlamofireActivityLogger */ = {
+                       isa = PBXGroup;
+                       children = (
+                               C32203867C907AC35E7B64C374D54195 /* LoggeableRequest.swift */,
+                               FA80AF01EB6F8C828B12CA686720BCF5 /* Logger.swift */,
+                               FDAF8E81AE4DDFBC78DDEC4C39A073C9 /* Tools.swift */,
+                               90FE502EEC711DD7F35FE740DF049CA4 /* Support Files */,
+                       );
+                       name = AlamofireActivityLogger;
+                       path = AlamofireActivityLogger;
+                       sourceTree = "<group>";
+               };
+               D3FD5E629E54511182B7DA412962263A /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               F40F2B300FA1483A110F22D05E0470B3 /* Info.plist */,
+                               F20D4BCFB8DB98002A6891B8201F765D /* SwiftKeychainWrapper.modulemap */,
+                               6A43C662AC0818E1DA41050DA811BA97 /* SwiftKeychainWrapper.xcconfig */,
+                               165E1BA5EC237CD62B66F0F55037F269 /* SwiftKeychainWrapper-dummy.m */,
+                               F43729E28A11711576AB478FBB69B931 /* SwiftKeychainWrapper-prefix.pch */,
+                               239CBA52FEF031788D07DBF21BE742E4 /* SwiftKeychainWrapper-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/SwiftKeychainWrapper";
+                       sourceTree = "<group>";
+               };
+               D8D0F08FCCAD2A06E2AA0A071F512720 /* JSQWebViewController */ = {
+                       isa = PBXGroup;
+                       children = (
+                               DA0B520E2EE0857DCD4B89E33AA8F06E /* WebViewController.swift */,
+                               511A16D779FB63E7B779D6B47DBE9C0A /* Support Files */,
+                       );
+                       name = JSQWebViewController;
+                       path = JSQWebViewController;
+                       sourceTree = "<group>";
+               };
+               DEF103B18FC482F01A3E6F613BDA306F /* AppDelegateSwizzler */ = {
+                       isa = PBXGroup;
+                       children = (
+                               62F944B2EC591C1D211C295341CADE77 /* GULAppDelegateSwizzler.h */,
+                               DDE5567F4018883FFB851237B4CB839C /* GULAppDelegateSwizzler.m */,
+                               A499040F440DBB0BA31258B194703264 /* GULAppDelegateSwizzler_Private.h */,
+                               9BAB9A55E1DAE6C7836391C9CDC942B6 /* GULLoggerCodes.h */,
+                       );
+                       name = AppDelegateSwizzler;
+                       sourceTree = "<group>";
+               };
+               DF98B25F46BC615A236546E0E1DEC357 /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               BD0C0339F7D29150F3C16CE5C6A047BA /* FIRAnalyticsConnector.framework */,
+                               71E1A97221D2058BC3406E9A3A784E36 /* FirebaseAnalytics.framework */,
+                               EFF74401ABE190D825E20B007B43E406 /* FirebaseCoreDiagnostics.framework */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               E417A556BC11CA3A56E97BCCD79C5EF6 /* Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               29F9073DE228E0B9F78EE26B5AD02AF0 /* Info.plist */,
+                               DEE0C47DB821716AA0D71317CBEF8D48 /* SSZipArchive.modulemap */,
+                               8E53544433E2DD770130AA1E2E2DCF4D /* SSZipArchive.xcconfig */,
+                               23A4583BE5CEDDE176D8A3C11916D33E /* SSZipArchive-dummy.m */,
+                               B700720A592F03E24DAF4D98F45F06F5 /* SSZipArchive-prefix.pch */,
+                               566B9BE756758F1C246084E3A26FE6F6 /* SSZipArchive-umbrella.h */,
+                       );
+                       name = "Support Files";
+                       path = "../Target Support Files/SSZipArchive";
+                       sourceTree = "<group>";
+               };
+               EBD8349EBF650C21D25FEAD456A93FC7 /* NSData+zlib */ = {
+                       isa = PBXGroup;
+                       children = (
+                               551760570F42A57DBE4540EE1D5683B3 /* GULNSData+zlib.h */,
+                               398D45D2D76BE97410B68392E6D3A6B8 /* GULNSData+zlib.m */,
+                       );
+                       name = "NSData+zlib";
+                       sourceTree = "<group>";
+               };
+               F8A182268839C90A31F0F987A088E87B /* iOS */ = {
+                       isa = PBXGroup;
+                       children = (
+                               FC637EB57FE64DC55E93BA536D067809 /* CFNetwork.framework */,
+                               88AE1EEFE43FF13884C8AB2A06138B81 /* CoreGraphics.framework */,
+                               8824D6BF3D4DC73BEC7D0D1F38785226 /* Foundation.framework */,
+                               172A7ABC432D07D75B2B5BA8C0BA4592 /* QuartzCore.framework */,
+                               B16201B3D91EF42BC6478D973A85F4E3 /* Security.framework */,
+                               7B037AD884094D78946E98AECCBDBCB3 /* SystemConfiguration.framework */,
+                               05984A0DCE357F35A45231C58BADFD71 /* UIKit.framework */,
+                               1A1472ABD1E4C2EDD04CA4BEC0521713 /* WebKit.framework */,
+                       );
+                       name = iOS;
+                       sourceTree = "<group>";
+               };
+               FADF32E2008DA74E1610650187C443F2 /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               F68F301F91E5C3A1F8902F7DF484A725 /* GoogleAppMeasurement.framework */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               FB58DCB5D77B17D0C61761EC063D9422 /* CoreOnly */ = {
+                       isa = PBXGroup;
+                       children = (
+                               88442290CE172CD09060A11A2945E6A8 /* Firebase.h */,
+                       );
+                       name = CoreOnly;
+                       sourceTree = "<group>";
+               };
+               FBE2510A7746B57C173EB74BDDA3A968 /* OAuthSwift */ = {
+                       isa = PBXGroup;
+                       children = (
+                               5E9A697406390F3FC0AD51D2C04D46A0 /* Collection+OAuthSwift.swift */,
+                               61A0764E9029258456100401AF3A1D31 /* Data+OAuthSwift.swift */,
+                               680BC3B5B1C18CB129B3F626B32364F8 /* Dictionary+OAuthSwift.swift */,
+                               0DD1F2E48435B86FC4B95841449B2727 /* HMAC.swift */,
+                               3A67C7CD84356516F1F7D3986E9C09C6 /* Int+OAuthSwift.swift */,
+                               B365D8630E824F91C12C169371386488 /* NotificationCenter+OAuthSwift.swift */,
+                               8DA785A1C830431915F33A46AD84FEF7 /* NSError+OAuthSwift.swift */,
+                               4C59F12F85CDAAB121A1677774DF2C7F /* OAuth1Swift.swift */,
+                               EC3A799A30601BBE93164EDFEE22F371 /* OAuth2Swift.swift */,
+                               0AD231A8FF3DB1782497120FA6AEBB1F /* OAuthSwift.swift */,
+                               951F8277D8CC7B918EC3F93FB9E672B7 /* OAuthSwiftClient.swift */,
+                               D08CFF3FAFCD900564C2FBD18D08004D /* OAuthSwiftCredential.swift */,
+                               53C308E0551021233B7D7464DFB9CF6F /* OAuthSwiftError.swift */,
+                               7C66C0B1401F75949F378B18E584E000 /* OAuthSwiftHTTPRequest.swift */,
+                               C8C1441259AE0384F9D916435E4E1934 /* OAuthSwiftMultipartData.swift */,
+                               1798AF9275969301B5346C1DF5410CFE /* OAuthSwiftResponse.swift */,
+                               49AB6C96EADE159493F9CF27E33B2452 /* OAuthSwiftURLHandlerType.swift */,
+                               9E8C5B26C86483D9A133E4500AD5F3E5 /* OAuthWebViewController.swift */,
+                               C631E06F6A57B0AE8546402CFFB399D1 /* Objc.swift */,
+                               C7EFF3E888638C1BF7E636B5D3F02372 /* SHA1.swift */,
+                               EFBD1D154E2BDAF6A560BD281251E63B /* String+OAuthSwift.swift */,
+                               7DB7F3C979E6A543E2E6DB68E1B2B57E /* UIApplication+OAuthSwift.swift */,
+                               EB01793375BE6CD1AF6B3C2F9EF5824A /* URL+OAuthSwift.swift */,
+                               6EB55573C83483EB32EA80704AA7F376 /* Utils.swift */,
+                               A19EDB5C725F1B0055B1AD383A0FE5DE /* Support Files */,
+                       );
+                       name = OAuthSwift;
+                       path = OAuthSwift;
+                       sourceTree = "<group>";
+               };
+               FF381E2221DBCA43D1EE90FBD49A1B93 /* Logger */ = {
+                       isa = PBXGroup;
+                       children = (
+                               CE8CAA6373F642196F0C634C6207FFDF /* GULLogger.h */,
+                               43FB1CC94D2C1CA721B8517B02BB5EC3 /* GULLogger.m */,
+                               1408A9D036A6990304A3BEF6A655CBDA /* GULLoggerLevel.h */,
+                       );
+                       name = Logger;
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+               1A52A314D1549E9C26E145278947DEF9 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               16156484634519DBF44EC9F53ED24CF4 /* FolioReaderKit-umbrella.h in Headers */,
+                               99718A98B98F71465754F17EF9F1D6CF /* FolioReaderKit.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               1BCAD0F64D204C6871A434CE90CCDDB1 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               2ABBA3ADA9361D1A334126BB3638CD3F /* Any.pbobjc.h in Headers */,
+                               D8163C149C04D8EEAF514FF8D2793A06 /* Api.pbobjc.h in Headers */,
+                               A420636229F2CF99350F76899CEDCAAE /* Duration.pbobjc.h in Headers */,
+                               C47E0890DBC816176E3D01D4FAE56798 /* Empty.pbobjc.h in Headers */,
+                               AE0A293D64D5C8C9BC4A508331231083 /* FieldMask.pbobjc.h in Headers */,
+                               5DA946929FEBC12359A58ECA949E7D98 /* GPBArray.h in Headers */,
+                               662F1759FE8C2ECF8911CF2AFA446AA0 /* GPBArray_PackagePrivate.h in Headers */,
+                               0852BDA9C8F89EA2F10495E858DD76AF /* GPBBootstrap.h in Headers */,
+                               8BBEF850C762FA864916060EC8AB2821 /* GPBCodedInputStream.h in Headers */,
+                               BD82F14A4F355DBC495185EB37E86665 /* GPBCodedInputStream_PackagePrivate.h in Headers */,
+                               00742CD999CAA90421B9558657F3BFEF /* GPBCodedOutputStream.h in Headers */,
+                               BDC078DD5D057A5D018843EE06CA53C8 /* GPBCodedOutputStream_PackagePrivate.h in Headers */,
+                               023082A6D72A36532D6099A99A3E9101 /* GPBDescriptor.h in Headers */,
+                               2579ED6A1AE7C63170724ADB5A714717 /* GPBDescriptor_PackagePrivate.h in Headers */,
+                               980D1B333A71D6250A7737912581D4D9 /* GPBDictionary.h in Headers */,
+                               502C51559F5C58BF7CEEEB1CF9FDC045 /* GPBDictionary_PackagePrivate.h in Headers */,
+                               B6A3F8383A59C25ED224AEBE6879F5B0 /* GPBExtensionInternals.h in Headers */,
+                               E23DC013FF59BE5790CE675EE9D7325A /* GPBExtensionRegistry.h in Headers */,
+                               58EB7C8152CA11D8013F636778407728 /* GPBMessage.h in Headers */,
+                               475636CA83561EFF3E62F8D3E34DAEE4 /* GPBMessage_PackagePrivate.h in Headers */,
+                               32FD28CC21723B433DB5C00FA50CCAA7 /* GPBProtocolBuffers.h in Headers */,
+                               0678FC4DB726192529A8A278153D0996 /* GPBProtocolBuffers_RuntimeSupport.h in Headers */,
+                               7C0BD3F2B8DE0865A8E0A48922385AE6 /* GPBRootObject.h in Headers */,
+                               579E4BADCC61A81905CD4F9422740700 /* GPBRootObject_PackagePrivate.h in Headers */,
+                               97665278CC3323C5745205C9245831C2 /* GPBRuntimeTypes.h in Headers */,
+                               8989C8A69A11C1A52BE62DA21BDF8ABA /* GPBUnknownField.h in Headers */,
+                               E383C89D956C843A4426D609BE15537F /* GPBUnknownField_PackagePrivate.h in Headers */,
+                               98466C008D41A93B78FE27FFC40890B6 /* GPBUnknownFieldSet.h in Headers */,
+                               D538B82C7E65F7CE463D4C6D7C9AED2E /* GPBUnknownFieldSet_PackagePrivate.h in Headers */,
+                               9912AE753145BC77732BADA9E0069532 /* GPBUtilities.h in Headers */,
+                               F4706798CA79248896257375715BC101 /* GPBUtilities_PackagePrivate.h in Headers */,
+                               6BE3EFF731C1AEB200C4DE0DDB3584EC /* GPBWellKnownTypes.h in Headers */,
+                               FC2133A0E00C03A5358618F49F4C5469 /* GPBWireFormat.h in Headers */,
+                               9CA7916A2098BA1EE30155DA105317D3 /* Protobuf-umbrella.h in Headers */,
+                               5204D65C96DAD52AEB781EAF7298504E /* SourceContext.pbobjc.h in Headers */,
+                               656F9722496C3797B612201ADCB5ACA5 /* Struct.pbobjc.h in Headers */,
+                               546962FBBD986ECF27FB9CE1690B820B /* Timestamp.pbobjc.h in Headers */,
+                               9E75B30A41B50CFD5BFC89EC65D0AAC9 /* Type.pbobjc.h in Headers */,
+                               B85DA5827B8802F9BB6F592B8897947E /* Wrappers.pbobjc.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               289C0E47E6B6327190FEC5BDA43E5FC4 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               900B34291E7F49B1A52BCE9DDCE6E6F3 /* FontBlaster-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               2DEFDE54D1138047033AFAB6F82E1843 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               6FE1CD1571CA5065913B86D47689F3B3 /* MZDownloadManager-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               380BCFFF99ECBF584DD88EE0AC60FA21 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E290FCB16106E8AB43988955873CCDA6 /* GoogleUtilities-umbrella.h in Headers */,
+                               B964C5F1F8293F421DE36D227AEDCA0B /* GULAppDelegateSwizzler.h in Headers */,
+                               C087908BE81A71BAD372326D557C9528 /* GULAppDelegateSwizzler_Private.h in Headers */,
+                               D891EAFB106227CE19C8445C28509D2A /* GULAppEnvironmentUtil.h in Headers */,
+                               15C4562A91882668E0509A4C49265ACA /* GULLogger.h in Headers */,
+                               80B122FBC94F932F73B3CB08A8176E8C /* GULLoggerCodes.h in Headers */,
+                               892547A1AD5152F3DCF8C20F0A607F80 /* GULLoggerLevel.h in Headers */,
+                               F5338B4BBCB9220717CA96D6417D7BC2 /* GULMutableDictionary.h in Headers */,
+                               975A2D21AE6EB81D5349AF850728E5D7 /* GULNetwork.h in Headers */,
+                               9131856F764756E2A2B806107EFBE608 /* GULNetworkConstants.h in Headers */,
+                               B4A64FEECB4C4615A17AEC2B4F058611 /* GULNetworkLoggerProtocol.h in Headers */,
+                               A8D220DDEC5421EF632699031556C390 /* GULNetworkMessageCode.h in Headers */,
+                               D724088A363665433487170B58995146 /* GULNetworkURLSession.h in Headers */,
+                               DBD9E450420ADDB6A7C45F79160D252E /* GULNSData+zlib.h in Headers */,
+                               CB43E93AE5026D07BDBE67663E82FF63 /* GULOriginalIMPConvenienceMacros.h in Headers */,
+                               9EC8A08B215141017FAF89FA92738E80 /* GULReachabilityChecker+Internal.h in Headers */,
+                               E108C4C6258317867DB7BD4F6FE51EDD /* GULReachabilityChecker.h in Headers */,
+                               D7B2B69DB1CA1AE2B42BCD295B6D9307 /* GULReachabilityMessageCode.h in Headers */,
+                               11C5C2EC4E2EBEAD60ACC8A93EC0B2EE /* GULSwizzler.h in Headers */,
+                               74F8FEEBD2D51A5315C36399189B65E8 /* GULUserDefaults.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               3BB0CA2704B4E4E67C1E0F55FF8ECEB0 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               82291B80DF30DC3BF5F84BE780B7A794 /* RealmSwift-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               449B91827AB0541E726FA72E9B179E8E /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               6A6B8055DEE857F21D04A3FF7485A44E /* OAuthSwift-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               46760EF3492D0AA5DBB2A4BB4F19B01B /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               ABE291CCCFE48E189213631A3DC03721 /* NSError+RLMSync.h in Headers */,
+                               0127105DE35781CDA88A0FCE48F5E72D /* Realm.h in Headers */,
+                               ECFDA76C66868DCFD35BECF99B0068B0 /* RLMAccessor.h in Headers */,
+                               410D36DE05F759C2B105AC77C46B4E0B /* RLMArray.h in Headers */,
+                               0091F3D28C168C2EF8B3E3EAFC631202 /* RLMArray_Private.h in Headers */,
+                               6A5E2674F275B1EA573D8673A84DA984 /* RLMCollection.h in Headers */,
+                               6BAC2CBC9089C7C89D29B4DDFEC59A0F /* RLMCollection_Private.h in Headers */,
+                               D291D5CE2E27C37824F88C5BD10C5A32 /* RLMConstants.h in Headers */,
+                               8A4277DAFEEE4F1120E4854E20FB1C50 /* RLMListBase.h in Headers */,
+                               3C456892E267BA54104D3E201ED78146 /* RLMMigration.h in Headers */,
+                               E202978465C0C0AB115A56D6965C97FA /* RLMMigration_Private.h in Headers */,
+                               F8FDCEEAC87A1436D24A40738C945B23 /* RLMObject.h in Headers */,
+                               70D404723BA91BA1EBD8442587F7E4C0 /* RLMObject_Private.h in Headers */,
+                               44FA9AF3247A4A007AC516966031ABC0 /* RLMObjectBase.h in Headers */,
+                               5BFDB162AB870CB831D499EDA6D25C8E /* RLMObjectBase_Dynamic.h in Headers */,
+                               88CA03D951F6E22D0FAE5EC52BF1310D /* RLMObjectBase_Private.h in Headers */,
+                               378A2C2BCCC9C97B40BACC18B6247967 /* RLMObjectSchema.h in Headers */,
+                               7AB6088B4276502E47E9681F93DC28D4 /* RLMObjectSchema_Private.h in Headers */,
+                               87523532AD81389088F389D9ABF1A953 /* RLMObjectStore.h in Headers */,
+                               759BF3C710068C3C216586677E493F12 /* RLMOptionalBase.h in Headers */,
+                               1888CCC9E07EC907079DF9D6058014CD /* RLMPlatform.h in Headers */,
+                               A9FB27A766998F6AC8D31D811F9068F4 /* RLMProperty.h in Headers */,
+                               6F70AF82C5E93D1D29187C6FE141BAF3 /* RLMProperty_Private.h in Headers */,
+                               ED5C2982003937B649783B5CF9E97AA4 /* RLMRealm+Sync.h in Headers */,
+                               3C385BD23D7DE86023C31305720EC106 /* RLMRealm.h in Headers */,
+                               B1FB342273BF07C38E607FB0F7C9EFE8 /* RLMRealm_Dynamic.h in Headers */,
+                               CDF3A8A9ED7AAFB711EB8F0AF31DCB9F /* RLMRealm_Private.h in Headers */,
+                               11EDBB505C68DB2FF63F7B4EFD7EEA54 /* RLMRealmConfiguration+Sync.h in Headers */,
+                               984847FC535429EFA3945922B0E17A0F /* RLMRealmConfiguration.h in Headers */,
+                               CCEDEC445844E6B637DA2A026CB8D854 /* RLMRealmConfiguration_Private.h in Headers */,
+                               E9684D581ED545368F64191A32C00051 /* RLMResults.h in Headers */,
+                               A347846A8F6FA3E180C7431CA5440B58 /* RLMResults_Private.h in Headers */,
+                               CA702089270A891BB374FB47432A1DC7 /* RLMSchema.h in Headers */,
+                               F50701D8682CF06AF6F13E8478406BD6 /* RLMSchema_Private.h in Headers */,
+                               737FA14AA54CEA27C6970DD23735F412 /* RLMSyncConfiguration.h in Headers */,
+                               F7179C9B307A6E8075252263B2C8F5B3 /* RLMSyncConfiguration_Private.h in Headers */,
+                               8ECD93126F045EBB7633AB23EE84F1B0 /* RLMSyncCredentials.h in Headers */,
+                               D632058640983C63303E6A4EB12EF66B /* RLMSyncManager.h in Headers */,
+                               951D6869853F4D02D5DBF66847D45173 /* RLMSyncManager_Private.h in Headers */,
+                               50D9B09473B537797907C8FCDFC93CDF /* RLMSyncPermission.h in Headers */,
+                               86D305B61E79828EFBC40467AD34CE3A /* RLMSyncSession.h in Headers */,
+                               1291540BD398E4AF5A5684D613F4524A /* RLMSyncUser.h in Headers */,
+                               DC1E73AC9A04AF660FED52360A603DAB /* RLMSyncUtil.h in Headers */,
+                               799EEAF62D2BC7F7681F9AF0324FFB7C /* RLMSyncUtil_Private.h in Headers */,
+                               3FB245715D8F1A01546D39C6C9D98AB9 /* RLMThreadSafeReference.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               4A34A8C4C4BFB14D6E0504833C9159DA /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               0A4C5C81804C50CBDF57EF842DE97ED0 /* nanopb-umbrella.h in Headers */,
+                               47F644862D2F3FA7ACAC9AC409E11C83 /* pb.h in Headers */,
+                               427799CB422851F585C02C74820C0463 /* pb_common.h in Headers */,
+                               D143A48E638AFDBD788EBFDC833E04BC /* pb_decode.h in Headers */,
+                               C6AAA96E9F95646F5B19AA8CA8C82D96 /* pb_encode.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               5CE17C32A4504B751C3375AA3458D828 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               C31470E92B1A691AC3D3D55A127DCBF0 /* MBProgressHUD-umbrella.h in Headers */,
+                               D966EB561A0A6A10BAF414F0B952E95E /* MBProgressHUD.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               654B6636423A426904399EE79D3636F9 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               A83EBA98974578B84655FD73AED9963F /* Pods-WolneLektury-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               807F38156F708FD265BFAF42D7035343 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               FA5FE8862C97307E7516046C7C926F91 /* ZFDragableModalTransition-umbrella.h in Headers */,
+                               0E26CEA711582B8A2B09079170BB50D2 /* ZFModalTransitionAnimator.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               832F99B54435ABFC91511B06EA42FA9A /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E0F1AF0D2445D107E839AE6F807D6EDF /* JSQWebViewController-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               9AAC9721424D21D7E9529560DC79D0A8 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               5D75DA564BFADC9A873E57E1ECF4E92E /* SideMenu-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               9D873A58650AD5FB652CF8EFDB790FD4 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               7876BC2321617C237B93692E6DCC9F45 /* SwiftKeychainWrapper-umbrella.h in Headers */,
+                               633315B0A4E810C6350A9F2B6E8C1DEE /* SwiftKeychainWrapper.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               A4416D8D7BAB1B44603B29053B8C30A1 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               AE633B3532903E2EF04B5D7BA480C3C7 /* Kingfisher-umbrella.h in Headers */,
+                               74991D9F2A55044D75545D00A70C8CA9 /* Kingfisher.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               A528C09F86D32F3BB1DA1EE2158331F4 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               DEF220928DA65E9CD067D6EF494F787F /* MenuItemKit-umbrella.h in Headers */,
+                               8F857D1268C9E85BF2103CAEAC35EDCD /* MenuItemKit.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               A6A607506FEAAC7C41268D3E5CF4E5FE /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               EF1461221681BCA12A4147900A704727 /* Alamofire-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               BFD96857B47BD6A21F4E09C9C6852F83 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               1217AEF6A6F0148B88E2E6A62C33C56B /* AEXML-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               C096048F9C5E2146FBC747D8D132051E /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               D79A33A19224F27D9A01AB3FCF59F799 /* aes.h in Headers */,
+                               8ACC7142BE1924CC48F5ECB3CAFC1EF3 /* aes_ni.h in Headers */,
+                               B6697D7D32BBDC73A32DBEE6C94F5B20 /* aesopt.h in Headers */,
+                               AC84818A2D77A2919122284BE2BE7B08 /* aestab.h in Headers */,
+                               3906566AEC9254BF6566E5DF3BBFD4CD /* brg_endian.h in Headers */,
+                               8785F990581DF6C8B51B3BDAC6A87334 /* brg_types.h in Headers */,
+                               4548EDD8058F8F1C3CDBF04943E718FF /* crypt.h in Headers */,
+                               1F2009A78040CFF64E04DA7B632D840A /* fileenc.h in Headers */,
+                               E3AFFD602D64FB33BAE28D0D73B08670 /* hmac.h in Headers */,
+                               018C816F7E0AD679CE02B4BDB3C6D26A /* ioapi.h in Headers */,
+                               651DC312EDA04DF49A00D67D20A982BF /* ioapi_buf.h in Headers */,
+                               F43F62FAE6AE801555299C9F462842C4 /* ioapi_mem.h in Headers */,
+                               7953040EF657ED5CD6E5F235400E59ED /* minishared.h in Headers */,
+                               CB22520C2BDF53338EE25CC38CED3CB7 /* prng.h in Headers */,
+                               0579E3E6B6D59F7FE9C5A6FFFCC06284 /* pwd2key.h in Headers */,
+                               EACA38D04DFBDC1A7F70704D022BD6A9 /* sha1.h in Headers */,
+                               BA600C2097F18291FEAFD5F98322C4C4 /* SSZipArchive-umbrella.h in Headers */,
+                               6A4FCEA27B851AF3E4C7097A953303CD /* SSZipArchive.h in Headers */,
+                               76A9C1EC901BEA3FE51873831EEC2D1E /* SSZipCommon.h in Headers */,
+                               9BA5CDC177BFB299696081BF56DBEE02 /* unzip.h in Headers */,
+                               689A238CC3488CA2A44C7DAE4C71C427 /* zip.h in Headers */,
+                               02A4EDFF5D72B3EA0CC5CA897017DA2F /* ZipArchive.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               C498A6D1B1EA6A55970A08AEFA8E813E /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               5A02E2515DCBFAD6FF7D7E3BA545EE57 /* FirebaseMessaging-umbrella.h in Headers */,
+                               8BA29AE08FB5640473F27C9244782CC5 /* FirebaseMessaging.h in Headers */,
+                               7D986FCE9489A0759134A022FD2B6A82 /* FirebaseMessaging.h in Headers */,
+                               CAB79888713AD1AE289DDC24177894E1 /* FIRMessaging.h in Headers */,
+                               469DDB5B5F2165785D04941163FF7CEB /* FIRMessaging_Private.h in Headers */,
+                               E19B8C59AFACEE8F7AF63071889282EE /* FIRMessagingCheckinService.h in Headers */,
+                               1FB017C8665127124E5622BFC14BAE01 /* FIRMessagingClient.h in Headers */,
+                               72EC8F56CE889E7BB0C09C7FEB1CA475 /* FIRMessagingCodedInputStream.h in Headers */,
+                               D4E749A4E02E3A68B9378913D6B9E85D /* FIRMessagingConnection.h in Headers */,
+                               D50366A52954A24888BCD71108E1B8D9 /* FIRMessagingConstants.h in Headers */,
+                               CEE84B4BDE2D34D3101CB002E747B6D3 /* FIRMessagingContextManagerService.h in Headers */,
+                               1871055BFED069026C1BA1F0EBF80D84 /* FIRMessagingDataMessageManager.h in Headers */,
+                               556338F7D37DA2D8F7BD73F00E4507A6 /* FIRMessagingDefines.h in Headers */,
+                               73A70DB94B07194B5AD5B24EFDCF403D /* FIRMessagingDelayedMessageQueue.h in Headers */,
+                               AE6574015BBD1352327E7ED052444702 /* FIRMessagingInternalUtilities.h in Headers */,
+                               B572324693A1AC994775445E0DD5A97C /* FIRMessagingLogger.h in Headers */,
+                               06B1C042C3FAE54528A986AF2C2A0978 /* FIRMessagingPacketQueue.h in Headers */,
+                               FEB7295281E330F595EFC112E8CBAF4E /* FIRMessagingPendingTopicsList.h in Headers */,
+                               C4E789001269543FE1654D7BA0CFA4E0 /* FIRMessagingPersistentSyncMessage.h in Headers */,
+                               7E1BC12B4DFD06232C695FBC3E61D448 /* FIRMessagingPubSub.h in Headers */,
+                               CBD17AF9CA87FB377F4EE1E1BFEB9B81 /* FIRMessagingPubSubRegistrar.h in Headers */,
+                               77315EF5B7DDA235E02F549AB9D66E57 /* FIRMessagingReceiver.h in Headers */,
+                               E61F4B0E717EC8F97F20D19FDEC69A90 /* FIRMessagingRegistrar.h in Headers */,
+                               01829BFAB608257CA7457154606A71DD /* FIRMessagingRemoteNotificationsProxy.h in Headers */,
+                               7F1ECD77604532CE1DE5EB04A8A69863 /* FIRMessagingRmq2PersistentStore.h in Headers */,
+                               06264590012751B63A95DA1482138DEF /* FIRMessagingRmqManager.h in Headers */,
+                               C0E6233E2F0518B8FBFE8A17B699DA6D /* FIRMessagingSecureSocket.h in Headers */,
+                               0B9FCB0EDC4BDBB175BC9BC2A09D90F6 /* FIRMessagingSyncMessageManager.h in Headers */,
+                               AFACFB7AD394A77CA94FBA11ACAE171A /* FIRMessagingTopicOperation.h in Headers */,
+                               A47FF3D3C35B24F874E8B4A1FF9BE25F /* FIRMessagingTopicsCommon.h in Headers */,
+                               D47CB6A901F1D27ED2467977207577DB /* FIRMessagingUtilities.h in Headers */,
+                               8BF9337F999D245FB064B98432DB252E /* FIRMessagingVersionUtilities.h in Headers */,
+                               CB0EF305E7FB417EE9205AF4EC10F40F /* FIRMMessageCode.h in Headers */,
+                               D21800D777F3316095AA453225ECA626 /* GtalkCore.pbobjc.h in Headers */,
+                               83AE053633BE46D1C7AECB85BC90373B /* GtalkExtensions.pbobjc.h in Headers */,
+                               BA9FF7E126E6510C1A93D8702F28AF18 /* NSDictionary+FIRMessaging.h in Headers */,
+                               3F7D0723B053EFF5E23BADFF2017C8CA /* NSError+FIRMessaging.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               C9E98CAC482052036F2DA74C83793EC9 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               35EA02810BD8E554255704189C611C77 /* FIRAnalyticsConfiguration+Internal.h in Headers */,
+                               009D8E987B72AC718814C940C31AA86A /* FIRAnalyticsConfiguration.h in Headers */,
+                               BE9F2DA9E442EAD0FC82FB9D04476E88 /* FIRApp.h in Headers */,
+                               8A4EF2DACFFEFCF78D724E02C536794B /* FIRAppAssociationRegistration.h in Headers */,
+                               CFE77EB211CD332A018770078DB1FCA0 /* FIRAppInternal.h in Headers */,
+                               4A3EA2B64857488A7D376B2912A3D1A1 /* FIRBundleUtil.h in Headers */,
+                               F600D3CAA39166B8C8B59C55DADF57F1 /* FIRComponent.h in Headers */,
+                               AEDA45FBD321C4357FD8EF98EF751E71 /* FIRComponentContainer.h in Headers */,
+                               C898AFA88F75E2E9F703FF35249C7095 /* FIRComponentContainerInternal.h in Headers */,
+                               0A6E6EC25086B38191D51E8D21C4D751 /* FIRComponentRegistrant.h in Headers */,
+                               A95BA997891CD85177E8DC406B14D429 /* FIRComponentType.h in Headers */,
+                               487D10219F92875CBCD79DFE096C45A8 /* FIRConfiguration.h in Headers */,
+                               A52BEE1EFE79023CBB820877AC7BA489 /* FIRCoreConfigurable.h in Headers */,
+                               A5D44A77060CAA13F1151DBC7E42E586 /* FIRDependency.h in Headers */,
+                               D490E0B6EECF37004DAF724C6770E284 /* FirebaseCore-umbrella.h in Headers */,
+                               39530C3F489E2997186FFA7089C8F8AC /* FirebaseCore.h in Headers */,
+                               897E6F31468ABE5B9C8EF68994C54833 /* FIRErrorCode.h in Headers */,
+                               0F833929C636360E89880A26CD11A792 /* FIRErrors.h in Headers */,
+                               2001B9F27B5C4F512389BD0397B960C0 /* FIRLogger.h in Headers */,
+                               ACEF3F20D625C95223D1ED8AC7476E84 /* FIRLoggerLevel.h in Headers */,
+                               F64B055D96831705211AAFD9F061C9F1 /* FIROptions.h in Headers */,
+                               681CBE150BEC5038D0CFB5E0C5013FA4 /* FIROptionsInternal.h in Headers */,
+                               F17B1AEAC49DEE53F3853118676FD7DD /* FIRVersion.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               CF072462BA35784A0474F030B8D5F963 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               1312F66A5176BF8192E1B2177FF51356 /* Toast-Swift-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               E5162F0AA677FC265E4CEE7594F5B519 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               F3C06FC5B46FDBF8B73BDAAFD440507C /* AlamofireActivityLogger-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F97C449EBDC63F1F9F6DC0C67B08CEB3 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E5DA0C270AB43B80C91B45C5DDFAB219 /* MatomoTracker-umbrella.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+               0DD17D85AC06742489BCAFB83E51FDB0 /* FirebaseMessaging */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 52D0A75FD018888C202CB17A7DDCB759 /* Build configuration list for PBXNativeTarget "FirebaseMessaging" */;
+                       buildPhases = (
+                               C498A6D1B1EA6A55970A08AEFA8E813E /* Headers */,
+                               06FA7E000B28DF806B0A8574C7FA39C8 /* Sources */,
+                               3EFA960CA4B047B45EFF91F11347F88D /* Frameworks */,
+                               6407E061C1EF20E9594F96E5B5CDBD3A /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               4EAE125B5AE700C85CF58D939AF16CB4 /* PBXTargetDependency */,
+                               5A505E64A620E3EFF81065BDCF8AAB73 /* PBXTargetDependency */,
+                               85536FB74B0D5A69875D4E1B46A8FF83 /* PBXTargetDependency */,
+                       );
+                       name = FirebaseMessaging;
+                       productName = FirebaseMessaging;
+                       productReference = 457F370BE978B9A7B711AB40080ED382 /* FirebaseMessaging.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               13218BC4FEB9FA3C157F802AB364DCBB /* Realm */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 7CEC6540142BEEDF23E01837DABB4085 /* Build configuration list for PBXNativeTarget "Realm" */;
+                       buildPhases = (
+                               46760EF3492D0AA5DBB2A4BB4F19B01B /* Headers */,
+                               E6B2168F2463F5A23C71DA9E42CB7762 /* Sources */,
+                               ED62D79EE4F9879BFB576933C7A797DA /* Frameworks */,
+                               5F863B857C7DD523FBBE6C6F33F81AF2 /* Resources */,
+                               2E644E33F37AD2BCDAAF587E57D43722 /* Copy . Private Headers */,
+                               50CA721690027D834A9C9705A285FB24 /* Copy . Public Headers */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = Realm;
+                       productName = Realm;
+                       productReference = 60BF42A0CA4751A6D288CDE5D876CA40 /* Realm.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               1EB42C6E589197DB247315875A9AE141 /* FolioReaderKit */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 683F14EAC00E28302AEBA2CE1D3181B5 /* Build configuration list for PBXNativeTarget "FolioReaderKit" */;
+                       buildPhases = (
+                               1A52A314D1549E9C26E145278947DEF9 /* Headers */,
+                               EF072C06A862A52DCE66E6334305B361 /* Sources */,
+                               149BFCC3118409FF931C35DAFFD80226 /* Frameworks */,
+                               6F0E58C2C471AA33EEDC1D767435D926 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               BBB1D2B2B8257B1844F799BBA56E71F3 /* PBXTargetDependency */,
+                               05E8C912A3D2DD9E89B6C8BA485ADFE1 /* PBXTargetDependency */,
+                               B6E23311344A6575175D6AF3BE8A653C /* PBXTargetDependency */,
+                               13EA85FA565A2A3460A2C8F7D96C2C28 /* PBXTargetDependency */,
+                               FBF90A9BF5383BB7222F33417F2C773F /* PBXTargetDependency */,
+                               BA279092D2FD2DC13E98868BC6A4CD33 /* PBXTargetDependency */,
+                               A03801ADAA51A686C6058350B1D7EB20 /* PBXTargetDependency */,
+                               230BF6546D2E5FEA1D60B8CB0E9CEBC5 /* PBXTargetDependency */,
+                       );
+                       name = FolioReaderKit;
+                       productName = FolioReaderKit;
+                       productReference = 9AF454025C49412F40A5D08FF3EFD576 /* FolioReaderKit.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               2B2CE33B441C5D4BD9A5A04D3087E5C8 /* GoogleUtilities */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = EE760FDAF0752E4632E961732998C442 /* Build configuration list for PBXNativeTarget "GoogleUtilities" */;
+                       buildPhases = (
+                               380BCFFF99ECBF584DD88EE0AC60FA21 /* Headers */,
+                               F6EFDA8D1ED33B5160CC3F4C5CE9C0A0 /* Sources */,
+                               3BA345FDCB39F16EE60953B6F18492E0 /* Frameworks */,
+                               6609B2FC3FE461870D129770AB74BC25 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = GoogleUtilities;
+                       productName = GoogleUtilities;
+                       productReference = 8BA27C2B8AAF78AFF56284BFAEABBBEF /* GoogleUtilities.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               4338000AC6DBAF783F338F055DDE55C6 /* FirebaseCore */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 980C96B3FE48496D06991607F09EE4D8 /* Build configuration list for PBXNativeTarget "FirebaseCore" */;
+                       buildPhases = (
+                               C9E98CAC482052036F2DA74C83793EC9 /* Headers */,
+                               5F5FA332B786B26775715128005FEBF3 /* Sources */,
+                               C4ABFEE97A320BC0D912D90ED1C0311F /* Frameworks */,
+                               0E0199FFAE1D920BFA69E4D33330503F /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               F4CBCA49898E0F03ADC80CB4A6EE4D00 /* PBXTargetDependency */,
+                       );
+                       name = FirebaseCore;
+                       productName = FirebaseCore;
+                       productReference = F49AA6EB751F7512499D46DE90D3EAE6 /* FirebaseCore.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               673DBE5653C51962C057371B5CA8A2B4 /* MenuItemKit */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 9291CA90867C59FC17ADC42368C0F58F /* Build configuration list for PBXNativeTarget "MenuItemKit" */;
+                       buildPhases = (
+                               A528C09F86D32F3BB1DA1EE2158331F4 /* Headers */,
+                               00198027755E13B6B7AC1AC2D14587B4 /* Sources */,
+                               16D444A7CCF2B0694ACECBADD81414CB /* Frameworks */,
+                               60B056B00C949C9E6D205624FDB50E49 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = MenuItemKit;
+                       productName = MenuItemKit;
+                       productReference = 73A38D551097D8B8470056E0B48FD703 /* MenuItemKit.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               71E4E70C7BAC6389783D3A3C8EB1F910 /* Protobuf */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 135EDC2D971C105FAC0ED65476D53BAB /* Build configuration list for PBXNativeTarget "Protobuf" */;
+                       buildPhases = (
+                               1BCAD0F64D204C6871A434CE90CCDDB1 /* Headers */,
+                               6F43672231228D284CA4D38EDE402990 /* Sources */,
+                               0CF52E852695FAAC81E05083B79F4F61 /* Frameworks */,
+                               E90E004EED7E9BA5A6FF2A50CBB1498E /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = Protobuf;
+                       productName = Protobuf;
+                       productReference = 3AF9BC4C9A3CD19279B980ED20FBAD39 /* Protobuf.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               76948DED43ECFC74BB41AA7F11DA2200 /* RealmSwift */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = C9CCC065D8E0D1198BDE2EF35A3A8A06 /* Build configuration list for PBXNativeTarget "RealmSwift" */;
+                       buildPhases = (
+                               3BB0CA2704B4E4E67C1E0F55FF8ECEB0 /* Headers */,
+                               F62B8DEE079200A39859B95D2B0D6885 /* Sources */,
+                               D87A98D96FAB24DA408B8541DBB20CD2 /* Frameworks */,
+                               95A05F3D465DB3B56C07E553B8919626 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               D277072FA14C2EF800819D9A52B74FD7 /* PBXTargetDependency */,
+                       );
+                       name = RealmSwift;
+                       productName = RealmSwift;
+                       productReference = 8A8A815B8D521F8CDEB202EE76039FB7 /* RealmSwift.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               8364BB258018143B83798DE31C13546D /* nanopb */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = A54B95AC2E23BB0947B35EFE5E1F0EFB /* Build configuration list for PBXNativeTarget "nanopb" */;
+                       buildPhases = (
+                               4A34A8C4C4BFB14D6E0504833C9159DA /* Headers */,
+                               12D3F587AEF7D8BD85FB723377576392 /* Sources */,
+                               6E984081A095D70D07D998B48BC38F15 /* Frameworks */,
+                               FFEE618D0ABA9FE323734237056FF21F /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = nanopb;
+                       productName = nanopb;
+                       productReference = BB48738A79C0712C66A87BA10D8F71B0 /* nanopb.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               87C645C5CBE6078EBEE1659ABEEF780D /* JSQWebViewController */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 125B2F3819124660E3BCAB534FBD9E40 /* Build configuration list for PBXNativeTarget "JSQWebViewController" */;
+                       buildPhases = (
+                               832F99B54435ABFC91511B06EA42FA9A /* Headers */,
+                               F6117FDE3DB964BFD5C9255BAC4FB5AD /* Sources */,
+                               BEFF92BF85B66920AF555957E6A0E222 /* Frameworks */,
+                               33A8F7B04C4B10726502EDE02CDC184E /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = JSQWebViewController;
+                       productName = JSQWebViewController;
+                       productReference = 92BF3A91822F9A6AA5AC338BDF21C78E /* JSQWebViewController.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               8CD09A1EC484C4C08A7FBF85CA871914 /* SwiftKeychainWrapper */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 2DC105300897278DD37A125F248C400D /* Build configuration list for PBXNativeTarget "SwiftKeychainWrapper" */;
+                       buildPhases = (
+                               9D873A58650AD5FB652CF8EFDB790FD4 /* Headers */,
+                               6B45D397692AB066C2737A350C3B0B13 /* Sources */,
+                               F14DC5876305DE082BDBCD988CE23864 /* Frameworks */,
+                               F104F5D359392B17390789BFC5B1126B /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = SwiftKeychainWrapper;
+                       productName = SwiftKeychainWrapper;
+                       productReference = 95E96EB83DCAB6611B1B9CE36203FD71 /* SwiftKeychainWrapper.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               8D956160DB7545834B961CB5E8DBBD1B /* AlamofireActivityLogger */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = C2CD5E9A2839EB096268BCDFFC32D888 /* Build configuration list for PBXNativeTarget "AlamofireActivityLogger" */;
+                       buildPhases = (
+                               E5162F0AA677FC265E4CEE7594F5B519 /* Headers */,
+                               1092929577AF95AA173AE77051923F1B /* Sources */,
+                               76FED3C0A0FC13C147C353C828762C65 /* Frameworks */,
+                               F2D3636A25188AC8B1BCA7C31983009A /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               36BEB60199135BBC6388CDC1A0BD32A8 /* PBXTargetDependency */,
+                       );
+                       name = AlamofireActivityLogger;
+                       productName = AlamofireActivityLogger;
+                       productReference = 17E87A50423EE2843AB0EB08528F52EC /* AlamofireActivityLogger.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               939DA4FAAB94FC5EE0B3FCF4BAA2C49E /* AEXML */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = E35E6AA414D91179E03510FF66D5F9E4 /* Build configuration list for PBXNativeTarget "AEXML" */;
+                       buildPhases = (
+                               BFD96857B47BD6A21F4E09C9C6852F83 /* Headers */,
+                               132528D8BDE3D37349DF1D60BCE3275A /* Sources */,
+                               F4E6020736A4B63C32D7AAF0B8C3674A /* Frameworks */,
+                               FB172799C004174FC54C84DCB5182447 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = AEXML;
+                       productName = AEXML;
+                       productReference = 26216404833D73818C5EFEC7E16DA183 /* AEXML.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               A0243AC4D5FF53ED067BF1609A6898A0 /* FontBlaster */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = C93264544A481CEA664A3A1345E9FF50 /* Build configuration list for PBXNativeTarget "FontBlaster" */;
+                       buildPhases = (
+                               289C0E47E6B6327190FEC5BDA43E5FC4 /* Headers */,
+                               CF9EF1C34D3222332B971C72E3DF728A /* Sources */,
+                               AC5DE671039B379159BBC2DF39C825DF /* Frameworks */,
+                               00D342916A2C6D50C12D4F2A0C6D73F3 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = FontBlaster;
+                       productName = FontBlaster;
+                       productReference = DE2432A0214596A77E0715BF9EDA6217 /* FontBlaster.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               A15CD3220CB655EF65975142EDBD8C6E /* Kingfisher */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 055CB86039F87F17BFAACCE5B1899A4E /* Build configuration list for PBXNativeTarget "Kingfisher" */;
+                       buildPhases = (
+                               A4416D8D7BAB1B44603B29053B8C30A1 /* Headers */,
+                               D9F6E34E36CF832E207A40E6DE2EBB3C /* Sources */,
+                               55C36F7CC1AF497CD89C8420A717D06F /* Frameworks */,
+                               907E8FAD109F4265A17FD9B5C5BCDD9D /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = Kingfisher;
+                       productName = Kingfisher;
+                       productReference = 9D3AC472F71CAA627B1A729C76C47E40 /* Kingfisher.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               A938E07DF5A500E5C4FA1C9FD1886EB2 /* MatomoTracker */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 0CCA827EBDB82C7A989668BE9C2F8F50 /* Build configuration list for PBXNativeTarget "MatomoTracker" */;
+                       buildPhases = (
+                               F97C449EBDC63F1F9F6DC0C67B08CEB3 /* Headers */,
+                               6633DDE85BEE0EBF315B105A2B96104F /* Sources */,
+                               8F3E0087AE718FE6739991305CB4B11A /* Frameworks */,
+                               21B221F279750C4D2CF490E0F4D241C2 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = MatomoTracker;
+                       productName = MatomoTracker;
+                       productReference = D489E4F14592AD2E12AB3FE975D754FD /* MatomoTracker.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               AD408BAB384B6DDA88A50A16FB158B66 /* MBProgressHUD */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 09600571F63852ACA3B7A23B4729B6A4 /* Build configuration list for PBXNativeTarget "MBProgressHUD" */;
+                       buildPhases = (
+                               5CE17C32A4504B751C3375AA3458D828 /* Headers */,
+                               88F228B332626246A6113EBE81AB9792 /* Sources */,
+                               35A398F6CC37A8D8E469498B848E286F /* Frameworks */,
+                               96C60439BF31F7D396AAA2AAD2622A98 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = MBProgressHUD;
+                       productName = MBProgressHUD;
+                       productReference = B8746C7F6910CD010EF2C8342D472881 /* MBProgressHUD.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               C8C2C570FCCD7CC9014E895FBBEEF1FC /* SideMenu */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 8F4C2F96551CD65DFE781EE31CC581FF /* Build configuration list for PBXNativeTarget "SideMenu" */;
+                       buildPhases = (
+                               9AAC9721424D21D7E9529560DC79D0A8 /* Headers */,
+                               94E4ECB01C45E4563265D9E84E7926C9 /* Sources */,
+                               3D375098AF4E80A3884E2EBE5C64940D /* Frameworks */,
+                               32BDCE018F879027D5043EFB973654B4 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = SideMenu;
+                       productName = SideMenu;
+                       productReference = FF3E6E1A3AAB0EAF989465DAEDBB72B3 /* SideMenu.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               CE310247D34682517FA45BE1C20210CF /* SSZipArchive */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 3318BC48F0DB1BD1B63799853383A071 /* Build configuration list for PBXNativeTarget "SSZipArchive" */;
+                       buildPhases = (
+                               C096048F9C5E2146FBC747D8D132051E /* Headers */,
+                               96CEC253D87DF1C33C076FAD330B3494 /* Sources */,
+                               FF98F5D38366A576835533C3EDA5286E /* Frameworks */,
+                               4C486F240A8CEAF36424613411E6BEA1 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = SSZipArchive;
+                       productName = SSZipArchive;
+                       productReference = ADE94AD745E9D666DE8E2CEDAC68333D /* SSZipArchive.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               D449FC7BC81F775BC2E4BB53D77B64D6 /* ZFDragableModalTransition */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 0828BAB04BC354092C84127A22839A0F /* Build configuration list for PBXNativeTarget "ZFDragableModalTransition" */;
+                       buildPhases = (
+                               807F38156F708FD265BFAF42D7035343 /* Headers */,
+                               14EE8AC52BC1649B05DB136B392436E0 /* Sources */,
+                               4F5E6400BFA73BDC27205E23C09D84F9 /* Frameworks */,
+                               D14F131EAD682831C017070BAEADDA17 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = ZFDragableModalTransition;
+                       productName = ZFDragableModalTransition;
+                       productReference = 37215B06C349AEA05F2D6634E784DCC0 /* ZFDragableModalTransition.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               DEB0D4FCD7FECFEDBCB372932FCB07CB /* MZDownloadManager */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 2F7A501F7E043A1DD65393B88DD0C6ED /* Build configuration list for PBXNativeTarget "MZDownloadManager" */;
+                       buildPhases = (
+                               2DEFDE54D1138047033AFAB6F82E1843 /* Headers */,
+                               93759BFB7D8503E1DEF72DBCB89AAB3F /* Sources */,
+                               2F448EC1059CA90FE162E2864E1804FD /* Frameworks */,
+                               CC3B825D426F5E610F299B4FFF48174E /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = MZDownloadManager;
+                       productName = MZDownloadManager;
+                       productReference = 8180AFDD65D23809C369C6C0D0AE2006 /* MZDownloadManager.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               E6D2D23AF894E245CA516BC67EBF8703 /* Toast-Swift */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 29AA372A358AB7E30BD4693EAC1853C2 /* Build configuration list for PBXNativeTarget "Toast-Swift" */;
+                       buildPhases = (
+                               CF072462BA35784A0474F030B8D5F963 /* Headers */,
+                               AC241C2CEA6625AD4A7591D7A1853122 /* Sources */,
+                               38F36558E979BB5F4A18013938C7D8B7 /* Frameworks */,
+                               12D9A8ABDF12FC59A2C4F443F4960948 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = "Toast-Swift";
+                       productName = "Toast-Swift";
+                       productReference = 2E6F3CBA4A6A0429B6813DB5778F3D40 /* Toast_Swift.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               E76458C58C9140B6A16D60547E68E80C /* Alamofire */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 427F0F003A1AD80AE00155AFCDEFAC20 /* Build configuration list for PBXNativeTarget "Alamofire" */;
+                       buildPhases = (
+                               A6A607506FEAAC7C41268D3E5CF4E5FE /* Headers */,
+                               CC399CEC576B42C962CEB290481CAF95 /* Sources */,
+                               6E8AF668A2161F7D6F680F721DB65D2D /* Frameworks */,
+                               3DDB7E21141D7764AE4658D5B6AFF8C6 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = Alamofire;
+                       productName = Alamofire;
+                       productReference = B6A4879596BEAAC18CA41AD231043F17 /* Alamofire.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               EF83FCC10E7453ACCB801B656501E2F6 /* OAuthSwift */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = A0C06C9BF416D71CB5103EF8C8729E7A /* Build configuration list for PBXNativeTarget "OAuthSwift" */;
+                       buildPhases = (
+                               449B91827AB0541E726FA72E9B179E8E /* Headers */,
+                               60A94A6F5C559908A77A309C98AC631A /* Sources */,
+                               E411FE86C35FE60BCDED88381E3A1DE4 /* Frameworks */,
+                               1839B9C4027C89A1120F8E2F6BB75A7D /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = OAuthSwift;
+                       productName = OAuthSwift;
+                       productReference = 08E6C7A1FFCA6B21341142FFDC8761EE /* OAuthSwift.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+               FDFD44D80A6A01C71A321ADCC051FC07 /* Pods-WolneLektury */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = CFAECCEC9BA5434011916F290A0EDFFC /* Build configuration list for PBXNativeTarget "Pods-WolneLektury" */;
+                       buildPhases = (
+                               654B6636423A426904399EE79D3636F9 /* Headers */,
+                               F1FA147D9B7805FC419DAAA81186112F /* Sources */,
+                               82FED27C7ED0BF5001015F002AE62C27 /* Frameworks */,
+                               F2A81572C8A746D892735BB95A4CECBF /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               95340C80680036946254C7C7E42B598C /* PBXTargetDependency */,
+                               F6F11FDDCFA767EE39B5A314AE2CBA2C /* PBXTargetDependency */,
+                               93D3DB0B4BA00A9962D9D622B45796BB /* PBXTargetDependency */,
+                               4759C5699CED82DB77EE538FBE2B6123 /* PBXTargetDependency */,
+                               30ED4DEBFB7BEE33D9F6999D486AABB6 /* PBXTargetDependency */,
+                               29A5AC78BEB55771809956141CF697B0 /* PBXTargetDependency */,
+                               C14260A38E262542BAEEE29A8F11B645 /* PBXTargetDependency */,
+                               ED4604285DA69B5BBA943344A5B988F0 /* PBXTargetDependency */,
+                               D85BB41B0FEBEEA0C9DBB2B45646869A /* PBXTargetDependency */,
+                               0D1AAC295BCDA7588BBB939BD871D036 /* PBXTargetDependency */,
+                               1D88A85163533378C9F286D7CEAC508B /* PBXTargetDependency */,
+                               FA560D1DFE9DA58EA9E45BC0EC4CA94E /* PBXTargetDependency */,
+                               790D04DA66ED8977E75C8AE176C8F41A /* PBXTargetDependency */,
+                               3BC83CA1CADCFC5FA925F1E4A646A87C /* PBXTargetDependency */,
+                               B45422F8B2240C086CD9B51D0D4EAA7A /* PBXTargetDependency */,
+                               9D50E295CD62AF8C48A4E382EDEC6446 /* PBXTargetDependency */,
+                               AD4C50889E1C6C3D0FB3E6E2F7457A46 /* PBXTargetDependency */,
+                               DD920B2852EB1D88B9332BAF2BA08A3E /* PBXTargetDependency */,
+                               AEF97EBDF12605C84C89D13ADB1EBFB7 /* PBXTargetDependency */,
+                               655CEC8339B1B760D96006B4675DE6A8 /* PBXTargetDependency */,
+                               704B9E6981A97352C92218F574B93397 /* PBXTargetDependency */,
+                               C4ACD2909716BA74462C4005A98E3B67 /* PBXTargetDependency */,
+                               F0FEA998FF4EBDA7A28E3E40400912E0 /* PBXTargetDependency */,
+                               C635411A43743CCC71A0834186507970 /* PBXTargetDependency */,
+                       );
+                       name = "Pods-WolneLektury";
+                       productName = "Pods-WolneLektury";
+                       productReference = A9EE7F7299E48AE9BAE6A21FC4E45526 /* Pods_WolneLektury.framework */;
+                       productType = "com.apple.product-type.framework";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               D41D8CD98F00B204E9800998ECF8427E /* Project object */ = {
+                       isa = PBXProject;
+                       attributes = {
+                               LastSwiftUpdateCheck = 0930;
+                               LastUpgradeCheck = 0930;
+                       };
+                       buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */;
+                       compatibilityVersion = "Xcode 3.2";
+                       developmentRegion = English;
+                       hasScannedForEncodings = 0;
+                       knownRegions = (
+                               en,
+                       );
+                       mainGroup = 7DB346D0F39D3F0E887471402A8071AB;
+                       productRefGroup = 6A39F4DC00843FFC458FE4616AD48B9D /* Products */;
+                       projectDirPath = "";
+                       projectRoot = "";
+                       targets = (
+                               939DA4FAAB94FC5EE0B3FCF4BAA2C49E /* AEXML */,
+                               E76458C58C9140B6A16D60547E68E80C /* Alamofire */,
+                               8D956160DB7545834B961CB5E8DBBD1B /* AlamofireActivityLogger */,
+                               4338000AC6DBAF783F338F055DDE55C6 /* FirebaseCore */,
+                               0DD17D85AC06742489BCAFB83E51FDB0 /* FirebaseMessaging */,
+                               1EB42C6E589197DB247315875A9AE141 /* FolioReaderKit */,
+                               A0243AC4D5FF53ED067BF1609A6898A0 /* FontBlaster */,
+                               2B2CE33B441C5D4BD9A5A04D3087E5C8 /* GoogleUtilities */,
+                               87C645C5CBE6078EBEE1659ABEEF780D /* JSQWebViewController */,
+                               A15CD3220CB655EF65975142EDBD8C6E /* Kingfisher */,
+                               A938E07DF5A500E5C4FA1C9FD1886EB2 /* MatomoTracker */,
+                               AD408BAB384B6DDA88A50A16FB158B66 /* MBProgressHUD */,
+                               673DBE5653C51962C057371B5CA8A2B4 /* MenuItemKit */,
+                               DEB0D4FCD7FECFEDBCB372932FCB07CB /* MZDownloadManager */,
+                               8364BB258018143B83798DE31C13546D /* nanopb */,
+                               EF83FCC10E7453ACCB801B656501E2F6 /* OAuthSwift */,
+                               FDFD44D80A6A01C71A321ADCC051FC07 /* Pods-WolneLektury */,
+                               71E4E70C7BAC6389783D3A3C8EB1F910 /* Protobuf */,
+                               13218BC4FEB9FA3C157F802AB364DCBB /* Realm */,
+                               76948DED43ECFC74BB41AA7F11DA2200 /* RealmSwift */,
+                               C8C2C570FCCD7CC9014E895FBBEEF1FC /* SideMenu */,
+                               CE310247D34682517FA45BE1C20210CF /* SSZipArchive */,
+                               8CD09A1EC484C4C08A7FBF85CA871914 /* SwiftKeychainWrapper */,
+                               E6D2D23AF894E245CA516BC67EBF8703 /* Toast-Swift */,
+                               D449FC7BC81F775BC2E4BB53D77B64D6 /* ZFDragableModalTransition */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+               00D342916A2C6D50C12D4F2A0C6D73F3 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               0E0199FFAE1D920BFA69E4D33330503F /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               12D9A8ABDF12FC59A2C4F443F4960948 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               1839B9C4027C89A1120F8E2F6BB75A7D /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               21B221F279750C4D2CF490E0F4D241C2 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               32BDCE018F879027D5043EFB973654B4 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               33A8F7B04C4B10726502EDE02CDC184E /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               3DDB7E21141D7764AE4658D5B6AFF8C6 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               4C486F240A8CEAF36424613411E6BEA1 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               5F863B857C7DD523FBBE6C6F33F81AF2 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               60B056B00C949C9E6D205624FDB50E49 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6407E061C1EF20E9594F96E5B5CDBD3A /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6609B2FC3FE461870D129770AB74BC25 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6F0E58C2C471AA33EEDC1D767435D926 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               7F17AC1ED6D7AED732EDAEB7C0B4103C /* Andada-Bold.otf in Resources */,
+                               207B542DB4417E34853F931AC63A5CAC /* Andada-BoldItalic.otf in Resources */,
+                               0A2B15303BC26D2BC3C907BB9BDB6A30 /* Andada-Italic.otf in Resources */,
+                               7DF4208F0ED7849BF881FF06D9F0F476 /* Andada-Regular.otf in Resources */,
+                               4D7671FDCF69EFE3ED3D2784829916BC /* Bridge.js in Resources */,
+                               7F1E5BEFFDDCC19F2FCC3B791CB4DF8C /* Images.xcassets in Resources */,
+                               4461B138968C3C131E90EDE2484F1963 /* Lato-Bold.ttf in Resources */,
+                               2598B4D94CB3B0490D8620CDCA6CE774 /* Lato-BoldItalic.ttf in Resources */,
+                               D19792C2E4A14796A74B63E03A109642 /* Lato-Italic.ttf in Resources */,
+                               4D742248C548884F45E3FDA82657EB39 /* Lato-Regular.ttf in Resources */,
+                               2BAC30A9FB2B3D8AF2A4878093EF2E83 /* Lora-Bold.ttf in Resources */,
+                               E9A2A0E1987B6254968A155DCE98E201 /* Lora-BoldItalic.ttf in Resources */,
+                               318FF8801C5C975BDBF82EB2A5E4CEBF /* Lora-Italic.ttf in Resources */,
+                               601CCF982A0CE8CD05B3FA83FD2B6229 /* Lora-Regular.ttf in Resources */,
+                               CDC451CD2717CF465EE0088C5F814225 /* Raleway-Bold.ttf in Resources */,
+                               CEC7604E8FF1DCF8EBEBB4EAA4F8DE4B /* Raleway-BoldItalic.ttf in Resources */,
+                               042EFC1017533312205A943D889EB58B /* Raleway-Italic.ttf in Resources */,
+                               A168479F8F7841A22AAD62F7A029A0DE /* Raleway-Regular.ttf in Resources */,
+                               C591500094347AAEEB0DA12A7555B0F7 /* Style.css in Resources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               907E8FAD109F4265A17FD9B5C5BCDD9D /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               95A05F3D465DB3B56C07E553B8919626 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               96C60439BF31F7D396AAA2AAD2622A98 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               CC3B825D426F5E610F299B4FFF48174E /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               D14F131EAD682831C017070BAEADDA17 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               E90E004EED7E9BA5A6FF2A50CBB1498E /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F104F5D359392B17390789BFC5B1126B /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F2A81572C8A746D892735BB95A4CECBF /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F2D3636A25188AC8B1BCA7C31983009A /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               FB172799C004174FC54C84DCB5182447 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               FFEE618D0ABA9FE323734237056FF21F /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+               00198027755E13B6B7AC1AC2D14587B4 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               FD32EC5A94EE80217BE8F428956897D7 /* Internals.swift in Sources */,
+                               89BEA56583B63F9F07B07094A9A687D1 /* MenuItemKit-dummy.m in Sources */,
+                               04CF5777491412E48AEE272D75A56F77 /* Swizzlings.m in Sources */,
+                               72431300C9DACB7D5CA00D5A85114050 /* Swizzlings.swift in Sources */,
+                               D7DC434934FDD5A7EE1D36F9BF5E4AC7 /* UIMenuItem.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               06FA7E000B28DF806B0A8574C7FA39C8 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               F51893EBAA6334E89ECE1A62511CE6D0 /* FirebaseMessaging-dummy.m in Sources */,
+                               F3118053FC15EA6080A8AAA4CFE5E945 /* FIRMessaging.m in Sources */,
+                               9BF96E6F904DDCF6EDECBBC840C2F314 /* FIRMessagingCheckinService.m in Sources */,
+                               8E10EE937C903154C94EF756B76D9DF5 /* FIRMessagingClient.m in Sources */,
+                               A9DB4DA63493082FBE08873CDD30608B /* FIRMessagingCodedInputStream.m in Sources */,
+                               8A09F85B24B1F26EA9E0FBBCCDC497C0 /* FIRMessagingConnection.m in Sources */,
+                               F9E97C597DF42C783D99D7ADA0B2BB4F /* FIRMessagingConstants.m in Sources */,
+                               41A7890A42E1D0E0D7AF47E053E98F64 /* FIRMessagingContextManagerService.m in Sources */,
+                               F48ED2E945391BD5132F07ED5BA09577 /* FIRMessagingDataMessageManager.m in Sources */,
+                               87EA88C8DEEFDE135776F01CD31DE4FD /* FIRMessagingDelayedMessageQueue.m in Sources */,
+                               7DE1F186E56EBAA07D2F4961E78438F3 /* FIRMessagingLogger.m in Sources */,
+                               91A13910AA34A5D9021EFB5AF76E3807 /* FIRMessagingPacketQueue.m in Sources */,
+                               15C9919A9A4506E500E6B4F9015499E7 /* FIRMessagingPendingTopicsList.m in Sources */,
+                               DBD0B446E327AA7EAFA659468ADF856E /* FIRMessagingPersistentSyncMessage.m in Sources */,
+                               E7DABCBC335268A1E1578F43EAC5F638 /* FIRMessagingPubSub.m in Sources */,
+                               2E05889FB32F6C8AB33AE8854A5D9CA8 /* FIRMessagingPubSubRegistrar.m in Sources */,
+                               9DCECCE3655A709508C929D3770C7D97 /* FIRMessagingReceiver.m in Sources */,
+                               ED3A0EC643AA230DD128771C2EB56C1E /* FIRMessagingRegistrar.m in Sources */,
+                               25A81E5381E3BA30B223DD15B5216C88 /* FIRMessagingRemoteNotificationsProxy.m in Sources */,
+                               7D6F755661B4FD496E74E2A707A210B0 /* FIRMessagingRmq2PersistentStore.m in Sources */,
+                               25A69A0D9CB1E51FF9CD48D00869C361 /* FIRMessagingRmqManager.m in Sources */,
+                               BCD6A1F3C955437467963A3DB7E4DE31 /* FIRMessagingSecureSocket.m in Sources */,
+                               4121DDEF0418D39AB3F5EAE215013F25 /* FIRMessagingSyncMessageManager.m in Sources */,
+                               9598DD3F30A87385E507352646B3A1E2 /* FIRMessagingTopicOperation.m in Sources */,
+                               3E02E8903767E2C1DF6802E1A90D4CA9 /* FIRMessagingUtilities.m in Sources */,
+                               75AA6AFBEA5C650C1A3B0A23C96E4B1F /* FIRMessagingVersionUtilities.m in Sources */,
+                               1E2A6F7480A4D6FCCB51D8E80FD20944 /* GtalkCore.pbobjc.m in Sources */,
+                               D3F4FCD6B069F0717222E2EAED5B4FE9 /* GtalkExtensions.pbobjc.m in Sources */,
+                               942DBC0B89C8BAE0D5CEC47CFD84406B /* NSDictionary+FIRMessaging.m in Sources */,
+                               F844B6B5A00BB0C228F4B0AB50D7EA26 /* NSError+FIRMessaging.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               1092929577AF95AA173AE77051923F1B /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               47B8810DB4416A9E5CA39F27C1193706 /* AlamofireActivityLogger-dummy.m in Sources */,
+                               66354F1A710D61367F5401B5D88E5CFC /* LoggeableRequest.swift in Sources */,
+                               86E19D48949A95AD016D27AB7BB8AE41 /* Logger.swift in Sources */,
+                               388CDEE9BC70517AEF5184E807928E39 /* Tools.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               12D3F587AEF7D8BD85FB723377576392 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               AAA6A3680C85C31B477E4C906DE9815A /* nanopb-dummy.m in Sources */,
+                               3552218D2E421ECA1909B93DE2FB959A /* pb_common.c in Sources */,
+                               E1331724A7D92526AA8714A62C353932 /* pb_decode.c in Sources */,
+                               C46B7BDE25252D8EB990DEB9FE7CD98A /* pb_encode.c in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               132528D8BDE3D37349DF1D60BCE3275A /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               6ED5B5CE6E6697D6E87170B8F7E20CF8 /* AEXML-dummy.m in Sources */,
+                               6D4F02F9947B695D3074491BB4D917ED /* Document.swift in Sources */,
+                               C9D626FE1D508CDCA87F0825540D1873 /* Element.swift in Sources */,
+                               8CBBE676E43BBBCE90BA4D320D972208 /* Error.swift in Sources */,
+                               AD2E5F4E82A54D5CD24E4FA34A7D8FFC /* Options.swift in Sources */,
+                               9716EF4B9C2B31231060FFC1DEB40DA3 /* Parser.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               14EE8AC52BC1649B05DB136B392436E0 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               16F90DAC8AA5BC6E1B63E2D93F7B8F09 /* ZFDragableModalTransition-dummy.m in Sources */,
+                               727804B0F19D9A317560D81BCC3631E6 /* ZFModalTransitionAnimator.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               5F5FA332B786B26775715128005FEBF3 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               0175DD92F9F6E8C72EE40DF0B9A5AD31 /* FIRAnalyticsConfiguration.m in Sources */,
+                               2833252EE9A62335D18C1AE8C312AC80 /* FIRApp.m in Sources */,
+                               FDD30714805DCA7A56F6D140C73C119D /* FIRAppAssociationRegistration.m in Sources */,
+                               1AE2A9FBCD4892379D6EA33EBB059E46 /* FIRBundleUtil.m in Sources */,
+                               A6190300A689C5F3E9116E39BA2985B7 /* FIRComponent.m in Sources */,
+                               C9B4ADD0D0F5552A71C1DE332431350E /* FIRComponentContainer.m in Sources */,
+                               AAFB81C326930D69B9D3D346EBC919FE /* FIRComponentType.m in Sources */,
+                               B8B9C790E3BF944F6186C4C06CA699C6 /* FIRConfiguration.m in Sources */,
+                               0D86017D181E63BA2748D0B4675FEE9A /* FIRDependency.m in Sources */,
+                               B102D9296D1A1CE12DDFDA38F7ADBC1F /* FirebaseCore-dummy.m in Sources */,
+                               3EE16703619890CD8BA87B53FC36D952 /* FIRErrors.m in Sources */,
+                               B9FAC68D54CD7F85CEC7A9A90821A1D3 /* FIRLogger.m in Sources */,
+                               1CEAB5B7698F0EB7EEFEC72B3BE26D1F /* FIROptions.m in Sources */,
+                               24D95C24CFFEA030873E55B2E7136CD8 /* FIRVersion.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               60A94A6F5C559908A77A309C98AC631A /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               0A1908969828EF0B672312582540E304 /* Collection+OAuthSwift.swift in Sources */,
+                               615B4F07638913112ACF0FDFA305BB36 /* Data+OAuthSwift.swift in Sources */,
+                               7B17CD91C88596B5F2D19D16CB28A4FD /* Dictionary+OAuthSwift.swift in Sources */,
+                               A7A4A80FE4D52D1492141FB82CED7679 /* HMAC.swift in Sources */,
+                               54EC154AEE5C8B1E2DE1D39EB0071F30 /* Int+OAuthSwift.swift in Sources */,
+                               6F78BFAAAD958F7B294EAD931C21C3C1 /* NotificationCenter+OAuthSwift.swift in Sources */,
+                               9D2D4F83EB0072F2F9976666F90412D0 /* NSError+OAuthSwift.swift in Sources */,
+                               602C1949653A4615A5DD6AC91B824BBC /* OAuth1Swift.swift in Sources */,
+                               0E8E16BFB2519FDDFC29EEACEBC25784 /* OAuth2Swift.swift in Sources */,
+                               95A48A974C9115D1C848C6FAB5CE5FB8 /* OAuthSwift-dummy.m in Sources */,
+                               2C0B55F2EFCFCE6A903D5F9C7F9E2FC7 /* OAuthSwift.swift in Sources */,
+                               BEEB7138D830B9414158F024CF836B2E /* OAuthSwiftClient.swift in Sources */,
+                               F0E90BDEF941B43FBEB3469F9BD2056F /* OAuthSwiftCredential.swift in Sources */,
+                               ED9967754FA36BC9BE4F248E94F0134B /* OAuthSwiftError.swift in Sources */,
+                               C417DA5E48DE74B3E2C6F38BD6E8AA82 /* OAuthSwiftHTTPRequest.swift in Sources */,
+                               C599CE94C59B4740EC39FD0115B3EC6E /* OAuthSwiftMultipartData.swift in Sources */,
+                               4337162E4FCBB327692532D2AADB24AB /* OAuthSwiftResponse.swift in Sources */,
+                               9842D46A74A89E0ED7809E92EC434290 /* OAuthSwiftURLHandlerType.swift in Sources */,
+                               CA69265D91583F5EEE1925B6D303E8B0 /* OAuthWebViewController.swift in Sources */,
+                               9233126AB5F69C247A408E26A9B7942C /* Objc.swift in Sources */,
+                               15C96676060BF2C17D270C3F029644B1 /* SHA1.swift in Sources */,
+                               1DED3FDEE669B446980A6E6F842F9549 /* String+OAuthSwift.swift in Sources */,
+                               54143F95ED39F0BF32782D9D7F9AAFD3 /* UIApplication+OAuthSwift.swift in Sources */,
+                               B79BE1F4BADB104866BB7829F473E02F /* URL+OAuthSwift.swift in Sources */,
+                               33288BB59843FAAD4A2330C75D48B80F /* Utils.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6633DDE85BEE0EBF315B105A2B96104F /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               33F9CED1A15F442B598833EA4A03F272 /* Application.swift in Sources */,
+                               F09C1A81CC96798491316A5068BD49C5 /* CustomDimension.swift in Sources */,
+                               F2A3EDC371140C9BA569CB5F6B39B3DF /* CustomVariable.swift in Sources */,
+                               91716377663C5129353AE65DDC9EAC55 /* Device.swift in Sources */,
+                               D6AC96954C61A1609B0E1F9BF0E55E6E /* Dispatcher.swift in Sources */,
+                               4FF5E89C1F4715E5676C25F96E3EEE85 /* Event.swift in Sources */,
+                               11677416550DE25FCABD80F8BE8CB0CA /* EventSerializer.swift in Sources */,
+                               55C1439CC2A316DC58C336349A7E01E4 /* Locale+HttpAcceptLanguage.swift in Sources */,
+                               98686D23553D62431BB3E7B33470129C /* Logger.swift in Sources */,
+                               15AC8AA4B5ED7E5BF2E80483CBAC5DDE /* MainThread.swift in Sources */,
+                               E01DAC754BB51E53AF0EE5403EAA021C /* MatomoTracker-dummy.m in Sources */,
+                               9CD281C3381240CCF5B1CBA25FC9A7C5 /* MatomoTracker.swift in Sources */,
+                               6B6E266F5393BC0F2B550420990DDF37 /* MatomoUserDefaults.swift in Sources */,
+                               5EDFAFC0BC5DA207670220C792609991 /* MemoryQueue.swift in Sources */,
+                               22D2A945A41753AE917AB2BFE8723E17 /* Queue.swift in Sources */,
+                               C97607EA1341DCD74DDDA94986512302 /* Session.swift in Sources */,
+                               CE7BCC6FB4611BA106CD07FDCDF7F9A4 /* URLSessionDispatcher.swift in Sources */,
+                               67B656A4361A03B54BEBA7C91F5628A4 /* Visitor.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6B45D397692AB066C2737A350C3B0B13 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               EAD92FB7BCD3E150505ED55F0EEE694D /* KeychainItemAccessibility.swift in Sources */,
+                               5592A36E78743E345801FA826E4E9EEF /* KeychainWrapper.swift in Sources */,
+                               1CD6F680112CA20AF60AA1F7785AF839 /* SwiftKeychainWrapper-dummy.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6F43672231228D284CA4D38EDE402990 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               32AD75DDBF58131E8A09D268EC722BF5 /* Any.pbobjc.m in Sources */,
+                               D9BFA451C350D31DBD48E928ADCC234F /* Api.pbobjc.m in Sources */,
+                               5C83031B7233347A62498786C3616AB2 /* Duration.pbobjc.m in Sources */,
+                               05A122F6664593DB1044E1C42632DE28 /* Empty.pbobjc.m in Sources */,
+                               39613AD5AF4DA0C13CE4B4AFC463B90F /* FieldMask.pbobjc.m in Sources */,
+                               D10786C81EC7B694E61F78F87A02F5E2 /* GPBArray.m in Sources */,
+                               A407454449431173E8D9BF7DD73EC859 /* GPBCodedInputStream.m in Sources */,
+                               A20B89DAA8EF5142AF1F9D1F6D9351A4 /* GPBCodedOutputStream.m in Sources */,
+                               2E1DF3B5FC2960D31B8C5023885CBE83 /* GPBDescriptor.m in Sources */,
+                               F11CF9D9D0DB80F5A8E73049D5E7ADC5 /* GPBDictionary.m in Sources */,
+                               BF8CA5C730F2DFD3748A63C1B29A51B9 /* GPBExtensionInternals.m in Sources */,
+                               A815CCD462141D7937E2BD32224F8516 /* GPBExtensionRegistry.m in Sources */,
+                               8C0D10DA3122D7BD7AAEB35E502C9BF4 /* GPBMessage.m in Sources */,
+                               D96BF0B05F160D6D21A774579CAA72CB /* GPBRootObject.m in Sources */,
+                               9453DB0ED8774AE633437A11A474D93E /* GPBUnknownField.m in Sources */,
+                               F53F400DEC55E2AC6DAC1158DF988C26 /* GPBUnknownFieldSet.m in Sources */,
+                               F1F9E107CF3F2DAEC683203BAC21039F /* GPBUtilities.m in Sources */,
+                               23088C73D99E659C6AF3D218CF278E90 /* GPBWellKnownTypes.m in Sources */,
+                               92167B5CE3EF41711C5347D710306CF4 /* GPBWireFormat.m in Sources */,
+                               17B6937A0AB61D1319E38144527699FB /* Protobuf-dummy.m in Sources */,
+                               0F9B3B52ED08BD554C447CE7E9D4FE04 /* SourceContext.pbobjc.m in Sources */,
+                               E947A20F257442D9C5B6A5C587D42DD7 /* Struct.pbobjc.m in Sources */,
+                               80C3AEB085EC7C9FF503B92EEBB0C4E2 /* Timestamp.pbobjc.m in Sources */,
+                               941351D8B8F0D0D877A2C7E5067EBBE3 /* Type.pbobjc.m in Sources */,
+                               4C92164BAF0CAF480EE22B78A7DB03CD /* Wrappers.pbobjc.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               88F228B332626246A6113EBE81AB9792 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               EEB4A455551D7B917E1AD4B0A1A964FF /* MBProgressHUD-dummy.m in Sources */,
+                               4BD3B1D588017EA43F3E98563319DC19 /* MBProgressHUD.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               93759BFB7D8503E1DEF72DBCB89AAB3F /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               8D7C6BD5D7B2671FFC800C600A255BFA /* MZDownloadManager-dummy.m in Sources */,
+                               57EB381CF010DA8ED4D9C4F9C40A0F4A /* MZDownloadManager.swift in Sources */,
+                               1D7EC3E556502CA2CF523B3AF823BB32 /* MZDownloadModel.swift in Sources */,
+                               517608EEF7DF8E8FA0BCB4EF84BCCD8E /* MZUtility.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               94E4ECB01C45E4563265D9E84E7926C9 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E27007C4C2C6290EB464FBC03B32474B /* SideMenu-dummy.m in Sources */,
+                               5CF30E2EF4AB65F79529D73AA5964434 /* SideMenuManager.swift in Sources */,
+                               41403EA071B1A174F010A55A10A7A992 /* SideMenuTransition.swift in Sources */,
+                               A32E0A2404ACF53852037EB065812C57 /* UISideMenuNavigationController.swift in Sources */,
+                               013E11191C3C5FE573B9AC49169D0958 /* UITableViewVibrantCell.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               96CEC253D87DF1C33C076FAD330B3494 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               9EC2BB6DB27F86658A9451AB726B018B /* aes_ni.c in Sources */,
+                               D41B930CB10DFF51D639B68CB5B89863 /* aescrypt.c in Sources */,
+                               927823650C468EA5082D7B5BFA2A6D42 /* aeskey.c in Sources */,
+                               2CDB4D166E4CBF5A3A2E868ED2530022 /* aestab.c in Sources */,
+                               5A35ADDBCBFEED97AF50A9AE2E4FD577 /* crypt.c in Sources */,
+                               3DB1DE26BB4BE2B4F99DC851F0EDEF54 /* fileenc.c in Sources */,
+                               810C5913A0776118199562DACAAC40EB /* hmac.c in Sources */,
+                               4E1E754909BF2487C287C6A3043A71A4 /* ioapi.c in Sources */,
+                               4537962FCACF5D81EAB89DA49599982C /* ioapi_buf.c in Sources */,
+                               6A8A6842F5FA4FF78BE3A1B2279232AB /* ioapi_mem.c in Sources */,
+                               CE4C5F49DDFEE03B141FDDF9BF9C340D /* minishared.c in Sources */,
+                               AB0522654C4F3AD21BAB960A6E2B556E /* prng.c in Sources */,
+                               32C34770A17BC1A4A41B37EB50210025 /* pwd2key.c in Sources */,
+                               E9F698ABDB9ED20BE2B51E54AFF20FC9 /* sha1.c in Sources */,
+                               A1F5532D985161407C5FC08C8905166B /* SSZipArchive-dummy.m in Sources */,
+                               C5541A4DD5B0E7BFF0898FBBE784250C /* SSZipArchive.m in Sources */,
+                               AE1CA95A3B55780E3E65385B5A6ED1FD /* unzip.c in Sources */,
+                               6B6C8C8779D1F44D865AE7ED4B4D8678 /* zip.c in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               AC241C2CEA6625AD4A7591D7A1853122 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               81408383F052E56545F0465BD28DCC6B /* Toast-Swift-dummy.m in Sources */,
+                               97BE0A5C72137771761CECCC14345FBF /* Toast.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               CC399CEC576B42C962CEB290481CAF95 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               BE4BA1EDE444A770F834605F4B65348E /* AFError.swift in Sources */,
+                               D4C3899574E9D5DF5E5DA52310560BCC /* Alamofire-dummy.m in Sources */,
+                               4E1A913EFB404FB11524718FF0298EFE /* Alamofire.swift in Sources */,
+                               DBE6E2E4D205545E7988CFA5057C31D6 /* DispatchQueue+Alamofire.swift in Sources */,
+                               FF9C7BC64DB23D2CED48197DE67F0335 /* MultipartFormData.swift in Sources */,
+                               B77705737566AE83ED7E448923D7FA60 /* NetworkReachabilityManager.swift in Sources */,
+                               500C8EDA60C07B0F127C7FC385E17D38 /* Notifications.swift in Sources */,
+                               796177DE2762F24DAC16A709FD954838 /* ParameterEncoding.swift in Sources */,
+                               965DACF3DC02857ECBE66C5CBA3DA5D4 /* Request.swift in Sources */,
+                               52237C35642089F77DD4D723CEB25737 /* Response.swift in Sources */,
+                               5EE5FED83B90A606A763CF1114D1D6FB /* ResponseSerialization.swift in Sources */,
+                               F9EA61D484CC15FDDAB0D8C0D26D7949 /* Result.swift in Sources */,
+                               6BEA14EC335E07C7063CD1383C0C443C /* ServerTrustPolicy.swift in Sources */,
+                               1FC3FD39157C2FFFF3869A1300730086 /* SessionDelegate.swift in Sources */,
+                               7068E8A7DDC1424EE8F24BC77E8746F4 /* SessionManager.swift in Sources */,
+                               36FF8853CB34A9297AFAA8F5F7456324 /* TaskDelegate.swift in Sources */,
+                               B424F524BBBE34E685129945993809A8 /* Timeline.swift in Sources */,
+                               58A9719584AFA2D108D9E5C585A79329 /* Validation.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               CF9EF1C34D3222332B971C72E3DF728A /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               6C1020CB1C2A89A8741875967A9B7787 /* FontBlaster-dummy.m in Sources */,
+                               D9A35C4A7153B8C94CD1DA7727DB566A /* FontBlaster.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               D9F6E34E36CF832E207A40E6DE2EBB3C /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               EA9BE16ECCC17CC9AD847C52D1B64671 /* AnimatedImageView.swift in Sources */,
+                               B239F6D91EC58C8046497C0909CB2E78 /* Box.swift in Sources */,
+                               95AB33523EE2DA5C145714D7279E8D27 /* CacheSerializer.swift in Sources */,
+                               B1D0744B9405A528508A49B4DE06A8DB /* Filter.swift in Sources */,
+                               453378DA661AD7FCBA79D1BFA59302EB /* FormatIndicatedCacheSerializer.swift in Sources */,
+                               BBE6B85E960BF7927A44E2329DE05A5F /* Image.swift in Sources */,
+                               90523ACEFEF8D56C35F5CF41CB70EE58 /* ImageCache.swift in Sources */,
+                               97EB2E795C2BE40106D734B5C0AEBDB3 /* ImageDownloader.swift in Sources */,
+                               07C0515BAB41A01EDFCB5164FB622170 /* ImageModifier.swift in Sources */,
+                               41D22E6F4C3E24D8FE690DE6D644AC3E /* ImagePrefetcher.swift in Sources */,
+                               DAC3F0C65BA82526594477C6DDF06745 /* ImageProcessor.swift in Sources */,
+                               2F84C0A84F2B5A0E9E3073417CA651C3 /* ImageTransition.swift in Sources */,
+                               75A1E4900C0AA7CE6E2454C64AA169E2 /* ImageView+Kingfisher.swift in Sources */,
+                               3554E5702FFEA0C2FC74B19EEC7D6325 /* Indicator.swift in Sources */,
+                               C9538BAC268AB8AAB4336A4E62B2EA1F /* Kingfisher-dummy.m in Sources */,
+                               20FEB969513418DFAC5090C858D10516 /* Kingfisher.swift in Sources */,
+                               66CCE448DAB752E36445FD69CD761C93 /* KingfisherManager.swift in Sources */,
+                               88CE4EE02A104D4BD65DE024ACF209F7 /* KingfisherOptionsInfo.swift in Sources */,
+                               008D5DDE0942B5C3EC51E6D4557DFD64 /* Placeholder.swift in Sources */,
+                               9EDFA1C34D4853B76A78B8E40B89C775 /* RequestModifier.swift in Sources */,
+                               179FCD8BC40E115DFA9C05F603A878A9 /* Resource.swift in Sources */,
+                               833B4550B26D2A945E08C978BB7BD505 /* String+MD5.swift in Sources */,
+                               F667936752E8742E44668CC80FC31720 /* ThreadHelper.swift in Sources */,
+                               34AEF9EE16EAEAF7FA0E66A48982F0C8 /* UIButton+Kingfisher.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               E6B2168F2463F5A23C71DA9E42CB7762 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               6B6450B5AE2DFD18D9DFB338F0F79BD5 /* binding_callback_thread_observer.cpp in Sources */,
+                               FF33EDC46088ED783E2B1BA415AC84A4 /* collection_change_builder.cpp in Sources */,
+                               90966D0CEAA5B97157C5CACC36671C23 /* collection_notifications.cpp in Sources */,
+                               6778CC3C9A4FD26056E56D16B24F42B7 /* collection_notifier.cpp in Sources */,
+                               FD38D7B6ED90C1C09EFD48DAAFCDE5D2 /* external_commit_helper.cpp in Sources */,
+                               0FBF22CE66F876D9CB1D09F22B743658 /* index_set.cpp in Sources */,
+                               99D878A13E43241F161775020BD80F32 /* keychain_helper.cpp in Sources */,
+                               DE496EDB682975EE3F22A6DD7CC44A10 /* list.cpp in Sources */,
+                               92B608263232B47001557148A8874F1D /* list_notifier.cpp in Sources */,
+                               CD9DD3D0C66EC7909572BA6A53B6CDA8 /* network_reachability_observer.cpp in Sources */,
+                               C0AC33AF5FE2D94A5D4A28B5BD190A74 /* NSError+RLMSync.m in Sources */,
+                               7C871C890E18F233E79E9743EA839B08 /* object.cpp in Sources */,
+                               9ECACEAD09A3D0231033532D62799534 /* object_notifier.cpp in Sources */,
+                               22F05ED1E0EB10DBCDB9B5CC1D0C2698 /* object_schema.cpp in Sources */,
+                               E6BE8C1DE57A430B0698E913A0BF93B9 /* object_store.cpp in Sources */,
+                               C10A68AE9667B0B614FA15298E366820 /* partial_sync.cpp in Sources */,
+                               7C5EBA165C3E36BDE6EFD25BC86C9251 /* placeholder.cpp in Sources */,
+                               A872241FBEBAFD4FFCAC9E39E5F34549 /* primitive_list_notifier.cpp in Sources */,
+                               6C36E4CB9562801D310E890FE8DF5107 /* Realm-dummy.m in Sources */,
+                               AE83443661BB982C102FEA9241A8689E /* realm_coordinator.cpp in Sources */,
+                               1B62DA07DA1516EFE97486D5806DDE46 /* results.cpp in Sources */,
+                               528A475990402BB9755D688F7E7F57AC /* results_notifier.cpp in Sources */,
+                               EFFCF738F52D8982F374F0DFF6E31CF5 /* RLMAccessor.mm in Sources */,
+                               7B92B58352349AB061E2DC676BE670CC /* RLMAnalytics.mm in Sources */,
+                               518FD0AB83B763CF76F044064E9995F4 /* RLMArray.mm in Sources */,
+                               3722F219D124C3525591F1262DB6B054 /* RLMClassInfo.mm in Sources */,
+                               7309FB2B42EDCA453F804DDEA6981FB3 /* RLMCollection.mm in Sources */,
+                               DA64C1471E8233CCDD71183C58913EC7 /* RLMConstants.m in Sources */,
+                               D1DA4EAD01D3C287A9B6E73CCFFFFB26 /* RLMJSONModels.m in Sources */,
+                               31F9103A1AA2692C0D131E1D8A063B92 /* RLMListBase.mm in Sources */,
+                               18B254948F230DA2D3DB86665FE2C83B /* RLMManagedArray.mm in Sources */,
+                               FB66877DD3A50E6892F267071FDD497C /* RLMMigration.mm in Sources */,
+                               9E6D778183C74562D0631A3CB1279157 /* RLMNetworkClient.mm in Sources */,
+                               9A5080678DC0D65FEA4F469C75975FE5 /* RLMObject.mm in Sources */,
+                               F51906E25AAFD6E62327B7397574F998 /* RLMObjectBase.mm in Sources */,
+                               17FA10574ADCDD6442F4E903EE64F75C /* RLMObjectSchema.mm in Sources */,
+                               D4FA61942176C432BC1D447F3697DEBA /* RLMObjectStore.mm in Sources */,
+                               C13F71F78AFBA66B73CE80D60A27BAC7 /* RLMObservation.mm in Sources */,
+                               7DA68975AD1D00BA8808AB41A54082C3 /* RLMOptionalBase.mm in Sources */,
+                               5C24A34DD99DDD7316EA7AB3DC0CE6E8 /* RLMPredicateUtil.mm in Sources */,
+                               9E250206EB49756E42E7066A99E87A29 /* RLMProperty.mm in Sources */,
+                               C7A51C3E97E628F007074B337D44E4DB /* RLMQueryUtil.mm in Sources */,
+                               1C1568B5E044F2A665B1647F74BD5356 /* RLMRealm+Sync.mm in Sources */,
+                               E190452FD228F46A54C15440A5C12DE1 /* RLMRealm.mm in Sources */,
+                               47800CD44B95F7CD27C4EFE24537F816 /* RLMRealmConfiguration+Sync.mm in Sources */,
+                               2B117AE5D1F324D55E3A62D0ACADB0A4 /* RLMRealmConfiguration.mm in Sources */,
+                               AB64E51C9C137EAA03865FF2904E9DD5 /* RLMRealmUtil.mm in Sources */,
+                               D240323F38901478EBC30FDD350A51EA /* RLMResults.mm in Sources */,
+                               504D5C61A998F589CA3907F70BEF92B1 /* RLMSchema.mm in Sources */,
+                               5FE2BF6881AFD13E4429F377BB153387 /* RLMSwiftSupport.m in Sources */,
+                               A3E489E4AD745C39082A645D9B4E51F9 /* RLMSyncConfiguration.mm in Sources */,
+                               2A484CD6CAFD0E0BF0065A806191BF10 /* RLMSyncCredentials.m in Sources */,
+                               E2EACE492AB8CF01D7B72C648D7315B0 /* RLMSyncManager.mm in Sources */,
+                               4DA0448649E8F979A300306BF59CF9C7 /* RLMSyncPermission.mm in Sources */,
+                               3EB63319904299429B2565B7D6D7F984 /* RLMSyncPermissionResults.mm in Sources */,
+                               89B5EBF2995898023F8443B2E6071559 /* RLMSyncSession.mm in Sources */,
+                               38A9418DB1E08F249F0C9FF9BF9A13FD /* RLMSyncSessionRefreshHandle.mm in Sources */,
+                               F13360DB4A30B9E2C1FEB874F4617D3A /* RLMSyncUser.mm in Sources */,
+                               D734ED9789332A91D374B481156D0DCF /* RLMSyncUtil.mm in Sources */,
+                               38A4CE8C383FDFD2649583533D64D074 /* RLMThreadSafeReference.mm in Sources */,
+                               1892CAA5A2130855409FCD85E18B6437 /* RLMUpdateChecker.mm in Sources */,
+                               86DD792B02A8F758BB5088B53C00B405 /* RLMUtil.mm in Sources */,
+                               FE8499CC84B668222CEFB2B0EB2173C1 /* schema.cpp in Sources */,
+                               D283C8EEA6FAE9D42AAE690FD8BC78E8 /* shared_realm.cpp in Sources */,
+                               0F08213563CC77B70662B3934DEA4060 /* sync_config.cpp in Sources */,
+                               7163EBADC5D6FA8EEE2348A5C5CB53D1 /* sync_file.cpp in Sources */,
+                               896BDB57CD0867624ADF5017511D5780 /* sync_manager.cpp in Sources */,
+                               0388D9D4A5313A85367043889A385D3C /* sync_metadata.cpp in Sources */,
+                               248945FF49A7072DC1EDB438BDCC5D64 /* sync_permission.cpp in Sources */,
+                               7AB46D076300E2093CB5077FF2E2ED81 /* sync_session.cpp in Sources */,
+                               65AAC1AE61E8BB8694844AC913343969 /* sync_user.cpp in Sources */,
+                               0E07CC88AC4C4B6AE45A8AA21F903232 /* system_configuration.cpp in Sources */,
+                               D2487D8620743518E0D0D2A012B33B15 /* thread_safe_reference.cpp in Sources */,
+                               00C6AAB66CB1FFC09AD31D6AA40DFE56 /* transact_log_handler.cpp in Sources */,
+                               6D1C369DD8369F361E9A2E2A123C87DE /* uuid.cpp in Sources */,
+                               1BF8F13A87CE0930A8A2F706FF894DB6 /* weak_realm_notifier.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               EF072C06A862A52DCE66E6334305B361 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               6435822D7351527ABE3F446D157348F4 /* Extensions.swift in Sources */,
+                               AD0CB2E82475505A45C0E13A0FB2628A /* FolioReaderAudioPlayer.swift in Sources */,
+                               6E2E50D2FB366F4E22FB175E61DE50B0 /* FolioReaderCenter.swift in Sources */,
+                               47524C3AFAC5EC486F2ABC0E146EF760 /* FolioReaderChapterList.swift in Sources */,
+                               1571E1BE0EED938CA88ADF8449F8A805 /* FolioReaderChapterListCell.swift in Sources */,
+                               E38B78B6CA84105DF5F8AE572177CD0D /* FolioReaderConfig.swift in Sources */,
+                               512005B87BEE5FA25BE5714A1CF6D29D /* FolioReaderContainer.swift in Sources */,
+                               4D9EFB38E1597E064355A122BB351917 /* FolioReaderFontsMenu.swift in Sources */,
+                               A38C4C09C9050193B9C36EF2E20069E0 /* FolioReaderHighlightList.swift in Sources */,
+                               79206EADC95965C170D359EAE615FF28 /* FolioReaderKit-dummy.m in Sources */,
+                               1F7FA8B0C9C9A58334B316DD63EDF5FD /* FolioReaderKit.swift in Sources */,
+                               D358E4455630F01D3214D1680ECDD20F /* FolioReaderPage.swift in Sources */,
+                               12F3CBE1723CC427D132A610539D1DC3 /* FolioReaderPageIndicator.swift in Sources */,
+                               8A30EAA74A0996419BD207F92DFD652B /* FolioReaderPlayerMenu.swift in Sources */,
+                               7D3D05CCBCF7BE685384C79A19C5D36D /* FolioReaderQuoteShare.swift in Sources */,
+                               97DEC66B2525E0B840E4796D007CF196 /* FolioReaderSharingProvider.swift in Sources */,
+                               F51562DC0E2E13ADBC885981E5F6920F /* FolioReaderUserDefaults.swift in Sources */,
+                               0F496EAE6D73FCAE5EB5C5B48751791E /* FolioReaderWebView.swift in Sources */,
+                               209B7AD282510AC70A483C033EB3CA1E /* FRBook.swift in Sources */,
+                               CD51EFD7426F2291BB5CE539312A9D45 /* FREpubParser.swift in Sources */,
+                               C7802895FAE85F99BB7C2116BE4D8DA0 /* FRMetadata.swift in Sources */,
+                               01FECC2B22E3FE698936ED47BE514888 /* FRResource.swift in Sources */,
+                               EA3377CCC9EEE507451F5024222E6377 /* FRResources.swift in Sources */,
+                               8E82FE260753B343D76FDA58389A72CD /* FRSmilElement.swift in Sources */,
+                               ACE60E955FBC54769746FA2C87C6C79C /* FRSmils.swift in Sources */,
+                               9BD83CDE9B986272ADEDC8724052287D /* FRSpine.swift in Sources */,
+                               3C75E6E5423044F2030C703BF880C506 /* FRTocReference.swift in Sources */,
+                               CDA8F5CBBFF8A44C3882A01BE169F7CB /* HADiscreteSlider.swift in Sources */,
+                               331A23585335B80070D936162CEE0537 /* Highlight+Helper.swift in Sources */,
+                               415845C60B06A5ABAA75E5C0D23158E8 /* Highlight.swift in Sources */,
+                               B90DDFC5A8728D00CDC60C2A0245A909 /* MediaType.swift in Sources */,
+                               897468434F0001FFC94F66C27E4F39D9 /* PageViewController.swift in Sources */,
+                               24FEB632227140B54BAD67E1062D6633 /* QuoteImage.swift in Sources */,
+                               C7A62A3BF5D85833A2C6FC4AF01CA9C8 /* ScrollScrubber.swift in Sources */,
+                               A19B968EFB027AF79BEA846262E6EF2C /* SMSegment.swift in Sources */,
+                               7EA1C26030A5AA6D6CA0E107EC89171D /* SMSegmentView.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F1FA147D9B7805FC419DAAA81186112F /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               15DA3AAE5DCD260807FDE95C53133FC0 /* Pods-WolneLektury-dummy.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F6117FDE3DB964BFD5C9255BAC4FB5AD /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               D1915B79A59D228E362C21F07F18D75E /* JSQWebViewController-dummy.m in Sources */,
+                               0FDA6BB0F64B7891997D6D361FC069AF /* WebViewController.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F62B8DEE079200A39859B95D2B0D6885 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E74E5BDBEE3EA42EBC71661137B43AE2 /* Aliases.swift in Sources */,
+                               139B9379F776BC05B6B819E09BE6E1D2 /* Error.swift in Sources */,
+                               94E1292824D6FC94E97E2D9C175D1C3E /* LinkingObjects.swift in Sources */,
+                               B8B48D5BA99EDC2EA08EAE87BF84A5AB /* List.swift in Sources */,
+                               B54493F39F42776CD05ECED24CD1761F /* Migration.swift in Sources */,
+                               A985909029B8F65FB62C9277C5AE8FAC /* Object.swift in Sources */,
+                               7D318B1096AB60265B397B5032AA6225 /* ObjectiveCSupport.swift in Sources */,
+                               8829CEFEC09F22535860AA11F5542425 /* ObjectSchema.swift in Sources */,
+                               DA53200FF1D2861EDDAD1BF8C253852F /* Optional.swift in Sources */,
+                               7A332BCDBB3A90B34304EB2BF8D9712B /* Property.swift in Sources */,
+                               C4AFF48A2EBFCA39EDA56971CCD1179C /* Realm.swift in Sources */,
+                               1FF438BC2ED870907E34DC89D7C6EA03 /* RealmCollection.swift in Sources */,
+                               53FC18A271CAD411EF3E1BEA44FF30EE /* RealmConfiguration.swift in Sources */,
+                               DA4875D773DC5D1CF32C56806F67F4FB /* RealmSwift-dummy.m in Sources */,
+                               D566C419398A952D6E73627A9039F46C /* Results.swift in Sources */,
+                               D71D4288624808FE415F3F69ABB7E1FA /* Schema.swift in Sources */,
+                               27319AAC3B3C7588918B3CDB17FAA981 /* SortDescriptor.swift in Sources */,
+                               45628F4ADA33649827094165E1EF6130 /* SwiftVersion.swift in Sources */,
+                               CD49C523E23DD6F03A083B4BC59D068F /* Sync.swift in Sources */,
+                               498191589F06997E6260BC116976A172 /* ThreadSafeReference.swift in Sources */,
+                               F9DD5C7C2F99839007CBFEDD8561423D /* Util.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F6EFDA8D1ED33B5160CC3F4C5CE9C0A0 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               804163B1F6F5F7B66E6AC4EAB5BAAB37 /* GoogleUtilities-dummy.m in Sources */,
+                               844063DFB5DFA2E4495E173CFFDD8419 /* GULAppDelegateSwizzler.m in Sources */,
+                               95998AD316485C5728F1B7ED02C5640C /* GULAppEnvironmentUtil.m in Sources */,
+                               84395DA299C5B7051B049D2D1A1BE8A6 /* GULLogger.m in Sources */,
+                               B6329FFFCBA250E12B34D86881D4F168 /* GULMutableDictionary.m in Sources */,
+                               57DB75CC5BF875F06FAAD1BC8BC92DE4 /* GULNetwork.m in Sources */,
+                               0E213D1CF1D10689669D27E03D17D5CA /* GULNetworkConstants.m in Sources */,
+                               77ED8AAE6516726FEBCC37BFE9FAE0A7 /* GULNetworkURLSession.m in Sources */,
+                               48D9D155785BA85911BED51563E4AF1D /* GULNSData+zlib.m in Sources */,
+                               B3F4C7A3BAEC0A616DBB463397C77A4F /* GULReachabilityChecker.m in Sources */,
+                               50B382E4D134FA9389118ABBA08386AE /* GULSwizzler.m in Sources */,
+                               DEDDEC971B9B7FB43037345DAFA68D6A /* GULUserDefaults.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+               05E8C912A3D2DD9E89B6C8BA485ADFE1 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = FontBlaster;
+                       target = A0243AC4D5FF53ED067BF1609A6898A0 /* FontBlaster */;
+                       targetProxy = F0CC6EFFDE3094DC5D73F1DA00D20FE1 /* PBXContainerItemProxy */;
+               };
+               0D1AAC295BCDA7588BBB939BD871D036 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = Kingfisher;
+                       target = A15CD3220CB655EF65975142EDBD8C6E /* Kingfisher */;
+                       targetProxy = 22D890E341A029066109E3A4DA62C0A9 /* PBXContainerItemProxy */;
+               };
+               13EA85FA565A2A3460A2C8F7D96C2C28 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = MenuItemKit;
+                       target = 673DBE5653C51962C057371B5CA8A2B4 /* MenuItemKit */;
+                       targetProxy = D936712DA2BB3899876B825056FE1F0E /* PBXContainerItemProxy */;
+               };
+               1D88A85163533378C9F286D7CEAC508B /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = MBProgressHUD;
+                       target = AD408BAB384B6DDA88A50A16FB158B66 /* MBProgressHUD */;
+                       targetProxy = 94AB6C677FF17F93C4CD3C8DA4F3D3E1 /* PBXContainerItemProxy */;
+               };
+               230BF6546D2E5FEA1D60B8CB0E9CEBC5 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = ZFDragableModalTransition;
+                       target = D449FC7BC81F775BC2E4BB53D77B64D6 /* ZFDragableModalTransition */;
+                       targetProxy = D4F3648965C515FF27BEBF3F3B70E429 /* PBXContainerItemProxy */;
+               };
+               29A5AC78BEB55771809956141CF697B0 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = FolioReaderKit;
+                       target = 1EB42C6E589197DB247315875A9AE141 /* FolioReaderKit */;
+                       targetProxy = C09F454687D19EF80188E947AA045D53 /* PBXContainerItemProxy */;
+               };
+               30ED4DEBFB7BEE33D9F6999D486AABB6 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = FirebaseMessaging;
+                       target = 0DD17D85AC06742489BCAFB83E51FDB0 /* FirebaseMessaging */;
+                       targetProxy = EC6740D2628F020744661F197771AF80 /* PBXContainerItemProxy */;
+               };
+               36BEB60199135BBC6388CDC1A0BD32A8 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = Alamofire;
+                       target = E76458C58C9140B6A16D60547E68E80C /* Alamofire */;
+                       targetProxy = 4F6AC847EF22FC528E647D4C456AA3E1 /* PBXContainerItemProxy */;
+               };
+               3BC83CA1CADCFC5FA925F1E4A646A87C /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = MenuItemKit;
+                       target = 673DBE5653C51962C057371B5CA8A2B4 /* MenuItemKit */;
+                       targetProxy = 42DAA0DF2C3A059786C66B4FD98F10DE /* PBXContainerItemProxy */;
+               };
+               4759C5699CED82DB77EE538FBE2B6123 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = FirebaseCore;
+                       target = 4338000AC6DBAF783F338F055DDE55C6 /* FirebaseCore */;
+                       targetProxy = 8FF4D201921D8BB020C76ED5C65EB196 /* PBXContainerItemProxy */;
+               };
+               4EAE125B5AE700C85CF58D939AF16CB4 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = FirebaseCore;
+                       target = 4338000AC6DBAF783F338F055DDE55C6 /* FirebaseCore */;
+                       targetProxy = 35A78108E1664F82A14159857CFF0FCA /* PBXContainerItemProxy */;
+               };
+               5A505E64A620E3EFF81065BDCF8AAB73 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = GoogleUtilities;
+                       target = 2B2CE33B441C5D4BD9A5A04D3087E5C8 /* GoogleUtilities */;
+                       targetProxy = D4C953138F7589A9A33210FD8AB5A2C9 /* PBXContainerItemProxy */;
+               };
+               655CEC8339B1B760D96006B4675DE6A8 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = SideMenu;
+                       target = C8C2C570FCCD7CC9014E895FBBEEF1FC /* SideMenu */;
+                       targetProxy = DD198968AE4E005ED8EA9E9601356F3B /* PBXContainerItemProxy */;
+               };
+               704B9E6981A97352C92218F574B93397 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = SwiftKeychainWrapper;
+                       target = 8CD09A1EC484C4C08A7FBF85CA871914 /* SwiftKeychainWrapper */;
+                       targetProxy = 91CB6F1E706029F6E6FEA6183E1C4550 /* PBXContainerItemProxy */;
+               };
+               790D04DA66ED8977E75C8AE176C8F41A /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = MatomoTracker;
+                       target = A938E07DF5A500E5C4FA1C9FD1886EB2 /* MatomoTracker */;
+                       targetProxy = 53BF719B7B3E5EE951AB21D67921A2C5 /* PBXContainerItemProxy */;
+               };
+               85536FB74B0D5A69875D4E1B46A8FF83 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = Protobuf;
+                       target = 71E4E70C7BAC6389783D3A3C8EB1F910 /* Protobuf */;
+                       targetProxy = C9CEA78207D0D29B7DD3FA50EF6E85E2 /* PBXContainerItemProxy */;
+               };
+               93D3DB0B4BA00A9962D9D622B45796BB /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = AlamofireActivityLogger;
+                       target = 8D956160DB7545834B961CB5E8DBBD1B /* AlamofireActivityLogger */;
+                       targetProxy = FB569D0C72CF34C76C7A57CDF99D4C5F /* PBXContainerItemProxy */;
+               };
+               95340C80680036946254C7C7E42B598C /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = AEXML;
+                       target = 939DA4FAAB94FC5EE0B3FCF4BAA2C49E /* AEXML */;
+                       targetProxy = B62A90D776EFF517673A4D36F5B715EA /* PBXContainerItemProxy */;
+               };
+               9D50E295CD62AF8C48A4E382EDEC6446 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = Protobuf;
+                       target = 71E4E70C7BAC6389783D3A3C8EB1F910 /* Protobuf */;
+                       targetProxy = E3928A04DE3CDEE92EAA19264BE55509 /* PBXContainerItemProxy */;
+               };
+               A03801ADAA51A686C6058350B1D7EB20 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = SSZipArchive;
+                       target = CE310247D34682517FA45BE1C20210CF /* SSZipArchive */;
+                       targetProxy = 5BD2C6D6D4FA5ACC09607C98CE853C43 /* PBXContainerItemProxy */;
+               };
+               AD4C50889E1C6C3D0FB3E6E2F7457A46 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = Realm;
+                       target = 13218BC4FEB9FA3C157F802AB364DCBB /* Realm */;
+                       targetProxy = C26CF6553971B4CD56C7A64AE9D460D6 /* PBXContainerItemProxy */;
+               };
+               AEF97EBDF12605C84C89D13ADB1EBFB7 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = SSZipArchive;
+                       target = CE310247D34682517FA45BE1C20210CF /* SSZipArchive */;
+                       targetProxy = C2506ECA219D0DA3E086096D720CAC9B /* PBXContainerItemProxy */;
+               };
+               B45422F8B2240C086CD9B51D0D4EAA7A /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = OAuthSwift;
+                       target = EF83FCC10E7453ACCB801B656501E2F6 /* OAuthSwift */;
+                       targetProxy = C8D096F97B29ABA3CEDE27707C64A8C2 /* PBXContainerItemProxy */;
+               };
+               B6E23311344A6575175D6AF3BE8A653C /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = JSQWebViewController;
+                       target = 87C645C5CBE6078EBEE1659ABEEF780D /* JSQWebViewController */;
+                       targetProxy = 0B89BA46392931BB20153DE364AE349D /* PBXContainerItemProxy */;
+               };
+               BA279092D2FD2DC13E98868BC6A4CD33 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = RealmSwift;
+                       target = 76948DED43ECFC74BB41AA7F11DA2200 /* RealmSwift */;
+                       targetProxy = 2476160EA5824AAAFA83863389371F85 /* PBXContainerItemProxy */;
+               };
+               BBB1D2B2B8257B1844F799BBA56E71F3 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = AEXML;
+                       target = 939DA4FAAB94FC5EE0B3FCF4BAA2C49E /* AEXML */;
+                       targetProxy = F9B3EE8B7D9C78A8BD9C8D385A77BC2A /* PBXContainerItemProxy */;
+               };
+               C14260A38E262542BAEEE29A8F11B645 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = FontBlaster;
+                       target = A0243AC4D5FF53ED067BF1609A6898A0 /* FontBlaster */;
+                       targetProxy = 6FC94F22C84D5CC00531C6AD243FA2A0 /* PBXContainerItemProxy */;
+               };
+               C4ACD2909716BA74462C4005A98E3B67 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = "Toast-Swift";
+                       target = E6D2D23AF894E245CA516BC67EBF8703 /* Toast-Swift */;
+                       targetProxy = 311F8D431BE2BC22FB49F1A2BAFC0C39 /* PBXContainerItemProxy */;
+               };
+               C635411A43743CCC71A0834186507970 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = nanopb;
+                       target = 8364BB258018143B83798DE31C13546D /* nanopb */;
+                       targetProxy = BF529E2F449B4C20A7581E433B6448CD /* PBXContainerItemProxy */;
+               };
+               D277072FA14C2EF800819D9A52B74FD7 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = Realm;
+                       target = 13218BC4FEB9FA3C157F802AB364DCBB /* Realm */;
+                       targetProxy = 1D71CB61901BB7470E1880F4F547C2DC /* PBXContainerItemProxy */;
+               };
+               D85BB41B0FEBEEA0C9DBB2B45646869A /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = JSQWebViewController;
+                       target = 87C645C5CBE6078EBEE1659ABEEF780D /* JSQWebViewController */;
+                       targetProxy = 727B317CFE7E8D41021C5F5A9E47190D /* PBXContainerItemProxy */;
+               };
+               DD920B2852EB1D88B9332BAF2BA08A3E /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = RealmSwift;
+                       target = 76948DED43ECFC74BB41AA7F11DA2200 /* RealmSwift */;
+                       targetProxy = 4F07FC1E60C35CAFAF5F6BE7A3414EAF /* PBXContainerItemProxy */;
+               };
+               ED4604285DA69B5BBA943344A5B988F0 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = GoogleUtilities;
+                       target = 2B2CE33B441C5D4BD9A5A04D3087E5C8 /* GoogleUtilities */;
+                       targetProxy = E23933E264AC64CFAB19F86C52879EE3 /* PBXContainerItemProxy */;
+               };
+               F0FEA998FF4EBDA7A28E3E40400912E0 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = ZFDragableModalTransition;
+                       target = D449FC7BC81F775BC2E4BB53D77B64D6 /* ZFDragableModalTransition */;
+                       targetProxy = 812D50BE3323CD5C876E5FA6665E9005 /* PBXContainerItemProxy */;
+               };
+               F4CBCA49898E0F03ADC80CB4A6EE4D00 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = GoogleUtilities;
+                       target = 2B2CE33B441C5D4BD9A5A04D3087E5C8 /* GoogleUtilities */;
+                       targetProxy = 05E26F3DCAECB2BD02C47C15ECC3E1FC /* PBXContainerItemProxy */;
+               };
+               F6F11FDDCFA767EE39B5A314AE2CBA2C /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = Alamofire;
+                       target = E76458C58C9140B6A16D60547E68E80C /* Alamofire */;
+                       targetProxy = B3D4DAC5C40401E39DB4AC6895B267ED /* PBXContainerItemProxy */;
+               };
+               FA560D1DFE9DA58EA9E45BC0EC4CA94E /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = MZDownloadManager;
+                       target = DEB0D4FCD7FECFEDBCB372932FCB07CB /* MZDownloadManager */;
+                       targetProxy = 02316B1E1E26FD5C54880A8C8B0FF05D /* PBXContainerItemProxy */;
+               };
+               FBF90A9BF5383BB7222F33417F2C773F /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       name = Realm;
+                       target = 13218BC4FEB9FA3C157F802AB364DCBB /* Realm */;
+                       targetProxy = 7B48C4FE112E5187B0430861D683250E /* PBXContainerItemProxy */;
+               };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+               0EBFB0879D79F16531E44FEFDCDFE0EF /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 405DF96346BB3ABC89A8AEEF6B9B995F /* RealmSwift.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/RealmSwift/RealmSwift-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/RealmSwift/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/RealmSwift/RealmSwift.modulemap";
+                               PRODUCT_MODULE_NAME = RealmSwift;
+                               PRODUCT_NAME = RealmSwift;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               11501F35A84F5D7DD09552223A70AEB4 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = DCD69015A6C555B93460E69425DB1406 /* Alamofire.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/Alamofire/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap";
+                               PRODUCT_MODULE_NAME = Alamofire;
+                               PRODUCT_NAME = Alamofire;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               1986CCC4AE22B7FC7FAAE31F2408C00F /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 17476BC599C0EE4947789CF3EC5E1024 /* AlamofireActivityLogger.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/AlamofireActivityLogger/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger.modulemap";
+                               PRODUCT_MODULE_NAME = AlamofireActivityLogger;
+                               PRODUCT_NAME = AlamofireActivityLogger;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               203A739710B86E21844BD64EA7C6EFB7 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 06967CA663F790072A7AFB661576619B /* JSQWebViewController.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/JSQWebViewController/JSQWebViewController-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/JSQWebViewController/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/JSQWebViewController/JSQWebViewController.modulemap";
+                               PRODUCT_MODULE_NAME = JSQWebViewController;
+                               PRODUCT_NAME = JSQWebViewController;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               22342032799A61A89B15BB1157F67DD0 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 80A169221157FA4EC5B1B57B2E943A32 /* Toast-Swift.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/Toast-Swift/Toast-Swift-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/Toast-Swift/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/Toast-Swift/Toast-Swift.modulemap";
+                               PRODUCT_MODULE_NAME = Toast_Swift;
+                               PRODUCT_NAME = Toast_Swift;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               273F54E61CA289F5B77584A1E79932FB /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 9A52F85BB96ED2C9CB95229AEA2579A0 /* MZDownloadManager.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/MZDownloadManager/MZDownloadManager-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/MZDownloadManager/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/MZDownloadManager/MZDownloadManager.modulemap";
+                               PRODUCT_MODULE_NAME = MZDownloadManager;
+                               PRODUCT_NAME = MZDownloadManager;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               28458E017F205A5013A1FFCEF62127D8 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 191B12415ABB5A507EEBB049752E30AD /* FontBlaster.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/FontBlaster/FontBlaster-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/FontBlaster/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/FontBlaster/FontBlaster.modulemap";
+                               PRODUCT_MODULE_NAME = FontBlaster;
+                               PRODUCT_NAME = FontBlaster;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               2A3DF4A289C2B70883EF96352CD7BC9D /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = F4D421C457DF7CD3454C132F83B10591 /* Realm.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/Realm/Realm-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/Realm/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/Realm/Realm.modulemap";
+                               PRODUCT_MODULE_NAME = Realm;
+                               PRODUCT_NAME = Realm;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               2A6EA5685FC1E0B318BACE65F2818F73 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 80A169221157FA4EC5B1B57B2E943A32 /* Toast-Swift.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/Toast-Swift/Toast-Swift-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/Toast-Swift/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/Toast-Swift/Toast-Swift.modulemap";
+                               PRODUCT_MODULE_NAME = Toast_Swift;
+                               PRODUCT_NAME = Toast_Swift;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               2E08C644A8BC9EDF0BF126A75DC355EF /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 17476BC599C0EE4947789CF3EC5E1024 /* AlamofireActivityLogger.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/AlamofireActivityLogger/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger.modulemap";
+                               PRODUCT_MODULE_NAME = AlamofireActivityLogger;
+                               PRODUCT_NAME = AlamofireActivityLogger;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               3523B454DD03CCC5F2E00F021E28D275 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 03DF232DE3B9FC2B3ABA654016B16028 /* FirebaseCore.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MACH_O_TYPE = staticlib;
+                               MODULEMAP_FILE = "Target Support Files/FirebaseCore/FirebaseCore.modulemap";
+                               PRODUCT_MODULE_NAME = FirebaseCore;
+                               PRODUCT_NAME = FirebaseCore;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               352903499A324D190BD5FE5621D96CD0 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = F31594F98BBECEB6EC59E4DA97410A6D /* OAuthSwift.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/OAuthSwift/OAuthSwift-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/OAuthSwift/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/OAuthSwift/OAuthSwift.modulemap";
+                               PRODUCT_MODULE_NAME = OAuthSwift;
+                               PRODUCT_NAME = OAuthSwift;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               453A910A0E19F83C731076B8DB7A40BD /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 43CDCABD9284457D43B646E90B874944 /* MBProgressHUD.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/MBProgressHUD/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/MBProgressHUD/MBProgressHUD.modulemap";
+                               PRODUCT_MODULE_NAME = MBProgressHUD;
+                               PRODUCT_NAME = MBProgressHUD;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               55767C589D9D8404082662D6E60025E4 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 6A43C662AC0818E1DA41050DA811BA97 /* SwiftKeychainWrapper.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/SwiftKeychainWrapper/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper.modulemap";
+                               PRODUCT_MODULE_NAME = SwiftKeychainWrapper;
+                               PRODUCT_NAME = SwiftKeychainWrapper;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               563F12B9A730E695B83D11D7F26A2DD9 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 6A43C662AC0818E1DA41050DA811BA97 /* SwiftKeychainWrapper.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/SwiftKeychainWrapper/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper.modulemap";
+                               PRODUCT_MODULE_NAME = SwiftKeychainWrapper;
+                               PRODUCT_NAME = SwiftKeychainWrapper;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               567ABB49FAE1ED1B97F4672E3BA7AE15 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 9D4BD4F234C5733679D2D1FAC1B99FE4 /* Pods-WolneLektury.release.xcconfig */;
+                       buildSettings = {
+                               ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               INFOPLIST_FILE = "Target Support Files/Pods-WolneLektury/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MACH_O_TYPE = staticlib;
+                               MODULEMAP_FILE = "Target Support Files/Pods-WolneLektury/Pods-WolneLektury.modulemap";
+                               OTHER_LDFLAGS = "";
+                               OTHER_LIBTOOLFLAGS = "";
+                               PODS_ROOT = "$(SRCROOT)";
+                               PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
+                               PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               586BF1FE0F35274F30CD2F350789187C /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 867A9A2B86FF919DE6428765E5138E5E /* FolioReaderKit.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/FolioReaderKit/FolioReaderKit-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/FolioReaderKit/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/FolioReaderKit/FolioReaderKit.modulemap";
+                               PRODUCT_MODULE_NAME = FolioReaderKit;
+                               PRODUCT_NAME = FolioReaderKit;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               59C35B02D17F4DE4B43A276F4FE9CB3C /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 9A52F85BB96ED2C9CB95229AEA2579A0 /* MZDownloadManager.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/MZDownloadManager/MZDownloadManager-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/MZDownloadManager/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/MZDownloadManager/MZDownloadManager.modulemap";
+                               PRODUCT_MODULE_NAME = MZDownloadManager;
+                               PRODUCT_NAME = MZDownloadManager;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               59DA22CE56B4898E5DF0045AA81A5DBB /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 191B12415ABB5A507EEBB049752E30AD /* FontBlaster.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/FontBlaster/FontBlaster-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/FontBlaster/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/FontBlaster/FontBlaster.modulemap";
+                               PRODUCT_MODULE_NAME = FontBlaster;
+                               PRODUCT_NAME = FontBlaster;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               5F96F31568E048DAEB7BEDF740F9DC02 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = D795F064F1050FA6050617BB0936FBD1 /* Protobuf.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/Protobuf/Protobuf-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/Protobuf/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/Protobuf/Protobuf.modulemap";
+                               PRODUCT_MODULE_NAME = Protobuf;
+                               PRODUCT_NAME = Protobuf;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               60DAF49CA7A9F362148D49C3C3123B2A /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CODE_SIGNING_ALLOWED = NO;
+                               CODE_SIGNING_REQUIRED = NO;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               ENABLE_TESTABILITY = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "POD_CONFIGURATION_DEBUG=1",
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+                               MTL_FAST_MATH = YES;
+                               ONLY_ACTIVE_ARCH = YES;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               STRIP_INSTALLED_PRODUCT = NO;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+                               SYMROOT = "${SRCROOT}/../build";
+                       };
+                       name = Debug;
+               };
+               67C31257BD2956F928E46AD198EEFEE2 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 439ED54E64F4037372B80993935BB837 /* AEXML.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/AEXML/AEXML-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/AEXML/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/AEXML/AEXML.modulemap";
+                               PRODUCT_MODULE_NAME = AEXML;
+                               PRODUCT_NAME = AEXML;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               683BCE35374D48D4410401EFD71A8165 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 832216D1A72BB1036524B4C72D8DD3AD /* Kingfisher.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/Kingfisher/Kingfisher-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/Kingfisher/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/Kingfisher/Kingfisher.modulemap";
+                               PRODUCT_MODULE_NAME = Kingfisher;
+                               PRODUCT_NAME = Kingfisher;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               6877FCE7F4E34509CF340272E22E9209 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = D795F064F1050FA6050617BB0936FBD1 /* Protobuf.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/Protobuf/Protobuf-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/Protobuf/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/Protobuf/Protobuf.modulemap";
+                               PRODUCT_MODULE_NAME = Protobuf;
+                               PRODUCT_NAME = Protobuf;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               7992D0D34ABA312C30B7159FCE588721 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = B056E5B697BF12D5A943B914A8944339 /* MatomoTracker.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/MatomoTracker/MatomoTracker-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/MatomoTracker/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/MatomoTracker/MatomoTracker.modulemap";
+                               PRODUCT_MODULE_NAME = MatomoTracker;
+                               PRODUCT_NAME = MatomoTracker;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               8067E87092D8DF53A219C9356121678F /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 867A9A2B86FF919DE6428765E5138E5E /* FolioReaderKit.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/FolioReaderKit/FolioReaderKit-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/FolioReaderKit/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/FolioReaderKit/FolioReaderKit.modulemap";
+                               PRODUCT_MODULE_NAME = FolioReaderKit;
+                               PRODUCT_NAME = FolioReaderKit;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               80FCA0767E93DDA94260B939B486908E /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 1AEFAE3B5379808E09A395D84018890C /* MenuItemKit.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/MenuItemKit/MenuItemKit-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/MenuItemKit/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/MenuItemKit/MenuItemKit.modulemap";
+                               PRODUCT_MODULE_NAME = MenuItemKit;
+                               PRODUCT_NAME = MenuItemKit;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               87DAF51EA4C922EF4000F5C04F7D38DA /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 52C57B4C09979E16295F7D9EF93E8588 /* FirebaseMessaging.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MACH_O_TYPE = staticlib;
+                               MODULEMAP_FILE = "Target Support Files/FirebaseMessaging/FirebaseMessaging.modulemap";
+                               PRODUCT_MODULE_NAME = FirebaseMessaging;
+                               PRODUCT_NAME = FirebaseMessaging;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               8992D63DE98B74A2C62854B72DFDFB4B /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 1AEFAE3B5379808E09A395D84018890C /* MenuItemKit.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/MenuItemKit/MenuItemKit-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/MenuItemKit/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/MenuItemKit/MenuItemKit.modulemap";
+                               PRODUCT_MODULE_NAME = MenuItemKit;
+                               PRODUCT_NAME = MenuItemKit;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               955C2097ADB7DCCC7AC8064301C148EB /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = B51E1C8BEE26A23D7600FD8BD5C9FCF2 /* SideMenu.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/SideMenu/SideMenu-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/SideMenu/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/SideMenu/SideMenu.modulemap";
+                               PRODUCT_MODULE_NAME = SideMenu;
+                               PRODUCT_NAME = SideMenu;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               987606D51C2E01D22590FA35D963FFA1 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = C5ECEF2B1DFCF0C01450C80D3BD0610F /* nanopb.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/nanopb/nanopb-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/nanopb/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/nanopb/nanopb.modulemap";
+                               PRODUCT_MODULE_NAME = nanopb;
+                               PRODUCT_NAME = nanopb;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               9F3C3C371DA6B3F2F0A2EC0A08B424BE /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = F9CDED3C6CB31F72B4C9F0B29CB4D813 /* ZFDragableModalTransition.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/ZFDragableModalTransition/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition.modulemap";
+                               PRODUCT_MODULE_NAME = ZFDragableModalTransition;
+                               PRODUCT_NAME = ZFDragableModalTransition;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               A2847F68B361DEAF27E9C1559BE66920 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = F4D421C457DF7CD3454C132F83B10591 /* Realm.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/Realm/Realm-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/Realm/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/Realm/Realm.modulemap";
+                               PRODUCT_MODULE_NAME = Realm;
+                               PRODUCT_NAME = Realm;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               A88634A4B0D0F57EE16CB8586992C9C8 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 8E53544433E2DD770130AA1E2E2DCF4D /* SSZipArchive.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/SSZipArchive/SSZipArchive-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/SSZipArchive/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/SSZipArchive/SSZipArchive.modulemap";
+                               PRODUCT_MODULE_NAME = SSZipArchive;
+                               PRODUCT_NAME = SSZipArchive;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               B1D64254EC31C74D63088510946CA778 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 52C57B4C09979E16295F7D9EF93E8588 /* FirebaseMessaging.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MACH_O_TYPE = staticlib;
+                               MODULEMAP_FILE = "Target Support Files/FirebaseMessaging/FirebaseMessaging.modulemap";
+                               PRODUCT_MODULE_NAME = FirebaseMessaging;
+                               PRODUCT_NAME = FirebaseMessaging;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               B4C602418AEB2E488343D6CDC09EE198 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 03DF232DE3B9FC2B3ABA654016B16028 /* FirebaseCore.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MACH_O_TYPE = staticlib;
+                               MODULEMAP_FILE = "Target Support Files/FirebaseCore/FirebaseCore.modulemap";
+                               PRODUCT_MODULE_NAME = FirebaseCore;
+                               PRODUCT_NAME = FirebaseCore;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               B752333354631ED67CA4EA7997964D2D /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 832216D1A72BB1036524B4C72D8DD3AD /* Kingfisher.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/Kingfisher/Kingfisher-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/Kingfisher/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/Kingfisher/Kingfisher.modulemap";
+                               PRODUCT_MODULE_NAME = Kingfisher;
+                               PRODUCT_NAME = Kingfisher;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               BC26ACE7A8E469B7CB4DF93AB98ED9BA /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = DCD69015A6C555B93460E69425DB1406 /* Alamofire.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/Alamofire/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap";
+                               PRODUCT_MODULE_NAME = Alamofire;
+                               PRODUCT_NAME = Alamofire;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               BC80898460D83F0275E426ACA0EFBD71 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 06967CA663F790072A7AFB661576619B /* JSQWebViewController.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/JSQWebViewController/JSQWebViewController-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/JSQWebViewController/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/JSQWebViewController/JSQWebViewController.modulemap";
+                               PRODUCT_MODULE_NAME = JSQWebViewController;
+                               PRODUCT_NAME = JSQWebViewController;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               BCDE469473EA1A46255724FAC3763C7B /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = B056E5B697BF12D5A943B914A8944339 /* MatomoTracker.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/MatomoTracker/MatomoTracker-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/MatomoTracker/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/MatomoTracker/MatomoTracker.modulemap";
+                               PRODUCT_MODULE_NAME = MatomoTracker;
+                               PRODUCT_NAME = MatomoTracker;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               C4EAA84F44D044E108500A81C635F21E /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CODE_SIGNING_ALLOWED = NO;
+                               CODE_SIGNING_REQUIRED = NO;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               ENABLE_NS_ASSERTIONS = NO;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "POD_CONFIGURATION_RELEASE=1",
+                                       "$(inherited)",
+                               );
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               MTL_FAST_MATH = YES;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               STRIP_INSTALLED_PRODUCT = NO;
+                               SYMROOT = "${SRCROOT}/../build";
+                       };
+                       name = Release;
+               };
+               C5883D877038776EEC2AC7C4C10684F9 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = F9CDED3C6CB31F72B4C9F0B29CB4D813 /* ZFDragableModalTransition.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/ZFDragableModalTransition/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition.modulemap";
+                               PRODUCT_MODULE_NAME = ZFDragableModalTransition;
+                               PRODUCT_NAME = ZFDragableModalTransition;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               CC867DBDFC74961695191C520C6B3571 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 405DF96346BB3ABC89A8AEEF6B9B995F /* RealmSwift.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/RealmSwift/RealmSwift-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/RealmSwift/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/RealmSwift/RealmSwift.modulemap";
+                               PRODUCT_MODULE_NAME = RealmSwift;
+                               PRODUCT_NAME = RealmSwift;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               D345B6805500CA090D0E6950EDA8CA87 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 43CDCABD9284457D43B646E90B874944 /* MBProgressHUD.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/MBProgressHUD/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/MBProgressHUD/MBProgressHUD.modulemap";
+                               PRODUCT_MODULE_NAME = MBProgressHUD;
+                               PRODUCT_NAME = MBProgressHUD;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               D6DB38AFC112B8F8D6021593D6FF8474 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 8E53544433E2DD770130AA1E2E2DCF4D /* SSZipArchive.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/SSZipArchive/SSZipArchive-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/SSZipArchive/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/SSZipArchive/SSZipArchive.modulemap";
+                               PRODUCT_MODULE_NAME = SSZipArchive;
+                               PRODUCT_NAME = SSZipArchive;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               D82CA8A25D8667A1399ECDE41A9DF5D2 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 518860BA42EF7435BFDD67CC13858D49 /* GoogleUtilities.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/GoogleUtilities/GoogleUtilities-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/GoogleUtilities/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/GoogleUtilities/GoogleUtilities.modulemap";
+                               PRODUCT_MODULE_NAME = GoogleUtilities;
+                               PRODUCT_NAME = GoogleUtilities;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               DD074CE3F4FE83CFA7A6122CADDCB478 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = A24FF4F622BC306700C4E27CB55217B9 /* Pods-WolneLektury.debug.xcconfig */;
+                       buildSettings = {
+                               ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               INFOPLIST_FILE = "Target Support Files/Pods-WolneLektury/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MACH_O_TYPE = staticlib;
+                               MODULEMAP_FILE = "Target Support Files/Pods-WolneLektury/Pods-WolneLektury.modulemap";
+                               OTHER_LDFLAGS = "";
+                               OTHER_LIBTOOLFLAGS = "";
+                               PODS_ROOT = "$(SRCROOT)";
+                               PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
+                               PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               E135A628B6844248845A6E97DA1DEFD9 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = C5ECEF2B1DFCF0C01450C80D3BD0610F /* nanopb.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/nanopb/nanopb-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/nanopb/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/nanopb/nanopb.modulemap";
+                               PRODUCT_MODULE_NAME = nanopb;
+                               PRODUCT_NAME = nanopb;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               E29E5A772FD1E040ED5D147449999DC6 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = B51E1C8BEE26A23D7600FD8BD5C9FCF2 /* SideMenu.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/SideMenu/SideMenu-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/SideMenu/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/SideMenu/SideMenu.modulemap";
+                               PRODUCT_MODULE_NAME = SideMenu;
+                               PRODUCT_NAME = SideMenu;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Debug;
+               };
+               EA75B16BCDCD7C7851F532B50B01FFEE /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 439ED54E64F4037372B80993935BB837 /* AEXML.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/AEXML/AEXML-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/AEXML/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/AEXML/AEXML.modulemap";
+                               PRODUCT_MODULE_NAME = AEXML;
+                               PRODUCT_NAME = AEXML;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               F4A1518ACE5235985496D2CBB4069E47 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = F31594F98BBECEB6EC59E4DA97410A6D /* OAuthSwift.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/OAuthSwift/OAuthSwift-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/OAuthSwift/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/OAuthSwift/OAuthSwift.modulemap";
+                               PRODUCT_MODULE_NAME = OAuthSwift;
+                               PRODUCT_NAME = OAuthSwift;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_COMPILATION_MODE = wholemodule;
+                               SWIFT_OPTIMIZATION_LEVEL = "-O";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+               FE550E46287127BBD0110C84FBC5F27A /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 518860BA42EF7435BFDD67CC13858D49 /* GoogleUtilities.xcconfig */;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEFINES_MODULE = YES;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               DYLIB_INSTALL_NAME_BASE = "@rpath";
+                               GCC_PREFIX_HEADER = "Target Support Files/GoogleUtilities/GoogleUtilities-prefix.pch";
+                               INFOPLIST_FILE = "Target Support Files/GoogleUtilities/Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               MODULEMAP_FILE = "Target Support Files/GoogleUtilities/GoogleUtilities.modulemap";
+                               PRODUCT_MODULE_NAME = GoogleUtilities;
+                               PRODUCT_NAME = GoogleUtilities;
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_PREFIX = "";
+                       };
+                       name = Release;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               055CB86039F87F17BFAACCE5B1899A4E /* Build configuration list for PBXNativeTarget "Kingfisher" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               683BCE35374D48D4410401EFD71A8165 /* Debug */,
+                               B752333354631ED67CA4EA7997964D2D /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               0828BAB04BC354092C84127A22839A0F /* Build configuration list for PBXNativeTarget "ZFDragableModalTransition" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               9F3C3C371DA6B3F2F0A2EC0A08B424BE /* Debug */,
+                               C5883D877038776EEC2AC7C4C10684F9 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               09600571F63852ACA3B7A23B4729B6A4 /* Build configuration list for PBXNativeTarget "MBProgressHUD" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               D345B6805500CA090D0E6950EDA8CA87 /* Debug */,
+                               453A910A0E19F83C731076B8DB7A40BD /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               0CCA827EBDB82C7A989668BE9C2F8F50 /* Build configuration list for PBXNativeTarget "MatomoTracker" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               7992D0D34ABA312C30B7159FCE588721 /* Debug */,
+                               BCDE469473EA1A46255724FAC3763C7B /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               125B2F3819124660E3BCAB534FBD9E40 /* Build configuration list for PBXNativeTarget "JSQWebViewController" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               BC80898460D83F0275E426ACA0EFBD71 /* Debug */,
+                               203A739710B86E21844BD64EA7C6EFB7 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               135EDC2D971C105FAC0ED65476D53BAB /* Build configuration list for PBXNativeTarget "Protobuf" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               6877FCE7F4E34509CF340272E22E9209 /* Debug */,
+                               5F96F31568E048DAEB7BEDF740F9DC02 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               29AA372A358AB7E30BD4693EAC1853C2 /* Build configuration list for PBXNativeTarget "Toast-Swift" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               2A6EA5685FC1E0B318BACE65F2818F73 /* Debug */,
+                               22342032799A61A89B15BB1157F67DD0 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               60DAF49CA7A9F362148D49C3C3123B2A /* Debug */,
+                               C4EAA84F44D044E108500A81C635F21E /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               2DC105300897278DD37A125F248C400D /* Build configuration list for PBXNativeTarget "SwiftKeychainWrapper" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               55767C589D9D8404082662D6E60025E4 /* Debug */,
+                               563F12B9A730E695B83D11D7F26A2DD9 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               2F7A501F7E043A1DD65393B88DD0C6ED /* Build configuration list for PBXNativeTarget "MZDownloadManager" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               59C35B02D17F4DE4B43A276F4FE9CB3C /* Debug */,
+                               273F54E61CA289F5B77584A1E79932FB /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               3318BC48F0DB1BD1B63799853383A071 /* Build configuration list for PBXNativeTarget "SSZipArchive" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               D6DB38AFC112B8F8D6021593D6FF8474 /* Debug */,
+                               A88634A4B0D0F57EE16CB8586992C9C8 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               427F0F003A1AD80AE00155AFCDEFAC20 /* Build configuration list for PBXNativeTarget "Alamofire" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               BC26ACE7A8E469B7CB4DF93AB98ED9BA /* Debug */,
+                               11501F35A84F5D7DD09552223A70AEB4 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               52D0A75FD018888C202CB17A7DDCB759 /* Build configuration list for PBXNativeTarget "FirebaseMessaging" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               87DAF51EA4C922EF4000F5C04F7D38DA /* Debug */,
+                               B1D64254EC31C74D63088510946CA778 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               683F14EAC00E28302AEBA2CE1D3181B5 /* Build configuration list for PBXNativeTarget "FolioReaderKit" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               8067E87092D8DF53A219C9356121678F /* Debug */,
+                               586BF1FE0F35274F30CD2F350789187C /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               7CEC6540142BEEDF23E01837DABB4085 /* Build configuration list for PBXNativeTarget "Realm" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               2A3DF4A289C2B70883EF96352CD7BC9D /* Debug */,
+                               A2847F68B361DEAF27E9C1559BE66920 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               8F4C2F96551CD65DFE781EE31CC581FF /* Build configuration list for PBXNativeTarget "SideMenu" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               E29E5A772FD1E040ED5D147449999DC6 /* Debug */,
+                               955C2097ADB7DCCC7AC8064301C148EB /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               9291CA90867C59FC17ADC42368C0F58F /* Build configuration list for PBXNativeTarget "MenuItemKit" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               8992D63DE98B74A2C62854B72DFDFB4B /* Debug */,
+                               80FCA0767E93DDA94260B939B486908E /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               980C96B3FE48496D06991607F09EE4D8 /* Build configuration list for PBXNativeTarget "FirebaseCore" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               B4C602418AEB2E488343D6CDC09EE198 /* Debug */,
+                               3523B454DD03CCC5F2E00F021E28D275 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               A0C06C9BF416D71CB5103EF8C8729E7A /* Build configuration list for PBXNativeTarget "OAuthSwift" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               352903499A324D190BD5FE5621D96CD0 /* Debug */,
+                               F4A1518ACE5235985496D2CBB4069E47 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               A54B95AC2E23BB0947B35EFE5E1F0EFB /* Build configuration list for PBXNativeTarget "nanopb" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               987606D51C2E01D22590FA35D963FFA1 /* Debug */,
+                               E135A628B6844248845A6E97DA1DEFD9 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               C2CD5E9A2839EB096268BCDFFC32D888 /* Build configuration list for PBXNativeTarget "AlamofireActivityLogger" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               1986CCC4AE22B7FC7FAAE31F2408C00F /* Debug */,
+                               2E08C644A8BC9EDF0BF126A75DC355EF /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               C93264544A481CEA664A3A1345E9FF50 /* Build configuration list for PBXNativeTarget "FontBlaster" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               28458E017F205A5013A1FFCEF62127D8 /* Debug */,
+                               59DA22CE56B4898E5DF0045AA81A5DBB /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               C9CCC065D8E0D1198BDE2EF35A3A8A06 /* Build configuration list for PBXNativeTarget "RealmSwift" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               CC867DBDFC74961695191C520C6B3571 /* Debug */,
+                               0EBFB0879D79F16531E44FEFDCDFE0EF /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               CFAECCEC9BA5434011916F290A0EDFFC /* Build configuration list for PBXNativeTarget "Pods-WolneLektury" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               DD074CE3F4FE83CFA7A6122CADDCB478 /* Debug */,
+                               567ABB49FAE1ED1B97F4672E3BA7AE15 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               E35E6AA414D91179E03510FF66D5F9E4 /* Build configuration list for PBXNativeTarget "AEXML" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               67C31257BD2956F928E46AD198EEFEE2 /* Debug */,
+                               EA75B16BCDCD7C7851F532B50B01FFEE /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               EE760FDAF0752E4632E961732998C442 /* Build configuration list for PBXNativeTarget "GoogleUtilities" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               D82CA8A25D8667A1399ECDE41A9DF5D2 /* Debug */,
+                               FE550E46287127BBD0110C84FBC5F27A /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+}
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/AEXML.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/AEXML.xcscheme
new file mode 100644 (file)
index 0000000..205452a
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "939DA4FAAB94FC5EE0B3FCF4BAA2C49E"
+               BlueprintName = "AEXML"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "AEXML.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Alamofire.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Alamofire.xcscheme
new file mode 100644 (file)
index 0000000..7481111
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "E76458C58C9140B6A16D60547E68E80C"
+               BlueprintName = "Alamofire"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "Alamofire.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/AlamofireActivityLogger.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/AlamofireActivityLogger.xcscheme
new file mode 100644 (file)
index 0000000..8b90e6c
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "8D956160DB7545834B961CB5E8DBBD1B"
+               BlueprintName = "AlamofireActivityLogger"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "AlamofireActivityLogger.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FirebaseCore.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FirebaseCore.xcscheme
new file mode 100644 (file)
index 0000000..e4f55b2
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "4338000AC6DBAF783F338F055DDE55C6"
+               BlueprintName = "FirebaseCore"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "FirebaseCore.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FirebaseMessaging.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FirebaseMessaging.xcscheme
new file mode 100644 (file)
index 0000000..13b8bd1
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "0DD17D85AC06742489BCAFB83E51FDB0"
+               BlueprintName = "FirebaseMessaging"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "FirebaseMessaging.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FolioReaderKit.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FolioReaderKit.xcscheme
new file mode 100644 (file)
index 0000000..716da04
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "1EB42C6E589197DB247315875A9AE141"
+               BlueprintName = "FolioReaderKit"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "FolioReaderKit.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FontBlaster.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/FontBlaster.xcscheme
new file mode 100644 (file)
index 0000000..902fcdc
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "A0243AC4D5FF53ED067BF1609A6898A0"
+               BlueprintName = "FontBlaster"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "FontBlaster.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/GoogleUtilities.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/GoogleUtilities.xcscheme
new file mode 100644 (file)
index 0000000..02e9e8c
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "2B2CE33B441C5D4BD9A5A04D3087E5C8"
+               BlueprintName = "GoogleUtilities"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "GoogleUtilities.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/JSQWebViewController.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/JSQWebViewController.xcscheme
new file mode 100644 (file)
index 0000000..62eb551
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "87C645C5CBE6078EBEE1659ABEEF780D"
+               BlueprintName = "JSQWebViewController"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "JSQWebViewController.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Kingfisher.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Kingfisher.xcscheme
new file mode 100644 (file)
index 0000000..1141417
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "A15CD3220CB655EF65975142EDBD8C6E"
+               BlueprintName = "Kingfisher"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "Kingfisher.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MBProgressHUD.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MBProgressHUD.xcscheme
new file mode 100644 (file)
index 0000000..d7559f9
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "AD408BAB384B6DDA88A50A16FB158B66"
+               BlueprintName = "MBProgressHUD"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "MBProgressHUD.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MZDownloadManager.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MZDownloadManager.xcscheme
new file mode 100644 (file)
index 0000000..d8785c0
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "DEB0D4FCD7FECFEDBCB372932FCB07CB"
+               BlueprintName = "MZDownloadManager"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "MZDownloadManager.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MatomoTracker.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MatomoTracker.xcscheme
new file mode 100644 (file)
index 0000000..6c27f65
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "A938E07DF5A500E5C4FA1C9FD1886EB2"
+               BlueprintName = "MatomoTracker"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "MatomoTracker.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MenuItemKit.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/MenuItemKit.xcscheme
new file mode 100644 (file)
index 0000000..bd3ef01
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "673DBE5653C51962C057371B5CA8A2B4"
+               BlueprintName = "MenuItemKit"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "MenuItemKit.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/OAuthSwift.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/OAuthSwift.xcscheme
new file mode 100644 (file)
index 0000000..8da0a07
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "EF83FCC10E7453ACCB801B656501E2F6"
+               BlueprintName = "OAuthSwift"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "OAuthSwift.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Pods-WolneLektury.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Pods-WolneLektury.xcscheme
new file mode 100644 (file)
index 0000000..583d2d7
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "FDFD44D80A6A01C71A321ADCC051FC07"
+               BuildableName = "Pods_WolneLektury.framework"
+               BlueprintName = "Pods-WolneLektury"
+               ReferencedContainer = "container:Pods.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "FDFD44D80A6A01C71A321ADCC051FC07"
+            BuildableName = "Pods_WolneLektury.framework"
+            BlueprintName = "Pods-WolneLektury"
+            ReferencedContainer = "container:Pods.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Protobuf.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Protobuf.xcscheme
new file mode 100644 (file)
index 0000000..f963902
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "71E4E70C7BAC6389783D3A3C8EB1F910"
+               BlueprintName = "Protobuf"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "Protobuf.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Realm.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Realm.xcscheme
new file mode 100644 (file)
index 0000000..9a3a673
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "13218BC4FEB9FA3C157F802AB364DCBB"
+               BlueprintName = "Realm"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "Realm.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/RealmSwift.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/RealmSwift.xcscheme
new file mode 100644 (file)
index 0000000..0dd0e13
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "76948DED43ECFC74BB41AA7F11DA2200"
+               BlueprintName = "RealmSwift"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "RealmSwift.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/SSZipArchive.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/SSZipArchive.xcscheme
new file mode 100644 (file)
index 0000000..a3a5cb4
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CE310247D34682517FA45BE1C20210CF"
+               BlueprintName = "SSZipArchive"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "SSZipArchive.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/SideMenu.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/SideMenu.xcscheme
new file mode 100644 (file)
index 0000000..1a56efe
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "C8C2C570FCCD7CC9014E895FBBEEF1FC"
+               BlueprintName = "SideMenu"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "SideMenu.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/SwiftKeychainWrapper.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/SwiftKeychainWrapper.xcscheme
new file mode 100644 (file)
index 0000000..cb1436f
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "8CD09A1EC484C4C08A7FBF85CA871914"
+               BlueprintName = "SwiftKeychainWrapper"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "SwiftKeychainWrapper.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Toast-Swift.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/Toast-Swift.xcscheme
new file mode 100644 (file)
index 0000000..998680f
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "E6D2D23AF894E245CA516BC67EBF8703"
+               BuildableName = "Toast_Swift.framework"
+               BlueprintName = "Toast-Swift"
+               ReferencedContainer = "container:Pods.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "E6D2D23AF894E245CA516BC67EBF8703"
+            BuildableName = "Toast_Swift.framework"
+            BlueprintName = "Toast-Swift"
+            ReferencedContainer = "container:Pods.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/ZFDragableModalTransition.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/ZFDragableModalTransition.xcscheme
new file mode 100644 (file)
index 0000000..126b264
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "D449FC7BC81F775BC2E4BB53D77B64D6"
+               BlueprintName = "ZFDragableModalTransition"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "ZFDragableModalTransition.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/nanopb.xcscheme b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/nanopb.xcscheme
new file mode 100644 (file)
index 0000000..bcdd16f
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0930"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForAnalyzing = "YES"
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "8364BB258018143B83798DE31C13546D"
+               BlueprintName = "nanopb"
+               ReferencedContainer = "container:Pods.xcodeproj"
+               BuildableName = "nanopb.framework">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      buildConfiguration = "Debug"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES"
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/xcschememanagement.plist b/iOS/Pods/Pods.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644 (file)
index 0000000..e9aeac4
--- /dev/null
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>SchemeUserState</key>
+       <dict>
+               <key>AEXML.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>Alamofire.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>AlamofireActivityLogger.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>FirebaseCore.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>FirebaseMessaging.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>FolioReaderKit.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>FontBlaster.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>GoogleUtilities.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>JSQWebViewController.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>Kingfisher.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>MBProgressHUD.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>MZDownloadManager.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>MatomoTracker.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>MenuItemKit.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>OAuthSwift.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>Pods-WolneLektury.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>Protobuf.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>Realm.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>RealmSwift.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>SSZipArchive.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>SideMenu.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>SwiftKeychainWrapper.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>Toast-Swift.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>ZFDragableModalTransition.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+               <key>nanopb.xcscheme</key>
+               <dict>
+                       <key>isShown</key>
+                       <false/>
+               </dict>
+       </dict>
+       <key>SuppressBuildableAutocreation</key>
+       <dict/>
+</dict>
+</plist>
diff --git a/iOS/Pods/Protobuf/LICENSE b/iOS/Pods/Protobuf/LICENSE
new file mode 100644 (file)
index 0000000..19b305b
--- /dev/null
@@ -0,0 +1,32 @@
+Copyright 2008 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.
+
+Code generated by the Protocol Buffer compiler is owned by the owner
+of the input file used when generating it.  This code is not
+standalone and requires a support library to be linked with it.  This
+support library is itself covered by the above license.
diff --git a/iOS/Pods/Protobuf/README.md b/iOS/Pods/Protobuf/README.md
new file mode 100644 (file)
index 0000000..1a45ee6
--- /dev/null
@@ -0,0 +1,85 @@
+Protocol Buffers - Google's data interchange format
+===================================================
+
+Copyright 2008 Google Inc.
+
+https://developers.google.com/protocol-buffers/
+
+Overview
+--------
+
+Protocol Buffers (a.k.a., protobuf) are Google's language-neutral,
+platform-neutral, extensible mechanism for serializing structured data. You
+can find [protobuf's documentation on the Google Developers site](https://developers.google.com/protocol-buffers/).
+
+This README file contains protobuf installation instructions. To install
+protobuf, you need to install the protocol compiler (used to compile .proto
+files) and the protobuf runtime for your chosen programming language.
+
+Protocol Compiler Installation
+------------------------------
+
+The protocol compiler is written in C++. If you are using C++, please follow
+the [C++ Installation Instructions](src/README.md) to install protoc along
+with the C++ runtime.
+
+For non-C++ users, the simplest way to install the protocol compiler is to
+download a pre-built binary from our release page:
+
+  [https://github.com/google/protobuf/releases](https://github.com/google/protobuf/releases)
+
+In the downloads section of each release, you can find pre-built binaries in
+zip packages: protoc-$VERSION-$PLATFORM.zip. It contains the protoc binary
+as well as a set of standard .proto files distributed along with protobuf.
+
+If you are looking for an old version that is not available in the release
+page, check out the maven repo here:
+
+  [https://repo1.maven.org/maven2/com/google/protobuf/protoc/](https://repo1.maven.org/maven2/com/google/protobuf/protoc/)
+
+These pre-built binaries are only provided for released versions. If you want
+to use the github master version at HEAD, or you need to modify protobuf code,
+or you are using C++, it's recommended to build your own protoc binary from
+source.
+
+If you would like to build protoc binary from source, see the [C++ Installation
+Instructions](src/README.md).
+
+Protobuf Runtime Installation
+-----------------------------
+
+Protobuf supports several different programming languages. For each programming
+language, you can find instructions in the corresponding source directory about
+how to install protobuf runtime for that specific language:
+
+| Language                             | Source                                                      | Ubuntu | MacOS | Windows |
+|--------------------------------------|-------------------------------------------------------------|--------|-------|---------|
+| C++ (include C++ runtime and protoc) | [src](src)                                                  | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-cpp_distcheck.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fcpp_distcheck%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fcpp%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-cpp_distcheck.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fcpp_distcheck%2Fcontinuous) | [![Build status](https://ci.appveyor.com/api/projects/status/73ctee6ua4w2ruin?svg=true)](https://ci.appveyor.com/project/protobuf/protobuf) |
+| Java                                 | [java](java)                                                | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-java_compatibility.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fjava_compatibility%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-java_jdk7.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fjava_jdk7%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-java_oracle7.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fjava_oracle7%2Fcontinuous) | | |
+| Python                               | [python](python)                                            | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python_compatibility.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython_compatibility%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython_cpp%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-python.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fpython%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-python_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fpython_cpp%2Fcontinuous) | |
+| Objective-C                          | [objectivec](objectivec)                                    | | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-objectivec_cocoapods_integration.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fobjectivec_cocoapods_integration%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-objectivec_ios_debug.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fobjectivec_ios_debug%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-objectivec_ios_release.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fobjectivec_ios_release%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-objectivec_osx.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fobjectivec_osx%2Fcontinuous) | |
+| C#                                   | [csharp](csharp)                                            | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-csharp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fcsharp%2Fcontinuous) | | [![Build status](https://ci.appveyor.com/api/projects/status/73ctee6ua4w2ruin?svg=true)](https://ci.appveyor.com/project/protobuf/protobuf) |
+| JavaScript                           | [js](js)                                                    | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-javascript.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fjavascript%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-javascript.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fjavascript%2Fcontinuous) | |
+| Ruby                                 | [ruby](ruby)                                                | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-ruby_all.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fruby_all%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-ruby21.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fruby21%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-ruby22.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fruby22%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-jruby.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fjruby%2Fcontinuous) | |
+| Go                                   | [golang/protobuf](https://github.com/golang/protobuf)       | | | |
+| PHP                                  | [php](php)                                                  | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-php_all.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fphp_all%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-32-bit.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2F32-bit%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-php5.6_mac.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fphp5.6_mac%2Fcontinuous)<br/>[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-php7.0_mac.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fphp7.0_mac%2Fcontinuous) | |
+| Dart                                 | [dart-lang/protobuf](https://github.com/dart-lang/protobuf) | | | |
+
+Quick Start
+-----------
+
+The best way to learn how to use protobuf is to follow the tutorials in our
+developer guide:
+
+https://developers.google.com/protocol-buffers/docs/tutorials
+
+If you want to learn from code examples, take a look at the examples in the
+[examples](examples) directory.
+
+Documentation
+-------------
+
+The complete documentation for Protocol Buffers is available via the
+web at:
+
+https://developers.google.com/protocol-buffers/
diff --git a/iOS/Pods/Protobuf/objectivec/GPBArray.h b/iOS/Pods/Protobuf/objectivec/GPBArray.h
new file mode 100644 (file)
index 0000000..638b288
--- /dev/null
@@ -0,0 +1,1967 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBRuntimeTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+//%PDDM-EXPAND DECLARE_ARRAYS()
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int32
+
+/**
+ * Class used for repeated fields of int32_t values. This performs better than
+ * boxing into NSNumbers in NSArrays.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt32Array : NSObject <NSCopying>
+
+/** The number of elements contained in the array. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * @return A newly instanced and empty GPBInt32Array.
+ **/
++ (instancetype)array;
+
+/**
+ * Creates and initializes a GPBInt32Array with the single element given.
+ *
+ * @param value The value to be placed in the array.
+ *
+ * @return A newly instanced GPBInt32Array with value in it.
+ **/
++ (instancetype)arrayWithValue:(int32_t)value;
+
+/**
+ * Creates and initializes a GPBInt32Array with the contents of the given
+ * array.
+ *
+ * @param array Array with the contents to be put into the new array.
+ *
+ * @return A newly instanced GPBInt32Array with the contents of array.
+ **/
++ (instancetype)arrayWithValueArray:(GPBInt32Array *)array;
+
+/**
+ * Creates and initializes a GPBInt32Array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly instanced GPBInt32Array with a capacity of count.
+ **/
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+/**
+ * @return A newly initialized and empty GPBInt32Array.
+ **/
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param values An array with the values to put inside this array.
+ * @param count  The number of elements to copy into the array.
+ *
+ * @return A newly initialized GPBInt32Array with a copy of the values.
+ **/
+- (instancetype)initWithValues:(const int32_t [__nullable])values
+                         count:(NSUInteger)count;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param array An array with the values to put inside this array.
+ *
+ * @return A newly initialized GPBInt32Array with a copy of the values.
+ **/
+- (instancetype)initWithValueArray:(GPBInt32Array *)array;
+
+/**
+ * Initializes the array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly initialized GPBInt32Array with a capacity of count.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+/**
+ * Gets the value at the given index.
+ *
+ * @param index The index of the value to get.
+ *
+ * @return The value at the given index.
+ **/
+- (int32_t)valueAtIndex:(NSUInteger)index;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param opts  Options to control the enumeration.
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Adds a value to this array.
+ *
+ * @param value The value to add to this array.
+ **/
+- (void)addValue:(int32_t)value;
+
+/**
+ * Adds values to this array.
+ *
+ * @param values The values to add to this array.
+ * @param count  The number of elements to add.
+ **/
+- (void)addValues:(const int32_t [__nullable])values count:(NSUInteger)count;
+
+/**
+ * Adds the values from the given array to this array.
+ *
+ * @param array The array containing the elements to add to this array.
+ **/
+- (void)addValuesFromArray:(GPBInt32Array *)array;
+
+/**
+ * Inserts a value into the given position.
+ *
+ * @param value The value to add to this array.
+ * @param index The index into which to insert the value.
+ **/
+- (void)insertValue:(int32_t)value atIndex:(NSUInteger)index;
+
+/**
+ * Replaces the value at the given index with the given value.
+ *
+ * @param index The index for which to replace the value.
+ * @param value The value to replace with.
+ **/
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int32_t)value;
+
+/**
+ * Removes the value at the given index.
+ *
+ * @param index The index of the value to remove.
+ **/
+- (void)removeValueAtIndex:(NSUInteger)index;
+
+/**
+ * Removes all the values from this array.
+ **/
+- (void)removeAll;
+
+/**
+ * Exchanges the values between the given indexes.
+ *
+ * @param idx1 The index of the first element to exchange.
+ * @param idx2 The index of the second element to exchange.
+ **/
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - UInt32
+
+/**
+ * Class used for repeated fields of uint32_t values. This performs better than
+ * boxing into NSNumbers in NSArrays.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt32Array : NSObject <NSCopying>
+
+/** The number of elements contained in the array. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * @return A newly instanced and empty GPBUInt32Array.
+ **/
++ (instancetype)array;
+
+/**
+ * Creates and initializes a GPBUInt32Array with the single element given.
+ *
+ * @param value The value to be placed in the array.
+ *
+ * @return A newly instanced GPBUInt32Array with value in it.
+ **/
++ (instancetype)arrayWithValue:(uint32_t)value;
+
+/**
+ * Creates and initializes a GPBUInt32Array with the contents of the given
+ * array.
+ *
+ * @param array Array with the contents to be put into the new array.
+ *
+ * @return A newly instanced GPBUInt32Array with the contents of array.
+ **/
++ (instancetype)arrayWithValueArray:(GPBUInt32Array *)array;
+
+/**
+ * Creates and initializes a GPBUInt32Array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly instanced GPBUInt32Array with a capacity of count.
+ **/
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+/**
+ * @return A newly initialized and empty GPBUInt32Array.
+ **/
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param values An array with the values to put inside this array.
+ * @param count  The number of elements to copy into the array.
+ *
+ * @return A newly initialized GPBUInt32Array with a copy of the values.
+ **/
+- (instancetype)initWithValues:(const uint32_t [__nullable])values
+                         count:(NSUInteger)count;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param array An array with the values to put inside this array.
+ *
+ * @return A newly initialized GPBUInt32Array with a copy of the values.
+ **/
+- (instancetype)initWithValueArray:(GPBUInt32Array *)array;
+
+/**
+ * Initializes the array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly initialized GPBUInt32Array with a capacity of count.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+/**
+ * Gets the value at the given index.
+ *
+ * @param index The index of the value to get.
+ *
+ * @return The value at the given index.
+ **/
+- (uint32_t)valueAtIndex:(NSUInteger)index;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithBlock:(void (^)(uint32_t value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param opts  Options to control the enumeration.
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(uint32_t value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Adds a value to this array.
+ *
+ * @param value The value to add to this array.
+ **/
+- (void)addValue:(uint32_t)value;
+
+/**
+ * Adds values to this array.
+ *
+ * @param values The values to add to this array.
+ * @param count  The number of elements to add.
+ **/
+- (void)addValues:(const uint32_t [__nullable])values count:(NSUInteger)count;
+
+/**
+ * Adds the values from the given array to this array.
+ *
+ * @param array The array containing the elements to add to this array.
+ **/
+- (void)addValuesFromArray:(GPBUInt32Array *)array;
+
+/**
+ * Inserts a value into the given position.
+ *
+ * @param value The value to add to this array.
+ * @param index The index into which to insert the value.
+ **/
+- (void)insertValue:(uint32_t)value atIndex:(NSUInteger)index;
+
+/**
+ * Replaces the value at the given index with the given value.
+ *
+ * @param index The index for which to replace the value.
+ * @param value The value to replace with.
+ **/
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(uint32_t)value;
+
+/**
+ * Removes the value at the given index.
+ *
+ * @param index The index of the value to remove.
+ **/
+- (void)removeValueAtIndex:(NSUInteger)index;
+
+/**
+ * Removes all the values from this array.
+ **/
+- (void)removeAll;
+
+/**
+ * Exchanges the values between the given indexes.
+ *
+ * @param idx1 The index of the first element to exchange.
+ * @param idx2 The index of the second element to exchange.
+ **/
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - Int64
+
+/**
+ * Class used for repeated fields of int64_t values. This performs better than
+ * boxing into NSNumbers in NSArrays.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt64Array : NSObject <NSCopying>
+
+/** The number of elements contained in the array. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * @return A newly instanced and empty GPBInt64Array.
+ **/
++ (instancetype)array;
+
+/**
+ * Creates and initializes a GPBInt64Array with the single element given.
+ *
+ * @param value The value to be placed in the array.
+ *
+ * @return A newly instanced GPBInt64Array with value in it.
+ **/
++ (instancetype)arrayWithValue:(int64_t)value;
+
+/**
+ * Creates and initializes a GPBInt64Array with the contents of the given
+ * array.
+ *
+ * @param array Array with the contents to be put into the new array.
+ *
+ * @return A newly instanced GPBInt64Array with the contents of array.
+ **/
++ (instancetype)arrayWithValueArray:(GPBInt64Array *)array;
+
+/**
+ * Creates and initializes a GPBInt64Array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly instanced GPBInt64Array with a capacity of count.
+ **/
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+/**
+ * @return A newly initialized and empty GPBInt64Array.
+ **/
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param values An array with the values to put inside this array.
+ * @param count  The number of elements to copy into the array.
+ *
+ * @return A newly initialized GPBInt64Array with a copy of the values.
+ **/
+- (instancetype)initWithValues:(const int64_t [__nullable])values
+                         count:(NSUInteger)count;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param array An array with the values to put inside this array.
+ *
+ * @return A newly initialized GPBInt64Array with a copy of the values.
+ **/
+- (instancetype)initWithValueArray:(GPBInt64Array *)array;
+
+/**
+ * Initializes the array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly initialized GPBInt64Array with a capacity of count.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+/**
+ * Gets the value at the given index.
+ *
+ * @param index The index of the value to get.
+ *
+ * @return The value at the given index.
+ **/
+- (int64_t)valueAtIndex:(NSUInteger)index;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithBlock:(void (^)(int64_t value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param opts  Options to control the enumeration.
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int64_t value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Adds a value to this array.
+ *
+ * @param value The value to add to this array.
+ **/
+- (void)addValue:(int64_t)value;
+
+/**
+ * Adds values to this array.
+ *
+ * @param values The values to add to this array.
+ * @param count  The number of elements to add.
+ **/
+- (void)addValues:(const int64_t [__nullable])values count:(NSUInteger)count;
+
+/**
+ * Adds the values from the given array to this array.
+ *
+ * @param array The array containing the elements to add to this array.
+ **/
+- (void)addValuesFromArray:(GPBInt64Array *)array;
+
+/**
+ * Inserts a value into the given position.
+ *
+ * @param value The value to add to this array.
+ * @param index The index into which to insert the value.
+ **/
+- (void)insertValue:(int64_t)value atIndex:(NSUInteger)index;
+
+/**
+ * Replaces the value at the given index with the given value.
+ *
+ * @param index The index for which to replace the value.
+ * @param value The value to replace with.
+ **/
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int64_t)value;
+
+/**
+ * Removes the value at the given index.
+ *
+ * @param index The index of the value to remove.
+ **/
+- (void)removeValueAtIndex:(NSUInteger)index;
+
+/**
+ * Removes all the values from this array.
+ **/
+- (void)removeAll;
+
+/**
+ * Exchanges the values between the given indexes.
+ *
+ * @param idx1 The index of the first element to exchange.
+ * @param idx2 The index of the second element to exchange.
+ **/
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - UInt64
+
+/**
+ * Class used for repeated fields of uint64_t values. This performs better than
+ * boxing into NSNumbers in NSArrays.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt64Array : NSObject <NSCopying>
+
+/** The number of elements contained in the array. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * @return A newly instanced and empty GPBUInt64Array.
+ **/
++ (instancetype)array;
+
+/**
+ * Creates and initializes a GPBUInt64Array with the single element given.
+ *
+ * @param value The value to be placed in the array.
+ *
+ * @return A newly instanced GPBUInt64Array with value in it.
+ **/
++ (instancetype)arrayWithValue:(uint64_t)value;
+
+/**
+ * Creates and initializes a GPBUInt64Array with the contents of the given
+ * array.
+ *
+ * @param array Array with the contents to be put into the new array.
+ *
+ * @return A newly instanced GPBUInt64Array with the contents of array.
+ **/
++ (instancetype)arrayWithValueArray:(GPBUInt64Array *)array;
+
+/**
+ * Creates and initializes a GPBUInt64Array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly instanced GPBUInt64Array with a capacity of count.
+ **/
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+/**
+ * @return A newly initialized and empty GPBUInt64Array.
+ **/
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param values An array with the values to put inside this array.
+ * @param count  The number of elements to copy into the array.
+ *
+ * @return A newly initialized GPBUInt64Array with a copy of the values.
+ **/
+- (instancetype)initWithValues:(const uint64_t [__nullable])values
+                         count:(NSUInteger)count;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param array An array with the values to put inside this array.
+ *
+ * @return A newly initialized GPBUInt64Array with a copy of the values.
+ **/
+- (instancetype)initWithValueArray:(GPBUInt64Array *)array;
+
+/**
+ * Initializes the array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly initialized GPBUInt64Array with a capacity of count.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+/**
+ * Gets the value at the given index.
+ *
+ * @param index The index of the value to get.
+ *
+ * @return The value at the given index.
+ **/
+- (uint64_t)valueAtIndex:(NSUInteger)index;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithBlock:(void (^)(uint64_t value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param opts  Options to control the enumeration.
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(uint64_t value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Adds a value to this array.
+ *
+ * @param value The value to add to this array.
+ **/
+- (void)addValue:(uint64_t)value;
+
+/**
+ * Adds values to this array.
+ *
+ * @param values The values to add to this array.
+ * @param count  The number of elements to add.
+ **/
+- (void)addValues:(const uint64_t [__nullable])values count:(NSUInteger)count;
+
+/**
+ * Adds the values from the given array to this array.
+ *
+ * @param array The array containing the elements to add to this array.
+ **/
+- (void)addValuesFromArray:(GPBUInt64Array *)array;
+
+/**
+ * Inserts a value into the given position.
+ *
+ * @param value The value to add to this array.
+ * @param index The index into which to insert the value.
+ **/
+- (void)insertValue:(uint64_t)value atIndex:(NSUInteger)index;
+
+/**
+ * Replaces the value at the given index with the given value.
+ *
+ * @param index The index for which to replace the value.
+ * @param value The value to replace with.
+ **/
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(uint64_t)value;
+
+/**
+ * Removes the value at the given index.
+ *
+ * @param index The index of the value to remove.
+ **/
+- (void)removeValueAtIndex:(NSUInteger)index;
+
+/**
+ * Removes all the values from this array.
+ **/
+- (void)removeAll;
+
+/**
+ * Exchanges the values between the given indexes.
+ *
+ * @param idx1 The index of the first element to exchange.
+ * @param idx2 The index of the second element to exchange.
+ **/
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - Float
+
+/**
+ * Class used for repeated fields of float values. This performs better than
+ * boxing into NSNumbers in NSArrays.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBFloatArray : NSObject <NSCopying>
+
+/** The number of elements contained in the array. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * @return A newly instanced and empty GPBFloatArray.
+ **/
++ (instancetype)array;
+
+/**
+ * Creates and initializes a GPBFloatArray with the single element given.
+ *
+ * @param value The value to be placed in the array.
+ *
+ * @return A newly instanced GPBFloatArray with value in it.
+ **/
++ (instancetype)arrayWithValue:(float)value;
+
+/**
+ * Creates and initializes a GPBFloatArray with the contents of the given
+ * array.
+ *
+ * @param array Array with the contents to be put into the new array.
+ *
+ * @return A newly instanced GPBFloatArray with the contents of array.
+ **/
++ (instancetype)arrayWithValueArray:(GPBFloatArray *)array;
+
+/**
+ * Creates and initializes a GPBFloatArray with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly instanced GPBFloatArray with a capacity of count.
+ **/
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+/**
+ * @return A newly initialized and empty GPBFloatArray.
+ **/
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param values An array with the values to put inside this array.
+ * @param count  The number of elements to copy into the array.
+ *
+ * @return A newly initialized GPBFloatArray with a copy of the values.
+ **/
+- (instancetype)initWithValues:(const float [__nullable])values
+                         count:(NSUInteger)count;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param array An array with the values to put inside this array.
+ *
+ * @return A newly initialized GPBFloatArray with a copy of the values.
+ **/
+- (instancetype)initWithValueArray:(GPBFloatArray *)array;
+
+/**
+ * Initializes the array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly initialized GPBFloatArray with a capacity of count.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+/**
+ * Gets the value at the given index.
+ *
+ * @param index The index of the value to get.
+ *
+ * @return The value at the given index.
+ **/
+- (float)valueAtIndex:(NSUInteger)index;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithBlock:(void (^)(float value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param opts  Options to control the enumeration.
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(float value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Adds a value to this array.
+ *
+ * @param value The value to add to this array.
+ **/
+- (void)addValue:(float)value;
+
+/**
+ * Adds values to this array.
+ *
+ * @param values The values to add to this array.
+ * @param count  The number of elements to add.
+ **/
+- (void)addValues:(const float [__nullable])values count:(NSUInteger)count;
+
+/**
+ * Adds the values from the given array to this array.
+ *
+ * @param array The array containing the elements to add to this array.
+ **/
+- (void)addValuesFromArray:(GPBFloatArray *)array;
+
+/**
+ * Inserts a value into the given position.
+ *
+ * @param value The value to add to this array.
+ * @param index The index into which to insert the value.
+ **/
+- (void)insertValue:(float)value atIndex:(NSUInteger)index;
+
+/**
+ * Replaces the value at the given index with the given value.
+ *
+ * @param index The index for which to replace the value.
+ * @param value The value to replace with.
+ **/
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(float)value;
+
+/**
+ * Removes the value at the given index.
+ *
+ * @param index The index of the value to remove.
+ **/
+- (void)removeValueAtIndex:(NSUInteger)index;
+
+/**
+ * Removes all the values from this array.
+ **/
+- (void)removeAll;
+
+/**
+ * Exchanges the values between the given indexes.
+ *
+ * @param idx1 The index of the first element to exchange.
+ * @param idx2 The index of the second element to exchange.
+ **/
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - Double
+
+/**
+ * Class used for repeated fields of double values. This performs better than
+ * boxing into NSNumbers in NSArrays.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBDoubleArray : NSObject <NSCopying>
+
+/** The number of elements contained in the array. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * @return A newly instanced and empty GPBDoubleArray.
+ **/
++ (instancetype)array;
+
+/**
+ * Creates and initializes a GPBDoubleArray with the single element given.
+ *
+ * @param value The value to be placed in the array.
+ *
+ * @return A newly instanced GPBDoubleArray with value in it.
+ **/
++ (instancetype)arrayWithValue:(double)value;
+
+/**
+ * Creates and initializes a GPBDoubleArray with the contents of the given
+ * array.
+ *
+ * @param array Array with the contents to be put into the new array.
+ *
+ * @return A newly instanced GPBDoubleArray with the contents of array.
+ **/
++ (instancetype)arrayWithValueArray:(GPBDoubleArray *)array;
+
+/**
+ * Creates and initializes a GPBDoubleArray with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly instanced GPBDoubleArray with a capacity of count.
+ **/
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+/**
+ * @return A newly initialized and empty GPBDoubleArray.
+ **/
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param values An array with the values to put inside this array.
+ * @param count  The number of elements to copy into the array.
+ *
+ * @return A newly initialized GPBDoubleArray with a copy of the values.
+ **/
+- (instancetype)initWithValues:(const double [__nullable])values
+                         count:(NSUInteger)count;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param array An array with the values to put inside this array.
+ *
+ * @return A newly initialized GPBDoubleArray with a copy of the values.
+ **/
+- (instancetype)initWithValueArray:(GPBDoubleArray *)array;
+
+/**
+ * Initializes the array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly initialized GPBDoubleArray with a capacity of count.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+/**
+ * Gets the value at the given index.
+ *
+ * @param index The index of the value to get.
+ *
+ * @return The value at the given index.
+ **/
+- (double)valueAtIndex:(NSUInteger)index;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithBlock:(void (^)(double value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param opts  Options to control the enumeration.
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(double value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Adds a value to this array.
+ *
+ * @param value The value to add to this array.
+ **/
+- (void)addValue:(double)value;
+
+/**
+ * Adds values to this array.
+ *
+ * @param values The values to add to this array.
+ * @param count  The number of elements to add.
+ **/
+- (void)addValues:(const double [__nullable])values count:(NSUInteger)count;
+
+/**
+ * Adds the values from the given array to this array.
+ *
+ * @param array The array containing the elements to add to this array.
+ **/
+- (void)addValuesFromArray:(GPBDoubleArray *)array;
+
+/**
+ * Inserts a value into the given position.
+ *
+ * @param value The value to add to this array.
+ * @param index The index into which to insert the value.
+ **/
+- (void)insertValue:(double)value atIndex:(NSUInteger)index;
+
+/**
+ * Replaces the value at the given index with the given value.
+ *
+ * @param index The index for which to replace the value.
+ * @param value The value to replace with.
+ **/
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(double)value;
+
+/**
+ * Removes the value at the given index.
+ *
+ * @param index The index of the value to remove.
+ **/
+- (void)removeValueAtIndex:(NSUInteger)index;
+
+/**
+ * Removes all the values from this array.
+ **/
+- (void)removeAll;
+
+/**
+ * Exchanges the values between the given indexes.
+ *
+ * @param idx1 The index of the first element to exchange.
+ * @param idx2 The index of the second element to exchange.
+ **/
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - Bool
+
+/**
+ * Class used for repeated fields of BOOL values. This performs better than
+ * boxing into NSNumbers in NSArrays.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBBoolArray : NSObject <NSCopying>
+
+/** The number of elements contained in the array. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * @return A newly instanced and empty GPBBoolArray.
+ **/
++ (instancetype)array;
+
+/**
+ * Creates and initializes a GPBBoolArray with the single element given.
+ *
+ * @param value The value to be placed in the array.
+ *
+ * @return A newly instanced GPBBoolArray with value in it.
+ **/
++ (instancetype)arrayWithValue:(BOOL)value;
+
+/**
+ * Creates and initializes a GPBBoolArray with the contents of the given
+ * array.
+ *
+ * @param array Array with the contents to be put into the new array.
+ *
+ * @return A newly instanced GPBBoolArray with the contents of array.
+ **/
++ (instancetype)arrayWithValueArray:(GPBBoolArray *)array;
+
+/**
+ * Creates and initializes a GPBBoolArray with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly instanced GPBBoolArray with a capacity of count.
+ **/
++ (instancetype)arrayWithCapacity:(NSUInteger)count;
+
+/**
+ * @return A newly initialized and empty GPBBoolArray.
+ **/
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param values An array with the values to put inside this array.
+ * @param count  The number of elements to copy into the array.
+ *
+ * @return A newly initialized GPBBoolArray with a copy of the values.
+ **/
+- (instancetype)initWithValues:(const BOOL [__nullable])values
+                         count:(NSUInteger)count;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param array An array with the values to put inside this array.
+ *
+ * @return A newly initialized GPBBoolArray with a copy of the values.
+ **/
+- (instancetype)initWithValueArray:(GPBBoolArray *)array;
+
+/**
+ * Initializes the array with the given capacity.
+ *
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly initialized GPBBoolArray with a capacity of count.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)count;
+
+/**
+ * Gets the value at the given index.
+ *
+ * @param index The index of the value to get.
+ *
+ * @return The value at the given index.
+ **/
+- (BOOL)valueAtIndex:(NSUInteger)index;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithBlock:(void (^)(BOOL value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param opts  Options to control the enumeration.
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(BOOL value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Adds a value to this array.
+ *
+ * @param value The value to add to this array.
+ **/
+- (void)addValue:(BOOL)value;
+
+/**
+ * Adds values to this array.
+ *
+ * @param values The values to add to this array.
+ * @param count  The number of elements to add.
+ **/
+- (void)addValues:(const BOOL [__nullable])values count:(NSUInteger)count;
+
+/**
+ * Adds the values from the given array to this array.
+ *
+ * @param array The array containing the elements to add to this array.
+ **/
+- (void)addValuesFromArray:(GPBBoolArray *)array;
+
+/**
+ * Inserts a value into the given position.
+ *
+ * @param value The value to add to this array.
+ * @param index The index into which to insert the value.
+ **/
+- (void)insertValue:(BOOL)value atIndex:(NSUInteger)index;
+
+/**
+ * Replaces the value at the given index with the given value.
+ *
+ * @param index The index for which to replace the value.
+ * @param value The value to replace with.
+ **/
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(BOOL)value;
+
+/**
+ * Removes the value at the given index.
+ *
+ * @param index The index of the value to remove.
+ **/
+- (void)removeValueAtIndex:(NSUInteger)index;
+
+/**
+ * Removes all the values from this array.
+ **/
+- (void)removeAll;
+
+/**
+ * Exchanges the values between the given indexes.
+ *
+ * @param idx1 The index of the first element to exchange.
+ * @param idx2 The index of the second element to exchange.
+ **/
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+#pragma mark - Enum
+
+/**
+ * This class is used for repeated fields of int32_t values. This performs
+ * better than boxing into NSNumbers in NSArrays.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBEnumArray : NSObject <NSCopying>
+
+/** The number of elements contained in the array. */
+@property(nonatomic, readonly) NSUInteger count;
+/** The validation function to check if the enums are valid. */
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
+/**
+ * @return A newly instanced and empty GPBEnumArray.
+ **/
++ (instancetype)array;
+
+/**
+ * Creates and initializes a GPBEnumArray with the enum validation function
+ * given.
+ *
+ * @param func The enum validation function for the array.
+ *
+ * @return A newly instanced GPBEnumArray.
+ **/
++ (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+
+/**
+ * Creates and initializes a GPBEnumArray with the enum validation function
+ * given and the single raw value given.
+ *
+ * @param func  The enum validation function for the array.
+ * @param value The raw value to add to this array.
+ *
+ * @return A newly instanced GPBEnumArray.
+ **/
++ (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                   rawValue:(int32_t)value;
+
+/**
+ * Creates and initializes a GPBEnumArray that adds the elements from the
+ * given array.
+ *
+ * @param array Array containing the values to add to the new array.
+ *
+ * @return A newly instanced GPBEnumArray.
+ **/
++ (instancetype)arrayWithValueArray:(GPBEnumArray *)array;
+
+/**
+ * Creates and initializes a GPBEnumArray with the given enum validation
+ * function and with the givencapacity.
+ *
+ * @param func  The enum validation function for the array.
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly instanced GPBEnumArray with a capacity of count.
+ **/
++ (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                   capacity:(NSUInteger)count;
+
+/**
+ * Initializes the array with the given enum validation function.
+ *
+ * @param func The enum validation function for the array.
+ *
+ * @return A newly initialized GPBEnumArray with a copy of the values.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+    NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param func   The enum validation function for the array.
+ * @param values An array with the values to put inside this array.
+ * @param count  The number of elements to copy into the array.
+ *
+ * @return A newly initialized GPBEnumArray with a copy of the values.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [__nullable])values
+                                     count:(NSUInteger)count;
+
+/**
+ * Initializes the array, copying the given values.
+ *
+ * @param array An array with the values to put inside this array.
+ *
+ * @return A newly initialized GPBEnumArray with a copy of the values.
+ **/
+- (instancetype)initWithValueArray:(GPBEnumArray *)array;
+
+/**
+ * Initializes the array with the given capacity.
+ *
+ * @param func  The enum validation function for the array.
+ * @param count The capacity needed for the array.
+ *
+ * @return A newly initialized GPBEnumArray with a capacity of count.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)count;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value at index is not a
+// valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+/**
+ * Gets the value at the given index.
+ *
+ * @param index The index of the value to get.
+ *
+ * @return The value at the given index.
+ **/
+- (int32_t)valueAtIndex:(NSUInteger)index;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param opts  Options to control the enumeration.
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+
+// These methods bypass the validationFunc to provide access to values that were not
+// known at the time the binary was compiled.
+
+/**
+ * Gets the raw enum value at the given index.
+ *
+ * @param index The index of the raw enum value to get.
+ *
+ * @return The raw enum value at the given index.
+ **/
+- (int32_t)rawValueAtIndex:(NSUInteger)index;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateRawValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+
+/**
+ * Enumerates the values on this array with the given block.
+ *
+ * @param opts  Options to control the enumeration.
+ * @param block The block to enumerate with.
+ *   **value**: The current value being enumerated.
+ *   **idx**:   The index of the current value.
+ *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateRawValuesWithOptions:(NSEnumerationOptions)opts
+                           usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+/**
+ * Adds a value to this array.
+ *
+ * @param value The value to add to this array.
+ **/
+- (void)addValue:(int32_t)value;
+
+/**
+ * Adds values to this array.
+ *
+ * @param values The values to add to this array.
+ * @param count  The number of elements to add.
+ **/
+- (void)addValues:(const int32_t [__nullable])values count:(NSUInteger)count;
+
+
+/**
+ * Inserts a value into the given position.
+ *
+ * @param value The value to add to this array.
+ * @param index The index into which to insert the value.
+ **/
+- (void)insertValue:(int32_t)value atIndex:(NSUInteger)index;
+
+/**
+ * Replaces the value at the given index with the given value.
+ *
+ * @param index The index for which to replace the value.
+ * @param value The value to replace with.
+ **/
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int32_t)value;
+
+// These methods bypass the validationFunc to provide setting of values that were not
+// known at the time the binary was compiled.
+
+/**
+ * Adds a raw enum value to this array.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param value The raw enum value to add to the array.
+ **/
+- (void)addRawValue:(int32_t)value;
+
+/**
+ * Adds raw enum values to this array.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param array Array containing the raw enum values to add to this array.
+ **/
+- (void)addRawValuesFromArray:(GPBEnumArray *)array;
+
+/**
+ * Adds raw enum values to this array.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param values Array containing the raw enum values to add to this array.
+ * @param count  The number of raw values to add.
+ **/
+- (void)addRawValues:(const int32_t [__nullable])values count:(NSUInteger)count;
+
+/**
+ * Inserts a raw enum value at the given index.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param value Raw enum value to add.
+ * @param index The index into which to insert the value.
+ **/
+- (void)insertRawValue:(int32_t)value atIndex:(NSUInteger)index;
+
+/**
+ * Replaces the raw enum value at the given index with the given value.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param index The index for which to replace the value.
+ * @param value The raw enum value to replace with.
+ **/
+- (void)replaceValueAtIndex:(NSUInteger)index withRawValue:(int32_t)value;
+
+// No validation applies to these methods.
+
+/**
+ * Removes the value at the given index.
+ *
+ * @param index The index of the value to remove.
+ **/
+- (void)removeValueAtIndex:(NSUInteger)index;
+
+/**
+ * Removes all the values from this array.
+ **/
+- (void)removeAll;
+
+/**
+ * Exchanges the values between the given indexes.
+ *
+ * @param idx1 The index of the first element to exchange.
+ * @param idx2 The index of the second element to exchange.
+ **/
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2;
+
+@end
+
+//%PDDM-EXPAND-END DECLARE_ARRAYS()
+
+NS_ASSUME_NONNULL_END
+
+//%PDDM-DEFINE DECLARE_ARRAYS()
+//%ARRAY_INTERFACE_SIMPLE(Int32, int32_t)
+//%ARRAY_INTERFACE_SIMPLE(UInt32, uint32_t)
+//%ARRAY_INTERFACE_SIMPLE(Int64, int64_t)
+//%ARRAY_INTERFACE_SIMPLE(UInt64, uint64_t)
+//%ARRAY_INTERFACE_SIMPLE(Float, float)
+//%ARRAY_INTERFACE_SIMPLE(Double, double)
+//%ARRAY_INTERFACE_SIMPLE(Bool, BOOL)
+//%ARRAY_INTERFACE_ENUM(Enum, int32_t)
+
+//
+// The common case (everything but Enum)
+//
+
+//%PDDM-DEFINE ARRAY_INTERFACE_SIMPLE(NAME, TYPE)
+//%#pragma mark - NAME
+//%
+//%/**
+//% * Class used for repeated fields of ##TYPE## values. This performs better than
+//% * boxing into NSNumbers in NSArrays.
+//% *
+//% * @note This class is not meant to be subclassed.
+//% **/
+//%@interface GPB##NAME##Array : NSObject <NSCopying>
+//%
+//%/** The number of elements contained in the array. */
+//%@property(nonatomic, readonly) NSUInteger count;
+//%
+//%/**
+//% * @return A newly instanced and empty GPB##NAME##Array.
+//% **/
+//%+ (instancetype)array;
+//%
+//%/**
+//% * Creates and initializes a GPB##NAME##Array with the single element given.
+//% *
+//% * @param value The value to be placed in the array.
+//% *
+//% * @return A newly instanced GPB##NAME##Array with value in it.
+//% **/
+//%+ (instancetype)arrayWithValue:(TYPE)value;
+//%
+//%/**
+//% * Creates and initializes a GPB##NAME##Array with the contents of the given
+//% * array.
+//% *
+//% * @param array Array with the contents to be put into the new array.
+//% *
+//% * @return A newly instanced GPB##NAME##Array with the contents of array.
+//% **/
+//%+ (instancetype)arrayWithValueArray:(GPB##NAME##Array *)array;
+//%
+//%/**
+//% * Creates and initializes a GPB##NAME##Array with the given capacity.
+//% *
+//% * @param count The capacity needed for the array.
+//% *
+//% * @return A newly instanced GPB##NAME##Array with a capacity of count.
+//% **/
+//%+ (instancetype)arrayWithCapacity:(NSUInteger)count;
+//%
+//%/**
+//% * @return A newly initialized and empty GPB##NAME##Array.
+//% **/
+//%- (instancetype)init NS_DESIGNATED_INITIALIZER;
+//%
+//%/**
+//% * Initializes the array, copying the given values.
+//% *
+//% * @param values An array with the values to put inside this array.
+//% * @param count  The number of elements to copy into the array.
+//% *
+//% * @return A newly initialized GPB##NAME##Array with a copy of the values.
+//% **/
+//%- (instancetype)initWithValues:(const TYPE [__nullable])values
+//%                         count:(NSUInteger)count;
+//%
+//%/**
+//% * Initializes the array, copying the given values.
+//% *
+//% * @param array An array with the values to put inside this array.
+//% *
+//% * @return A newly initialized GPB##NAME##Array with a copy of the values.
+//% **/
+//%- (instancetype)initWithValueArray:(GPB##NAME##Array *)array;
+//%
+//%/**
+//% * Initializes the array with the given capacity.
+//% *
+//% * @param count The capacity needed for the array.
+//% *
+//% * @return A newly initialized GPB##NAME##Array with a capacity of count.
+//% **/
+//%- (instancetype)initWithCapacity:(NSUInteger)count;
+//%
+//%ARRAY_IMMUTABLE_INTERFACE(NAME, TYPE, Basic)
+//%
+//%ARRAY_MUTABLE_INTERFACE(NAME, TYPE, Basic)
+//%
+//%@end
+//%
+
+//
+// Macros specific to Enums (to tweak their interface).
+//
+
+//%PDDM-DEFINE ARRAY_INTERFACE_ENUM(NAME, TYPE)
+//%#pragma mark - NAME
+//%
+//%/**
+//% * This class is used for repeated fields of ##TYPE## values. This performs
+//% * better than boxing into NSNumbers in NSArrays.
+//% *
+//% * @note This class is not meant to be subclassed.
+//% **/
+//%@interface GPB##NAME##Array : NSObject <NSCopying>
+//%
+//%/** The number of elements contained in the array. */
+//%@property(nonatomic, readonly) NSUInteger count;
+//%/** The validation function to check if the enums are valid. */
+//%@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+//%
+//%/**
+//% * @return A newly instanced and empty GPB##NAME##Array.
+//% **/
+//%+ (instancetype)array;
+//%
+//%/**
+//% * Creates and initializes a GPB##NAME##Array with the enum validation function
+//% * given.
+//% *
+//% * @param func The enum validation function for the array.
+//% *
+//% * @return A newly instanced GPB##NAME##Array.
+//% **/
+//%+ (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+//%
+//%/**
+//% * Creates and initializes a GPB##NAME##Array with the enum validation function
+//% * given and the single raw value given.
+//% *
+//% * @param func  The enum validation function for the array.
+//% * @param value The raw value to add to this array.
+//% *
+//% * @return A newly instanced GPB##NAME##Array.
+//% **/
+//%+ (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func
+//%                                   rawValue:(TYPE)value;
+//%
+//%/**
+//% * Creates and initializes a GPB##NAME##Array that adds the elements from the
+//% * given array.
+//% *
+//% * @param array Array containing the values to add to the new array.
+//% *
+//% * @return A newly instanced GPB##NAME##Array.
+//% **/
+//%+ (instancetype)arrayWithValueArray:(GPB##NAME##Array *)array;
+//%
+//%/**
+//% * Creates and initializes a GPB##NAME##Array with the given enum validation
+//% * function and with the givencapacity.
+//% *
+//% * @param func  The enum validation function for the array.
+//% * @param count The capacity needed for the array.
+//% *
+//% * @return A newly instanced GPB##NAME##Array with a capacity of count.
+//% **/
+//%+ (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func
+//%                                   capacity:(NSUInteger)count;
+//%
+//%/**
+//% * Initializes the array with the given enum validation function.
+//% *
+//% * @param func The enum validation function for the array.
+//% *
+//% * @return A newly initialized GPB##NAME##Array with a copy of the values.
+//% **/
+//%- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+//%    NS_DESIGNATED_INITIALIZER;
+//%
+//%/**
+//% * Initializes the array, copying the given values.
+//% *
+//% * @param func   The enum validation function for the array.
+//% * @param values An array with the values to put inside this array.
+//% * @param count  The number of elements to copy into the array.
+//% *
+//% * @return A newly initialized GPB##NAME##Array with a copy of the values.
+//% **/
+//%- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+//%                                 rawValues:(const TYPE [__nullable])values
+//%                                     count:(NSUInteger)count;
+//%
+//%/**
+//% * Initializes the array, copying the given values.
+//% *
+//% * @param array An array with the values to put inside this array.
+//% *
+//% * @return A newly initialized GPB##NAME##Array with a copy of the values.
+//% **/
+//%- (instancetype)initWithValueArray:(GPB##NAME##Array *)array;
+//%
+//%/**
+//% * Initializes the array with the given capacity.
+//% *
+//% * @param func  The enum validation function for the array.
+//% * @param count The capacity needed for the array.
+//% *
+//% * @return A newly initialized GPB##NAME##Array with a capacity of count.
+//% **/
+//%- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+//%                                  capacity:(NSUInteger)count;
+//%
+//%// These will return kGPBUnrecognizedEnumeratorValue if the value at index is not a
+//%// valid enumerator as defined by validationFunc. If the actual value is
+//%// desired, use "raw" version of the method.
+//%
+//%ARRAY_IMMUTABLE_INTERFACE(NAME, TYPE, NAME)
+//%
+//%// These methods bypass the validationFunc to provide access to values that were not
+//%// known at the time the binary was compiled.
+//%
+//%/**
+//% * Gets the raw enum value at the given index.
+//% *
+//% * @param index The index of the raw enum value to get.
+//% *
+//% * @return The raw enum value at the given index.
+//% **/
+//%- (TYPE)rawValueAtIndex:(NSUInteger)index;
+//%
+//%/**
+//% * Enumerates the values on this array with the given block.
+//% *
+//% * @param block The block to enumerate with.
+//% *   **value**: The current value being enumerated.
+//% *   **idx**:   The index of the current value.
+//% *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+//% **/
+//%- (void)enumerateRawValuesWithBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block;
+//%
+//%/**
+//% * Enumerates the values on this array with the given block.
+//% *
+//% * @param opts  Options to control the enumeration.
+//% * @param block The block to enumerate with.
+//% *   **value**: The current value being enumerated.
+//% *   **idx**:   The index of the current value.
+//% *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+//% **/
+//%- (void)enumerateRawValuesWithOptions:(NSEnumerationOptions)opts
+//%                           usingBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block;
+//%
+//%// If value is not a valid enumerator as defined by validationFunc, these
+//%// methods will assert in debug, and will log in release and assign the value
+//%// to the default value. Use the rawValue methods below to assign non enumerator
+//%// values.
+//%
+//%ARRAY_MUTABLE_INTERFACE(NAME, TYPE, NAME)
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE ARRAY_IMMUTABLE_INTERFACE(NAME, TYPE, HELPER_NAME)
+//%/**
+//% * Gets the value at the given index.
+//% *
+//% * @param index The index of the value to get.
+//% *
+//% * @return The value at the given index.
+//% **/
+//%- (TYPE)valueAtIndex:(NSUInteger)index;
+//%
+//%/**
+//% * Enumerates the values on this array with the given block.
+//% *
+//% * @param block The block to enumerate with.
+//% *   **value**: The current value being enumerated.
+//% *   **idx**:   The index of the current value.
+//% *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+//% **/
+//%- (void)enumerateValuesWithBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block;
+//%
+//%/**
+//% * Enumerates the values on this array with the given block.
+//% *
+//% * @param opts  Options to control the enumeration.
+//% * @param block The block to enumerate with.
+//% *   **value**: The current value being enumerated.
+//% *   **idx**:   The index of the current value.
+//% *   **stop**:  A pointer to a boolean that when set stops the enumeration.
+//% **/
+//%- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+//%                        usingBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block;
+
+//%PDDM-DEFINE ARRAY_MUTABLE_INTERFACE(NAME, TYPE, HELPER_NAME)
+//%/**
+//% * Adds a value to this array.
+//% *
+//% * @param value The value to add to this array.
+//% **/
+//%- (void)addValue:(TYPE)value;
+//%
+//%/**
+//% * Adds values to this array.
+//% *
+//% * @param values The values to add to this array.
+//% * @param count  The number of elements to add.
+//% **/
+//%- (void)addValues:(const TYPE [__nullable])values count:(NSUInteger)count;
+//%
+//%ARRAY_EXTRA_MUTABLE_METHODS1_##HELPER_NAME(NAME, TYPE)
+//%/**
+//% * Inserts a value into the given position.
+//% *
+//% * @param value The value to add to this array.
+//% * @param index The index into which to insert the value.
+//% **/
+//%- (void)insertValue:(TYPE)value atIndex:(NSUInteger)index;
+//%
+//%/**
+//% * Replaces the value at the given index with the given value.
+//% *
+//% * @param index The index for which to replace the value.
+//% * @param value The value to replace with.
+//% **/
+//%- (void)replaceValueAtIndex:(NSUInteger)index withValue:(TYPE)value;
+//%ARRAY_EXTRA_MUTABLE_METHODS2_##HELPER_NAME(NAME, TYPE)
+//%/**
+//% * Removes the value at the given index.
+//% *
+//% * @param index The index of the value to remove.
+//% **/
+//%- (void)removeValueAtIndex:(NSUInteger)index;
+//%
+//%/**
+//% * Removes all the values from this array.
+//% **/
+//%- (void)removeAll;
+//%
+//%/**
+//% * Exchanges the values between the given indexes.
+//% *
+//% * @param idx1 The index of the first element to exchange.
+//% * @param idx2 The index of the second element to exchange.
+//% **/
+//%- (void)exchangeValueAtIndex:(NSUInteger)idx1
+//%            withValueAtIndex:(NSUInteger)idx2;
+
+//
+// These are hooks invoked by the above to do insert as needed.
+//
+
+//%PDDM-DEFINE ARRAY_EXTRA_MUTABLE_METHODS1_Basic(NAME, TYPE)
+//%/**
+//% * Adds the values from the given array to this array.
+//% *
+//% * @param array The array containing the elements to add to this array.
+//% **/
+//%- (void)addValuesFromArray:(GPB##NAME##Array *)array;
+//%
+//%PDDM-DEFINE ARRAY_EXTRA_MUTABLE_METHODS2_Basic(NAME, TYPE)
+// Empty
+//%PDDM-DEFINE ARRAY_EXTRA_MUTABLE_METHODS1_Enum(NAME, TYPE)
+// Empty
+//%PDDM-DEFINE ARRAY_EXTRA_MUTABLE_METHODS2_Enum(NAME, TYPE)
+//%
+//%// These methods bypass the validationFunc to provide setting of values that were not
+//%// known at the time the binary was compiled.
+//%
+//%/**
+//% * Adds a raw enum value to this array.
+//% *
+//% * @note This method bypass the validationFunc to enable the setting of values that
+//% *       were not known at the time the binary was compiled.
+//% *
+//% * @param value The raw enum value to add to the array.
+//% **/
+//%- (void)addRawValue:(TYPE)value;
+//%
+//%/**
+//% * Adds raw enum values to this array.
+//% *
+//% * @note This method bypass the validationFunc to enable the setting of values that
+//% *       were not known at the time the binary was compiled.
+//% *
+//% * @param array Array containing the raw enum values to add to this array.
+//% **/
+//%- (void)addRawValuesFromArray:(GPB##NAME##Array *)array;
+//%
+//%/**
+//% * Adds raw enum values to this array.
+//% *
+//% * @note This method bypass the validationFunc to enable the setting of values that
+//% *       were not known at the time the binary was compiled.
+//% *
+//% * @param values Array containing the raw enum values to add to this array.
+//% * @param count  The number of raw values to add.
+//% **/
+//%- (void)addRawValues:(const TYPE [__nullable])values count:(NSUInteger)count;
+//%
+//%/**
+//% * Inserts a raw enum value at the given index.
+//% *
+//% * @note This method bypass the validationFunc to enable the setting of values that
+//% *       were not known at the time the binary was compiled.
+//% *
+//% * @param value Raw enum value to add.
+//% * @param index The index into which to insert the value.
+//% **/
+//%- (void)insertRawValue:(TYPE)value atIndex:(NSUInteger)index;
+//%
+//%/**
+//% * Replaces the raw enum value at the given index with the given value.
+//% *
+//% * @note This method bypass the validationFunc to enable the setting of values that
+//% *       were not known at the time the binary was compiled.
+//% *
+//% * @param index The index for which to replace the value.
+//% * @param value The raw enum value to replace with.
+//% **/
+//%- (void)replaceValueAtIndex:(NSUInteger)index withRawValue:(TYPE)value;
+//%
+//%// No validation applies to these methods.
+//%
diff --git a/iOS/Pods/Protobuf/objectivec/GPBArray.m b/iOS/Pods/Protobuf/objectivec/GPBArray.m
new file mode 100644 (file)
index 0000000..122e030
--- /dev/null
@@ -0,0 +1,2551 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBArray_PackagePrivate.h"
+
+#import "GPBMessage_PackagePrivate.h"
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+// Mutable arrays use an internal buffer that can always hold a multiple of this elements.
+#define kChunkSize 16
+#define CapacityFromCount(x) (((x / kChunkSize) + 1) * kChunkSize)
+
+static BOOL ArrayDefault_IsValidValue(int32_t value) {
+  // Anything but the bad value marker is allowed.
+  return (value != kGPBUnrecognizedEnumeratorValue);
+}
+
+//%PDDM-DEFINE VALIDATE_RANGE(INDEX, COUNT)
+//%  if (INDEX >= COUNT) {
+//%    [NSException raise:NSRangeException
+//%                format:@"Index (%lu) beyond bounds (%lu)",
+//%                       (unsigned long)INDEX, (unsigned long)COUNT];
+//%  }
+//%PDDM-DEFINE MAYBE_GROW_TO_SET_COUNT(NEW_COUNT)
+//%  if (NEW_COUNT > _capacity) {
+//%    [self internalResizeToCapacity:CapacityFromCount(NEW_COUNT)];
+//%  }
+//%  _count = NEW_COUNT;
+//%PDDM-DEFINE SET_COUNT_AND_MAYBE_SHRINK(NEW_COUNT)
+//%  _count = NEW_COUNT;
+//%  if ((NEW_COUNT + (2 * kChunkSize)) < _capacity) {
+//%    [self internalResizeToCapacity:CapacityFromCount(NEW_COUNT)];
+//%  }
+
+//
+// Macros for the common basic cases.
+//
+
+//%PDDM-DEFINE ARRAY_INTERFACE_SIMPLE(NAME, TYPE, FORMAT)
+//%#pragma mark - NAME
+//%
+//%@implementation GPB##NAME##Array {
+//% @package
+//%  TYPE *_values;
+//%  NSUInteger _count;
+//%  NSUInteger _capacity;
+//%}
+//%
+//%@synthesize count = _count;
+//%
+//%+ (instancetype)array {
+//%  return [[[self alloc] init] autorelease];
+//%}
+//%
+//%+ (instancetype)arrayWithValue:(TYPE)value {
+//%  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+//%  // the type correct.
+//%  return [[(GPB##NAME##Array*)[self alloc] initWithValues:&value count:1] autorelease];
+//%}
+//%
+//%+ (instancetype)arrayWithValueArray:(GPB##NAME##Array *)array {
+//%  return [[(GPB##NAME##Array*)[self alloc] initWithValueArray:array] autorelease];
+//%}
+//%
+//%+ (instancetype)arrayWithCapacity:(NSUInteger)count {
+//%  return [[[self alloc] initWithCapacity:count] autorelease];
+//%}
+//%
+//%- (instancetype)init {
+//%  self = [super init];
+//%  // No work needed;
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithValueArray:(GPB##NAME##Array *)array {
+//%  return [self initWithValues:array->_values count:array->_count];
+//%}
+//%
+//%- (instancetype)initWithValues:(const TYPE [])values count:(NSUInteger)count {
+//%  self = [self init];
+//%  if (self) {
+//%    if (count && values) {
+//%      _values = reallocf(_values, count * sizeof(TYPE));
+//%      if (_values != NULL) {
+//%        _capacity = count;
+//%        memcpy(_values, values, count * sizeof(TYPE));
+//%        _count = count;
+//%      } else {
+//%        [self release];
+//%        [NSException raise:NSMallocException
+//%                    format:@"Failed to allocate %lu bytes",
+//%                           (unsigned long)(count * sizeof(TYPE))];
+//%      }
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithCapacity:(NSUInteger)count {
+//%  self = [self initWithValues:NULL count:0];
+//%  if (self && count) {
+//%    [self internalResizeToCapacity:count];
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)copyWithZone:(NSZone *)zone {
+//%  return [[GPB##NAME##Array allocWithZone:zone] initWithValues:_values count:_count];
+//%}
+//%
+//%ARRAY_IMMUTABLE_CORE(NAME, TYPE, , FORMAT)
+//%
+//%- (TYPE)valueAtIndex:(NSUInteger)index {
+//%VALIDATE_RANGE(index, _count)
+//%  return _values[index];
+//%}
+//%
+//%ARRAY_MUTABLE_CORE(NAME, TYPE, , FORMAT)
+//%@end
+//%
+
+//
+// Some core macros used for both the simple types and Enums.
+//
+
+//%PDDM-DEFINE ARRAY_IMMUTABLE_CORE(NAME, TYPE, ACCESSOR_NAME, FORMAT)
+//%- (void)dealloc {
+//%  NSAssert(!_autocreator,
+//%           @"%@: Autocreator must be cleared before release, autocreator: %@",
+//%           [self class], _autocreator);
+//%  free(_values);
+//%  [super dealloc];
+//%}
+//%
+//%- (BOOL)isEqual:(id)other {
+//%  if (self == other) {
+//%    return YES;
+//%  }
+//%  if (![other isKindOfClass:[GPB##NAME##Array class]]) {
+//%    return NO;
+//%  }
+//%  GPB##NAME##Array *otherArray = other;
+//%  return (_count == otherArray->_count
+//%          && memcmp(_values, otherArray->_values, (_count * sizeof(TYPE))) == 0);
+//%}
+//%
+//%- (NSUInteger)hash {
+//%  // Follow NSArray's lead, and use the count as the hash.
+//%  return _count;
+//%}
+//%
+//%- (NSString *)description {
+//%  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+//%  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+//%    if (i == 0) {
+//%      [result appendFormat:@"##FORMAT##", _values[i]];
+//%    } else {
+//%      [result appendFormat:@", ##FORMAT##", _values[i]];
+//%    }
+//%  }
+//%  [result appendFormat:@" }"];
+//%  return result;
+//%}
+//%
+//%- (void)enumerate##ACCESSOR_NAME##ValuesWithBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block {
+//%  [self enumerate##ACCESSOR_NAME##ValuesWithOptions:(NSEnumerationOptions)0 usingBlock:block];
+//%}
+//%
+//%- (void)enumerate##ACCESSOR_NAME##ValuesWithOptions:(NSEnumerationOptions)opts
+//%                  ACCESSOR_NAME$S      usingBlock:(void (^)(TYPE value, NSUInteger idx, BOOL *stop))block {
+//%  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+//%  BOOL stop = NO;
+//%  if ((opts & NSEnumerationReverse) == 0) {
+//%    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+//%      block(_values[i], i, &stop);
+//%      if (stop) break;
+//%    }
+//%  } else if (_count > 0) {
+//%    for (NSUInteger i = _count; i > 0; --i) {
+//%      block(_values[i - 1], (i - 1), &stop);
+//%      if (stop) break;
+//%    }
+//%  }
+//%}
+
+//%PDDM-DEFINE MUTATION_HOOK_None()
+//%PDDM-DEFINE MUTATION_METHODS(NAME, TYPE, ACCESSOR_NAME, HOOK_1, HOOK_2)
+//%- (void)add##ACCESSOR_NAME##Value:(TYPE)value {
+//%  [self add##ACCESSOR_NAME##Values:&value count:1];
+//%}
+//%
+//%- (void)add##ACCESSOR_NAME##Values:(const TYPE [])values count:(NSUInteger)count {
+//%  if (values == NULL || count == 0) return;
+//%MUTATION_HOOK_##HOOK_1()  NSUInteger initialCount = _count;
+//%  NSUInteger newCount = initialCount + count;
+//%MAYBE_GROW_TO_SET_COUNT(newCount)
+//%  memcpy(&_values[initialCount], values, count * sizeof(TYPE));
+//%  if (_autocreator) {
+//%    GPBAutocreatedArrayModified(_autocreator, self);
+//%  }
+//%}
+//%
+//%- (void)insert##ACCESSOR_NAME##Value:(TYPE)value atIndex:(NSUInteger)index {
+//%VALIDATE_RANGE(index, _count + 1)
+//%MUTATION_HOOK_##HOOK_2()  NSUInteger initialCount = _count;
+//%  NSUInteger newCount = initialCount + 1;
+//%MAYBE_GROW_TO_SET_COUNT(newCount)
+//%  if (index != initialCount) {
+//%    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(TYPE));
+//%  }
+//%  _values[index] = value;
+//%  if (_autocreator) {
+//%    GPBAutocreatedArrayModified(_autocreator, self);
+//%  }
+//%}
+//%
+//%- (void)replaceValueAtIndex:(NSUInteger)index with##ACCESSOR_NAME##Value:(TYPE)value {
+//%VALIDATE_RANGE(index, _count)
+//%MUTATION_HOOK_##HOOK_2()  _values[index] = value;
+//%}
+
+//%PDDM-DEFINE ARRAY_MUTABLE_CORE(NAME, TYPE, ACCESSOR_NAME, FORMAT)
+//%- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+//%  _values = reallocf(_values, newCapacity * sizeof(TYPE));
+//%  if (_values == NULL) {
+//%    _capacity = 0;
+//%    _count = 0;
+//%    [NSException raise:NSMallocException
+//%                format:@"Failed to allocate %lu bytes",
+//%                       (unsigned long)(newCapacity * sizeof(TYPE))];
+//%  }
+//%  _capacity = newCapacity;
+//%}
+//%
+//%MUTATION_METHODS(NAME, TYPE, ACCESSOR_NAME, None, None)
+//%
+//%- (void)add##ACCESSOR_NAME##ValuesFromArray:(GPB##NAME##Array *)array {
+//%  [self add##ACCESSOR_NAME##Values:array->_values count:array->_count];
+//%}
+//%
+//%- (void)removeValueAtIndex:(NSUInteger)index {
+//%VALIDATE_RANGE(index, _count)
+//%  NSUInteger newCount = _count - 1;
+//%  if (index != newCount) {
+//%    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(TYPE));
+//%  }
+//%SET_COUNT_AND_MAYBE_SHRINK(newCount)
+//%}
+//%
+//%- (void)removeAll {
+//%SET_COUNT_AND_MAYBE_SHRINK(0)
+//%}
+//%
+//%- (void)exchangeValueAtIndex:(NSUInteger)idx1
+//%            withValueAtIndex:(NSUInteger)idx2 {
+//%VALIDATE_RANGE(idx1, _count)
+//%VALIDATE_RANGE(idx2, _count)
+//%  TYPE temp = _values[idx1];
+//%  _values[idx1] = _values[idx2];
+//%  _values[idx2] = temp;
+//%}
+//%
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(Int32, int32_t, %d)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int32
+
+@implementation GPBInt32Array {
+ @package
+  int32_t *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] init] autorelease];
+}
+
++ (instancetype)arrayWithValue:(int32_t)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBInt32Array*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBInt32Array *)array {
+  return [[(GPBInt32Array*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  self = [super init];
+  // No work needed;
+  return self;
+}
+
+- (instancetype)initWithValueArray:(GPBInt32Array *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const int32_t [])values count:(NSUInteger)count {
+  self = [self init];
+  if (self) {
+    if (count && values) {
+      _values = reallocf(_values, count * sizeof(int32_t));
+      if (_values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(int32_t));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(int32_t))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32Array allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32Array class]]) {
+    return NO;
+  }
+  GPBInt32Array *otherArray = other;
+  return (_count == otherArray->_count
+          && memcmp(_values, otherArray->_values, (_count * sizeof(int32_t))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%d", _values[i]];
+    } else {
+      [result appendFormat:@", %d", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:(NSEnumerationOptions)0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (int32_t)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(int32_t));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(int32_t))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(int32_t)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const int32_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  memcpy(&_values[initialCount], values, count * sizeof(int32_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(int32_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(int32_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int32_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBInt32Array *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(int32_t));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  int32_t temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(UInt32, uint32_t, %u)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt32
+
+@implementation GPBUInt32Array {
+ @package
+  uint32_t *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] init] autorelease];
+}
+
++ (instancetype)arrayWithValue:(uint32_t)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBUInt32Array*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBUInt32Array *)array {
+  return [[(GPBUInt32Array*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  self = [super init];
+  // No work needed;
+  return self;
+}
+
+- (instancetype)initWithValueArray:(GPBUInt32Array *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const uint32_t [])values count:(NSUInteger)count {
+  self = [self init];
+  if (self) {
+    if (count && values) {
+      _values = reallocf(_values, count * sizeof(uint32_t));
+      if (_values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(uint32_t));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(uint32_t))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32Array allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32Array class]]) {
+    return NO;
+  }
+  GPBUInt32Array *otherArray = other;
+  return (_count == otherArray->_count
+          && memcmp(_values, otherArray->_values, (_count * sizeof(uint32_t))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%u", _values[i]];
+    } else {
+      [result appendFormat:@", %u", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(uint32_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:(NSEnumerationOptions)0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(uint32_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (uint32_t)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(uint32_t));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(uint32_t))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(uint32_t)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const uint32_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  memcpy(&_values[initialCount], values, count * sizeof(uint32_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(uint32_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(uint32_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(uint32_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBUInt32Array *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(uint32_t));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  uint32_t temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(Int64, int64_t, %lld)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int64
+
+@implementation GPBInt64Array {
+ @package
+  int64_t *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] init] autorelease];
+}
+
++ (instancetype)arrayWithValue:(int64_t)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBInt64Array*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBInt64Array *)array {
+  return [[(GPBInt64Array*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  self = [super init];
+  // No work needed;
+  return self;
+}
+
+- (instancetype)initWithValueArray:(GPBInt64Array *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const int64_t [])values count:(NSUInteger)count {
+  self = [self init];
+  if (self) {
+    if (count && values) {
+      _values = reallocf(_values, count * sizeof(int64_t));
+      if (_values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(int64_t));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(int64_t))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64Array allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64Array class]]) {
+    return NO;
+  }
+  GPBInt64Array *otherArray = other;
+  return (_count == otherArray->_count
+          && memcmp(_values, otherArray->_values, (_count * sizeof(int64_t))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%lld", _values[i]];
+    } else {
+      [result appendFormat:@", %lld", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(int64_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:(NSEnumerationOptions)0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int64_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (int64_t)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(int64_t));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(int64_t))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(int64_t)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const int64_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  memcpy(&_values[initialCount], values, count * sizeof(int64_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(int64_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(int64_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int64_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBInt64Array *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(int64_t));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  int64_t temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(UInt64, uint64_t, %llu)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt64
+
+@implementation GPBUInt64Array {
+ @package
+  uint64_t *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] init] autorelease];
+}
+
++ (instancetype)arrayWithValue:(uint64_t)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBUInt64Array*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBUInt64Array *)array {
+  return [[(GPBUInt64Array*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  self = [super init];
+  // No work needed;
+  return self;
+}
+
+- (instancetype)initWithValueArray:(GPBUInt64Array *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const uint64_t [])values count:(NSUInteger)count {
+  self = [self init];
+  if (self) {
+    if (count && values) {
+      _values = reallocf(_values, count * sizeof(uint64_t));
+      if (_values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(uint64_t));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(uint64_t))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64Array allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64Array class]]) {
+    return NO;
+  }
+  GPBUInt64Array *otherArray = other;
+  return (_count == otherArray->_count
+          && memcmp(_values, otherArray->_values, (_count * sizeof(uint64_t))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%llu", _values[i]];
+    } else {
+      [result appendFormat:@", %llu", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(uint64_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:(NSEnumerationOptions)0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(uint64_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (uint64_t)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(uint64_t));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(uint64_t))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(uint64_t)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const uint64_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  memcpy(&_values[initialCount], values, count * sizeof(uint64_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(uint64_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(uint64_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(uint64_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBUInt64Array *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(uint64_t));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  uint64_t temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(Float, float, %f)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Float
+
+@implementation GPBFloatArray {
+ @package
+  float *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] init] autorelease];
+}
+
++ (instancetype)arrayWithValue:(float)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBFloatArray*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBFloatArray *)array {
+  return [[(GPBFloatArray*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  self = [super init];
+  // No work needed;
+  return self;
+}
+
+- (instancetype)initWithValueArray:(GPBFloatArray *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const float [])values count:(NSUInteger)count {
+  self = [self init];
+  if (self) {
+    if (count && values) {
+      _values = reallocf(_values, count * sizeof(float));
+      if (_values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(float));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(float))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBFloatArray allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBFloatArray class]]) {
+    return NO;
+  }
+  GPBFloatArray *otherArray = other;
+  return (_count == otherArray->_count
+          && memcmp(_values, otherArray->_values, (_count * sizeof(float))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%f", _values[i]];
+    } else {
+      [result appendFormat:@", %f", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(float value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:(NSEnumerationOptions)0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(float value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (float)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(float));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(float))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(float)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const float [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  memcpy(&_values[initialCount], values, count * sizeof(float));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(float)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(float));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(float)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBFloatArray *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(float));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  float temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(Double, double, %lf)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Double
+
+@implementation GPBDoubleArray {
+ @package
+  double *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] init] autorelease];
+}
+
++ (instancetype)arrayWithValue:(double)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBDoubleArray*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBDoubleArray *)array {
+  return [[(GPBDoubleArray*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  self = [super init];
+  // No work needed;
+  return self;
+}
+
+- (instancetype)initWithValueArray:(GPBDoubleArray *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const double [])values count:(NSUInteger)count {
+  self = [self init];
+  if (self) {
+    if (count && values) {
+      _values = reallocf(_values, count * sizeof(double));
+      if (_values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(double));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(double))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBDoubleArray allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBDoubleArray class]]) {
+    return NO;
+  }
+  GPBDoubleArray *otherArray = other;
+  return (_count == otherArray->_count
+          && memcmp(_values, otherArray->_values, (_count * sizeof(double))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%lf", _values[i]];
+    } else {
+      [result appendFormat:@", %lf", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(double value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:(NSEnumerationOptions)0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(double value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (double)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(double));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(double))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(double)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const double [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  memcpy(&_values[initialCount], values, count * sizeof(double));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(double)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(double));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(double)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBDoubleArray *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(double));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  double temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND ARRAY_INTERFACE_SIMPLE(Bool, BOOL, %d)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool
+
+@implementation GPBBoolArray {
+ @package
+  BOOL *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+
++ (instancetype)array {
+  return [[[self alloc] init] autorelease];
+}
+
++ (instancetype)arrayWithValue:(BOOL)value {
+  // Cast is needed so the compiler knows what class we are invoking initWithValues: on to get
+  // the type correct.
+  return [[(GPBBoolArray*)[self alloc] initWithValues:&value count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBBoolArray *)array {
+  return [[(GPBBoolArray*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithCapacity:(NSUInteger)count {
+  return [[[self alloc] initWithCapacity:count] autorelease];
+}
+
+- (instancetype)init {
+  self = [super init];
+  // No work needed;
+  return self;
+}
+
+- (instancetype)initWithValueArray:(GPBBoolArray *)array {
+  return [self initWithValues:array->_values count:array->_count];
+}
+
+- (instancetype)initWithValues:(const BOOL [])values count:(NSUInteger)count {
+  self = [self init];
+  if (self) {
+    if (count && values) {
+      _values = reallocf(_values, count * sizeof(BOOL));
+      if (_values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(BOOL));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(BOOL))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)count {
+  self = [self initWithValues:NULL count:0];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolArray allocWithZone:zone] initWithValues:_values count:_count];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolArray class]]) {
+    return NO;
+  }
+  GPBBoolArray *otherArray = other;
+  return (_count == otherArray->_count
+          && memcmp(_values, otherArray->_values, (_count * sizeof(BOOL))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%d", _values[i]];
+    } else {
+      [result appendFormat:@", %d", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(BOOL value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:(NSEnumerationOptions)0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(BOOL value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+
+- (BOOL)valueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  return _values[index];
+}
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(BOOL));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(BOOL))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addValue:(BOOL)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const BOOL [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  memcpy(&_values[initialCount], values, count * sizeof(BOOL));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(BOOL)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(BOOL));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(BOOL)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addValuesFromArray:(GPBBoolArray *)array {
+  [self addValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(BOOL));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  BOOL temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+@end
+
+//%PDDM-EXPAND-END (7 expansions)
+
+#pragma mark - Enum
+
+@implementation GPBEnumArray {
+ @package
+  GPBEnumValidationFunc _validationFunc;
+  int32_t *_values;
+  NSUInteger _count;
+  NSUInteger _capacity;
+}
+
+@synthesize count = _count;
+@synthesize validationFunc = _validationFunc;
+
++ (instancetype)array {
+  return [[[self alloc] initWithValidationFunction:NULL] autorelease];
+}
+
++ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [[[self alloc] initWithValidationFunction:func] autorelease];
+}
+
++ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func
+                                   rawValue:(int32_t)value {
+  return [[[self alloc] initWithValidationFunction:func
+                                         rawValues:&value
+                                             count:1] autorelease];
+}
+
++ (instancetype)arrayWithValueArray:(GPBEnumArray *)array {
+  return [[(GPBEnumArray*)[self alloc] initWithValueArray:array] autorelease];
+}
+
++ (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func
+                                   capacity:(NSUInteger)count {
+  return [[[self alloc] initWithValidationFunction:func capacity:count] autorelease];
+}
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL];
+}
+
+- (instancetype)initWithValueArray:(GPBEnumArray *)array {
+  return [self initWithValidationFunction:array->_validationFunc
+                                rawValues:array->_values
+                                    count:array->_count];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  self = [super init];
+  if (self) {
+    _validationFunc = (func != NULL ? func : ArrayDefault_IsValidValue);
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])values
+                                     count:(NSUInteger)count {
+  self = [self initWithValidationFunction:func];
+  if (self) {
+    if (count && values) {
+      _values = reallocf(_values, count * sizeof(int32_t));
+      if (_values != NULL) {
+        _capacity = count;
+        memcpy(_values, values, count * sizeof(int32_t));
+        _count = count;
+      } else {
+        [self release];
+        [NSException raise:NSMallocException
+                    format:@"Failed to allocate %lu bytes",
+                           (unsigned long)(count * sizeof(int32_t))];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)count {
+  self = [self initWithValidationFunction:func];
+  if (self && count) {
+    [self internalResizeToCapacity:count];
+  }
+  return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBEnumArray allocWithZone:zone]
+             initWithValidationFunction:_validationFunc
+                              rawValues:_values
+                                  count:_count];
+}
+
+//%PDDM-EXPAND ARRAY_IMMUTABLE_CORE(Enum, int32_t, Raw, %d)
+// This block of code is generated, do not edit it directly.
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  free(_values);
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBEnumArray class]]) {
+    return NO;
+  }
+  GPBEnumArray *otherArray = other;
+  return (_count == otherArray->_count
+          && memcmp(_values, otherArray->_values, (_count * sizeof(int32_t))) == 0);
+}
+
+- (NSUInteger)hash {
+  // Follow NSArray's lead, and use the count as the hash.
+  return _count;
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> { ", [self class], self];
+  for (NSUInteger i = 0, count = _count; i < count; ++i) {
+    if (i == 0) {
+      [result appendFormat:@"%d", _values[i]];
+    } else {
+      [result appendFormat:@", %d", _values[i]];
+    }
+  }
+  [result appendFormat:@" }"];
+  return result;
+}
+
+- (void)enumerateRawValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateRawValuesWithOptions:(NSEnumerationOptions)0 usingBlock:block];
+}
+
+- (void)enumerateRawValuesWithOptions:(NSEnumerationOptions)opts
+                           usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  if ((opts & NSEnumerationReverse) == 0) {
+    for (NSUInteger i = 0, count = _count; i < count; ++i) {
+      block(_values[i], i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    for (NSUInteger i = _count; i > 0; --i) {
+      block(_values[i - 1], (i - 1), &stop);
+      if (stop) break;
+    }
+  }
+}
+//%PDDM-EXPAND-END ARRAY_IMMUTABLE_CORE(Enum, int32_t, Raw, %d)
+
+- (int32_t)valueAtIndex:(NSUInteger)index {
+//%PDDM-EXPAND VALIDATE_RANGE(index, _count)
+// This block of code is generated, do not edit it directly.
+
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+//%PDDM-EXPAND-END VALIDATE_RANGE(index, _count)
+  int32_t result = _values[index];
+  if (!_validationFunc(result)) {
+    result = kGPBUnrecognizedEnumeratorValue;
+  }
+  return result;
+}
+
+- (int32_t)rawValueAtIndex:(NSUInteger)index {
+//%PDDM-EXPAND VALIDATE_RANGE(index, _count)
+// This block of code is generated, do not edit it directly.
+
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+//%PDDM-EXPAND-END VALIDATE_RANGE(index, _count)
+  return _values[index];
+}
+
+- (void)enumerateValuesWithBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  [self enumerateValuesWithOptions:(NSEnumerationOptions)0 usingBlock:block];
+}
+
+- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
+                        usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block {
+  // NSEnumerationConcurrent isn't currently supported (and Apple's docs say that is ok).
+  BOOL stop = NO;
+  GPBEnumValidationFunc func = _validationFunc;
+  if ((opts & NSEnumerationReverse) == 0) {
+    int32_t *scan = _values;
+    int32_t *end = scan + _count;
+    for (NSUInteger i = 0; scan < end; ++i, ++scan) {
+      int32_t value = *scan;
+      if (!func(value)) {
+        value = kGPBUnrecognizedEnumeratorValue;
+      }
+      block(value, i, &stop);
+      if (stop) break;
+    }
+  } else if (_count > 0) {
+    int32_t *end = _values;
+    int32_t *scan = end + (_count - 1);
+    for (NSUInteger i = (_count - 1); scan >= end; --i, --scan) {
+      int32_t value = *scan;
+      if (!func(value)) {
+        value = kGPBUnrecognizedEnumeratorValue;
+      }
+      block(value, i, &stop);
+      if (stop) break;
+    }
+  }
+}
+
+//%PDDM-EXPAND ARRAY_MUTABLE_CORE(Enum, int32_t, Raw, %d)
+// This block of code is generated, do not edit it directly.
+
+- (void)internalResizeToCapacity:(NSUInteger)newCapacity {
+  _values = reallocf(_values, newCapacity * sizeof(int32_t));
+  if (_values == NULL) {
+    _capacity = 0;
+    _count = 0;
+    [NSException raise:NSMallocException
+                format:@"Failed to allocate %lu bytes",
+                       (unsigned long)(newCapacity * sizeof(int32_t))];
+  }
+  _capacity = newCapacity;
+}
+
+- (void)addRawValue:(int32_t)value {
+  [self addRawValues:&value count:1];
+}
+
+- (void)addRawValues:(const int32_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  memcpy(&_values[initialCount], values, count * sizeof(int32_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertRawValue:(int32_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(int32_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withRawValue:(int32_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  _values[index] = value;
+}
+
+- (void)addRawValuesFromArray:(GPBEnumArray *)array {
+  [self addRawValues:array->_values count:array->_count];
+}
+
+- (void)removeValueAtIndex:(NSUInteger)index {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  NSUInteger newCount = _count - 1;
+  if (index != newCount) {
+    memmove(&_values[index], &_values[index + 1], (newCount - index) * sizeof(int32_t));
+  }
+  _count = newCount;
+  if ((newCount + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+}
+
+- (void)removeAll {
+  _count = 0;
+  if ((0 + (2 * kChunkSize)) < _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(0)];
+  }
+}
+
+- (void)exchangeValueAtIndex:(NSUInteger)idx1
+            withValueAtIndex:(NSUInteger)idx2 {
+  if (idx1 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx1, (unsigned long)_count];
+  }
+  if (idx2 >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)idx2, (unsigned long)_count];
+  }
+  int32_t temp = _values[idx1];
+  _values[idx1] = _values[idx2];
+  _values[idx2] = temp;
+}
+
+//%PDDM-EXPAND MUTATION_METHODS(Enum, int32_t, , EnumValidationList, EnumValidationOne)
+// This block of code is generated, do not edit it directly.
+
+- (void)addValue:(int32_t)value {
+  [self addValues:&value count:1];
+}
+
+- (void)addValues:(const int32_t [])values count:(NSUInteger)count {
+  if (values == NULL || count == 0) return;
+  GPBEnumValidationFunc func = _validationFunc;
+  for (NSUInteger i = 0; i < count; ++i) {
+    if (!func(values[i])) {
+      [NSException raise:NSInvalidArgumentException
+                  format:@"%@: Attempt to set an unknown enum value (%d)",
+                         [self class], values[i]];
+    }
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + count;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  memcpy(&_values[initialCount], values, count * sizeof(int32_t));
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)insertValue:(int32_t)value atIndex:(NSUInteger)index {
+  if (index >= _count + 1) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count + 1];
+  }
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@: Attempt to set an unknown enum value (%d)",
+                       [self class], value];
+  }
+  NSUInteger initialCount = _count;
+  NSUInteger newCount = initialCount + 1;
+  if (newCount > _capacity) {
+    [self internalResizeToCapacity:CapacityFromCount(newCount)];
+  }
+  _count = newCount;
+  if (index != initialCount) {
+    memmove(&_values[index + 1], &_values[index], (initialCount - index) * sizeof(int32_t));
+  }
+  _values[index] = value;
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int32_t)value {
+  if (index >= _count) {
+    [NSException raise:NSRangeException
+                format:@"Index (%lu) beyond bounds (%lu)",
+                       (unsigned long)index, (unsigned long)_count];
+  }
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@: Attempt to set an unknown enum value (%d)",
+                       [self class], value];
+  }
+  _values[index] = value;
+}
+//%PDDM-EXPAND-END (2 expansions)
+
+//%PDDM-DEFINE MUTATION_HOOK_EnumValidationList()
+//%  GPBEnumValidationFunc func = _validationFunc;
+//%  for (NSUInteger i = 0; i < count; ++i) {
+//%    if (!func(values[i])) {
+//%      [NSException raise:NSInvalidArgumentException
+//%                  format:@"%@: Attempt to set an unknown enum value (%d)",
+//%                         [self class], values[i]];
+//%    }
+//%  }
+//%
+//%PDDM-DEFINE MUTATION_HOOK_EnumValidationOne()
+//%  if (!_validationFunc(value)) {
+//%    [NSException raise:NSInvalidArgumentException
+//%                format:@"%@: Attempt to set an unknown enum value (%d)",
+//%                       [self class], value];
+//%  }
+//%
+
+@end
+
+#pragma mark - NSArray Subclass
+
+@implementation GPBAutocreatedArray {
+  NSMutableArray *_array;
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_array release];
+  [super dealloc];
+}
+
+#pragma mark Required NSArray overrides
+
+- (NSUInteger)count {
+  return [_array count];
+}
+
+- (id)objectAtIndex:(NSUInteger)idx {
+  return [_array objectAtIndex:idx];
+}
+
+#pragma mark Required NSMutableArray overrides
+
+// Only need to call GPBAutocreatedArrayModified() when adding things since
+// we only autocreate empty arrays.
+
+- (void)insertObject:(id)anObject atIndex:(NSUInteger)idx {
+  if (_array == nil) {
+    _array = [[NSMutableArray alloc] init];
+  }
+  [_array insertObject:anObject atIndex:idx];
+
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)removeObject:(id)anObject {
+  [_array removeObject:anObject];
+}
+
+- (void)removeObjectAtIndex:(NSUInteger)idx {
+  [_array removeObjectAtIndex:idx];
+}
+
+- (void)addObject:(id)anObject {
+  if (_array == nil) {
+    _array = [[NSMutableArray alloc] init];
+  }
+  [_array addObject:anObject];
+
+  if (_autocreator) {
+    GPBAutocreatedArrayModified(_autocreator, self);
+  }
+}
+
+- (void)removeLastObject {
+  [_array removeLastObject];
+}
+
+- (void)replaceObjectAtIndex:(NSUInteger)idx withObject:(id)anObject {
+  [_array replaceObjectAtIndex:idx withObject:anObject];
+}
+
+#pragma mark Extra things hooked
+
+- (id)copyWithZone:(NSZone *)zone {
+  if (_array == nil) {
+    return [[NSMutableArray allocWithZone:zone] init];
+  }
+  return [_array copyWithZone:zone];
+}
+
+- (id)mutableCopyWithZone:(NSZone *)zone {
+  if (_array == nil) {
+    return [[NSMutableArray allocWithZone:zone] init];
+  }
+  return [_array mutableCopyWithZone:zone];
+}
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
+                                  objects:(id __unsafe_unretained [])buffer
+                                    count:(NSUInteger)len {
+  return [_array countByEnumeratingWithState:state objects:buffer count:len];
+}
+
+- (void)enumerateObjectsUsingBlock:(void (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))block {
+  [_array enumerateObjectsUsingBlock:block];
+}
+
+- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts
+                         usingBlock:(void (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))block {
+  [_array enumerateObjectsWithOptions:opts usingBlock:block];
+}
+
+@end
+
+#pragma clang diagnostic pop
diff --git a/iOS/Pods/Protobuf/objectivec/GPBArray_PackagePrivate.h b/iOS/Pods/Protobuf/objectivec/GPBArray_PackagePrivate.h
new file mode 100644 (file)
index 0000000..35a4538
--- /dev/null
@@ -0,0 +1,130 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBArray.h"
+
+@class GPBMessage;
+
+//%PDDM-DEFINE DECLARE_ARRAY_EXTRAS()
+//%ARRAY_INTERFACE_EXTRAS(Int32, int32_t)
+//%ARRAY_INTERFACE_EXTRAS(UInt32, uint32_t)
+//%ARRAY_INTERFACE_EXTRAS(Int64, int64_t)
+//%ARRAY_INTERFACE_EXTRAS(UInt64, uint64_t)
+//%ARRAY_INTERFACE_EXTRAS(Float, float)
+//%ARRAY_INTERFACE_EXTRAS(Double, double)
+//%ARRAY_INTERFACE_EXTRAS(Bool, BOOL)
+//%ARRAY_INTERFACE_EXTRAS(Enum, int32_t)
+
+//%PDDM-DEFINE ARRAY_INTERFACE_EXTRAS(NAME, TYPE)
+//%#pragma mark - NAME
+//%
+//%@interface GPB##NAME##Array () {
+//% @package
+//%  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+//%}
+//%@end
+//%
+
+//%PDDM-EXPAND DECLARE_ARRAY_EXTRAS()
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int32
+
+@interface GPBInt32Array () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - UInt32
+
+@interface GPBUInt32Array () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Int64
+
+@interface GPBInt64Array () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - UInt64
+
+@interface GPBUInt64Array () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Float
+
+@interface GPBFloatArray () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Double
+
+@interface GPBDoubleArray () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Bool
+
+@interface GPBBoolArray () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Enum
+
+@interface GPBEnumArray () {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+//%PDDM-EXPAND-END DECLARE_ARRAY_EXTRAS()
+
+#pragma mark - NSArray Subclass
+
+@interface GPBAutocreatedArray : NSMutableArray {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
diff --git a/iOS/Pods/Protobuf/objectivec/GPBBootstrap.h b/iOS/Pods/Protobuf/objectivec/GPBBootstrap.h
new file mode 100644 (file)
index 0000000..ed53ae7
--- /dev/null
@@ -0,0 +1,123 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+/**
+ * The Objective C runtime has complete enough info that most protos don’t end
+ * up using this, so leaving it on is no cost or very little cost.  If you
+ * happen to see it causing bloat, this is the way to disable it. If you do
+ * need to disable it, try only disabling it for Release builds as having
+ * full TextFormat can be useful for debugging.
+ **/
+#ifndef GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+#define GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS 0
+#endif
+
+// Used in the generated code to give sizes to enums. int32_t was chosen based
+// on the fact that Protocol Buffers enums are limited to this range.
+#if !__has_feature(objc_fixed_enum)
+ #error All supported Xcode versions should support objc_fixed_enum.
+#endif
+
+// If the headers are imported into Objective-C++, we can run into an issue
+// where the defintion of NS_ENUM (really CF_ENUM) changes based on the C++
+// standard that is in effect.  If it isn't C++11 or higher, the definition
+// doesn't allow us to forward declare. We work around this one case by
+// providing a local definition. The default case has to use NS_ENUM for the
+// magic that is Swift bridging of enums.
+#if (defined(__cplusplus) && __cplusplus && __cplusplus < 201103L)
+ #define GPB_ENUM(X) enum X : int32_t X; enum X : int32_t
+#else
+ #define GPB_ENUM(X) NS_ENUM(int32_t, X)
+#endif
+
+/**
+ * GPB_ENUM_FWD_DECLARE is used for forward declaring enums, for example:
+ *
+ * ```
+ * GPB_ENUM_FWD_DECLARE(Foo_Enum)
+ *
+ * @interface BarClass : NSObject
+ * @property (nonatomic) enum Foo_Enum value;
+ * - (void)bazMethod:(enum Foo_Enum):value;
+ * @end
+ * ```
+ **/
+#define GPB_ENUM_FWD_DECLARE(X) enum X : int32_t
+
+/**
+ * Based upon CF_INLINE. Forces inlining in non DEBUG builds.
+ **/
+#if !defined(DEBUG)
+#define GPB_INLINE static __inline__ __attribute__((always_inline))
+#else
+#define GPB_INLINE static __inline__
+#endif
+
+/**
+ * For use in public headers that might need to deal with ARC.
+ **/
+#ifndef GPB_UNSAFE_UNRETAINED
+#if __has_feature(objc_arc)
+#define GPB_UNSAFE_UNRETAINED __unsafe_unretained
+#else
+#define GPB_UNSAFE_UNRETAINED
+#endif
+#endif
+
+// If property name starts with init we need to annotate it to get past ARC.
+// http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227
+//
+// Meant to be used internally by generated code.
+#define GPB_METHOD_FAMILY_NONE __attribute__((objc_method_family(none)))
+
+// ----------------------------------------------------------------------------
+// These version numbers are all internal to the ObjC Protobuf runtime; they
+// are used to ensure compatibility between the generated sources and the
+// headers being compiled against and/or the version of sources being run
+// against.
+//
+// They are all #defines so the values are captured into every .o file they
+// are used in and to allow comparisons in the preprocessor.
+
+// Current library runtime version.
+// - Gets bumped when the runtime makes changes to the interfaces between the
+//   generated code and runtime (things added/removed, etc).
+#define GOOGLE_PROTOBUF_OBJC_VERSION 30002
+
+// Minimum runtime version supported for compiling/running against.
+// - Gets changed when support for the older generated code is dropped.
+#define GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION 30001
+
+
+// This is a legacy constant now frozen in time for old generated code. If
+// GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION ever gets moved above 30001 then
+// this should also change to break code compiled with an old runtime that
+// can't be supported any more.
+#define GOOGLE_PROTOBUF_OBJC_GEN_VERSION 30001
diff --git a/iOS/Pods/Protobuf/objectivec/GPBCodedInputStream.h b/iOS/Pods/Protobuf/objectivec/GPBCodedInputStream.h
new file mode 100644 (file)
index 0000000..fbe5009
--- /dev/null
@@ -0,0 +1,253 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+@class GPBMessage;
+@class GPBExtensionRegistry;
+
+NS_ASSUME_NONNULL_BEGIN
+
+CF_EXTERN_C_BEGIN
+
+/**
+ * @c GPBCodedInputStream exception name. Exceptions raised from
+ * @c GPBCodedInputStream contain an underlying error in the userInfo dictionary
+ * under the GPBCodedInputStreamUnderlyingErrorKey key.
+ **/
+extern NSString *const GPBCodedInputStreamException;
+
+/** The key under which the underlying NSError from the exception is stored. */
+extern NSString *const GPBCodedInputStreamUnderlyingErrorKey;
+
+/** NSError domain used for @c GPBCodedInputStream errors. */
+extern NSString *const GPBCodedInputStreamErrorDomain;
+
+/**
+ * Error code for NSError with @c GPBCodedInputStreamErrorDomain.
+ **/
+typedef NS_ENUM(NSInteger, GPBCodedInputStreamErrorCode) {
+  /** The size does not fit in the remaining bytes to be read. */
+  GPBCodedInputStreamErrorInvalidSize = -100,
+  /** Attempted to read beyond the subsection limit. */
+  GPBCodedInputStreamErrorSubsectionLimitReached = -101,
+  /** The requested subsection limit is invalid. */
+  GPBCodedInputStreamErrorInvalidSubsectionLimit = -102,
+  /** Invalid tag read. */
+  GPBCodedInputStreamErrorInvalidTag = -103,
+  /** Invalid UTF-8 character in a string. */
+  GPBCodedInputStreamErrorInvalidUTF8 = -104,
+  /** Invalid VarInt read. */
+  GPBCodedInputStreamErrorInvalidVarInt = -105,
+  /** The maximum recursion depth of messages was exceeded. */
+  GPBCodedInputStreamErrorRecursionDepthExceeded = -106,
+};
+
+CF_EXTERN_C_END
+
+/**
+ * Reads and decodes protocol message fields.
+ *
+ * The common uses of protocol buffers shouldn't need to use this class.
+ * @c GPBMessage's provide a @c +parseFromData:error: and
+ * @c +parseFromData:extensionRegistry:error: method that will decode a
+ * message for you.
+ *
+ * @note Subclassing of @c GPBCodedInputStream is NOT supported.
+ **/
+@interface GPBCodedInputStream : NSObject
+
+/**
+ * Creates a new stream wrapping some data.
+ *
+ * @param data The data to wrap inside the stream.
+ *
+ * @return A newly instanced GPBCodedInputStream.
+ **/
++ (instancetype)streamWithData:(NSData *)data;
+
+/**
+ * Initializes a stream wrapping some data.
+ *
+ * @param data The data to wrap inside the stream.
+ *
+ * @return A newly initialized GPBCodedInputStream.
+ **/
+- (instancetype)initWithData:(NSData *)data;
+
+/**
+ * Attempts to read a field tag, returning zero if we have reached EOF.
+ * Protocol message parsers use this to read tags, since a protocol message
+ * may legally end wherever a tag occurs, and zero is not a valid tag number.
+ *
+ * @return The field tag, or zero if EOF was reached.
+ **/
+- (int32_t)readTag;
+
+/**
+ * @return A double read from the stream.
+ **/
+- (double)readDouble;
+/**
+ * @return A float read from the stream.
+ **/
+- (float)readFloat;
+/**
+ * @return A uint64 read from the stream.
+ **/
+- (uint64_t)readUInt64;
+/**
+ * @return A uint32 read from the stream.
+ **/
+- (uint32_t)readUInt32;
+/**
+ * @return An int64 read from the stream.
+ **/
+- (int64_t)readInt64;
+/**
+ * @return An int32 read from the stream.
+ **/
+- (int32_t)readInt32;
+/**
+ * @return A fixed64 read from the stream.
+ **/
+- (uint64_t)readFixed64;
+/**
+ * @return A fixed32 read from the stream.
+ **/
+- (uint32_t)readFixed32;
+/**
+ * @return An enum read from the stream.
+ **/
+- (int32_t)readEnum;
+/**
+ * @return A sfixed32 read from the stream.
+ **/
+- (int32_t)readSFixed32;
+/**
+ * @return A fixed64 read from the stream.
+ **/
+- (int64_t)readSFixed64;
+/**
+ * @return A sint32 read from the stream.
+ **/
+- (int32_t)readSInt32;
+/**
+ * @return A sint64 read from the stream.
+ **/
+- (int64_t)readSInt64;
+/**
+ * @return A boolean read from the stream.
+ **/
+- (BOOL)readBool;
+/**
+ * @return A string read from the stream.
+ **/
+- (NSString *)readString;
+/**
+ * @return Data read from the stream.
+ **/
+- (NSData *)readBytes;
+
+/**
+ * Read an embedded message field value from the stream.
+ *
+ * @param message           The message to set fields on as they are read.
+ * @param extensionRegistry An optional extension registry to use to lookup
+ *                          extensions for message.
+ **/
+- (void)readMessage:(GPBMessage *)message
+  extensionRegistry:(nullable GPBExtensionRegistry *)extensionRegistry;
+
+/**
+ * Reads and discards a single field, given its tag value.
+ *
+ * @param tag The tag number of the field to skip.
+ *
+ * @return NO if the tag is an endgroup tag (in which case nothing is skipped),
+ *         YES in all other cases.
+ **/
+- (BOOL)skipField:(int32_t)tag;
+
+/**
+ * Reads and discards an entire message. This will read either until EOF or
+ * until an endgroup tag, whichever comes first.
+ **/
+- (void)skipMessage;
+
+/**
+ * Check to see if the logical end of the stream has been reached.
+ *
+ * @note This can return NO when there is no more data, but the current parsing
+ *       expected more data.
+ *
+ * @return YES if the logical end of the stream has been reached, NO otherwise.
+ **/
+- (BOOL)isAtEnd;
+
+/**
+ * @return The offset into the stream.
+ **/
+- (size_t)position;
+
+/**
+ * Moves the limit to the given byte offset starting at the current location.
+ *
+ * @exception GPBCodedInputStreamException If the requested bytes exceeed the
+ *            current limit.
+ *
+ * @param byteLimit The number of bytes to move the limit, offset to the current
+ *                  location.
+ *
+ * @return The limit offset before moving the new limit.
+ */
+- (size_t)pushLimit:(size_t)byteLimit;
+
+/**
+ * Moves the limit back to the offset as it was before calling pushLimit:.
+ *
+ * @param oldLimit The number of bytes to move the current limit. Usually this
+ *                 is the value returned by the pushLimit: method.
+ */
+- (void)popLimit:(size_t)oldLimit;
+
+/**
+ * Verifies that the last call to -readTag returned the given tag value. This
+ * is used to verify that a nested group ended with the correct end tag.
+ *
+ * @exception NSParseErrorException If the value does not match the last tag.
+ *
+ * @param expected The tag that was expected.
+ **/
+- (void)checkLastTagWas:(int32_t)expected;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBCodedInputStream.m b/iOS/Pods/Protobuf/objectivec/GPBCodedInputStream.m
new file mode 100644 (file)
index 0000000..dd05ddb
--- /dev/null
@@ -0,0 +1,505 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBCodedInputStream_PackagePrivate.h"
+
+#import "GPBDictionary_PackagePrivate.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBUnknownFieldSet_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+#import "GPBWireFormat.h"
+
+NSString *const GPBCodedInputStreamException =
+    GPBNSStringifySymbol(GPBCodedInputStreamException);
+
+NSString *const GPBCodedInputStreamUnderlyingErrorKey =
+    GPBNSStringifySymbol(GPBCodedInputStreamUnderlyingErrorKey);
+
+NSString *const GPBCodedInputStreamErrorDomain =
+    GPBNSStringifySymbol(GPBCodedInputStreamErrorDomain);
+
+// Matching:
+// https://github.com/google/protobuf/blob/master/java/core/src/main/java/com/google/protobuf/CodedInputStream.java#L62
+//  private static final int DEFAULT_RECURSION_LIMIT = 100;
+// https://github.com/google/protobuf/blob/master/src/google/protobuf/io/coded_stream.cc#L86
+//  int CodedInputStream::default_recursion_limit_ = 100;
+static const NSUInteger kDefaultRecursionLimit = 100;
+
+static void RaiseException(NSInteger code, NSString *reason) {
+  NSDictionary *errorInfo = nil;
+  if ([reason length]) {
+    errorInfo = @{ GPBErrorReasonKey: reason };
+  }
+  NSError *error = [NSError errorWithDomain:GPBCodedInputStreamErrorDomain
+                                       code:code
+                                   userInfo:errorInfo];
+
+  NSDictionary *exceptionInfo =
+      @{ GPBCodedInputStreamUnderlyingErrorKey: error };
+  [[NSException exceptionWithName:GPBCodedInputStreamException
+                           reason:reason
+                         userInfo:exceptionInfo] raise];
+}
+
+static void CheckRecursionLimit(GPBCodedInputStreamState *state) {
+  if (state->recursionDepth >= kDefaultRecursionLimit) {
+    RaiseException(GPBCodedInputStreamErrorRecursionDepthExceeded, nil);
+  }
+}
+
+static void CheckSize(GPBCodedInputStreamState *state, size_t size) {
+  size_t newSize = state->bufferPos + size;
+  if (newSize > state->bufferSize) {
+    RaiseException(GPBCodedInputStreamErrorInvalidSize, nil);
+  }
+  if (newSize > state->currentLimit) {
+    // Fast forward to end of currentLimit;
+    state->bufferPos = state->currentLimit;
+    RaiseException(GPBCodedInputStreamErrorSubsectionLimitReached, nil);
+  }
+}
+
+static int8_t ReadRawByte(GPBCodedInputStreamState *state) {
+  CheckSize(state, sizeof(int8_t));
+  return ((int8_t *)state->bytes)[state->bufferPos++];
+}
+
+static int32_t ReadRawLittleEndian32(GPBCodedInputStreamState *state) {
+  CheckSize(state, sizeof(int32_t));
+  int32_t value = OSReadLittleInt32(state->bytes, state->bufferPos);
+  state->bufferPos += sizeof(int32_t);
+  return value;
+}
+
+static int64_t ReadRawLittleEndian64(GPBCodedInputStreamState *state) {
+  CheckSize(state, sizeof(int64_t));
+  int64_t value = OSReadLittleInt64(state->bytes, state->bufferPos);
+  state->bufferPos += sizeof(int64_t);
+  return value;
+}
+
+static int64_t ReadRawVarint64(GPBCodedInputStreamState *state) {
+  int32_t shift = 0;
+  int64_t result = 0;
+  while (shift < 64) {
+    int8_t b = ReadRawByte(state);
+    result |= (int64_t)((uint64_t)(b & 0x7F) << shift);
+    if ((b & 0x80) == 0) {
+      return result;
+    }
+    shift += 7;
+  }
+  RaiseException(GPBCodedInputStreamErrorInvalidVarInt, @"Invalid VarInt64");
+  return 0;
+}
+
+static int32_t ReadRawVarint32(GPBCodedInputStreamState *state) {
+  return (int32_t)ReadRawVarint64(state);
+}
+
+static void SkipRawData(GPBCodedInputStreamState *state, size_t size) {
+  CheckSize(state, size);
+  state->bufferPos += size;
+}
+
+double GPBCodedInputStreamReadDouble(GPBCodedInputStreamState *state) {
+  int64_t value = ReadRawLittleEndian64(state);
+  return GPBConvertInt64ToDouble(value);
+}
+
+float GPBCodedInputStreamReadFloat(GPBCodedInputStreamState *state) {
+  int32_t value = ReadRawLittleEndian32(state);
+  return GPBConvertInt32ToFloat(value);
+}
+
+uint64_t GPBCodedInputStreamReadUInt64(GPBCodedInputStreamState *state) {
+  uint64_t value = ReadRawVarint64(state);
+  return value;
+}
+
+uint32_t GPBCodedInputStreamReadUInt32(GPBCodedInputStreamState *state) {
+  uint32_t value = ReadRawVarint32(state);
+  return value;
+}
+
+int64_t GPBCodedInputStreamReadInt64(GPBCodedInputStreamState *state) {
+  int64_t value = ReadRawVarint64(state);
+  return value;
+}
+
+int32_t GPBCodedInputStreamReadInt32(GPBCodedInputStreamState *state) {
+  int32_t value = ReadRawVarint32(state);
+  return value;
+}
+
+uint64_t GPBCodedInputStreamReadFixed64(GPBCodedInputStreamState *state) {
+  uint64_t value = ReadRawLittleEndian64(state);
+  return value;
+}
+
+uint32_t GPBCodedInputStreamReadFixed32(GPBCodedInputStreamState *state) {
+  uint32_t value = ReadRawLittleEndian32(state);
+  return value;
+}
+
+int32_t GPBCodedInputStreamReadEnum(GPBCodedInputStreamState *state) {
+  int32_t value = ReadRawVarint32(state);
+  return value;
+}
+
+int32_t GPBCodedInputStreamReadSFixed32(GPBCodedInputStreamState *state) {
+  int32_t value = ReadRawLittleEndian32(state);
+  return value;
+}
+
+int64_t GPBCodedInputStreamReadSFixed64(GPBCodedInputStreamState *state) {
+  int64_t value = ReadRawLittleEndian64(state);
+  return value;
+}
+
+int32_t GPBCodedInputStreamReadSInt32(GPBCodedInputStreamState *state) {
+  int32_t value = GPBDecodeZigZag32(ReadRawVarint32(state));
+  return value;
+}
+
+int64_t GPBCodedInputStreamReadSInt64(GPBCodedInputStreamState *state) {
+  int64_t value = GPBDecodeZigZag64(ReadRawVarint64(state));
+  return value;
+}
+
+BOOL GPBCodedInputStreamReadBool(GPBCodedInputStreamState *state) {
+  return ReadRawVarint32(state) != 0;
+}
+
+int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) {
+  if (GPBCodedInputStreamIsAtEnd(state)) {
+    state->lastTag = 0;
+    return 0;
+  }
+
+  state->lastTag = ReadRawVarint32(state);
+  // Tags have to include a valid wireformat.
+  if (!GPBWireFormatIsValidTag(state->lastTag)) {
+    RaiseException(GPBCodedInputStreamErrorInvalidTag,
+                   @"Invalid wireformat in tag.");
+  }
+  // Zero is not a valid field number.
+  if (GPBWireFormatGetTagFieldNumber(state->lastTag) == 0) {
+    RaiseException(GPBCodedInputStreamErrorInvalidTag,
+                   @"A zero field number on the wire is invalid.");
+  }
+  return state->lastTag;
+}
+
+NSString *GPBCodedInputStreamReadRetainedString(
+    GPBCodedInputStreamState *state) {
+  int32_t size = ReadRawVarint32(state);
+  NSString *result;
+  if (size == 0) {
+    result = @"";
+  } else {
+    CheckSize(state, size);
+    result = [[NSString alloc] initWithBytes:&state->bytes[state->bufferPos]
+                                      length:size
+                                    encoding:NSUTF8StringEncoding];
+    state->bufferPos += size;
+    if (!result) {
+#ifdef DEBUG
+      // https://developers.google.com/protocol-buffers/docs/proto#scalar
+      NSLog(@"UTF-8 failure, is some field type 'string' when it should be "
+            @"'bytes'?");
+#endif
+      RaiseException(GPBCodedInputStreamErrorInvalidUTF8, nil);
+    }
+  }
+  return result;
+}
+
+NSData *GPBCodedInputStreamReadRetainedBytes(GPBCodedInputStreamState *state) {
+  int32_t size = ReadRawVarint32(state);
+  if (size < 0) return nil;
+  CheckSize(state, size);
+  NSData *result = [[NSData alloc] initWithBytes:state->bytes + state->bufferPos
+                                          length:size];
+  state->bufferPos += size;
+  return result;
+}
+
+NSData *GPBCodedInputStreamReadRetainedBytesNoCopy(
+    GPBCodedInputStreamState *state) {
+  int32_t size = ReadRawVarint32(state);
+  if (size < 0) return nil;
+  CheckSize(state, size);
+  // Cast is safe because freeWhenDone is NO.
+  NSData *result = [[NSData alloc]
+      initWithBytesNoCopy:(void *)(state->bytes + state->bufferPos)
+                   length:size
+             freeWhenDone:NO];
+  state->bufferPos += size;
+  return result;
+}
+
+size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state,
+                                    size_t byteLimit) {
+  byteLimit += state->bufferPos;
+  size_t oldLimit = state->currentLimit;
+  if (byteLimit > oldLimit) {
+    RaiseException(GPBCodedInputStreamErrorInvalidSubsectionLimit, nil);
+  }
+  state->currentLimit = byteLimit;
+  return oldLimit;
+}
+
+void GPBCodedInputStreamPopLimit(GPBCodedInputStreamState *state,
+                                 size_t oldLimit) {
+  state->currentLimit = oldLimit;
+}
+
+size_t GPBCodedInputStreamBytesUntilLimit(GPBCodedInputStreamState *state) {
+  return state->currentLimit - state->bufferPos;
+}
+
+BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state) {
+  return (state->bufferPos == state->bufferSize) ||
+         (state->bufferPos == state->currentLimit);
+}
+
+void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
+                                        int32_t value) {
+  if (state->lastTag != value) {
+    RaiseException(GPBCodedInputStreamErrorInvalidTag, @"Unexpected tag read");
+  }
+}
+
+@implementation GPBCodedInputStream
+
++ (instancetype)streamWithData:(NSData *)data {
+  return [[[self alloc] initWithData:data] autorelease];
+}
+
+- (instancetype)initWithData:(NSData *)data {
+  if ((self = [super init])) {
+#ifdef DEBUG
+    NSCAssert([self class] == [GPBCodedInputStream class],
+              @"Subclassing of GPBCodedInputStream is not allowed.");
+#endif
+    buffer_ = [data retain];
+    state_.bytes = (const uint8_t *)[data bytes];
+    state_.bufferSize = [data length];
+    state_.currentLimit = state_.bufferSize;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [buffer_ release];
+  [super dealloc];
+}
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+- (int32_t)readTag {
+  return GPBCodedInputStreamReadTag(&state_);
+}
+
+- (void)checkLastTagWas:(int32_t)value {
+  GPBCodedInputStreamCheckLastTagWas(&state_, value);
+}
+
+- (BOOL)skipField:(int32_t)tag {
+  NSAssert(GPBWireFormatIsValidTag(tag), @"Invalid tag");
+  switch (GPBWireFormatGetTagWireType(tag)) {
+    case GPBWireFormatVarint:
+      GPBCodedInputStreamReadInt32(&state_);
+      return YES;
+    case GPBWireFormatFixed64:
+      SkipRawData(&state_, sizeof(int64_t));
+      return YES;
+    case GPBWireFormatLengthDelimited:
+      SkipRawData(&state_, ReadRawVarint32(&state_));
+      return YES;
+    case GPBWireFormatStartGroup:
+      [self skipMessage];
+      GPBCodedInputStreamCheckLastTagWas(
+          &state_, GPBWireFormatMakeTag(GPBWireFormatGetTagFieldNumber(tag),
+                                        GPBWireFormatEndGroup));
+      return YES;
+    case GPBWireFormatEndGroup:
+      return NO;
+    case GPBWireFormatFixed32:
+      SkipRawData(&state_, sizeof(int32_t));
+      return YES;
+  }
+}
+
+- (void)skipMessage {
+  while (YES) {
+    int32_t tag = GPBCodedInputStreamReadTag(&state_);
+    if (tag == 0 || ![self skipField:tag]) {
+      return;
+    }
+  }
+}
+
+- (BOOL)isAtEnd {
+  return GPBCodedInputStreamIsAtEnd(&state_);
+}
+
+- (size_t)position {
+  return state_.bufferPos;
+}
+
+- (size_t)pushLimit:(size_t)byteLimit {
+  return GPBCodedInputStreamPushLimit(&state_, byteLimit);
+}
+
+- (void)popLimit:(size_t)oldLimit {
+  GPBCodedInputStreamPopLimit(&state_, oldLimit);
+}
+
+- (double)readDouble {
+  return GPBCodedInputStreamReadDouble(&state_);
+}
+
+- (float)readFloat {
+  return GPBCodedInputStreamReadFloat(&state_);
+}
+
+- (uint64_t)readUInt64 {
+  return GPBCodedInputStreamReadUInt64(&state_);
+}
+
+- (int64_t)readInt64 {
+  return GPBCodedInputStreamReadInt64(&state_);
+}
+
+- (int32_t)readInt32 {
+  return GPBCodedInputStreamReadInt32(&state_);
+}
+
+- (uint64_t)readFixed64 {
+  return GPBCodedInputStreamReadFixed64(&state_);
+}
+
+- (uint32_t)readFixed32 {
+  return GPBCodedInputStreamReadFixed32(&state_);
+}
+
+- (BOOL)readBool {
+  return GPBCodedInputStreamReadBool(&state_);
+}
+
+- (NSString *)readString {
+  return [GPBCodedInputStreamReadRetainedString(&state_) autorelease];
+}
+
+- (void)readGroup:(int32_t)fieldNumber
+              message:(GPBMessage *)message
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  CheckRecursionLimit(&state_);
+  ++state_.recursionDepth;
+  [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry];
+  GPBCodedInputStreamCheckLastTagWas(
+      &state_, GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup));
+  --state_.recursionDepth;
+}
+
+- (void)readUnknownGroup:(int32_t)fieldNumber
+                 message:(GPBUnknownFieldSet *)message {
+  CheckRecursionLimit(&state_);
+  ++state_.recursionDepth;
+  [message mergeFromCodedInputStream:self];
+  GPBCodedInputStreamCheckLastTagWas(
+      &state_, GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup));
+  --state_.recursionDepth;
+}
+
+- (void)readMessage:(GPBMessage *)message
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  CheckRecursionLimit(&state_);
+  int32_t length = ReadRawVarint32(&state_);
+  size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
+  ++state_.recursionDepth;
+  [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry];
+  GPBCodedInputStreamCheckLastTagWas(&state_, 0);
+  --state_.recursionDepth;
+  GPBCodedInputStreamPopLimit(&state_, oldLimit);
+}
+
+- (void)readMapEntry:(id)mapDictionary
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                field:(GPBFieldDescriptor *)field
+        parentMessage:(GPBMessage *)parentMessage {
+  CheckRecursionLimit(&state_);
+  int32_t length = ReadRawVarint32(&state_);
+  size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
+  ++state_.recursionDepth;
+  GPBDictionaryReadEntry(mapDictionary, self, extensionRegistry, field,
+                         parentMessage);
+  GPBCodedInputStreamCheckLastTagWas(&state_, 0);
+  --state_.recursionDepth;
+  GPBCodedInputStreamPopLimit(&state_, oldLimit);
+}
+
+- (NSData *)readBytes {
+  return [GPBCodedInputStreamReadRetainedBytes(&state_) autorelease];
+}
+
+- (uint32_t)readUInt32 {
+  return GPBCodedInputStreamReadUInt32(&state_);
+}
+
+- (int32_t)readEnum {
+  return GPBCodedInputStreamReadEnum(&state_);
+}
+
+- (int32_t)readSFixed32 {
+  return GPBCodedInputStreamReadSFixed32(&state_);
+}
+
+- (int64_t)readSFixed64 {
+  return GPBCodedInputStreamReadSFixed64(&state_);
+}
+
+- (int32_t)readSInt32 {
+  return GPBCodedInputStreamReadSInt32(&state_);
+}
+
+- (int64_t)readSInt64 {
+  return GPBCodedInputStreamReadSInt64(&state_);
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/iOS/Pods/Protobuf/objectivec/GPBCodedInputStream_PackagePrivate.h b/iOS/Pods/Protobuf/objectivec/GPBCodedInputStream_PackagePrivate.h
new file mode 100644 (file)
index 0000000..43ec6e7
--- /dev/null
@@ -0,0 +1,112 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+// This header is private to the ProtobolBuffers library and must NOT be
+// included by any sources outside this library. The contents of this file are
+// subject to change at any time without notice.
+
+#import "GPBCodedInputStream.h"
+
+@class GPBUnknownFieldSet;
+@class GPBFieldDescriptor;
+
+typedef struct GPBCodedInputStreamState {
+  const uint8_t *bytes;
+  size_t bufferSize;
+  size_t bufferPos;
+
+  // For parsing subsections of an input stream you can put a hard limit on
+  // how much should be read. Normally the limit is the end of the stream,
+  // but you can adjust it to anywhere, and if you hit it you will be at the
+  // end of the stream, until you adjust the limit.
+  size_t currentLimit;
+  int32_t lastTag;
+  NSUInteger recursionDepth;
+} GPBCodedInputStreamState;
+
+@interface GPBCodedInputStream () {
+ @package
+  struct GPBCodedInputStreamState state_;
+  NSData *buffer_;
+}
+
+// Group support is deprecated, so we hide this interface from users, but
+// support for older data.
+- (void)readGroup:(int32_t)fieldNumber
+              message:(GPBMessage *)message
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
+
+// Reads a group field value from the stream and merges it into the given
+// UnknownFieldSet.
+- (void)readUnknownGroup:(int32_t)fieldNumber
+                 message:(GPBUnknownFieldSet *)message;
+
+// Reads a map entry.
+- (void)readMapEntry:(id)mapDictionary
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                field:(GPBFieldDescriptor *)field
+        parentMessage:(GPBMessage *)parentMessage;
+@end
+
+CF_EXTERN_C_BEGIN
+
+int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state);
+
+double GPBCodedInputStreamReadDouble(GPBCodedInputStreamState *state);
+float GPBCodedInputStreamReadFloat(GPBCodedInputStreamState *state);
+uint64_t GPBCodedInputStreamReadUInt64(GPBCodedInputStreamState *state);
+uint32_t GPBCodedInputStreamReadUInt32(GPBCodedInputStreamState *state);
+int64_t GPBCodedInputStreamReadInt64(GPBCodedInputStreamState *state);
+int32_t GPBCodedInputStreamReadInt32(GPBCodedInputStreamState *state);
+uint64_t GPBCodedInputStreamReadFixed64(GPBCodedInputStreamState *state);
+uint32_t GPBCodedInputStreamReadFixed32(GPBCodedInputStreamState *state);
+int32_t GPBCodedInputStreamReadEnum(GPBCodedInputStreamState *state);
+int32_t GPBCodedInputStreamReadSFixed32(GPBCodedInputStreamState *state);
+int64_t GPBCodedInputStreamReadSFixed64(GPBCodedInputStreamState *state);
+int32_t GPBCodedInputStreamReadSInt32(GPBCodedInputStreamState *state);
+int64_t GPBCodedInputStreamReadSInt64(GPBCodedInputStreamState *state);
+BOOL GPBCodedInputStreamReadBool(GPBCodedInputStreamState *state);
+NSString *GPBCodedInputStreamReadRetainedString(GPBCodedInputStreamState *state)
+    __attribute((ns_returns_retained));
+NSData *GPBCodedInputStreamReadRetainedBytes(GPBCodedInputStreamState *state)
+    __attribute((ns_returns_retained));
+NSData *GPBCodedInputStreamReadRetainedBytesNoCopy(
+    GPBCodedInputStreamState *state) __attribute((ns_returns_retained));
+
+size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state,
+                                    size_t byteLimit);
+void GPBCodedInputStreamPopLimit(GPBCodedInputStreamState *state,
+                                 size_t oldLimit);
+size_t GPBCodedInputStreamBytesUntilLimit(GPBCodedInputStreamState *state);
+BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state);
+void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
+                                        int32_t value);
+
+CF_EXTERN_C_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBCodedOutputStream.h b/iOS/Pods/Protobuf/objectivec/GPBCodedOutputStream.h
new file mode 100644 (file)
index 0000000..23c404b
--- /dev/null
@@ -0,0 +1,748 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBRuntimeTypes.h"
+#import "GPBWireFormat.h"
+
+@class GPBBoolArray;
+@class GPBDoubleArray;
+@class GPBEnumArray;
+@class GPBFloatArray;
+@class GPBMessage;
+@class GPBInt32Array;
+@class GPBInt64Array;
+@class GPBUInt32Array;
+@class GPBUInt64Array;
+@class GPBUnknownFieldSet;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * @c GPBCodedOutputStream exception names.
+ **/
+extern NSString *const GPBCodedOutputStreamException_OutOfSpace;
+extern NSString *const GPBCodedOutputStreamException_WriteFailed;
+
+/**
+ * Writes out protocol message fields.
+ *
+ * The common uses of protocol buffers shouldn't need to use this class.
+ * GPBMessage's provide a -data method that will serialize the message for you.
+ *
+ * @note Any -write* api can raise the GPBCodedOutputStreamException_*
+ *       exceptions.
+ *
+ * @note Subclassing of GPBCodedOutputStream is NOT supported.
+ **/
+@interface GPBCodedOutputStream : NSObject
+
+/**
+ * Creates a stream to fill in the given data. Data must be sized to fit or
+ * an error will be raised when out of space.
+ *
+ * @param data The data where the stream will be written to.
+ *
+ * @return A newly instanced GPBCodedOutputStream.
+ **/
++ (instancetype)streamWithData:(NSMutableData *)data;
+
+/**
+ * Creates a stream to write into the given NSOutputStream.
+ *
+ * @param output The output stream where the stream will be written to.
+ *
+ * @return A newly instanced GPBCodedOutputStream.
+ **/
++ (instancetype)streamWithOutputStream:(NSOutputStream *)output;
+
+/**
+ * Initializes a stream to fill in the given data. Data must be sized to fit
+ * or an error will be raised when out of space.
+ *
+ * @param data The data where the stream will be written to.
+ *
+ * @return A newly initialized GPBCodedOutputStream.
+ **/
+- (instancetype)initWithData:(NSMutableData *)data;
+
+/**
+ * Initializes a stream to write into the given @c NSOutputStream.
+ *
+ * @param output The output stream where the stream will be written to.
+ *
+ * @return A newly initialized GPBCodedOutputStream.
+ **/
+- (instancetype)initWithOutputStream:(NSOutputStream *)output;
+
+/**
+ * Flush any buffered data out.
+ **/
+- (void)flush;
+
+/**
+ * Write the raw byte out.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeRawByte:(uint8_t)value;
+
+/**
+ * Write the tag for the given field number and wire format.
+ *
+ * @param fieldNumber The field number.
+ * @param format      The wire format the data for the field will be in.
+ **/
+- (void)writeTag:(uint32_t)fieldNumber format:(GPBWireFormat)format;
+
+/**
+ * Write a 32bit value out in little endian format.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeRawLittleEndian32:(int32_t)value;
+/**
+ * Write a 64bit value out in little endian format.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeRawLittleEndian64:(int64_t)value;
+
+/**
+ * Write a 32bit value out in varint format.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeRawVarint32:(int32_t)value;
+/**
+ * Write a 64bit value out in varint format.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeRawVarint64:(int64_t)value;
+
+/**
+ * Write a size_t out as a 32bit varint value.
+ *
+ * @note This will truncate 64 bit values to 32.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeRawVarintSizeTAs32:(size_t)value;
+
+/**
+ * Writes the contents of an NSData out.
+ *
+ * @param data The data to write out.
+ **/
+- (void)writeRawData:(NSData *)data;
+/**
+ * Writes out the given data.
+ *
+ * @param data   The data blob to write out.
+ * @param offset The offset into the blob to start writing out.
+ * @param length The number of bytes from the blob to write out.
+ **/
+- (void)writeRawPtr:(const void *)data
+             offset:(size_t)offset
+             length:(size_t)length;
+
+//%PDDM-EXPAND _WRITE_DECLS()
+// This block of code is generated, do not edit it directly.
+
+/**
+ * Write a double for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeDouble:(int32_t)fieldNumber value:(double)value;
+/**
+ * Write a packed array of double for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeDoubleArray:(int32_t)fieldNumber
+                  values:(GPBDoubleArray *)values
+                     tag:(uint32_t)tag;
+/**
+ * Write a double without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeDoubleNoTag:(double)value;
+
+/**
+ * Write a float for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeFloat:(int32_t)fieldNumber value:(float)value;
+/**
+ * Write a packed array of float for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeFloatArray:(int32_t)fieldNumber
+                 values:(GPBFloatArray *)values
+                    tag:(uint32_t)tag;
+/**
+ * Write a float without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeFloatNoTag:(float)value;
+
+/**
+ * Write a uint64_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeUInt64:(int32_t)fieldNumber value:(uint64_t)value;
+/**
+ * Write a packed array of uint64_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeUInt64Array:(int32_t)fieldNumber
+                  values:(GPBUInt64Array *)values
+                     tag:(uint32_t)tag;
+/**
+ * Write a uint64_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeUInt64NoTag:(uint64_t)value;
+
+/**
+ * Write a int64_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeInt64:(int32_t)fieldNumber value:(int64_t)value;
+/**
+ * Write a packed array of int64_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeInt64Array:(int32_t)fieldNumber
+                 values:(GPBInt64Array *)values
+                    tag:(uint32_t)tag;
+/**
+ * Write a int64_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeInt64NoTag:(int64_t)value;
+
+/**
+ * Write a int32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeInt32:(int32_t)fieldNumber value:(int32_t)value;
+/**
+ * Write a packed array of int32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeInt32Array:(int32_t)fieldNumber
+                 values:(GPBInt32Array *)values
+                    tag:(uint32_t)tag;
+/**
+ * Write a int32_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeInt32NoTag:(int32_t)value;
+
+/**
+ * Write a uint32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeUInt32:(int32_t)fieldNumber value:(uint32_t)value;
+/**
+ * Write a packed array of uint32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeUInt32Array:(int32_t)fieldNumber
+                  values:(GPBUInt32Array *)values
+                     tag:(uint32_t)tag;
+/**
+ * Write a uint32_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeUInt32NoTag:(uint32_t)value;
+
+/**
+ * Write a uint64_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeFixed64:(int32_t)fieldNumber value:(uint64_t)value;
+/**
+ * Write a packed array of uint64_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeFixed64Array:(int32_t)fieldNumber
+                   values:(GPBUInt64Array *)values
+                      tag:(uint32_t)tag;
+/**
+ * Write a uint64_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeFixed64NoTag:(uint64_t)value;
+
+/**
+ * Write a uint32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeFixed32:(int32_t)fieldNumber value:(uint32_t)value;
+/**
+ * Write a packed array of uint32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeFixed32Array:(int32_t)fieldNumber
+                   values:(GPBUInt32Array *)values
+                      tag:(uint32_t)tag;
+/**
+ * Write a uint32_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeFixed32NoTag:(uint32_t)value;
+
+/**
+ * Write a int32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeSInt32:(int32_t)fieldNumber value:(int32_t)value;
+/**
+ * Write a packed array of int32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeSInt32Array:(int32_t)fieldNumber
+                  values:(GPBInt32Array *)values
+                     tag:(uint32_t)tag;
+/**
+ * Write a int32_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeSInt32NoTag:(int32_t)value;
+
+/**
+ * Write a int64_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeSInt64:(int32_t)fieldNumber value:(int64_t)value;
+/**
+ * Write a packed array of int64_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeSInt64Array:(int32_t)fieldNumber
+                  values:(GPBInt64Array *)values
+                     tag:(uint32_t)tag;
+/**
+ * Write a int64_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeSInt64NoTag:(int64_t)value;
+
+/**
+ * Write a int64_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeSFixed64:(int32_t)fieldNumber value:(int64_t)value;
+/**
+ * Write a packed array of int64_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeSFixed64Array:(int32_t)fieldNumber
+                    values:(GPBInt64Array *)values
+                       tag:(uint32_t)tag;
+/**
+ * Write a int64_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeSFixed64NoTag:(int64_t)value;
+
+/**
+ * Write a int32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeSFixed32:(int32_t)fieldNumber value:(int32_t)value;
+/**
+ * Write a packed array of int32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeSFixed32Array:(int32_t)fieldNumber
+                    values:(GPBInt32Array *)values
+                       tag:(uint32_t)tag;
+/**
+ * Write a int32_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeSFixed32NoTag:(int32_t)value;
+
+/**
+ * Write a BOOL for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeBool:(int32_t)fieldNumber value:(BOOL)value;
+/**
+ * Write a packed array of BOOL for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeBoolArray:(int32_t)fieldNumber
+                values:(GPBBoolArray *)values
+                   tag:(uint32_t)tag;
+/**
+ * Write a BOOL without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeBoolNoTag:(BOOL)value;
+
+/**
+ * Write a int32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeEnum:(int32_t)fieldNumber value:(int32_t)value;
+/**
+ * Write a packed array of int32_t for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ * @param tag         The tag assigned to the values.
+ **/
+- (void)writeEnumArray:(int32_t)fieldNumber
+                values:(GPBEnumArray *)values
+                   tag:(uint32_t)tag;
+/**
+ * Write a int32_t without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeEnumNoTag:(int32_t)value;
+
+/**
+ * Write a NSString for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeString:(int32_t)fieldNumber value:(NSString *)value;
+/**
+ * Write an array of NSString for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ **/
+- (void)writeStringArray:(int32_t)fieldNumber values:(NSArray<NSString*> *)values;
+/**
+ * Write a NSString without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeStringNoTag:(NSString *)value;
+
+/**
+ * Write a GPBMessage for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeMessage:(int32_t)fieldNumber value:(GPBMessage *)value;
+/**
+ * Write an array of GPBMessage for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ **/
+- (void)writeMessageArray:(int32_t)fieldNumber values:(NSArray<GPBMessage*> *)values;
+/**
+ * Write a GPBMessage without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeMessageNoTag:(GPBMessage *)value;
+
+/**
+ * Write a NSData for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeBytes:(int32_t)fieldNumber value:(NSData *)value;
+/**
+ * Write an array of NSData for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ **/
+- (void)writeBytesArray:(int32_t)fieldNumber values:(NSArray<NSData*> *)values;
+/**
+ * Write a NSData without any tag.
+ *
+ * @param value The value to write out.
+ **/
+- (void)writeBytesNoTag:(NSData *)value;
+
+/**
+ * Write a GPBMessage for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeGroup:(int32_t)fieldNumber
+             value:(GPBMessage *)value;
+/**
+ * Write an array of GPBMessage for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ **/
+- (void)writeGroupArray:(int32_t)fieldNumber values:(NSArray<GPBMessage*> *)values;
+/**
+ * Write a GPBMessage without any tag (but does write the endGroup tag).
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeGroupNoTag:(int32_t)fieldNumber
+                  value:(GPBMessage *)value;
+
+/**
+ * Write a GPBUnknownFieldSet for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeUnknownGroup:(int32_t)fieldNumber
+                    value:(GPBUnknownFieldSet *)value;
+/**
+ * Write an array of GPBUnknownFieldSet for the given field number.
+ *
+ * @param fieldNumber The field number assigned to the values.
+ * @param values      The values to write out.
+ **/
+- (void)writeUnknownGroupArray:(int32_t)fieldNumber values:(NSArray<GPBUnknownFieldSet*> *)values;
+/**
+ * Write a GPBUnknownFieldSet without any tag (but does write the endGroup tag).
+ *
+ * @param fieldNumber The field number assigned to the value.
+ * @param value       The value to write out.
+ **/
+- (void)writeUnknownGroupNoTag:(int32_t)fieldNumber
+                         value:(GPBUnknownFieldSet *)value;
+
+//%PDDM-EXPAND-END _WRITE_DECLS()
+
+/**
+Write a MessageSet extension field to the stream. For historical reasons,
+the wire format differs from normal fields.
+
+@param fieldNumber The extension field number to write out.
+@param value       The message from where to get the extension.
+*/
+- (void)writeMessageSetExtension:(int32_t)fieldNumber value:(GPBMessage *)value;
+
+/**
+Write an unparsed MessageSet extension field to the stream. For historical
+reasons, the wire format differs from normal fields.
+
+@param fieldNumber The extension field number to write out.
+@param value       The raw message from where to get the extension.
+*/
+- (void)writeRawMessageSetExtension:(int32_t)fieldNumber value:(NSData *)value;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+// Write methods for types that can be in packed arrays.
+//%PDDM-DEFINE _WRITE_PACKABLE_DECLS(NAME, ARRAY_TYPE, TYPE)
+//%/**
+//% * Write a TYPE for the given field number.
+//% *
+//% * @param fieldNumber The field number assigned to the value.
+//% * @param value       The value to write out.
+//% **/
+//%- (void)write##NAME:(int32_t)fieldNumber value:(TYPE)value;
+//%/**
+//% * Write a packed array of TYPE for the given field number.
+//% *
+//% * @param fieldNumber The field number assigned to the values.
+//% * @param values      The values to write out.
+//% * @param tag         The tag assigned to the values.
+//% **/
+//%- (void)write##NAME##Array:(int32_t)fieldNumber
+//%       NAME$S     values:(GPB##ARRAY_TYPE##Array *)values
+//%       NAME$S        tag:(uint32_t)tag;
+//%/**
+//% * Write a TYPE without any tag.
+//% *
+//% * @param value The value to write out.
+//% **/
+//%- (void)write##NAME##NoTag:(TYPE)value;
+//%
+// Write methods for types that aren't in packed arrays.
+//%PDDM-DEFINE _WRITE_UNPACKABLE_DECLS(NAME, TYPE)
+//%/**
+//% * Write a TYPE for the given field number.
+//% *
+//% * @param fieldNumber The field number assigned to the value.
+//% * @param value       The value to write out.
+//% **/
+//%- (void)write##NAME:(int32_t)fieldNumber value:(TYPE *)value;
+//%/**
+//% * Write an array of TYPE for the given field number.
+//% *
+//% * @param fieldNumber The field number assigned to the values.
+//% * @param values      The values to write out.
+//% **/
+//%- (void)write##NAME##Array:(int32_t)fieldNumber values:(NSArray<##TYPE##*> *)values;
+//%/**
+//% * Write a TYPE without any tag.
+//% *
+//% * @param value The value to write out.
+//% **/
+//%- (void)write##NAME##NoTag:(TYPE *)value;
+//%
+// Special write methods for Groups.
+//%PDDM-DEFINE _WRITE_GROUP_DECLS(NAME, TYPE)
+//%/**
+//% * Write a TYPE for the given field number.
+//% *
+//% * @param fieldNumber The field number assigned to the value.
+//% * @param value       The value to write out.
+//% **/
+//%- (void)write##NAME:(int32_t)fieldNumber
+//%       NAME$S value:(TYPE *)value;
+//%/**
+//% * Write an array of TYPE for the given field number.
+//% *
+//% * @param fieldNumber The field number assigned to the values.
+//% * @param values      The values to write out.
+//% **/
+//%- (void)write##NAME##Array:(int32_t)fieldNumber values:(NSArray<##TYPE##*> *)values;
+//%/**
+//% * Write a TYPE without any tag (but does write the endGroup tag).
+//% *
+//% * @param fieldNumber The field number assigned to the value.
+//% * @param value       The value to write out.
+//% **/
+//%- (void)write##NAME##NoTag:(int32_t)fieldNumber
+//%            NAME$S value:(TYPE *)value;
+//%
+
+// One macro to hide it all up above.
+//%PDDM-DEFINE _WRITE_DECLS()
+//%_WRITE_PACKABLE_DECLS(Double, Double, double)
+//%_WRITE_PACKABLE_DECLS(Float, Float, float)
+//%_WRITE_PACKABLE_DECLS(UInt64, UInt64, uint64_t)
+//%_WRITE_PACKABLE_DECLS(Int64, Int64, int64_t)
+//%_WRITE_PACKABLE_DECLS(Int32, Int32, int32_t)
+//%_WRITE_PACKABLE_DECLS(UInt32, UInt32, uint32_t)
+//%_WRITE_PACKABLE_DECLS(Fixed64, UInt64, uint64_t)
+//%_WRITE_PACKABLE_DECLS(Fixed32, UInt32, uint32_t)
+//%_WRITE_PACKABLE_DECLS(SInt32, Int32, int32_t)
+//%_WRITE_PACKABLE_DECLS(SInt64, Int64, int64_t)
+//%_WRITE_PACKABLE_DECLS(SFixed64, Int64, int64_t)
+//%_WRITE_PACKABLE_DECLS(SFixed32, Int32, int32_t)
+//%_WRITE_PACKABLE_DECLS(Bool, Bool, BOOL)
+//%_WRITE_PACKABLE_DECLS(Enum, Enum, int32_t)
+//%_WRITE_UNPACKABLE_DECLS(String, NSString)
+//%_WRITE_UNPACKABLE_DECLS(Message, GPBMessage)
+//%_WRITE_UNPACKABLE_DECLS(Bytes, NSData)
+//%_WRITE_GROUP_DECLS(Group, GPBMessage)
+//%_WRITE_GROUP_DECLS(UnknownGroup, GPBUnknownFieldSet)
diff --git a/iOS/Pods/Protobuf/objectivec/GPBCodedOutputStream.m b/iOS/Pods/Protobuf/objectivec/GPBCodedOutputStream.m
new file mode 100644 (file)
index 0000000..b846c2f
--- /dev/null
@@ -0,0 +1,1210 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBCodedOutputStream_PackagePrivate.h"
+
+#import <mach/vm_param.h>
+
+#import "GPBArray.h"
+#import "GPBUnknownFieldSet_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+
+// These values are the existing values so as not to break any code that might
+// have already been inspecting them when they weren't documented/exposed.
+NSString *const GPBCodedOutputStreamException_OutOfSpace = @"OutOfSpace";
+NSString *const GPBCodedOutputStreamException_WriteFailed = @"WriteFailed";
+
+// Structure for containing state of a GPBCodedInputStream. Brought out into
+// a struct so that we can inline several common functions instead of dealing
+// with overhead of ObjC dispatch.
+typedef struct GPBOutputBufferState {
+  uint8_t *bytes;
+  size_t size;
+  size_t position;
+  NSOutputStream *output;
+} GPBOutputBufferState;
+
+@implementation GPBCodedOutputStream {
+  GPBOutputBufferState state_;
+  NSMutableData *buffer_;
+}
+
+static const int32_t LITTLE_ENDIAN_32_SIZE = sizeof(uint32_t);
+static const int32_t LITTLE_ENDIAN_64_SIZE = sizeof(uint64_t);
+
+// Internal helper that writes the current buffer to the output. The
+// buffer position is reset to its initial value when this returns.
+static void GPBRefreshBuffer(GPBOutputBufferState *state) {
+  if (state->output == nil) {
+    // We're writing to a single buffer.
+    [NSException raise:GPBCodedOutputStreamException_OutOfSpace format:@""];
+  }
+  if (state->position != 0) {
+    NSInteger written =
+        [state->output write:state->bytes maxLength:state->position];
+    if (written != (NSInteger)state->position) {
+      [NSException raise:GPBCodedOutputStreamException_WriteFailed format:@""];
+    }
+    state->position = 0;
+  }
+}
+
+static void GPBWriteRawByte(GPBOutputBufferState *state, uint8_t value) {
+  if (state->position == state->size) {
+    GPBRefreshBuffer(state);
+  }
+  state->bytes[state->position++] = value;
+}
+
+static void GPBWriteRawVarint32(GPBOutputBufferState *state, int32_t value) {
+  while (YES) {
+    if ((value & ~0x7F) == 0) {
+      uint8_t val = (uint8_t)value;
+      GPBWriteRawByte(state, val);
+      return;
+    } else {
+      GPBWriteRawByte(state, (value & 0x7F) | 0x80);
+      value = GPBLogicalRightShift32(value, 7);
+    }
+  }
+}
+
+static void GPBWriteRawVarint64(GPBOutputBufferState *state, int64_t value) {
+  while (YES) {
+    if ((value & ~0x7FL) == 0) {
+      uint8_t val = (uint8_t)value;
+      GPBWriteRawByte(state, val);
+      return;
+    } else {
+      GPBWriteRawByte(state, ((int32_t)value & 0x7F) | 0x80);
+      value = GPBLogicalRightShift64(value, 7);
+    }
+  }
+}
+
+static void GPBWriteInt32NoTag(GPBOutputBufferState *state, int32_t value) {
+  if (value >= 0) {
+    GPBWriteRawVarint32(state, value);
+  } else {
+    // Must sign-extend
+    GPBWriteRawVarint64(state, value);
+  }
+}
+
+static void GPBWriteUInt32(GPBOutputBufferState *state, int32_t fieldNumber,
+                           uint32_t value) {
+  GPBWriteTagWithFormat(state, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint32(state, value);
+}
+
+static void GPBWriteTagWithFormat(GPBOutputBufferState *state,
+                                  uint32_t fieldNumber, GPBWireFormat format) {
+  GPBWriteRawVarint32(state, GPBWireFormatMakeTag(fieldNumber, format));
+}
+
+static void GPBWriteRawLittleEndian32(GPBOutputBufferState *state,
+                                      int32_t value) {
+  GPBWriteRawByte(state, (value)&0xFF);
+  GPBWriteRawByte(state, (value >> 8) & 0xFF);
+  GPBWriteRawByte(state, (value >> 16) & 0xFF);
+  GPBWriteRawByte(state, (value >> 24) & 0xFF);
+}
+
+static void GPBWriteRawLittleEndian64(GPBOutputBufferState *state,
+                                      int64_t value) {
+  GPBWriteRawByte(state, (int32_t)(value)&0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 8) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 16) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 24) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 32) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 40) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 48) & 0xFF);
+  GPBWriteRawByte(state, (int32_t)(value >> 56) & 0xFF);
+}
+
+- (void)dealloc {
+  [self flush];
+  [state_.output close];
+  [state_.output release];
+  [buffer_ release];
+
+  [super dealloc];
+}
+
+- (instancetype)initWithOutputStream:(NSOutputStream *)output {
+  NSMutableData *data = [NSMutableData dataWithLength:PAGE_SIZE];
+  return [self initWithOutputStream:output data:data];
+}
+
+- (instancetype)initWithData:(NSMutableData *)data {
+  return [self initWithOutputStream:nil data:data];
+}
+
+// This initializer isn't exposed, but it is the designated initializer.
+// Setting OutputStream and NSData is to control the buffering behavior/size
+// of the work, but that is more obvious via the bufferSize: version.
+- (instancetype)initWithOutputStream:(NSOutputStream *)output
+                                data:(NSMutableData *)data {
+  if ((self = [super init])) {
+    buffer_ = [data retain];
+    state_.bytes = [data mutableBytes];
+    state_.size = [data length];
+    state_.output = [output retain];
+    [state_.output open];
+  }
+  return self;
+}
+
++ (instancetype)streamWithOutputStream:(NSOutputStream *)output {
+  NSMutableData *data = [NSMutableData dataWithLength:PAGE_SIZE];
+  return [[[self alloc] initWithOutputStream:output
+                                        data:data] autorelease];
+}
+
++ (instancetype)streamWithData:(NSMutableData *)data {
+  return [[[self alloc] initWithData:data] autorelease];
+}
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+- (void)writeDoubleNoTag:(double)value {
+  GPBWriteRawLittleEndian64(&state_, GPBConvertDoubleToInt64(value));
+}
+
+- (void)writeDouble:(int32_t)fieldNumber value:(double)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed64);
+  GPBWriteRawLittleEndian64(&state_, GPBConvertDoubleToInt64(value));
+}
+
+- (void)writeFloatNoTag:(float)value {
+  GPBWriteRawLittleEndian32(&state_, GPBConvertFloatToInt32(value));
+}
+
+- (void)writeFloat:(int32_t)fieldNumber value:(float)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed32);
+  GPBWriteRawLittleEndian32(&state_, GPBConvertFloatToInt32(value));
+}
+
+- (void)writeUInt64NoTag:(uint64_t)value {
+  GPBWriteRawVarint64(&state_, value);
+}
+
+- (void)writeUInt64:(int32_t)fieldNumber value:(uint64_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint64(&state_, value);
+}
+
+- (void)writeInt64NoTag:(int64_t)value {
+  GPBWriteRawVarint64(&state_, value);
+}
+
+- (void)writeInt64:(int32_t)fieldNumber value:(int64_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint64(&state_, value);
+}
+
+- (void)writeInt32NoTag:(int32_t)value {
+  GPBWriteInt32NoTag(&state_, value);
+}
+
+- (void)writeInt32:(int32_t)fieldNumber value:(int32_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteInt32NoTag(&state_, value);
+}
+
+- (void)writeFixed64NoTag:(uint64_t)value {
+  GPBWriteRawLittleEndian64(&state_, value);
+}
+
+- (void)writeFixed64:(int32_t)fieldNumber value:(uint64_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed64);
+  GPBWriteRawLittleEndian64(&state_, value);
+}
+
+- (void)writeFixed32NoTag:(uint32_t)value {
+  GPBWriteRawLittleEndian32(&state_, value);
+}
+
+- (void)writeFixed32:(int32_t)fieldNumber value:(uint32_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed32);
+  GPBWriteRawLittleEndian32(&state_, value);
+}
+
+- (void)writeBoolNoTag:(BOOL)value {
+  GPBWriteRawByte(&state_, (value ? 1 : 0));
+}
+
+- (void)writeBool:(int32_t)fieldNumber value:(BOOL)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawByte(&state_, (value ? 1 : 0));
+}
+
+- (void)writeStringNoTag:(const NSString *)value {
+  size_t length = [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+  GPBWriteRawVarint32(&state_, (int32_t)length);
+  if (length == 0) {
+    return;
+  }
+
+  const char *quickString =
+      CFStringGetCStringPtr((CFStringRef)value, kCFStringEncodingUTF8);
+
+  // Fast path: Most strings are short, if the buffer already has space,
+  // add to it directly.
+  NSUInteger bufferBytesLeft = state_.size - state_.position;
+  if (bufferBytesLeft >= length) {
+    NSUInteger usedBufferLength = 0;
+    BOOL result;
+    if (quickString != NULL) {
+      memcpy(state_.bytes + state_.position, quickString, length);
+      usedBufferLength = length;
+      result = YES;
+    } else {
+      result = [value getBytes:state_.bytes + state_.position
+                     maxLength:bufferBytesLeft
+                    usedLength:&usedBufferLength
+                      encoding:NSUTF8StringEncoding
+                       options:(NSStringEncodingConversionOptions)0
+                         range:NSMakeRange(0, [value length])
+                remainingRange:NULL];
+    }
+    if (result) {
+      NSAssert2((usedBufferLength == length),
+                @"Our UTF8 calc was wrong? %tu vs %zd", usedBufferLength,
+                length);
+      state_.position += usedBufferLength;
+      return;
+    }
+  } else if (quickString != NULL) {
+    [self writeRawPtr:quickString offset:0 length:length];
+  } else {
+    // Slow path: just get it as data and write it out.
+    NSData *utf8Data = [value dataUsingEncoding:NSUTF8StringEncoding];
+    NSAssert2(([utf8Data length] == length),
+              @"Strings UTF8 length was wrong? %tu vs %zd", [utf8Data length],
+              length);
+    [self writeRawData:utf8Data];
+  }
+}
+
+- (void)writeString:(int32_t)fieldNumber value:(NSString *)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatLengthDelimited);
+  [self writeStringNoTag:value];
+}
+
+- (void)writeGroupNoTag:(int32_t)fieldNumber value:(GPBMessage *)value {
+  [value writeToCodedOutputStream:self];
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatEndGroup);
+}
+
+- (void)writeGroup:(int32_t)fieldNumber value:(GPBMessage *)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatStartGroup);
+  [self writeGroupNoTag:fieldNumber value:value];
+}
+
+- (void)writeUnknownGroupNoTag:(int32_t)fieldNumber
+                         value:(const GPBUnknownFieldSet *)value {
+  [value writeToCodedOutputStream:self];
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatEndGroup);
+}
+
+- (void)writeUnknownGroup:(int32_t)fieldNumber
+                    value:(GPBUnknownFieldSet *)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatStartGroup);
+  [self writeUnknownGroupNoTag:fieldNumber value:value];
+}
+
+- (void)writeMessageNoTag:(GPBMessage *)value {
+  GPBWriteRawVarint32(&state_, (int32_t)[value serializedSize]);
+  [value writeToCodedOutputStream:self];
+}
+
+- (void)writeMessage:(int32_t)fieldNumber value:(GPBMessage *)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatLengthDelimited);
+  [self writeMessageNoTag:value];
+}
+
+- (void)writeBytesNoTag:(NSData *)value {
+  GPBWriteRawVarint32(&state_, (int32_t)[value length]);
+  [self writeRawData:value];
+}
+
+- (void)writeBytes:(int32_t)fieldNumber value:(NSData *)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatLengthDelimited);
+  [self writeBytesNoTag:value];
+}
+
+- (void)writeUInt32NoTag:(uint32_t)value {
+  GPBWriteRawVarint32(&state_, value);
+}
+
+- (void)writeUInt32:(int32_t)fieldNumber value:(uint32_t)value {
+  GPBWriteUInt32(&state_, fieldNumber, value);
+}
+
+- (void)writeEnumNoTag:(int32_t)value {
+  GPBWriteRawVarint32(&state_, value);
+}
+
+- (void)writeEnum:(int32_t)fieldNumber value:(int32_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint32(&state_, value);
+}
+
+- (void)writeSFixed32NoTag:(int32_t)value {
+  GPBWriteRawLittleEndian32(&state_, value);
+}
+
+- (void)writeSFixed32:(int32_t)fieldNumber value:(int32_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed32);
+  GPBWriteRawLittleEndian32(&state_, value);
+}
+
+- (void)writeSFixed64NoTag:(int64_t)value {
+  GPBWriteRawLittleEndian64(&state_, value);
+}
+
+- (void)writeSFixed64:(int32_t)fieldNumber value:(int64_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatFixed64);
+  GPBWriteRawLittleEndian64(&state_, value);
+}
+
+- (void)writeSInt32NoTag:(int32_t)value {
+  GPBWriteRawVarint32(&state_, GPBEncodeZigZag32(value));
+}
+
+- (void)writeSInt32:(int32_t)fieldNumber value:(int32_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint32(&state_, GPBEncodeZigZag32(value));
+}
+
+- (void)writeSInt64NoTag:(int64_t)value {
+  GPBWriteRawVarint64(&state_, GPBEncodeZigZag64(value));
+}
+
+- (void)writeSInt64:(int32_t)fieldNumber value:(int64_t)value {
+  GPBWriteTagWithFormat(&state_, fieldNumber, GPBWireFormatVarint);
+  GPBWriteRawVarint64(&state_, GPBEncodeZigZag64(value));
+}
+
+//%PDDM-DEFINE WRITE_PACKABLE_DEFNS(NAME, ARRAY_TYPE, TYPE, ACCESSOR_NAME)
+//%- (void)write##NAME##Array:(int32_t)fieldNumber
+//%       NAME$S     values:(GPB##ARRAY_TYPE##Array *)values
+//%       NAME$S        tag:(uint32_t)tag {
+//%  if (tag != 0) {
+//%    if (values.count == 0) return;
+//%    __block size_t dataSize = 0;
+//%    [values enumerate##ACCESSOR_NAME##ValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%#pragma unused(idx, stop)
+//%      dataSize += GPBCompute##NAME##SizeNoTag(value);
+//%    }];
+//%    GPBWriteRawVarint32(&state_, tag);
+//%    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+//%    [values enumerate##ACCESSOR_NAME##ValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%#pragma unused(idx, stop)
+//%      [self write##NAME##NoTag:value];
+//%    }];
+//%  } else {
+//%    [values enumerate##ACCESSOR_NAME##ValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) {
+//%#pragma unused(idx, stop)
+//%      [self write##NAME:fieldNumber value:value];
+//%    }];
+//%  }
+//%}
+//%
+//%PDDM-DEFINE WRITE_UNPACKABLE_DEFNS(NAME, TYPE)
+//%- (void)write##NAME##Array:(int32_t)fieldNumber values:(NSArray *)values {
+//%  for (TYPE *value in values) {
+//%    [self write##NAME:fieldNumber value:value];
+//%  }
+//%}
+//%
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Double, Double, double, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeDoubleArray:(int32_t)fieldNumber
+                  values:(GPBDoubleArray *)values
+                     tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeDoubleSizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeDoubleNoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(double value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeDouble:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Float, Float, float, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeFloatArray:(int32_t)fieldNumber
+                 values:(GPBFloatArray *)values
+                    tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeFloatSizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFloatNoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(float value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFloat:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(UInt64, UInt64, uint64_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeUInt64Array:(int32_t)fieldNumber
+                  values:(GPBUInt64Array *)values
+                     tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeUInt64SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeUInt64NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeUInt64:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Int64, Int64, int64_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeInt64Array:(int32_t)fieldNumber
+                 values:(GPBInt64Array *)values
+                    tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeInt64SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeInt64NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeInt64:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Int32, Int32, int32_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeInt32Array:(int32_t)fieldNumber
+                 values:(GPBInt32Array *)values
+                    tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeInt32SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeInt32NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeInt32:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(UInt32, UInt32, uint32_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeUInt32Array:(int32_t)fieldNumber
+                  values:(GPBUInt32Array *)values
+                     tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeUInt32SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeUInt32NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeUInt32:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Fixed64, UInt64, uint64_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeFixed64Array:(int32_t)fieldNumber
+                   values:(GPBUInt64Array *)values
+                      tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeFixed64SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFixed64NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFixed64:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Fixed32, UInt32, uint32_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeFixed32Array:(int32_t)fieldNumber
+                   values:(GPBUInt32Array *)values
+                      tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeFixed32SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFixed32NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeFixed32:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(SInt32, Int32, int32_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeSInt32Array:(int32_t)fieldNumber
+                  values:(GPBInt32Array *)values
+                     tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeSInt32SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSInt32NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSInt32:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(SInt64, Int64, int64_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeSInt64Array:(int32_t)fieldNumber
+                  values:(GPBInt64Array *)values
+                     tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeSInt64SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSInt64NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSInt64:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(SFixed64, Int64, int64_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeSFixed64Array:(int32_t)fieldNumber
+                    values:(GPBInt64Array *)values
+                       tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeSFixed64SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSFixed64NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSFixed64:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(SFixed32, Int32, int32_t, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeSFixed32Array:(int32_t)fieldNumber
+                    values:(GPBInt32Array *)values
+                       tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeSFixed32SizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSFixed32NoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeSFixed32:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Bool, Bool, BOOL, )
+// This block of code is generated, do not edit it directly.
+
+- (void)writeBoolArray:(int32_t)fieldNumber
+                values:(GPBBoolArray *)values
+                   tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeBoolSizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeBoolNoTag:value];
+    }];
+  } else {
+    [values enumerateValuesWithBlock:^(BOOL value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeBool:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_PACKABLE_DEFNS(Enum, Enum, int32_t, Raw)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeEnumArray:(int32_t)fieldNumber
+                values:(GPBEnumArray *)values
+                   tag:(uint32_t)tag {
+  if (tag != 0) {
+    if (values.count == 0) return;
+    __block size_t dataSize = 0;
+    [values enumerateRawValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      dataSize += GPBComputeEnumSizeNoTag(value);
+    }];
+    GPBWriteRawVarint32(&state_, tag);
+    GPBWriteRawVarint32(&state_, (int32_t)dataSize);
+    [values enumerateRawValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeEnumNoTag:value];
+    }];
+  } else {
+    [values enumerateRawValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+      [self writeEnum:fieldNumber value:value];
+    }];
+  }
+}
+
+//%PDDM-EXPAND WRITE_UNPACKABLE_DEFNS(String, NSString)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeStringArray:(int32_t)fieldNumber values:(NSArray *)values {
+  for (NSString *value in values) {
+    [self writeString:fieldNumber value:value];
+  }
+}
+
+//%PDDM-EXPAND WRITE_UNPACKABLE_DEFNS(Message, GPBMessage)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeMessageArray:(int32_t)fieldNumber values:(NSArray *)values {
+  for (GPBMessage *value in values) {
+    [self writeMessage:fieldNumber value:value];
+  }
+}
+
+//%PDDM-EXPAND WRITE_UNPACKABLE_DEFNS(Bytes, NSData)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeBytesArray:(int32_t)fieldNumber values:(NSArray *)values {
+  for (NSData *value in values) {
+    [self writeBytes:fieldNumber value:value];
+  }
+}
+
+//%PDDM-EXPAND WRITE_UNPACKABLE_DEFNS(Group, GPBMessage)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeGroupArray:(int32_t)fieldNumber values:(NSArray *)values {
+  for (GPBMessage *value in values) {
+    [self writeGroup:fieldNumber value:value];
+  }
+}
+
+//%PDDM-EXPAND WRITE_UNPACKABLE_DEFNS(UnknownGroup, GPBUnknownFieldSet)
+// This block of code is generated, do not edit it directly.
+
+- (void)writeUnknownGroupArray:(int32_t)fieldNumber values:(NSArray *)values {
+  for (GPBUnknownFieldSet *value in values) {
+    [self writeUnknownGroup:fieldNumber value:value];
+  }
+}
+
+//%PDDM-EXPAND-END (19 expansions)
+
+- (void)writeMessageSetExtension:(int32_t)fieldNumber
+                           value:(GPBMessage *)value {
+  GPBWriteTagWithFormat(&state_, GPBWireFormatMessageSetItem,
+                        GPBWireFormatStartGroup);
+  GPBWriteUInt32(&state_, GPBWireFormatMessageSetTypeId, fieldNumber);
+  [self writeMessage:GPBWireFormatMessageSetMessage value:value];
+  GPBWriteTagWithFormat(&state_, GPBWireFormatMessageSetItem,
+                        GPBWireFormatEndGroup);
+}
+
+- (void)writeRawMessageSetExtension:(int32_t)fieldNumber value:(NSData *)value {
+  GPBWriteTagWithFormat(&state_, GPBWireFormatMessageSetItem,
+                        GPBWireFormatStartGroup);
+  GPBWriteUInt32(&state_, GPBWireFormatMessageSetTypeId, fieldNumber);
+  [self writeBytes:GPBWireFormatMessageSetMessage value:value];
+  GPBWriteTagWithFormat(&state_, GPBWireFormatMessageSetItem,
+                        GPBWireFormatEndGroup);
+}
+
+- (void)flush {
+  if (state_.output != nil) {
+    GPBRefreshBuffer(&state_);
+  }
+}
+
+- (void)writeRawByte:(uint8_t)value {
+  GPBWriteRawByte(&state_, value);
+}
+
+- (void)writeRawData:(const NSData *)data {
+  [self writeRawPtr:[data bytes] offset:0 length:[data length]];
+}
+
+- (void)writeRawPtr:(const void *)value
+             offset:(size_t)offset
+             length:(size_t)length {
+  if (value == nil || length == 0) {
+    return;
+  }
+
+  NSUInteger bufferLength = state_.size;
+  NSUInteger bufferBytesLeft = bufferLength - state_.position;
+  if (bufferBytesLeft >= length) {
+    // We have room in the current buffer.
+    memcpy(state_.bytes + state_.position, ((uint8_t *)value) + offset, length);
+    state_.position += length;
+  } else {
+    // Write extends past current buffer.  Fill the rest of this buffer and
+    // flush.
+    size_t bytesWritten = bufferBytesLeft;
+    memcpy(state_.bytes + state_.position, ((uint8_t *)value) + offset,
+           bytesWritten);
+    offset += bytesWritten;
+    length -= bytesWritten;
+    state_.position = bufferLength;
+    GPBRefreshBuffer(&state_);
+    bufferLength = state_.size;
+
+    // Now deal with the rest.
+    // Since we have an output stream, this is our buffer
+    // and buffer offset == 0
+    if (length <= bufferLength) {
+      // Fits in new buffer.
+      memcpy(state_.bytes, ((uint8_t *)value) + offset, length);
+      state_.position = length;
+    } else {
+      // Write is very big.  Let's do it all at once.
+      NSInteger written = [state_.output write:((uint8_t *)value) + offset maxLength:length];
+      if (written != (NSInteger)length) {
+        [NSException raise:GPBCodedOutputStreamException_WriteFailed format:@""];
+      }
+    }
+  }
+}
+
+- (void)writeTag:(uint32_t)fieldNumber format:(GPBWireFormat)format {
+  GPBWriteTagWithFormat(&state_, fieldNumber, format);
+}
+
+- (void)writeRawVarint32:(int32_t)value {
+  GPBWriteRawVarint32(&state_, value);
+}
+
+- (void)writeRawVarintSizeTAs32:(size_t)value {
+  // Note the truncation.
+  GPBWriteRawVarint32(&state_, (int32_t)value);
+}
+
+- (void)writeRawVarint64:(int64_t)value {
+  GPBWriteRawVarint64(&state_, value);
+}
+
+- (void)writeRawLittleEndian32:(int32_t)value {
+  GPBWriteRawLittleEndian32(&state_, value);
+}
+
+- (void)writeRawLittleEndian64:(int64_t)value {
+  GPBWriteRawLittleEndian64(&state_, value);
+}
+
+#pragma clang diagnostic pop
+
+@end
+
+size_t GPBComputeDoubleSizeNoTag(Float64 value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_64_SIZE;
+}
+
+size_t GPBComputeFloatSizeNoTag(Float32 value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_32_SIZE;
+}
+
+size_t GPBComputeUInt64SizeNoTag(uint64_t value) {
+  return GPBComputeRawVarint64Size(value);
+}
+
+size_t GPBComputeInt64SizeNoTag(int64_t value) {
+  return GPBComputeRawVarint64Size(value);
+}
+
+size_t GPBComputeInt32SizeNoTag(int32_t value) {
+  if (value >= 0) {
+    return GPBComputeRawVarint32Size(value);
+  } else {
+    // Must sign-extend.
+    return 10;
+  }
+}
+
+size_t GPBComputeSizeTSizeAsInt32NoTag(size_t value) {
+  return GPBComputeInt32SizeNoTag((int32_t)value);
+}
+
+size_t GPBComputeFixed64SizeNoTag(uint64_t value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_64_SIZE;
+}
+
+size_t GPBComputeFixed32SizeNoTag(uint32_t value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_32_SIZE;
+}
+
+size_t GPBComputeBoolSizeNoTag(BOOL value) {
+#pragma unused(value)
+  return 1;
+}
+
+size_t GPBComputeStringSizeNoTag(NSString *value) {
+  NSUInteger length = [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+  return GPBComputeRawVarint32SizeForInteger(length) + length;
+}
+
+size_t GPBComputeGroupSizeNoTag(GPBMessage *value) {
+  return [value serializedSize];
+}
+
+size_t GPBComputeUnknownGroupSizeNoTag(GPBUnknownFieldSet *value) {
+  return value.serializedSize;
+}
+
+size_t GPBComputeMessageSizeNoTag(GPBMessage *value) {
+  size_t size = [value serializedSize];
+  return GPBComputeRawVarint32SizeForInteger(size) + size;
+}
+
+size_t GPBComputeBytesSizeNoTag(NSData *value) {
+  NSUInteger valueLength = [value length];
+  return GPBComputeRawVarint32SizeForInteger(valueLength) + valueLength;
+}
+
+size_t GPBComputeUInt32SizeNoTag(int32_t value) {
+  return GPBComputeRawVarint32Size(value);
+}
+
+size_t GPBComputeEnumSizeNoTag(int32_t value) {
+  return GPBComputeRawVarint32Size(value);
+}
+
+size_t GPBComputeSFixed32SizeNoTag(int32_t value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_32_SIZE;
+}
+
+size_t GPBComputeSFixed64SizeNoTag(int64_t value) {
+#pragma unused(value)
+  return LITTLE_ENDIAN_64_SIZE;
+}
+
+size_t GPBComputeSInt32SizeNoTag(int32_t value) {
+  return GPBComputeRawVarint32Size(GPBEncodeZigZag32(value));
+}
+
+size_t GPBComputeSInt64SizeNoTag(int64_t value) {
+  return GPBComputeRawVarint64Size(GPBEncodeZigZag64(value));
+}
+
+size_t GPBComputeDoubleSize(int32_t fieldNumber, double value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeDoubleSizeNoTag(value);
+}
+
+size_t GPBComputeFloatSize(int32_t fieldNumber, float value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeFloatSizeNoTag(value);
+}
+
+size_t GPBComputeUInt64Size(int32_t fieldNumber, uint64_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeUInt64SizeNoTag(value);
+}
+
+size_t GPBComputeInt64Size(int32_t fieldNumber, int64_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeInt64SizeNoTag(value);
+}
+
+size_t GPBComputeInt32Size(int32_t fieldNumber, int32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeInt32SizeNoTag(value);
+}
+
+size_t GPBComputeFixed64Size(int32_t fieldNumber, uint64_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeFixed64SizeNoTag(value);
+}
+
+size_t GPBComputeFixed32Size(int32_t fieldNumber, uint32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeFixed32SizeNoTag(value);
+}
+
+size_t GPBComputeBoolSize(int32_t fieldNumber, BOOL value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeBoolSizeNoTag(value);
+}
+
+size_t GPBComputeStringSize(int32_t fieldNumber, NSString *value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeStringSizeNoTag(value);
+}
+
+size_t GPBComputeGroupSize(int32_t fieldNumber, GPBMessage *value) {
+  return GPBComputeTagSize(fieldNumber) * 2 + GPBComputeGroupSizeNoTag(value);
+}
+
+size_t GPBComputeUnknownGroupSize(int32_t fieldNumber,
+                                  GPBUnknownFieldSet *value) {
+  return GPBComputeTagSize(fieldNumber) * 2 +
+         GPBComputeUnknownGroupSizeNoTag(value);
+}
+
+size_t GPBComputeMessageSize(int32_t fieldNumber, GPBMessage *value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeMessageSizeNoTag(value);
+}
+
+size_t GPBComputeBytesSize(int32_t fieldNumber, NSData *value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeBytesSizeNoTag(value);
+}
+
+size_t GPBComputeUInt32Size(int32_t fieldNumber, uint32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeUInt32SizeNoTag(value);
+}
+
+size_t GPBComputeEnumSize(int32_t fieldNumber, int32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeEnumSizeNoTag(value);
+}
+
+size_t GPBComputeSFixed32Size(int32_t fieldNumber, int32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeSFixed32SizeNoTag(value);
+}
+
+size_t GPBComputeSFixed64Size(int32_t fieldNumber, int64_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeSFixed64SizeNoTag(value);
+}
+
+size_t GPBComputeSInt32Size(int32_t fieldNumber, int32_t value) {
+  return GPBComputeTagSize(fieldNumber) + GPBComputeSInt32SizeNoTag(value);
+}
+
+size_t GPBComputeSInt64Size(int32_t fieldNumber, int64_t value) {
+  return GPBComputeTagSize(fieldNumber) +
+         GPBComputeRawVarint64Size(GPBEncodeZigZag64(value));
+}
+
+size_t GPBComputeMessageSetExtensionSize(int32_t fieldNumber,
+                                         GPBMessage *value) {
+  return GPBComputeTagSize(GPBWireFormatMessageSetItem) * 2 +
+         GPBComputeUInt32Size(GPBWireFormatMessageSetTypeId, fieldNumber) +
+         GPBComputeMessageSize(GPBWireFormatMessageSetMessage, value);
+}
+
+size_t GPBComputeRawMessageSetExtensionSize(int32_t fieldNumber,
+                                            NSData *value) {
+  return GPBComputeTagSize(GPBWireFormatMessageSetItem) * 2 +
+         GPBComputeUInt32Size(GPBWireFormatMessageSetTypeId, fieldNumber) +
+         GPBComputeBytesSize(GPBWireFormatMessageSetMessage, value);
+}
+
+size_t GPBComputeTagSize(int32_t fieldNumber) {
+  return GPBComputeRawVarint32Size(
+      GPBWireFormatMakeTag(fieldNumber, GPBWireFormatVarint));
+}
+
+size_t GPBComputeWireFormatTagSize(int field_number, GPBDataType dataType) {
+  size_t result = GPBComputeTagSize(field_number);
+  if (dataType == GPBDataTypeGroup) {
+    // Groups have both a start and an end tag.
+    return result * 2;
+  } else {
+    return result;
+  }
+}
+
+size_t GPBComputeRawVarint32Size(int32_t value) {
+  // value is treated as unsigned, so it won't be sign-extended if negative.
+  if ((value & (0xffffffff << 7)) == 0) return 1;
+  if ((value & (0xffffffff << 14)) == 0) return 2;
+  if ((value & (0xffffffff << 21)) == 0) return 3;
+  if ((value & (0xffffffff << 28)) == 0) return 4;
+  return 5;
+}
+
+size_t GPBComputeRawVarint32SizeForInteger(NSInteger value) {
+  // Note the truncation.
+  return GPBComputeRawVarint32Size((int32_t)value);
+}
+
+size_t GPBComputeRawVarint64Size(int64_t value) {
+  if ((value & (0xffffffffffffffffL << 7)) == 0) return 1;
+  if ((value & (0xffffffffffffffffL << 14)) == 0) return 2;
+  if ((value & (0xffffffffffffffffL << 21)) == 0) return 3;
+  if ((value & (0xffffffffffffffffL << 28)) == 0) return 4;
+  if ((value & (0xffffffffffffffffL << 35)) == 0) return 5;
+  if ((value & (0xffffffffffffffffL << 42)) == 0) return 6;
+  if ((value & (0xffffffffffffffffL << 49)) == 0) return 7;
+  if ((value & (0xffffffffffffffffL << 56)) == 0) return 8;
+  if ((value & (0xffffffffffffffffL << 63)) == 0) return 9;
+  return 10;
+}
diff --git a/iOS/Pods/Protobuf/objectivec/GPBCodedOutputStream_PackagePrivate.h b/iOS/Pods/Protobuf/objectivec/GPBCodedOutputStream_PackagePrivate.h
new file mode 100644 (file)
index 0000000..2e7bb4c
--- /dev/null
@@ -0,0 +1,126 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBCodedOutputStream.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+CF_EXTERN_C_BEGIN
+
+size_t GPBComputeDoubleSize(int32_t fieldNumber, double value)
+    __attribute__((const));
+size_t GPBComputeFloatSize(int32_t fieldNumber, float value)
+    __attribute__((const));
+size_t GPBComputeUInt64Size(int32_t fieldNumber, uint64_t value)
+    __attribute__((const));
+size_t GPBComputeInt64Size(int32_t fieldNumber, int64_t value)
+    __attribute__((const));
+size_t GPBComputeInt32Size(int32_t fieldNumber, int32_t value)
+    __attribute__((const));
+size_t GPBComputeFixed64Size(int32_t fieldNumber, uint64_t value)
+    __attribute__((const));
+size_t GPBComputeFixed32Size(int32_t fieldNumber, uint32_t value)
+    __attribute__((const));
+size_t GPBComputeBoolSize(int32_t fieldNumber, BOOL value)
+    __attribute__((const));
+size_t GPBComputeStringSize(int32_t fieldNumber, NSString *value)
+    __attribute__((const));
+size_t GPBComputeGroupSize(int32_t fieldNumber, GPBMessage *value)
+    __attribute__((const));
+size_t GPBComputeUnknownGroupSize(int32_t fieldNumber,
+                                  GPBUnknownFieldSet *value)
+    __attribute__((const));
+size_t GPBComputeMessageSize(int32_t fieldNumber, GPBMessage *value)
+    __attribute__((const));
+size_t GPBComputeBytesSize(int32_t fieldNumber, NSData *value)
+    __attribute__((const));
+size_t GPBComputeUInt32Size(int32_t fieldNumber, uint32_t value)
+    __attribute__((const));
+size_t GPBComputeSFixed32Size(int32_t fieldNumber, int32_t value)
+    __attribute__((const));
+size_t GPBComputeSFixed64Size(int32_t fieldNumber, int64_t value)
+    __attribute__((const));
+size_t GPBComputeSInt32Size(int32_t fieldNumber, int32_t value)
+    __attribute__((const));
+size_t GPBComputeSInt64Size(int32_t fieldNumber, int64_t value)
+    __attribute__((const));
+size_t GPBComputeTagSize(int32_t fieldNumber) __attribute__((const));
+size_t GPBComputeWireFormatTagSize(int field_number, GPBDataType dataType)
+    __attribute__((const));
+
+size_t GPBComputeDoubleSizeNoTag(double value) __attribute__((const));
+size_t GPBComputeFloatSizeNoTag(float value) __attribute__((const));
+size_t GPBComputeUInt64SizeNoTag(uint64_t value) __attribute__((const));
+size_t GPBComputeInt64SizeNoTag(int64_t value) __attribute__((const));
+size_t GPBComputeInt32SizeNoTag(int32_t value) __attribute__((const));
+size_t GPBComputeFixed64SizeNoTag(uint64_t value) __attribute__((const));
+size_t GPBComputeFixed32SizeNoTag(uint32_t value) __attribute__((const));
+size_t GPBComputeBoolSizeNoTag(BOOL value) __attribute__((const));
+size_t GPBComputeStringSizeNoTag(NSString *value) __attribute__((const));
+size_t GPBComputeGroupSizeNoTag(GPBMessage *value) __attribute__((const));
+size_t GPBComputeUnknownGroupSizeNoTag(GPBUnknownFieldSet *value)
+    __attribute__((const));
+size_t GPBComputeMessageSizeNoTag(GPBMessage *value) __attribute__((const));
+size_t GPBComputeBytesSizeNoTag(NSData *value) __attribute__((const));
+size_t GPBComputeUInt32SizeNoTag(int32_t value) __attribute__((const));
+size_t GPBComputeEnumSizeNoTag(int32_t value) __attribute__((const));
+size_t GPBComputeSFixed32SizeNoTag(int32_t value) __attribute__((const));
+size_t GPBComputeSFixed64SizeNoTag(int64_t value) __attribute__((const));
+size_t GPBComputeSInt32SizeNoTag(int32_t value) __attribute__((const));
+size_t GPBComputeSInt64SizeNoTag(int64_t value) __attribute__((const));
+
+// Note that this will calculate the size of 64 bit values truncated to 32.
+size_t GPBComputeSizeTSizeAsInt32NoTag(size_t value) __attribute__((const));
+
+size_t GPBComputeRawVarint32Size(int32_t value) __attribute__((const));
+size_t GPBComputeRawVarint64Size(int64_t value) __attribute__((const));
+
+// Note that this will calculate the size of 64 bit values truncated to 32.
+size_t GPBComputeRawVarint32SizeForInteger(NSInteger value)
+    __attribute__((const));
+
+// Compute the number of bytes that would be needed to encode a
+// MessageSet extension to the stream.  For historical reasons,
+// the wire format differs from normal fields.
+size_t GPBComputeMessageSetExtensionSize(int32_t fieldNumber, GPBMessage *value)
+    __attribute__((const));
+
+// Compute the number of bytes that would be needed to encode an
+// unparsed MessageSet extension field to the stream.  For
+// historical reasons, the wire format differs from normal fields.
+size_t GPBComputeRawMessageSetExtensionSize(int32_t fieldNumber, NSData *value)
+    __attribute__((const));
+
+size_t GPBComputeEnumSize(int32_t fieldNumber, int32_t value)
+    __attribute__((const));
+
+CF_EXTERN_C_END
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBDescriptor.h b/iOS/Pods/Protobuf/objectivec/GPBDescriptor.h
new file mode 100644 (file)
index 0000000..651f4de
--- /dev/null
@@ -0,0 +1,288 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBRuntimeTypes.h"
+
+@class GPBEnumDescriptor;
+@class GPBFieldDescriptor;
+@class GPBFileDescriptor;
+@class GPBOneofDescriptor;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** Syntax used in the proto file. */
+typedef NS_ENUM(uint8_t, GPBFileSyntax) {
+  /** Unknown syntax. */
+  GPBFileSyntaxUnknown = 0,
+  /** Proto2 syntax. */
+  GPBFileSyntaxProto2 = 2,
+  /** Proto3 syntax. */
+  GPBFileSyntaxProto3 = 3,
+};
+
+/** Type of proto field. */
+typedef NS_ENUM(uint8_t, GPBFieldType) {
+  /** Optional/required field. Only valid for proto2 fields. */
+  GPBFieldTypeSingle,
+  /** Repeated field. */
+  GPBFieldTypeRepeated,
+  /** Map field. */
+  GPBFieldTypeMap,
+};
+
+/**
+ * Describes a proto message.
+ **/
+@interface GPBDescriptor : NSObject<NSCopying>
+
+/** Name of the message. */
+@property(nonatomic, readonly, copy) NSString *name;
+/** Fields declared in the message. */
+@property(nonatomic, readonly, strong, nullable) NSArray<GPBFieldDescriptor*> *fields;
+/** Oneofs declared in the message. */
+@property(nonatomic, readonly, strong, nullable) NSArray<GPBOneofDescriptor*> *oneofs;
+/** Extension range declared for the message. */
+@property(nonatomic, readonly, nullable) const GPBExtensionRange *extensionRanges;
+/** Number of extension ranges declared for the message. */
+@property(nonatomic, readonly) uint32_t extensionRangesCount;
+/** Descriptor for the file where the message was defined. */
+@property(nonatomic, readonly, assign) GPBFileDescriptor *file;
+
+/** Whether the message is in wire format or not. */
+@property(nonatomic, readonly, getter=isWireFormat) BOOL wireFormat;
+/** The class of this message. */
+@property(nonatomic, readonly) Class messageClass;
+/** Containing message descriptor if this message is nested, or nil otherwise. */
+@property(readonly, nullable) GPBDescriptor *containingType;
+/**
+ * Fully qualified name for this message (package.message). Can be nil if the
+ * value is unable to be computed.
+ */
+@property(readonly, nullable) NSString *fullName;
+
+/**
+ * Gets the field for the given number.
+ *
+ * @param fieldNumber The number for the field to get.
+ *
+ * @return The field descriptor for the given number, or nil if not found.
+ **/
+- (nullable GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber;
+
+/**
+ * Gets the field for the given name.
+ *
+ * @param name The name for the field to get.
+ *
+ * @return The field descriptor for the given name, or nil if not found.
+ **/
+- (nullable GPBFieldDescriptor *)fieldWithName:(NSString *)name;
+
+/**
+ * Gets the oneof for the given name.
+ *
+ * @param name The name for the oneof to get.
+ *
+ * @return The oneof descriptor for the given name, or nil if not found.
+ **/
+- (nullable GPBOneofDescriptor *)oneofWithName:(NSString *)name;
+
+@end
+
+/**
+ * Describes a proto file.
+ **/
+@interface GPBFileDescriptor : NSObject
+
+/** The package declared in the proto file. */
+@property(nonatomic, readonly, copy) NSString *package;
+/** The objc prefix declared in the proto file. */
+@property(nonatomic, readonly, copy, nullable) NSString *objcPrefix;
+/** The syntax of the proto file. */
+@property(nonatomic, readonly) GPBFileSyntax syntax;
+
+@end
+
+/**
+ * Describes a oneof field.
+ **/
+@interface GPBOneofDescriptor : NSObject
+/** Name of the oneof field. */
+@property(nonatomic, readonly) NSString *name;
+/** Fields declared in the oneof. */
+@property(nonatomic, readonly) NSArray<GPBFieldDescriptor*> *fields;
+
+/**
+ * Gets the field for the given number.
+ *
+ * @param fieldNumber The number for the field to get.
+ *
+ * @return The field descriptor for the given number, or nil if not found.
+ **/
+- (nullable GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber;
+
+/**
+ * Gets the field for the given name.
+ *
+ * @param name The name for the field to get.
+ *
+ * @return The field descriptor for the given name, or nil if not found.
+ **/
+- (nullable GPBFieldDescriptor *)fieldWithName:(NSString *)name;
+
+@end
+
+/**
+ * Describes a proto field.
+ **/
+@interface GPBFieldDescriptor : NSObject
+
+/** Name of the field. */
+@property(nonatomic, readonly, copy) NSString *name;
+/** Number associated with the field. */
+@property(nonatomic, readonly) uint32_t number;
+/** Data type contained in the field. */
+@property(nonatomic, readonly) GPBDataType dataType;
+/** Whether it has a default value or not. */
+@property(nonatomic, readonly) BOOL hasDefaultValue;
+/** Default value for the field. */
+@property(nonatomic, readonly) GPBGenericValue defaultValue;
+/** Whether this field is required. Only valid for proto2 fields. */
+@property(nonatomic, readonly, getter=isRequired) BOOL required;
+/** Whether this field is optional. */
+@property(nonatomic, readonly, getter=isOptional) BOOL optional;
+/** Type of field (single, repeated, map). */
+@property(nonatomic, readonly) GPBFieldType fieldType;
+/** Type of the key if the field is a map. The value's type is -fieldType. */
+@property(nonatomic, readonly) GPBDataType mapKeyDataType;
+/** Whether the field is packable. */
+@property(nonatomic, readonly, getter=isPackable) BOOL packable;
+
+/** The containing oneof if this field is part of one, nil otherwise. */
+@property(nonatomic, readonly, assign, nullable) GPBOneofDescriptor *containingOneof;
+
+/** Class of the message if the field is of message type. */
+@property(nonatomic, readonly, assign, nullable) Class msgClass;
+
+/** Descriptor for the enum if this field is an enum. */
+@property(nonatomic, readonly, strong, nullable) GPBEnumDescriptor *enumDescriptor;
+
+/**
+ * Checks whether the given enum raw value is a valid enum value.
+ *
+ * @param value The raw enum value to check.
+ *
+ * @return YES if value is a valid enum raw value.
+ **/
+- (BOOL)isValidEnumValue:(int32_t)value;
+
+/** @return Name for the text format, or nil if not known. */
+- (nullable NSString *)textFormatName;
+
+@end
+
+/**
+ * Describes a proto enum.
+ **/
+@interface GPBEnumDescriptor : NSObject
+
+/** Name of the enum. */
+@property(nonatomic, readonly, copy) NSString *name;
+/** Function that validates that raw values are valid enum values. */
+@property(nonatomic, readonly) GPBEnumValidationFunc enumVerifier;
+
+/**
+ * Returns the enum value name for the given raw enum.
+ *
+ * @param number The raw enum value.
+ *
+ * @return The name of the enum value passed, or nil if not valid.
+ **/
+- (nullable NSString *)enumNameForValue:(int32_t)number;
+
+/**
+ * Gets the enum raw value for the given enum name.
+ *
+ * @param outValue A pointer where the value will be set.
+ * @param name     The enum name for which to get the raw value.
+ *
+ * @return YES if a value was copied into the pointer, NO otherwise.
+ **/
+- (BOOL)getValue:(nullable int32_t *)outValue forEnumName:(NSString *)name;
+
+/**
+ * Returns the text format for the given raw enum value.
+ *
+ * @param number The raw enum value.
+ *
+ * @return The text format name for the raw enum value, or nil if not valid.
+ **/
+- (nullable NSString *)textFormatNameForValue:(int32_t)number;
+
+/**
+ * Gets the enum raw value for the given text format name.
+ *
+ * @param outValue       A pointer where the value will be set.
+ * @param textFormatName The text format name for which to get the raw value.
+ *
+ * @return YES if a value was copied into the pointer, NO otherwise.
+ **/
+- (BOOL)getValue:(nullable int32_t *)outValue forEnumTextFormatName:(NSString *)textFormatName;
+
+@end
+
+/**
+ * Describes a proto extension.
+ **/
+@interface GPBExtensionDescriptor : NSObject<NSCopying>
+/** Field number under which the extension is stored. */
+@property(nonatomic, readonly) uint32_t fieldNumber;
+/** The containing message class, i.e. the class extended by this extension. */
+@property(nonatomic, readonly) Class containingMessageClass;
+/** Data type contained in the extension. */
+@property(nonatomic, readonly) GPBDataType dataType;
+/** Whether the extension is repeated. */
+@property(nonatomic, readonly, getter=isRepeated) BOOL repeated;
+/** Whether the extension is packable. */
+@property(nonatomic, readonly, getter=isPackable) BOOL packable;
+/** The class of the message if the extension is of message type. */
+@property(nonatomic, readonly, assign) Class msgClass;
+/** The singleton name for the extension. */
+@property(nonatomic, readonly) NSString *singletonName;
+/** The enum descriptor if the extension is of enum type. */
+@property(nonatomic, readonly, strong, nullable) GPBEnumDescriptor *enumDescriptor;
+/** The default value for the extension. */
+@property(nonatomic, readonly, nullable) id defaultValue;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBDescriptor.m b/iOS/Pods/Protobuf/objectivec/GPBDescriptor.m
new file mode 100644 (file)
index 0000000..4b39c63
--- /dev/null
@@ -0,0 +1,1102 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBDescriptor_PackagePrivate.h"
+
+#import <objc/runtime.h>
+
+#import "GPBUtilities_PackagePrivate.h"
+#import "GPBWireFormat.h"
+#import "GPBMessage_PackagePrivate.h"
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+// The addresses of these variables are used as keys for objc_getAssociatedObject.
+static const char kTextFormatExtraValueKey = 0;
+static const char kParentClassNameValueKey = 0;
+static const char kClassNameSuffixKey = 0;
+
+// Utility function to generate selectors on the fly.
+static SEL SelFromStrings(const char *prefix, const char *middle,
+                          const char *suffix, BOOL takesArg) {
+  if (prefix == NULL && suffix == NULL && !takesArg) {
+    return sel_getUid(middle);
+  }
+  const size_t prefixLen = prefix != NULL ? strlen(prefix) : 0;
+  const size_t middleLen = strlen(middle);
+  const size_t suffixLen = suffix != NULL ? strlen(suffix) : 0;
+  size_t totalLen =
+      prefixLen + middleLen + suffixLen + 1;  // include space for null on end.
+  if (takesArg) {
+    totalLen += 1;
+  }
+  char buffer[totalLen];
+  if (prefix != NULL) {
+    memcpy(buffer, prefix, prefixLen);
+    memcpy(buffer + prefixLen, middle, middleLen);
+    buffer[prefixLen] = (char)toupper(buffer[prefixLen]);
+  } else {
+    memcpy(buffer, middle, middleLen);
+  }
+  if (suffix != NULL) {
+    memcpy(buffer + prefixLen + middleLen, suffix, suffixLen);
+  }
+  if (takesArg) {
+    buffer[totalLen - 2] = ':';
+  }
+  // Always null terminate it.
+  buffer[totalLen - 1] = 0;
+
+  SEL result = sel_getUid(buffer);
+  return result;
+}
+
+static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
+                                          NSArray *allMessageFields)
+    __attribute__((ns_returns_retained));
+
+static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
+                                          NSArray *allMessageFields) {
+  NSMutableArray *result = [[NSMutableArray alloc] init];
+  for (GPBFieldDescriptor *fieldDesc in allMessageFields) {
+    if (fieldDesc->description_->hasIndex == hasIndex) {
+      [result addObject:fieldDesc];
+    }
+  }
+  return result;
+}
+
+@implementation GPBDescriptor {
+  Class messageClass_;
+  GPBFileDescriptor *file_;
+  BOOL wireFormat_;
+}
+
+@synthesize messageClass = messageClass_;
+@synthesize fields = fields_;
+@synthesize oneofs = oneofs_;
+@synthesize extensionRanges = extensionRanges_;
+@synthesize extensionRangesCount = extensionRangesCount_;
+@synthesize file = file_;
+@synthesize wireFormat = wireFormat_;
+
++ (instancetype)
+    allocDescriptorForClass:(Class)messageClass
+                  rootClass:(Class)rootClass
+                       file:(GPBFileDescriptor *)file
+                     fields:(void *)fieldDescriptions
+                 fieldCount:(uint32_t)fieldCount
+                storageSize:(uint32_t)storageSize
+                      flags:(GPBDescriptorInitializationFlags)flags {
+  // The rootClass is no longer used, but it is passed in to ensure it
+  // was started up during initialization also.
+  (void)rootClass;
+  NSMutableArray *fields = nil;
+  GPBFileSyntax syntax = file.syntax;
+  BOOL fieldsIncludeDefault =
+      (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0;
+
+  void *desc;
+  for (uint32_t i = 0; i < fieldCount; ++i) {
+    if (fields == nil) {
+      fields = [[NSMutableArray alloc] initWithCapacity:fieldCount];
+    }
+    // Need correctly typed pointer for array indexing below to work.
+    if (fieldsIncludeDefault) {
+      GPBMessageFieldDescriptionWithDefault *fieldDescWithDefault = fieldDescriptions;
+      desc = &(fieldDescWithDefault[i]);
+    } else {
+      GPBMessageFieldDescription *fieldDesc = fieldDescriptions;
+      desc = &(fieldDesc[i]);
+    }
+    GPBFieldDescriptor *fieldDescriptor =
+        [[GPBFieldDescriptor alloc] initWithFieldDescription:desc
+                                             includesDefault:fieldsIncludeDefault
+                                                      syntax:syntax];
+    [fields addObject:fieldDescriptor];
+    [fieldDescriptor release];
+  }
+
+  BOOL wireFormat = (flags & GPBDescriptorInitializationFlag_WireFormat) != 0;
+  GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass
+                                                     file:file
+                                                   fields:fields
+                                              storageSize:storageSize
+                                               wireFormat:wireFormat];
+  [fields release];
+  return descriptor;
+}
+
+- (instancetype)initWithClass:(Class)messageClass
+                         file:(GPBFileDescriptor *)file
+                       fields:(NSArray *)fields
+                  storageSize:(uint32_t)storageSize
+                   wireFormat:(BOOL)wireFormat {
+  if ((self = [super init])) {
+    messageClass_ = messageClass;
+    file_ = file;
+    fields_ = [fields retain];
+    storageSize_ = storageSize;
+    wireFormat_ = wireFormat;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [fields_ release];
+  [oneofs_ release];
+  [super dealloc];
+}
+
+- (void)setupOneofs:(const char **)oneofNames
+              count:(uint32_t)count
+      firstHasIndex:(int32_t)firstHasIndex {
+  NSCAssert(firstHasIndex < 0, @"Should always be <0");
+  NSMutableArray *oneofs = [[NSMutableArray alloc] initWithCapacity:count];
+  for (uint32_t i = 0, hasIndex = firstHasIndex; i < count; ++i, --hasIndex) {
+    const char *name = oneofNames[i];
+    NSArray *fieldsForOneof = NewFieldsArrayForHasIndex(hasIndex, fields_);
+    NSCAssert(fieldsForOneof.count > 0,
+              @"No fields for this oneof? (%s:%d)", name, hasIndex);
+    GPBOneofDescriptor *oneofDescriptor =
+        [[GPBOneofDescriptor alloc] initWithName:name fields:fieldsForOneof];
+    [oneofs addObject:oneofDescriptor];
+    [oneofDescriptor release];
+    [fieldsForOneof release];
+  }
+  oneofs_ = oneofs;
+}
+
+- (void)setupExtraTextInfo:(const char *)extraTextFormatInfo {
+  // Extra info is a compile time option, so skip the work if not needed.
+  if (extraTextFormatInfo) {
+    NSValue *extraInfoValue = [NSValue valueWithPointer:extraTextFormatInfo];
+    for (GPBFieldDescriptor *fieldDescriptor in fields_) {
+      if (fieldDescriptor->description_->flags & GPBFieldTextFormatNameCustom) {
+        objc_setAssociatedObject(fieldDescriptor, &kTextFormatExtraValueKey,
+                                 extraInfoValue,
+                                 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+      }
+    }
+  }
+}
+
+- (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count {
+  extensionRanges_ = ranges;
+  extensionRangesCount_ = count;
+}
+
+- (void)setupContainingMessageClassName:(const char *)msgClassName {
+  // Note: Only fetch the class here, can't send messages to it because
+  // that could cause cycles back to this class within +initialize if
+  // two messages have each other in fields (i.e. - they build a graph).
+  NSAssert(objc_getClass(msgClassName), @"Class %s not defined", msgClassName);
+  NSValue *parentNameValue = [NSValue valueWithPointer:msgClassName];
+  objc_setAssociatedObject(self, &kParentClassNameValueKey,
+                           parentNameValue,
+                           OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (void)setupMessageClassNameSuffix:(NSString *)suffix {
+  if (suffix.length) {
+    objc_setAssociatedObject(self, &kClassNameSuffixKey,
+                             suffix,
+                             OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+  }
+}
+
+- (NSString *)name {
+  return NSStringFromClass(messageClass_);
+}
+
+- (GPBDescriptor *)containingType {
+  NSValue *parentNameValue =
+      objc_getAssociatedObject(self, &kParentClassNameValueKey);
+  if (!parentNameValue) {
+    return nil;
+  }
+  const char *parentName = [parentNameValue pointerValue];
+  Class parentClass = objc_getClass(parentName);
+  NSAssert(parentClass, @"Class %s not defined", parentName);
+  return [parentClass descriptor];
+}
+
+- (NSString *)fullName {
+  NSString *className = NSStringFromClass(self.messageClass);
+  GPBFileDescriptor *file = self.file;
+  NSString *objcPrefix = file.objcPrefix;
+  if (objcPrefix && ![className hasPrefix:objcPrefix]) {
+    NSAssert(0,
+             @"Class didn't have correct prefix? (%@ - %@)",
+             className, objcPrefix);
+    return nil;
+  }
+  GPBDescriptor *parent = self.containingType;
+
+  NSString *name = nil;
+  if (parent) {
+    NSString *parentClassName = NSStringFromClass(parent.messageClass);
+    // The generator will add _Class to avoid reserved words, drop it.
+    NSString *suffix = objc_getAssociatedObject(parent, &kClassNameSuffixKey);
+    if (suffix) {
+      if (![parentClassName hasSuffix:suffix]) {
+        NSAssert(0,
+                 @"ParentMessage class didn't have correct suffix? (%@ - %@)",
+                 className, suffix);
+        return nil;
+      }
+      parentClassName =
+          [parentClassName substringToIndex:(parentClassName.length - suffix.length)];
+    }
+    NSString *parentPrefix = [parentClassName stringByAppendingString:@"_"];
+    if (![className hasPrefix:parentPrefix]) {
+      NSAssert(0,
+               @"Class didn't have the correct parent name prefix? (%@ - %@)",
+               parentPrefix, className);
+      return nil;
+    }
+    name = [className substringFromIndex:parentPrefix.length];
+  } else {
+    name = [className substringFromIndex:objcPrefix.length];
+  }
+
+  // The generator will add _Class to avoid reserved words, drop it.
+  NSString *suffix = objc_getAssociatedObject(self, &kClassNameSuffixKey);
+  if (suffix) {
+    if (![name hasSuffix:suffix]) {
+      NSAssert(0,
+               @"Message class didn't have correct suffix? (%@ - %@)",
+               name, suffix);
+      return nil;
+    }
+    name = [name substringToIndex:(name.length - suffix.length)];
+  }
+
+  NSString *prefix = (parent != nil ? parent.fullName : file.package);
+  NSString *result;
+  if (prefix.length > 0) {
+    result = [NSString stringWithFormat:@"%@.%@", prefix, name];
+  } else {
+    result = name;
+  }
+  return result;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+#pragma unused(zone)
+  return [self retain];
+}
+
+- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
+  for (GPBFieldDescriptor *descriptor in fields_) {
+    if (GPBFieldNumber(descriptor) == fieldNumber) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
+  for (GPBFieldDescriptor *descriptor in fields_) {
+    if ([descriptor.name isEqual:name]) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+- (GPBOneofDescriptor *)oneofWithName:(NSString *)name {
+  for (GPBOneofDescriptor *descriptor in oneofs_) {
+    if ([descriptor.name isEqual:name]) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+@end
+
+@implementation GPBFileDescriptor {
+  NSString *package_;
+  NSString *objcPrefix_;
+  GPBFileSyntax syntax_;
+}
+
+@synthesize package = package_;
+@synthesize objcPrefix = objcPrefix_;
+@synthesize syntax = syntax_;
+
+- (instancetype)initWithPackage:(NSString *)package
+                     objcPrefix:(NSString *)objcPrefix
+                         syntax:(GPBFileSyntax)syntax {
+  self = [super init];
+  if (self) {
+    package_ = [package copy];
+    objcPrefix_ = [objcPrefix copy];
+    syntax_ = syntax;
+  }
+  return self;
+}
+
+- (instancetype)initWithPackage:(NSString *)package
+                         syntax:(GPBFileSyntax)syntax {
+  self = [super init];
+  if (self) {
+    package_ = [package copy];
+    syntax_ = syntax;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [package_ release];
+  [objcPrefix_ release];
+  [super dealloc];
+}
+
+@end
+
+@implementation GPBOneofDescriptor
+
+@synthesize fields = fields_;
+
+- (instancetype)initWithName:(const char *)name fields:(NSArray *)fields {
+  self = [super init];
+  if (self) {
+    name_ = name;
+    fields_ = [fields retain];
+    for (GPBFieldDescriptor *fieldDesc in fields) {
+      fieldDesc->containingOneof_ = self;
+    }
+
+    caseSel_ = SelFromStrings(NULL, name, "OneOfCase", NO);
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [fields_ release];
+  [super dealloc];
+}
+
+- (NSString *)name {
+  return (NSString * _Nonnull)@(name_);
+}
+
+- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
+  for (GPBFieldDescriptor *descriptor in fields_) {
+    if (GPBFieldNumber(descriptor) == fieldNumber) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
+  for (GPBFieldDescriptor *descriptor in fields_) {
+    if ([descriptor.name isEqual:name]) {
+      return descriptor;
+    }
+  }
+  return nil;
+}
+
+@end
+
+uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
+  GPBMessageFieldDescription *description = self->description_;
+  GPBWireFormat format;
+  if ((description->flags & GPBFieldMapKeyMask) != 0) {
+    // Maps are repeated messages on the wire.
+    format = GPBWireFormatForType(GPBDataTypeMessage, NO);
+  } else {
+    format = GPBWireFormatForType(description->dataType,
+                                  ((description->flags & GPBFieldPacked) != 0));
+  }
+  return GPBWireFormatMakeTag(description->number, format);
+}
+
+uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) {
+  GPBMessageFieldDescription *description = self->description_;
+  NSCAssert((description->flags & GPBFieldRepeated) != 0,
+            @"Only valid on repeated fields");
+  GPBWireFormat format =
+      GPBWireFormatForType(description->dataType,
+                           ((description->flags & GPBFieldPacked) == 0));
+  return GPBWireFormatMakeTag(description->number, format);
+}
+
+@implementation GPBFieldDescriptor {
+  GPBGenericValue defaultValue_;
+
+  // Message ivars
+  Class msgClass_;
+
+  // Enum ivars.
+  // If protos are generated with GenerateEnumDescriptors on then it will
+  // be a enumDescriptor, otherwise it will be a enumVerifier.
+  union {
+    GPBEnumDescriptor *enumDescriptor_;
+    GPBEnumValidationFunc enumVerifier_;
+  } enumHandling_;
+}
+
+@synthesize msgClass = msgClass_;
+@synthesize containingOneof = containingOneof_;
+
+- (instancetype)init {
+  // Throw an exception if people attempt to not use the designated initializer.
+  self = [super init];
+  if (self != nil) {
+    [self doesNotRecognizeSelector:_cmd];
+    self = nil;
+  }
+  return self;
+}
+
+- (instancetype)initWithFieldDescription:(void *)description
+                         includesDefault:(BOOL)includesDefault
+                                  syntax:(GPBFileSyntax)syntax {
+  if ((self = [super init])) {
+    GPBMessageFieldDescription *coreDesc;
+    if (includesDefault) {
+      coreDesc = &(((GPBMessageFieldDescriptionWithDefault *)description)->core);
+    } else {
+      coreDesc = description;
+    }
+    description_ = coreDesc;
+    getSel_ = sel_getUid(coreDesc->name);
+    setSel_ = SelFromStrings("set", coreDesc->name, NULL, YES);
+
+    GPBDataType dataType = coreDesc->dataType;
+    BOOL isMessage = GPBDataTypeIsMessage(dataType);
+    BOOL isMapOrArray = GPBFieldIsMapOrArray(self);
+
+    if (isMapOrArray) {
+      // map<>/repeated fields get a *Count property (inplace of a has*) to
+      // support checking if there are any entries without triggering
+      // autocreation.
+      hasOrCountSel_ = SelFromStrings(NULL, coreDesc->name, "_Count", NO);
+    } else {
+      // If there is a positive hasIndex, then:
+      //   - All fields types for proto2 messages get has* selectors.
+      //   - Only message fields for proto3 messages get has* selectors.
+      // Note: the positive check is to handle oneOfs, we can't check
+      // containingOneof_ because it isn't set until after initialization.
+      if ((coreDesc->hasIndex >= 0) &&
+          (coreDesc->hasIndex != GPBNoHasBit) &&
+          ((syntax != GPBFileSyntaxProto3) || isMessage)) {
+        hasOrCountSel_ = SelFromStrings("has", coreDesc->name, NULL, NO);
+        setHasSel_ = SelFromStrings("setHas", coreDesc->name, NULL, YES);
+      }
+    }
+
+    // Extra type specific data.
+    if (isMessage) {
+      const char *className = coreDesc->dataTypeSpecific.className;
+      // Note: Only fetch the class here, can't send messages to it because
+      // that could cause cycles back to this class within +initialize if
+      // two messages have each other in fields (i.e. - they build a graph).
+      msgClass_ = objc_getClass(className);
+      NSAssert(msgClass_, @"Class %s not defined", className);
+    } else if (dataType == GPBDataTypeEnum) {
+      if ((coreDesc->flags & GPBFieldHasEnumDescriptor) != 0) {
+        enumHandling_.enumDescriptor_ =
+            coreDesc->dataTypeSpecific.enumDescFunc();
+      } else {
+        enumHandling_.enumVerifier_ =
+            coreDesc->dataTypeSpecific.enumVerifier;
+      }
+    }
+
+    // Non map<>/repeated fields can have defaults in proto2 syntax.
+    if (!isMapOrArray && includesDefault) {
+      defaultValue_ = ((GPBMessageFieldDescriptionWithDefault *)description)->defaultValue;
+      if (dataType == GPBDataTypeBytes) {
+        // Data stored as a length prefixed (network byte order) c-string in
+        // descriptor structure.
+        const uint8_t *bytes = (const uint8_t *)defaultValue_.valueData;
+        if (bytes) {
+          uint32_t length;
+          memcpy(&length, bytes, sizeof(length));
+          length = ntohl(length);
+          bytes += sizeof(length);
+          defaultValue_.valueData =
+              [[NSData alloc] initWithBytes:bytes length:length];
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (void)dealloc {
+  if (description_->dataType == GPBDataTypeBytes &&
+      !(description_->flags & GPBFieldRepeated)) {
+    [defaultValue_.valueData release];
+  }
+  [super dealloc];
+}
+
+- (GPBDataType)dataType {
+  return description_->dataType;
+}
+
+- (BOOL)hasDefaultValue {
+  return (description_->flags & GPBFieldHasDefaultValue) != 0;
+}
+
+- (uint32_t)number {
+  return description_->number;
+}
+
+- (NSString *)name {
+  return (NSString * _Nonnull)@(description_->name);
+}
+
+- (BOOL)isRequired {
+  return (description_->flags & GPBFieldRequired) != 0;
+}
+
+- (BOOL)isOptional {
+  return (description_->flags & GPBFieldOptional) != 0;
+}
+
+- (GPBFieldType)fieldType {
+  GPBFieldFlags flags = description_->flags;
+  if ((flags & GPBFieldRepeated) != 0) {
+    return GPBFieldTypeRepeated;
+  } else if ((flags & GPBFieldMapKeyMask) != 0) {
+    return GPBFieldTypeMap;
+  } else {
+    return GPBFieldTypeSingle;
+  }
+}
+
+- (GPBDataType)mapKeyDataType {
+  switch (description_->flags & GPBFieldMapKeyMask) {
+    case GPBFieldMapKeyInt32:
+      return GPBDataTypeInt32;
+    case GPBFieldMapKeyInt64:
+      return GPBDataTypeInt64;
+    case GPBFieldMapKeyUInt32:
+      return GPBDataTypeUInt32;
+    case GPBFieldMapKeyUInt64:
+      return GPBDataTypeUInt64;
+    case GPBFieldMapKeySInt32:
+      return GPBDataTypeSInt32;
+    case GPBFieldMapKeySInt64:
+      return GPBDataTypeSInt64;
+    case GPBFieldMapKeyFixed32:
+      return GPBDataTypeFixed32;
+    case GPBFieldMapKeyFixed64:
+      return GPBDataTypeFixed64;
+    case GPBFieldMapKeySFixed32:
+      return GPBDataTypeSFixed32;
+    case GPBFieldMapKeySFixed64:
+      return GPBDataTypeSFixed64;
+    case GPBFieldMapKeyBool:
+      return GPBDataTypeBool;
+    case GPBFieldMapKeyString:
+      return GPBDataTypeString;
+
+    default:
+      NSAssert(0, @"Not a map type");
+      return GPBDataTypeInt32;  // For lack of anything better.
+  }
+}
+
+- (BOOL)isPackable {
+  return (description_->flags & GPBFieldPacked) != 0;
+}
+
+- (BOOL)isValidEnumValue:(int32_t)value {
+  NSAssert(description_->dataType == GPBDataTypeEnum,
+           @"Field Must be of type GPBDataTypeEnum");
+  if (description_->flags & GPBFieldHasEnumDescriptor) {
+    return enumHandling_.enumDescriptor_.enumVerifier(value);
+  } else {
+    return enumHandling_.enumVerifier_(value);
+  }
+}
+
+- (GPBEnumDescriptor *)enumDescriptor {
+  if (description_->flags & GPBFieldHasEnumDescriptor) {
+    return enumHandling_.enumDescriptor_;
+  } else {
+    return nil;
+  }
+}
+
+- (GPBGenericValue)defaultValue {
+  // Depends on the fact that defaultValue_ is initialized either to "0/nil" or
+  // to an actual defaultValue in our initializer.
+  GPBGenericValue value = defaultValue_;
+
+  if (!(description_->flags & GPBFieldRepeated)) {
+    // We special handle data and strings. If they are nil, we replace them
+    // with empty string/empty data.
+    GPBDataType type = description_->dataType;
+    if (type == GPBDataTypeBytes && value.valueData == nil) {
+      value.valueData = GPBEmptyNSData();
+    } else if (type == GPBDataTypeString && value.valueString == nil) {
+      value.valueString = @"";
+    }
+  }
+  return value;
+}
+
+- (NSString *)textFormatName {
+  if ((description_->flags & GPBFieldTextFormatNameCustom) != 0) {
+    NSValue *extraInfoValue =
+        objc_getAssociatedObject(self, &kTextFormatExtraValueKey);
+    // Support can be left out at generation time.
+    if (!extraInfoValue) {
+      return nil;
+    }
+    const uint8_t *extraTextFormatInfo = [extraInfoValue pointerValue];
+    return GPBDecodeTextFormatName(extraTextFormatInfo, GPBFieldNumber(self),
+                                   self.name);
+  }
+
+  // The logic here has to match SetCommonFieldVariables() from
+  // objectivec_field.cc in the proto compiler.
+  NSString *name = self.name;
+  NSUInteger len = [name length];
+
+  // Remove the "_p" added to reserved names.
+  if ([name hasSuffix:@"_p"]) {
+    name = [name substringToIndex:(len - 2)];
+    len = [name length];
+  }
+
+  // Remove "Array" from the end for repeated fields.
+  if (((description_->flags & GPBFieldRepeated) != 0) &&
+      [name hasSuffix:@"Array"]) {
+    name = [name substringToIndex:(len - 5)];
+    len = [name length];
+  }
+
+  // Groups vs. other fields.
+  if (description_->dataType == GPBDataTypeGroup) {
+    // Just capitalize the first letter.
+    unichar firstChar = [name characterAtIndex:0];
+    if (firstChar >= 'a' && firstChar <= 'z') {
+      NSString *firstCharString =
+          [NSString stringWithFormat:@"%C", (unichar)(firstChar - 'a' + 'A')];
+      NSString *result =
+          [name stringByReplacingCharactersInRange:NSMakeRange(0, 1)
+                                        withString:firstCharString];
+      return result;
+    }
+    return name;
+
+  } else {
+    // Undo the CamelCase.
+    NSMutableString *result = [NSMutableString stringWithCapacity:len];
+    for (uint32_t i = 0; i < len; i++) {
+      unichar c = [name characterAtIndex:i];
+      if (c >= 'A' && c <= 'Z') {
+        if (i > 0) {
+          [result appendFormat:@"_%C", (unichar)(c - 'A' + 'a')];
+        } else {
+          [result appendFormat:@"%C", c];
+        }
+      } else {
+        [result appendFormat:@"%C", c];
+      }
+    }
+    return result;
+  }
+}
+
+@end
+
+@implementation GPBEnumDescriptor {
+  NSString *name_;
+  // valueNames_ is a single c string with all of the value names appended
+  // together, each null terminated.  -calcValueNameOffsets fills in
+  // nameOffsets_ with the offsets to allow quicker access to the individual
+  // names.
+  const char *valueNames_;
+  const int32_t *values_;
+  GPBEnumValidationFunc enumVerifier_;
+  const uint8_t *extraTextFormatInfo_;
+  uint32_t *nameOffsets_;
+  uint32_t valueCount_;
+}
+
+@synthesize name = name_;
+@synthesize enumVerifier = enumVerifier_;
+
++ (instancetype)
+    allocDescriptorForName:(NSString *)name
+                valueNames:(const char *)valueNames
+                    values:(const int32_t *)values
+                     count:(uint32_t)valueCount
+              enumVerifier:(GPBEnumValidationFunc)enumVerifier {
+  GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name
+                                                  valueNames:valueNames
+                                                      values:values
+                                                       count:valueCount
+                                                enumVerifier:enumVerifier];
+  return descriptor;
+}
+
++ (instancetype)
+    allocDescriptorForName:(NSString *)name
+                valueNames:(const char *)valueNames
+                    values:(const int32_t *)values
+                     count:(uint32_t)valueCount
+              enumVerifier:(GPBEnumValidationFunc)enumVerifier
+       extraTextFormatInfo:(const char *)extraTextFormatInfo {
+  // Call the common case.
+  GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name
+                                                    valueNames:valueNames
+                                                        values:values
+                                                         count:valueCount
+                                                  enumVerifier:enumVerifier];
+  // Set the extra info.
+  descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo;
+  return descriptor;
+}
+
+- (instancetype)initWithName:(NSString *)name
+                  valueNames:(const char *)valueNames
+                      values:(const int32_t *)values
+                       count:(uint32_t)valueCount
+                enumVerifier:(GPBEnumValidationFunc)enumVerifier {
+  if ((self = [super init])) {
+    name_ = [name copy];
+    valueNames_ = valueNames;
+    values_ = values;
+    valueCount_ = valueCount;
+    enumVerifier_ = enumVerifier;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [name_ release];
+  if (nameOffsets_) free(nameOffsets_);
+  [super dealloc];
+}
+
+- (void)calcValueNameOffsets {
+  @synchronized(self) {
+    if (nameOffsets_ != NULL) {
+      return;
+    }
+    uint32_t *offsets = malloc(valueCount_ * sizeof(uint32_t));
+    const char *scan = valueNames_;
+    for (uint32_t i = 0; i < valueCount_; ++i) {
+      offsets[i] = (uint32_t)(scan - valueNames_);
+      while (*scan != '\0') ++scan;
+      ++scan;  // Step over the null.
+    }
+    nameOffsets_ = offsets;
+  }
+}
+
+- (NSString *)enumNameForValue:(int32_t)number {
+  if (nameOffsets_ == NULL) [self calcValueNameOffsets];
+
+  for (uint32_t i = 0; i < valueCount_; ++i) {
+    if (values_[i] == number) {
+      const char *valueName = valueNames_ + nameOffsets_[i];
+      NSString *fullName = [NSString stringWithFormat:@"%@_%s", name_, valueName];
+      return fullName;
+    }
+  }
+  return nil;
+}
+
+- (BOOL)getValue:(int32_t *)outValue forEnumName:(NSString *)name {
+  // Must have the prefix.
+  NSUInteger prefixLen = name_.length + 1;
+  if ((name.length <= prefixLen) || ![name hasPrefix:name_] ||
+      ([name characterAtIndex:prefixLen - 1] != '_')) {
+    return NO;
+  }
+
+  // Skip over the prefix.
+  const char *nameAsCStr = [name UTF8String];
+  nameAsCStr += prefixLen;
+
+  if (nameOffsets_ == NULL) [self calcValueNameOffsets];
+
+  // Find it.
+  for (uint32_t i = 0; i < valueCount_; ++i) {
+    const char *valueName = valueNames_ + nameOffsets_[i];
+    if (strcmp(nameAsCStr, valueName) == 0) {
+      if (outValue) {
+        *outValue = values_[i];
+      }
+      return YES;
+    }
+  }
+  return NO;
+}
+
+- (BOOL)getValue:(int32_t *)outValue forEnumTextFormatName:(NSString *)textFormatName {
+    if (nameOffsets_ == NULL) [self calcValueNameOffsets];
+
+    for (uint32_t i = 0; i < valueCount_; ++i) {
+        int32_t value = values_[i];
+        NSString *valueTextFormatName = [self textFormatNameForValue:value];
+        if ([valueTextFormatName isEqual:textFormatName]) {
+            if (outValue) {
+                *outValue = value;
+            }
+            return YES;
+        }
+    }
+    return NO;
+}
+
+- (NSString *)textFormatNameForValue:(int32_t)number {
+  if (nameOffsets_ == NULL) [self calcValueNameOffsets];
+
+  // Find the EnumValue descriptor and its index.
+  BOOL foundIt = NO;
+  uint32_t valueDescriptorIndex;
+  for (valueDescriptorIndex = 0; valueDescriptorIndex < valueCount_;
+       ++valueDescriptorIndex) {
+    if (values_[valueDescriptorIndex] == number) {
+      foundIt = YES;
+      break;
+    }
+  }
+
+  if (!foundIt) {
+    return nil;
+  }
+
+  NSString *result = nil;
+  // Naming adds an underscore between enum name and value name, skip that also.
+  const char *valueName = valueNames_ + nameOffsets_[valueDescriptorIndex];
+  NSString *shortName = @(valueName);
+
+  // See if it is in the map of special format handling.
+  if (extraTextFormatInfo_) {
+    result = GPBDecodeTextFormatName(extraTextFormatInfo_,
+                                     (int32_t)valueDescriptorIndex, shortName);
+  }
+  // Logic here needs to match what objectivec_enum.cc does in the proto
+  // compiler.
+  if (result == nil) {
+    NSUInteger len = [shortName length];
+    NSMutableString *worker = [NSMutableString stringWithCapacity:len];
+    for (NSUInteger i = 0; i < len; i++) {
+      unichar c = [shortName characterAtIndex:i];
+      if (i > 0 && c >= 'A' && c <= 'Z') {
+        [worker appendString:@"_"];
+      }
+      [worker appendFormat:@"%c", toupper((char)c)];
+    }
+    result = worker;
+  }
+  return result;
+}
+
+@end
+
+@implementation GPBExtensionDescriptor {
+  GPBGenericValue defaultValue_;
+}
+
+@synthesize containingMessageClass = containingMessageClass_;
+
+- (instancetype)initWithExtensionDescription:
+        (GPBExtensionDescription *)description {
+  if ((self = [super init])) {
+    description_ = description;
+
+#if defined(DEBUG) && DEBUG
+    const char *className = description->messageOrGroupClassName;
+    if (className) {
+      NSAssert(objc_lookUpClass(className) != Nil,
+               @"Class %s not defined", className);
+    }
+#endif
+
+    if (description->extendedClass) {
+      Class containingClass = objc_lookUpClass(description->extendedClass);
+      NSAssert(containingClass, @"Class %s not defined",
+               description->extendedClass);
+      containingMessageClass_ = containingClass;
+    }
+
+    GPBDataType type = description_->dataType;
+    if (type == GPBDataTypeBytes) {
+      // Data stored as a length prefixed c-string in descriptor records.
+      const uint8_t *bytes =
+          (const uint8_t *)description->defaultValue.valueData;
+      if (bytes) {
+        uint32_t length;
+        memcpy(&length, bytes, sizeof(length));
+        // The length is stored in network byte order.
+        length = ntohl(length);
+        bytes += sizeof(length);
+        defaultValue_.valueData =
+            [[NSData alloc] initWithBytes:bytes length:length];
+      }
+    } else if (type == GPBDataTypeMessage || type == GPBDataTypeGroup) {
+      // The default is looked up in -defaultValue instead since extensions
+      // aren't common, we avoid the hit startup hit and it avoid initialization
+      // order issues.
+    } else {
+      defaultValue_ = description->defaultValue;
+    }
+  }
+  return self;
+}
+
+- (void)dealloc {
+  if ((description_->dataType == GPBDataTypeBytes) &&
+      !GPBExtensionIsRepeated(description_)) {
+    [defaultValue_.valueData release];
+  }
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+#pragma unused(zone)
+  // Immutable.
+  return [self retain];
+}
+
+- (NSString *)singletonName {
+  return (NSString * _Nonnull)@(description_->singletonName);
+}
+
+- (const char *)singletonNameC {
+  return description_->singletonName;
+}
+
+- (uint32_t)fieldNumber {
+  return description_->fieldNumber;
+}
+
+- (GPBDataType)dataType {
+  return description_->dataType;
+}
+
+- (GPBWireFormat)wireType {
+  return GPBWireFormatForType(description_->dataType,
+                              GPBExtensionIsPacked(description_));
+}
+
+- (GPBWireFormat)alternateWireType {
+  NSAssert(GPBExtensionIsRepeated(description_),
+           @"Only valid on repeated extensions");
+  return GPBWireFormatForType(description_->dataType,
+                              !GPBExtensionIsPacked(description_));
+}
+
+- (BOOL)isRepeated {
+  return GPBExtensionIsRepeated(description_);
+}
+
+- (BOOL)isPackable {
+  return GPBExtensionIsPacked(description_);
+}
+
+- (Class)msgClass {
+  return objc_getClass(description_->messageOrGroupClassName);
+}
+
+- (GPBEnumDescriptor *)enumDescriptor {
+  if (description_->dataType == GPBDataTypeEnum) {
+    GPBEnumDescriptor *enumDescriptor = description_->enumDescriptorFunc();
+    return enumDescriptor;
+  }
+  return nil;
+}
+
+- (id)defaultValue {
+  if (GPBExtensionIsRepeated(description_)) {
+    return nil;
+  }
+
+  switch (description_->dataType) {
+    case GPBDataTypeBool:
+      return @(defaultValue_.valueBool);
+    case GPBDataTypeFloat:
+      return @(defaultValue_.valueFloat);
+    case GPBDataTypeDouble:
+      return @(defaultValue_.valueDouble);
+    case GPBDataTypeInt32:
+    case GPBDataTypeSInt32:
+    case GPBDataTypeEnum:
+    case GPBDataTypeSFixed32:
+      return @(defaultValue_.valueInt32);
+    case GPBDataTypeInt64:
+    case GPBDataTypeSInt64:
+    case GPBDataTypeSFixed64:
+      return @(defaultValue_.valueInt64);
+    case GPBDataTypeUInt32:
+    case GPBDataTypeFixed32:
+      return @(defaultValue_.valueUInt32);
+    case GPBDataTypeUInt64:
+    case GPBDataTypeFixed64:
+      return @(defaultValue_.valueUInt64);
+    case GPBDataTypeBytes:
+      // Like message fields, the default is zero length data.
+      return (defaultValue_.valueData ? defaultValue_.valueData
+                                      : GPBEmptyNSData());
+    case GPBDataTypeString:
+      // Like message fields, the default is zero length string.
+      return (defaultValue_.valueString ? defaultValue_.valueString : @"");
+    case GPBDataTypeGroup:
+    case GPBDataTypeMessage:
+      return nil;
+  }
+}
+
+- (NSComparisonResult)compareByFieldNumber:(GPBExtensionDescriptor *)other {
+  int32_t selfNumber = description_->fieldNumber;
+  int32_t otherNumber = other->description_->fieldNumber;
+  if (selfNumber < otherNumber) {
+    return NSOrderedAscending;
+  } else if (selfNumber == otherNumber) {
+    return NSOrderedSame;
+  } else {
+    return NSOrderedDescending;
+  }
+}
+
+@end
+
+#pragma clang diagnostic pop
diff --git a/iOS/Pods/Protobuf/objectivec/GPBDescriptor_PackagePrivate.h b/iOS/Pods/Protobuf/objectivec/GPBDescriptor_PackagePrivate.h
new file mode 100644 (file)
index 0000000..452b3f8
--- /dev/null
@@ -0,0 +1,325 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+// This header is private to the ProtobolBuffers library and must NOT be
+// included by any sources outside this library. The contents of this file are
+// subject to change at any time without notice.
+
+#import "GPBDescriptor.h"
+#import "GPBWireFormat.h"
+
+// Describes attributes of the field.
+typedef NS_OPTIONS(uint16_t, GPBFieldFlags) {
+  GPBFieldNone            = 0,
+  // These map to standard protobuf concepts.
+  GPBFieldRequired        = 1 << 0,
+  GPBFieldRepeated        = 1 << 1,
+  GPBFieldPacked          = 1 << 2,
+  GPBFieldOptional        = 1 << 3,
+  GPBFieldHasDefaultValue = 1 << 4,
+
+  // Indicates the field needs custom handling for the TextFormat name, if not
+  // set, the name can be derived from the ObjC name.
+  GPBFieldTextFormatNameCustom = 1 << 6,
+  // Indicates the field has an enum descriptor.
+  GPBFieldHasEnumDescriptor = 1 << 7,
+
+  // These are not standard protobuf concepts, they are specific to the
+  // Objective C runtime.
+
+  // These bits are used to mark the field as a map and what the key
+  // type is.
+  GPBFieldMapKeyMask     = 0xF << 8,
+  GPBFieldMapKeyInt32    =  1 << 8,
+  GPBFieldMapKeyInt64    =  2 << 8,
+  GPBFieldMapKeyUInt32   =  3 << 8,
+  GPBFieldMapKeyUInt64   =  4 << 8,
+  GPBFieldMapKeySInt32   =  5 << 8,
+  GPBFieldMapKeySInt64   =  6 << 8,
+  GPBFieldMapKeyFixed32  =  7 << 8,
+  GPBFieldMapKeyFixed64  =  8 << 8,
+  GPBFieldMapKeySFixed32 =  9 << 8,
+  GPBFieldMapKeySFixed64 = 10 << 8,
+  GPBFieldMapKeyBool     = 11 << 8,
+  GPBFieldMapKeyString   = 12 << 8,
+};
+
+// NOTE: The structures defined here have their members ordered to minimize
+// their size. This directly impacts the size of apps since these exist per
+// field/extension.
+
+// Describes a single field in a protobuf as it is represented as an ivar.
+typedef struct GPBMessageFieldDescription {
+  // Name of ivar.
+  const char *name;
+  union {
+    const char *className;  // Name for message class.
+    // For enums only: If EnumDescriptors are compiled in, it will be that,
+    // otherwise it will be the verifier.
+    GPBEnumDescriptorFunc enumDescFunc;
+    GPBEnumValidationFunc enumVerifier;
+  } dataTypeSpecific;
+  // The field number for the ivar.
+  uint32_t number;
+  // The index (in bits) into _has_storage_.
+  //   >= 0: the bit to use for a value being set.
+  //   = GPBNoHasBit(INT32_MAX): no storage used.
+  //   < 0: in a oneOf, use a full int32 to record the field active.
+  int32_t hasIndex;
+  // Offset of the variable into it's structure struct.
+  uint32_t offset;
+  // Field flags. Use accessor functions below.
+  GPBFieldFlags flags;
+  // Data type of the ivar.
+  GPBDataType dataType;
+} GPBMessageFieldDescription;
+
+// Fields in messages defined in a 'proto2' syntax file can provide a default
+// value. This struct provides the default along with the field info.
+typedef struct GPBMessageFieldDescriptionWithDefault {
+  // Default value for the ivar.
+  GPBGenericValue defaultValue;
+
+  GPBMessageFieldDescription core;
+} GPBMessageFieldDescriptionWithDefault;
+
+// Describes attributes of the extension.
+typedef NS_OPTIONS(uint8_t, GPBExtensionOptions) {
+  GPBExtensionNone          = 0,
+  // These map to standard protobuf concepts.
+  GPBExtensionRepeated      = 1 << 0,
+  GPBExtensionPacked        = 1 << 1,
+  GPBExtensionSetWireFormat = 1 << 2,
+};
+
+// An extension
+typedef struct GPBExtensionDescription {
+  GPBGenericValue defaultValue;
+  const char *singletonName;
+  const char *extendedClass;
+  const char *messageOrGroupClassName;
+  GPBEnumDescriptorFunc enumDescriptorFunc;
+  int32_t fieldNumber;
+  GPBDataType dataType;
+  GPBExtensionOptions options;
+} GPBExtensionDescription;
+
+typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) {
+  GPBDescriptorInitializationFlag_None              = 0,
+  GPBDescriptorInitializationFlag_FieldsWithDefault = 1 << 0,
+  GPBDescriptorInitializationFlag_WireFormat        = 1 << 1,
+};
+
+@interface GPBDescriptor () {
+ @package
+  NSArray *fields_;
+  NSArray *oneofs_;
+  uint32_t storageSize_;
+}
+
+// fieldDescriptions have to be long lived, they are held as raw pointers.
++ (instancetype)
+    allocDescriptorForClass:(Class)messageClass
+                  rootClass:(Class)rootClass
+                       file:(GPBFileDescriptor *)file
+                     fields:(void *)fieldDescriptions
+                 fieldCount:(uint32_t)fieldCount
+                storageSize:(uint32_t)storageSize
+                      flags:(GPBDescriptorInitializationFlags)flags;
+
+- (instancetype)initWithClass:(Class)messageClass
+                         file:(GPBFileDescriptor *)file
+                       fields:(NSArray *)fields
+                  storageSize:(uint32_t)storage
+                   wireFormat:(BOOL)wireFormat;
+
+// Called right after init to provide extra information to avoid init having
+// an explosion of args. These pointers are recorded, so they are expected
+// to live for the lifetime of the app.
+- (void)setupOneofs:(const char **)oneofNames
+              count:(uint32_t)count
+      firstHasIndex:(int32_t)firstHasIndex;
+- (void)setupExtraTextInfo:(const char *)extraTextFormatInfo;
+- (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count;
+- (void)setupContainingMessageClassName:(const char *)msgClassName;
+- (void)setupMessageClassNameSuffix:(NSString *)suffix;
+
+@end
+
+@interface GPBFileDescriptor ()
+- (instancetype)initWithPackage:(NSString *)package
+                     objcPrefix:(NSString *)objcPrefix
+                         syntax:(GPBFileSyntax)syntax;
+- (instancetype)initWithPackage:(NSString *)package
+                         syntax:(GPBFileSyntax)syntax;
+@end
+
+@interface GPBOneofDescriptor () {
+ @package
+  const char *name_;
+  NSArray *fields_;
+  SEL caseSel_;
+}
+// name must be long lived.
+- (instancetype)initWithName:(const char *)name fields:(NSArray *)fields;
+@end
+
+@interface GPBFieldDescriptor () {
+ @package
+  GPBMessageFieldDescription *description_;
+  GPB_UNSAFE_UNRETAINED GPBOneofDescriptor *containingOneof_;
+
+  SEL getSel_;
+  SEL setSel_;
+  SEL hasOrCountSel_;  // *Count for map<>/repeated fields, has* otherwise.
+  SEL setHasSel_;
+}
+
+// Single initializer
+// description has to be long lived, it is held as a raw pointer.
+- (instancetype)initWithFieldDescription:(void *)description
+                         includesDefault:(BOOL)includesDefault
+                                  syntax:(GPBFileSyntax)syntax;
+@end
+
+@interface GPBEnumDescriptor ()
+// valueNames, values and extraTextFormatInfo have to be long lived, they are
+// held as raw pointers.
++ (instancetype)
+    allocDescriptorForName:(NSString *)name
+                valueNames:(const char *)valueNames
+                    values:(const int32_t *)values
+                     count:(uint32_t)valueCount
+              enumVerifier:(GPBEnumValidationFunc)enumVerifier;
++ (instancetype)
+    allocDescriptorForName:(NSString *)name
+                valueNames:(const char *)valueNames
+                    values:(const int32_t *)values
+                     count:(uint32_t)valueCount
+              enumVerifier:(GPBEnumValidationFunc)enumVerifier
+       extraTextFormatInfo:(const char *)extraTextFormatInfo;
+
+- (instancetype)initWithName:(NSString *)name
+                  valueNames:(const char *)valueNames
+                      values:(const int32_t *)values
+                       count:(uint32_t)valueCount
+                enumVerifier:(GPBEnumValidationFunc)enumVerifier;
+@end
+
+@interface GPBExtensionDescriptor () {
+ @package
+  GPBExtensionDescription *description_;
+}
+@property(nonatomic, readonly) GPBWireFormat wireType;
+
+// For repeated extensions, alternateWireType is the wireType with the opposite
+// value for the packable property.  i.e. - if the extension was marked packed
+// it would be the wire type for unpacked; if the extension was marked unpacked,
+// it would be the wire type for packed.
+@property(nonatomic, readonly) GPBWireFormat alternateWireType;
+
+// description has to be long lived, it is held as a raw pointer.
+- (instancetype)initWithExtensionDescription:
+    (GPBExtensionDescription *)description;
+- (NSComparisonResult)compareByFieldNumber:(GPBExtensionDescriptor *)other;
+@end
+
+CF_EXTERN_C_BEGIN
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+GPB_INLINE BOOL GPBFieldIsMapOrArray(GPBFieldDescriptor *field) {
+  return (field->description_->flags &
+          (GPBFieldRepeated | GPBFieldMapKeyMask)) != 0;
+}
+
+GPB_INLINE GPBDataType GPBGetFieldDataType(GPBFieldDescriptor *field) {
+  return field->description_->dataType;
+}
+
+GPB_INLINE int32_t GPBFieldHasIndex(GPBFieldDescriptor *field) {
+  return field->description_->hasIndex;
+}
+
+GPB_INLINE uint32_t GPBFieldNumber(GPBFieldDescriptor *field) {
+  return field->description_->number;
+}
+
+#pragma clang diagnostic pop
+
+uint32_t GPBFieldTag(GPBFieldDescriptor *self);
+
+// For repeated fields, alternateWireType is the wireType with the opposite
+// value for the packable property.  i.e. - if the field was marked packed it
+// would be the wire type for unpacked; if the field was marked unpacked, it
+// would be the wire type for packed.
+uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self);
+
+GPB_INLINE BOOL GPBHasPreservingUnknownEnumSemantics(GPBFileSyntax syntax) {
+  return syntax == GPBFileSyntaxProto3;
+}
+
+GPB_INLINE BOOL GPBExtensionIsRepeated(GPBExtensionDescription *description) {
+  return (description->options & GPBExtensionRepeated) != 0;
+}
+
+GPB_INLINE BOOL GPBExtensionIsPacked(GPBExtensionDescription *description) {
+  return (description->options & GPBExtensionPacked) != 0;
+}
+
+GPB_INLINE BOOL GPBExtensionIsWireFormat(GPBExtensionDescription *description) {
+  return (description->options & GPBExtensionSetWireFormat) != 0;
+}
+
+// Helper for compile time assets.
+#ifndef GPBInternalCompileAssert
+  #if __has_feature(c_static_assert) || __has_extension(c_static_assert)
+    #define GPBInternalCompileAssert(test, msg) _Static_assert((test), #msg)
+  #else
+    // Pre-Xcode 7 support.
+    #define GPBInternalCompileAssertSymbolInner(line, msg) GPBInternalCompileAssert ## line ## __ ## msg
+    #define GPBInternalCompileAssertSymbol(line, msg) GPBInternalCompileAssertSymbolInner(line, msg)
+    #define GPBInternalCompileAssert(test, msg) \
+        typedef char GPBInternalCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
+  #endif  // __has_feature(c_static_assert) || __has_extension(c_static_assert)
+#endif // GPBInternalCompileAssert
+
+// Sanity check that there isn't padding between the field description
+// structures with and without a default.
+GPBInternalCompileAssert(sizeof(GPBMessageFieldDescriptionWithDefault) ==
+                         (sizeof(GPBGenericValue) +
+                          sizeof(GPBMessageFieldDescription)),
+                         DescriptionsWithDefault_different_size_than_expected);
+
+CF_EXTERN_C_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBDictionary.h b/iOS/Pods/Protobuf/objectivec/GPBDictionary.h
new file mode 100644 (file)
index 0000000..a81165e
--- /dev/null
@@ -0,0 +1,5770 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBRuntimeTypes.h"
+
+// Note on naming: for the classes holding numeric values, a more natural
+// naming of the method might be things like "-valueForKey:",
+// "-setValue:forKey:"; etc. But those selectors are also defined by Key Value
+// Coding (KVC) as categories on NSObject. So "overloading" the selectors with
+// other meanings can cause warnings (based on compiler settings), but more
+// importantly, some of those selector get called as KVC breaks up keypaths.
+// So if those selectors are used, using KVC will compile cleanly, but could
+// crash as it invokes those selectors with the wrong types of arguments.
+
+NS_ASSUME_NONNULL_BEGIN
+
+//%PDDM-EXPAND DECLARE_DICTIONARIES()
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt32 -> UInt32
+
+/**
+ * Class used for map fields of <uint32_t, uint32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt32UInt32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt32s:(const uint32_t [__nullable])values
+                        forKeys:(const uint32_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt32UInt32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(uint32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(uint32_t key, uint32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt32UInt32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt32:(uint32_t)value forKey:(uint32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt32ForKey:(uint32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Int32
+
+/**
+ * Class used for map fields of <uint32_t, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt32Int32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt32s:(const int32_t [__nullable])values
+                       forKeys:(const uint32_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt32Int32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(uint32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(uint32_t key, int32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt32Int32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt32:(int32_t)value forKey:(uint32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt32ForKey:(uint32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> UInt64
+
+/**
+ * Class used for map fields of <uint32_t, uint64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt32UInt64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt64s:(const uint64_t [__nullable])values
+                        forKeys:(const uint32_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt32UInt64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(uint32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(uint32_t key, uint64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt32UInt64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt64:(uint64_t)value forKey:(uint32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt64ForKey:(uint32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Int64
+
+/**
+ * Class used for map fields of <uint32_t, int64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt32Int64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt64s:(const int64_t [__nullable])values
+                       forKeys:(const uint32_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt32Int64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(uint32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(uint32_t key, int64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt32Int64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt64:(int64_t)value forKey:(uint32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt64ForKey:(uint32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Bool
+
+/**
+ * Class used for map fields of <uint32_t, BOOL>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt32BoolDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithBools:(const BOOL [__nullable])values
+                      forKeys:(const uint32_t [__nullable])keys
+                        count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt32BoolDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getBool:(nullable BOOL *)value forKey:(uint32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(uint32_t key, BOOL value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt32BoolDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setBool:(BOOL)value forKey:(uint32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeBoolForKey:(uint32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Float
+
+/**
+ * Class used for map fields of <uint32_t, float>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt32FloatDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithFloats:(const float [__nullable])values
+                       forKeys:(const uint32_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt32FloatDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getFloat:(nullable float *)value forKey:(uint32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(uint32_t key, float value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt32FloatDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setFloat:(float)value forKey:(uint32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeFloatForKey:(uint32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Double
+
+/**
+ * Class used for map fields of <uint32_t, double>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt32DoubleDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithDoubles:(const double [__nullable])values
+                        forKeys:(const uint32_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt32DoubleDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getDouble:(nullable double *)value forKey:(uint32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(uint32_t key, double value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt32DoubleDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setDouble:(double)value forKey:(uint32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeDoubleForKey:(uint32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Enum
+
+/**
+ * Class used for map fields of <uint32_t, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt32EnumDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+/** The validation function to check if the enums are valid. */
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
+/**
+ * Initializes a dictionary with the given validation function.
+ *
+ * @param func The enum validation function for the dictionary.
+ *
+ * @return A newly initialized dictionary.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+
+/**
+ * Initializes a dictionary with the entries given.
+ *
+ * @param func   The enum validation function for the dictionary.
+ * @param values The raw enum values values to be placed in the dictionary.
+ * @param keys   The keys under which to store the values.
+ * @param count  The number of entries to store in the dictionary.
+ *
+ * @return A newly initialized dictionary with the keys and values in it.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [__nullable])values
+                                   forKeys:(const uint32_t [__nullable])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes a dictionary with the entries from the given.
+ * dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to the dictionary.
+ *
+ * @return A newly initialized dictionary with the entries from the given
+ *         dictionary in it.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt32EnumDictionary *)dictionary;
+
+/**
+ * Initializes a dictionary with the given capacity.
+ *
+ * @param func     The enum validation function for the dictionary.
+ * @param numItems Capacity needed for the dictionary.
+ *
+ * @return A newly initialized dictionary with the given capacity.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getEnum:(nullable int32_t *)value forKey:(uint32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(uint32_t key, int32_t value, BOOL *stop))block;
+
+/**
+ * Gets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue Pointer into which the value will be set, if found.
+ * @param key      Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getRawValue:(nullable int32_t *)rawValue forKey:(uint32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:      The key for the current entry.
+ *   **rawValue**: The value for the current entry
+ *   **stop**:     A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(uint32_t key, int32_t rawValue, BOOL *stop))block;
+
+/**
+ * Adds the keys and raw enum values from another dictionary.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addRawEntriesFromDictionary:(GPBUInt32EnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setEnum:(int32_t)value forKey:(uint32_t)key;
+
+/**
+ * Sets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue The raw enum value to set.
+ * @param key      The key under which to store the raw enum value.
+ **/
+- (void)setRawValue:(int32_t)rawValue forKey:(uint32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeEnumForKey:(uint32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt32 -> Object
+
+/**
+ * Class used for map fields of <uint32_t, ObjectType>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt32ObjectDictionary<__covariant ObjectType> : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param objects      The values to be placed in this dictionary.
+ * @param keys         The keys under which to store the values.
+ * @param count        The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithObjects:(const ObjectType __nonnull GPB_UNSAFE_UNRETAINED [__nullable])objects
+                        forKeys:(const uint32_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt32ObjectDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Fetches the object stored under the given key.
+ *
+ * @param key Key under which the value is stored, if present.
+ *
+ * @return The object if found, nil otherwise.
+ **/
+- (ObjectType)objectForKey:(uint32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:         The key for the current entry.
+ *   **object**:      The value for the current entry
+ *   **stop**:        A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndObjectsUsingBlock:
+    (void (^)(uint32_t key, ObjectType object, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt32ObjectDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param object     The value to set.
+ * @param key        The key under which to store the value.
+ **/
+- (void)setObject:(ObjectType)object forKey:(uint32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeObjectForKey:(uint32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> UInt32
+
+/**
+ * Class used for map fields of <int32_t, uint32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt32UInt32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt32s:(const uint32_t [__nullable])values
+                        forKeys:(const int32_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt32UInt32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(int32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(int32_t key, uint32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt32UInt32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt32:(uint32_t)value forKey:(int32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt32ForKey:(int32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Int32
+
+/**
+ * Class used for map fields of <int32_t, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt32Int32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt32s:(const int32_t [__nullable])values
+                       forKeys:(const int32_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt32Int32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(int32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(int32_t key, int32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt32Int32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt32:(int32_t)value forKey:(int32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt32ForKey:(int32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> UInt64
+
+/**
+ * Class used for map fields of <int32_t, uint64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt32UInt64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt64s:(const uint64_t [__nullable])values
+                        forKeys:(const int32_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt32UInt64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(int32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(int32_t key, uint64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt32UInt64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt64:(uint64_t)value forKey:(int32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt64ForKey:(int32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Int64
+
+/**
+ * Class used for map fields of <int32_t, int64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt32Int64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt64s:(const int64_t [__nullable])values
+                       forKeys:(const int32_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt32Int64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(int32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(int32_t key, int64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt32Int64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt64:(int64_t)value forKey:(int32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt64ForKey:(int32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Bool
+
+/**
+ * Class used for map fields of <int32_t, BOOL>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt32BoolDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithBools:(const BOOL [__nullable])values
+                      forKeys:(const int32_t [__nullable])keys
+                        count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt32BoolDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getBool:(nullable BOOL *)value forKey:(int32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(int32_t key, BOOL value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt32BoolDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setBool:(BOOL)value forKey:(int32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeBoolForKey:(int32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Float
+
+/**
+ * Class used for map fields of <int32_t, float>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt32FloatDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithFloats:(const float [__nullable])values
+                       forKeys:(const int32_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt32FloatDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getFloat:(nullable float *)value forKey:(int32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(int32_t key, float value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt32FloatDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setFloat:(float)value forKey:(int32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeFloatForKey:(int32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Double
+
+/**
+ * Class used for map fields of <int32_t, double>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt32DoubleDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithDoubles:(const double [__nullable])values
+                        forKeys:(const int32_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt32DoubleDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getDouble:(nullable double *)value forKey:(int32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(int32_t key, double value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt32DoubleDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setDouble:(double)value forKey:(int32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeDoubleForKey:(int32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Enum
+
+/**
+ * Class used for map fields of <int32_t, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt32EnumDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+/** The validation function to check if the enums are valid. */
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
+/**
+ * Initializes a dictionary with the given validation function.
+ *
+ * @param func The enum validation function for the dictionary.
+ *
+ * @return A newly initialized dictionary.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+
+/**
+ * Initializes a dictionary with the entries given.
+ *
+ * @param func   The enum validation function for the dictionary.
+ * @param values The raw enum values values to be placed in the dictionary.
+ * @param keys   The keys under which to store the values.
+ * @param count  The number of entries to store in the dictionary.
+ *
+ * @return A newly initialized dictionary with the keys and values in it.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [__nullable])values
+                                   forKeys:(const int32_t [__nullable])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes a dictionary with the entries from the given.
+ * dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to the dictionary.
+ *
+ * @return A newly initialized dictionary with the entries from the given
+ *         dictionary in it.
+ **/
+- (instancetype)initWithDictionary:(GPBInt32EnumDictionary *)dictionary;
+
+/**
+ * Initializes a dictionary with the given capacity.
+ *
+ * @param func     The enum validation function for the dictionary.
+ * @param numItems Capacity needed for the dictionary.
+ *
+ * @return A newly initialized dictionary with the given capacity.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getEnum:(nullable int32_t *)value forKey:(int32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(int32_t key, int32_t value, BOOL *stop))block;
+
+/**
+ * Gets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue Pointer into which the value will be set, if found.
+ * @param key      Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getRawValue:(nullable int32_t *)rawValue forKey:(int32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:      The key for the current entry.
+ *   **rawValue**: The value for the current entry
+ *   **stop**:     A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(int32_t key, int32_t rawValue, BOOL *stop))block;
+
+/**
+ * Adds the keys and raw enum values from another dictionary.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addRawEntriesFromDictionary:(GPBInt32EnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setEnum:(int32_t)value forKey:(int32_t)key;
+
+/**
+ * Sets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue The raw enum value to set.
+ * @param key      The key under which to store the raw enum value.
+ **/
+- (void)setRawValue:(int32_t)rawValue forKey:(int32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeEnumForKey:(int32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int32 -> Object
+
+/**
+ * Class used for map fields of <int32_t, ObjectType>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt32ObjectDictionary<__covariant ObjectType> : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param objects      The values to be placed in this dictionary.
+ * @param keys         The keys under which to store the values.
+ * @param count        The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithObjects:(const ObjectType __nonnull GPB_UNSAFE_UNRETAINED [__nullable])objects
+                        forKeys:(const int32_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt32ObjectDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Fetches the object stored under the given key.
+ *
+ * @param key Key under which the value is stored, if present.
+ *
+ * @return The object if found, nil otherwise.
+ **/
+- (ObjectType)objectForKey:(int32_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:         The key for the current entry.
+ *   **object**:      The value for the current entry
+ *   **stop**:        A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndObjectsUsingBlock:
+    (void (^)(int32_t key, ObjectType object, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt32ObjectDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param object     The value to set.
+ * @param key        The key under which to store the value.
+ **/
+- (void)setObject:(ObjectType)object forKey:(int32_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeObjectForKey:(int32_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> UInt32
+
+/**
+ * Class used for map fields of <uint64_t, uint32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt64UInt32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt32s:(const uint32_t [__nullable])values
+                        forKeys:(const uint64_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt64UInt32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(uint64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(uint64_t key, uint32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt64UInt32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt32:(uint32_t)value forKey:(uint64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt32ForKey:(uint64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Int32
+
+/**
+ * Class used for map fields of <uint64_t, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt64Int32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt32s:(const int32_t [__nullable])values
+                       forKeys:(const uint64_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt64Int32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(uint64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(uint64_t key, int32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt64Int32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt32:(int32_t)value forKey:(uint64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt32ForKey:(uint64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> UInt64
+
+/**
+ * Class used for map fields of <uint64_t, uint64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt64UInt64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt64s:(const uint64_t [__nullable])values
+                        forKeys:(const uint64_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt64UInt64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(uint64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(uint64_t key, uint64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt64UInt64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt64:(uint64_t)value forKey:(uint64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt64ForKey:(uint64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Int64
+
+/**
+ * Class used for map fields of <uint64_t, int64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt64Int64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt64s:(const int64_t [__nullable])values
+                       forKeys:(const uint64_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt64Int64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(uint64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(uint64_t key, int64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt64Int64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt64:(int64_t)value forKey:(uint64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt64ForKey:(uint64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Bool
+
+/**
+ * Class used for map fields of <uint64_t, BOOL>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt64BoolDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithBools:(const BOOL [__nullable])values
+                      forKeys:(const uint64_t [__nullable])keys
+                        count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt64BoolDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getBool:(nullable BOOL *)value forKey:(uint64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(uint64_t key, BOOL value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt64BoolDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setBool:(BOOL)value forKey:(uint64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeBoolForKey:(uint64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Float
+
+/**
+ * Class used for map fields of <uint64_t, float>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt64FloatDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithFloats:(const float [__nullable])values
+                       forKeys:(const uint64_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt64FloatDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getFloat:(nullable float *)value forKey:(uint64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(uint64_t key, float value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt64FloatDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setFloat:(float)value forKey:(uint64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeFloatForKey:(uint64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Double
+
+/**
+ * Class used for map fields of <uint64_t, double>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt64DoubleDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithDoubles:(const double [__nullable])values
+                        forKeys:(const uint64_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt64DoubleDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getDouble:(nullable double *)value forKey:(uint64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(uint64_t key, double value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt64DoubleDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setDouble:(double)value forKey:(uint64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeDoubleForKey:(uint64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Enum
+
+/**
+ * Class used for map fields of <uint64_t, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt64EnumDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+/** The validation function to check if the enums are valid. */
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
+/**
+ * Initializes a dictionary with the given validation function.
+ *
+ * @param func The enum validation function for the dictionary.
+ *
+ * @return A newly initialized dictionary.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+
+/**
+ * Initializes a dictionary with the entries given.
+ *
+ * @param func   The enum validation function for the dictionary.
+ * @param values The raw enum values values to be placed in the dictionary.
+ * @param keys   The keys under which to store the values.
+ * @param count  The number of entries to store in the dictionary.
+ *
+ * @return A newly initialized dictionary with the keys and values in it.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [__nullable])values
+                                   forKeys:(const uint64_t [__nullable])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes a dictionary with the entries from the given.
+ * dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to the dictionary.
+ *
+ * @return A newly initialized dictionary with the entries from the given
+ *         dictionary in it.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt64EnumDictionary *)dictionary;
+
+/**
+ * Initializes a dictionary with the given capacity.
+ *
+ * @param func     The enum validation function for the dictionary.
+ * @param numItems Capacity needed for the dictionary.
+ *
+ * @return A newly initialized dictionary with the given capacity.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getEnum:(nullable int32_t *)value forKey:(uint64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(uint64_t key, int32_t value, BOOL *stop))block;
+
+/**
+ * Gets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue Pointer into which the value will be set, if found.
+ * @param key      Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getRawValue:(nullable int32_t *)rawValue forKey:(uint64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:      The key for the current entry.
+ *   **rawValue**: The value for the current entry
+ *   **stop**:     A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(uint64_t key, int32_t rawValue, BOOL *stop))block;
+
+/**
+ * Adds the keys and raw enum values from another dictionary.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addRawEntriesFromDictionary:(GPBUInt64EnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setEnum:(int32_t)value forKey:(uint64_t)key;
+
+/**
+ * Sets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue The raw enum value to set.
+ * @param key      The key under which to store the raw enum value.
+ **/
+- (void)setRawValue:(int32_t)rawValue forKey:(uint64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeEnumForKey:(uint64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - UInt64 -> Object
+
+/**
+ * Class used for map fields of <uint64_t, ObjectType>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBUInt64ObjectDictionary<__covariant ObjectType> : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param objects      The values to be placed in this dictionary.
+ * @param keys         The keys under which to store the values.
+ * @param count        The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithObjects:(const ObjectType __nonnull GPB_UNSAFE_UNRETAINED [__nullable])objects
+                        forKeys:(const uint64_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBUInt64ObjectDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Fetches the object stored under the given key.
+ *
+ * @param key Key under which the value is stored, if present.
+ *
+ * @return The object if found, nil otherwise.
+ **/
+- (ObjectType)objectForKey:(uint64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:         The key for the current entry.
+ *   **object**:      The value for the current entry
+ *   **stop**:        A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndObjectsUsingBlock:
+    (void (^)(uint64_t key, ObjectType object, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBUInt64ObjectDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param object     The value to set.
+ * @param key        The key under which to store the value.
+ **/
+- (void)setObject:(ObjectType)object forKey:(uint64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeObjectForKey:(uint64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> UInt32
+
+/**
+ * Class used for map fields of <int64_t, uint32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt64UInt32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt32s:(const uint32_t [__nullable])values
+                        forKeys:(const int64_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt64UInt32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(int64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(int64_t key, uint32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt64UInt32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt32:(uint32_t)value forKey:(int64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt32ForKey:(int64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Int32
+
+/**
+ * Class used for map fields of <int64_t, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt64Int32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt32s:(const int32_t [__nullable])values
+                       forKeys:(const int64_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt64Int32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(int64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(int64_t key, int32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt64Int32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt32:(int32_t)value forKey:(int64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt32ForKey:(int64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> UInt64
+
+/**
+ * Class used for map fields of <int64_t, uint64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt64UInt64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt64s:(const uint64_t [__nullable])values
+                        forKeys:(const int64_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt64UInt64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(int64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(int64_t key, uint64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt64UInt64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt64:(uint64_t)value forKey:(int64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt64ForKey:(int64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Int64
+
+/**
+ * Class used for map fields of <int64_t, int64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt64Int64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt64s:(const int64_t [__nullable])values
+                       forKeys:(const int64_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt64Int64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(int64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(int64_t key, int64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt64Int64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt64:(int64_t)value forKey:(int64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt64ForKey:(int64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Bool
+
+/**
+ * Class used for map fields of <int64_t, BOOL>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt64BoolDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithBools:(const BOOL [__nullable])values
+                      forKeys:(const int64_t [__nullable])keys
+                        count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt64BoolDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getBool:(nullable BOOL *)value forKey:(int64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(int64_t key, BOOL value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt64BoolDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setBool:(BOOL)value forKey:(int64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeBoolForKey:(int64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Float
+
+/**
+ * Class used for map fields of <int64_t, float>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt64FloatDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithFloats:(const float [__nullable])values
+                       forKeys:(const int64_t [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt64FloatDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getFloat:(nullable float *)value forKey:(int64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(int64_t key, float value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt64FloatDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setFloat:(float)value forKey:(int64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeFloatForKey:(int64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Double
+
+/**
+ * Class used for map fields of <int64_t, double>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt64DoubleDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithDoubles:(const double [__nullable])values
+                        forKeys:(const int64_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt64DoubleDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getDouble:(nullable double *)value forKey:(int64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(int64_t key, double value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt64DoubleDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setDouble:(double)value forKey:(int64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeDoubleForKey:(int64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Enum
+
+/**
+ * Class used for map fields of <int64_t, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt64EnumDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+/** The validation function to check if the enums are valid. */
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
+/**
+ * Initializes a dictionary with the given validation function.
+ *
+ * @param func The enum validation function for the dictionary.
+ *
+ * @return A newly initialized dictionary.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+
+/**
+ * Initializes a dictionary with the entries given.
+ *
+ * @param func   The enum validation function for the dictionary.
+ * @param values The raw enum values values to be placed in the dictionary.
+ * @param keys   The keys under which to store the values.
+ * @param count  The number of entries to store in the dictionary.
+ *
+ * @return A newly initialized dictionary with the keys and values in it.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [__nullable])values
+                                   forKeys:(const int64_t [__nullable])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes a dictionary with the entries from the given.
+ * dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to the dictionary.
+ *
+ * @return A newly initialized dictionary with the entries from the given
+ *         dictionary in it.
+ **/
+- (instancetype)initWithDictionary:(GPBInt64EnumDictionary *)dictionary;
+
+/**
+ * Initializes a dictionary with the given capacity.
+ *
+ * @param func     The enum validation function for the dictionary.
+ * @param numItems Capacity needed for the dictionary.
+ *
+ * @return A newly initialized dictionary with the given capacity.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getEnum:(nullable int32_t *)value forKey:(int64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(int64_t key, int32_t value, BOOL *stop))block;
+
+/**
+ * Gets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue Pointer into which the value will be set, if found.
+ * @param key      Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getRawValue:(nullable int32_t *)rawValue forKey:(int64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:      The key for the current entry.
+ *   **rawValue**: The value for the current entry
+ *   **stop**:     A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(int64_t key, int32_t rawValue, BOOL *stop))block;
+
+/**
+ * Adds the keys and raw enum values from another dictionary.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addRawEntriesFromDictionary:(GPBInt64EnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setEnum:(int32_t)value forKey:(int64_t)key;
+
+/**
+ * Sets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue The raw enum value to set.
+ * @param key      The key under which to store the raw enum value.
+ **/
+- (void)setRawValue:(int32_t)rawValue forKey:(int64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeEnumForKey:(int64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Int64 -> Object
+
+/**
+ * Class used for map fields of <int64_t, ObjectType>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBInt64ObjectDictionary<__covariant ObjectType> : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param objects      The values to be placed in this dictionary.
+ * @param keys         The keys under which to store the values.
+ * @param count        The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithObjects:(const ObjectType __nonnull GPB_UNSAFE_UNRETAINED [__nullable])objects
+                        forKeys:(const int64_t [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBInt64ObjectDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Fetches the object stored under the given key.
+ *
+ * @param key Key under which the value is stored, if present.
+ *
+ * @return The object if found, nil otherwise.
+ **/
+- (ObjectType)objectForKey:(int64_t)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:         The key for the current entry.
+ *   **object**:      The value for the current entry
+ *   **stop**:        A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndObjectsUsingBlock:
+    (void (^)(int64_t key, ObjectType object, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBInt64ObjectDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param object     The value to set.
+ * @param key        The key under which to store the value.
+ **/
+- (void)setObject:(ObjectType)object forKey:(int64_t)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeObjectForKey:(int64_t)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> UInt32
+
+/**
+ * Class used for map fields of <BOOL, uint32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBBoolUInt32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt32s:(const uint32_t [__nullable])values
+                        forKeys:(const BOOL [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBBoolUInt32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(BOOL)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(BOOL key, uint32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBBoolUInt32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt32:(uint32_t)value forKey:(BOOL)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt32ForKey:(BOOL)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Int32
+
+/**
+ * Class used for map fields of <BOOL, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBBoolInt32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt32s:(const int32_t [__nullable])values
+                       forKeys:(const BOOL [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBBoolInt32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(BOOL)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(BOOL key, int32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBBoolInt32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt32:(int32_t)value forKey:(BOOL)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt32ForKey:(BOOL)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> UInt64
+
+/**
+ * Class used for map fields of <BOOL, uint64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBBoolUInt64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt64s:(const uint64_t [__nullable])values
+                        forKeys:(const BOOL [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBBoolUInt64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(BOOL)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(BOOL key, uint64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBBoolUInt64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt64:(uint64_t)value forKey:(BOOL)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt64ForKey:(BOOL)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Int64
+
+/**
+ * Class used for map fields of <BOOL, int64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBBoolInt64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt64s:(const int64_t [__nullable])values
+                       forKeys:(const BOOL [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBBoolInt64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(BOOL)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(BOOL key, int64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBBoolInt64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt64:(int64_t)value forKey:(BOOL)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt64ForKey:(BOOL)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Bool
+
+/**
+ * Class used for map fields of <BOOL, BOOL>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBBoolBoolDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithBools:(const BOOL [__nullable])values
+                      forKeys:(const BOOL [__nullable])keys
+                        count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBBoolBoolDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getBool:(nullable BOOL *)value forKey:(BOOL)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(BOOL key, BOOL value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBBoolBoolDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setBool:(BOOL)value forKey:(BOOL)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeBoolForKey:(BOOL)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Float
+
+/**
+ * Class used for map fields of <BOOL, float>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBBoolFloatDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithFloats:(const float [__nullable])values
+                       forKeys:(const BOOL [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBBoolFloatDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getFloat:(nullable float *)value forKey:(BOOL)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(BOOL key, float value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBBoolFloatDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setFloat:(float)value forKey:(BOOL)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeFloatForKey:(BOOL)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Double
+
+/**
+ * Class used for map fields of <BOOL, double>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBBoolDoubleDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithDoubles:(const double [__nullable])values
+                        forKeys:(const BOOL [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBBoolDoubleDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getDouble:(nullable double *)value forKey:(BOOL)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(BOOL key, double value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBBoolDoubleDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setDouble:(double)value forKey:(BOOL)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeDoubleForKey:(BOOL)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Enum
+
+/**
+ * Class used for map fields of <BOOL, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBBoolEnumDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+/** The validation function to check if the enums are valid. */
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
+/**
+ * Initializes a dictionary with the given validation function.
+ *
+ * @param func The enum validation function for the dictionary.
+ *
+ * @return A newly initialized dictionary.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+
+/**
+ * Initializes a dictionary with the entries given.
+ *
+ * @param func   The enum validation function for the dictionary.
+ * @param values The raw enum values values to be placed in the dictionary.
+ * @param keys   The keys under which to store the values.
+ * @param count  The number of entries to store in the dictionary.
+ *
+ * @return A newly initialized dictionary with the keys and values in it.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [__nullable])values
+                                   forKeys:(const BOOL [__nullable])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes a dictionary with the entries from the given.
+ * dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to the dictionary.
+ *
+ * @return A newly initialized dictionary with the entries from the given
+ *         dictionary in it.
+ **/
+- (instancetype)initWithDictionary:(GPBBoolEnumDictionary *)dictionary;
+
+/**
+ * Initializes a dictionary with the given capacity.
+ *
+ * @param func     The enum validation function for the dictionary.
+ * @param numItems Capacity needed for the dictionary.
+ *
+ * @return A newly initialized dictionary with the given capacity.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getEnum:(nullable int32_t *)value forKey:(BOOL)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(BOOL key, int32_t value, BOOL *stop))block;
+
+/**
+ * Gets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue Pointer into which the value will be set, if found.
+ * @param key      Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getRawValue:(nullable int32_t *)rawValue forKey:(BOOL)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:      The key for the current entry.
+ *   **rawValue**: The value for the current entry
+ *   **stop**:     A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(BOOL key, int32_t rawValue, BOOL *stop))block;
+
+/**
+ * Adds the keys and raw enum values from another dictionary.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addRawEntriesFromDictionary:(GPBBoolEnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setEnum:(int32_t)value forKey:(BOOL)key;
+
+/**
+ * Sets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue The raw enum value to set.
+ * @param key      The key under which to store the raw enum value.
+ **/
+- (void)setRawValue:(int32_t)rawValue forKey:(BOOL)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeEnumForKey:(BOOL)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - Bool -> Object
+
+/**
+ * Class used for map fields of <BOOL, ObjectType>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBBoolObjectDictionary<__covariant ObjectType> : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param objects      The values to be placed in this dictionary.
+ * @param keys         The keys under which to store the values.
+ * @param count        The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithObjects:(const ObjectType __nonnull GPB_UNSAFE_UNRETAINED [__nullable])objects
+                        forKeys:(const BOOL [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBBoolObjectDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Fetches the object stored under the given key.
+ *
+ * @param key Key under which the value is stored, if present.
+ *
+ * @return The object if found, nil otherwise.
+ **/
+- (ObjectType)objectForKey:(BOOL)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:         The key for the current entry.
+ *   **object**:      The value for the current entry
+ *   **stop**:        A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndObjectsUsingBlock:
+    (void (^)(BOOL key, ObjectType object, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBBoolObjectDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param object     The value to set.
+ * @param key        The key under which to store the value.
+ **/
+- (void)setObject:(ObjectType)object forKey:(BOOL)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeObjectForKey:(BOOL)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> UInt32
+
+/**
+ * Class used for map fields of <NSString, uint32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBStringUInt32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt32s:(const uint32_t [__nullable])values
+                        forKeys:(const NSString * __nonnull GPB_UNSAFE_UNRETAINED [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBStringUInt32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(NSString *)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(NSString *key, uint32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBStringUInt32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt32:(uint32_t)value forKey:(NSString *)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt32ForKey:(NSString *)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Int32
+
+/**
+ * Class used for map fields of <NSString, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBStringInt32Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt32s:(const int32_t [__nullable])values
+                       forKeys:(const NSString * __nonnull GPB_UNSAFE_UNRETAINED [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBStringInt32Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(NSString *)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(NSString *key, int32_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBStringInt32Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt32:(int32_t)value forKey:(NSString *)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt32ForKey:(NSString *)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> UInt64
+
+/**
+ * Class used for map fields of <NSString, uint64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBStringUInt64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithUInt64s:(const uint64_t [__nullable])values
+                        forKeys:(const NSString * __nonnull GPB_UNSAFE_UNRETAINED [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBStringUInt64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(NSString *)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(NSString *key, uint64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBStringUInt64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setUInt64:(uint64_t)value forKey:(NSString *)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeUInt64ForKey:(NSString *)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Int64
+
+/**
+ * Class used for map fields of <NSString, int64_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBStringInt64Dictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithInt64s:(const int64_t [__nullable])values
+                       forKeys:(const NSString * __nonnull GPB_UNSAFE_UNRETAINED [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBStringInt64Dictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(NSString *)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(NSString *key, int64_t value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBStringInt64Dictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setInt64:(int64_t)value forKey:(NSString *)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeInt64ForKey:(NSString *)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Bool
+
+/**
+ * Class used for map fields of <NSString, BOOL>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBStringBoolDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithBools:(const BOOL [__nullable])values
+                      forKeys:(const NSString * __nonnull GPB_UNSAFE_UNRETAINED [__nullable])keys
+                        count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBStringBoolDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getBool:(nullable BOOL *)value forKey:(NSString *)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(NSString *key, BOOL value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBStringBoolDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setBool:(BOOL)value forKey:(NSString *)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeBoolForKey:(NSString *)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Float
+
+/**
+ * Class used for map fields of <NSString, float>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBStringFloatDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithFloats:(const float [__nullable])values
+                       forKeys:(const NSString * __nonnull GPB_UNSAFE_UNRETAINED [__nullable])keys
+                         count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBStringFloatDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getFloat:(nullable float *)value forKey:(NSString *)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(NSString *key, float value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBStringFloatDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setFloat:(float)value forKey:(NSString *)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeFloatForKey:(NSString *)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Double
+
+/**
+ * Class used for map fields of <NSString, double>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBStringDoubleDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+
+/**
+ * Initializes this dictionary, copying the given values and keys.
+ *
+ * @param values      The values to be placed in this dictionary.
+ * @param keys        The keys under which to store the values.
+ * @param count       The number of elements to copy into the dictionary.
+ *
+ * @return A newly initialized dictionary with a copy of the values and keys.
+ **/
+- (instancetype)initWithDoubles:(const double [__nullable])values
+                        forKeys:(const NSString * __nonnull GPB_UNSAFE_UNRETAINED [__nullable])keys
+                          count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes this dictionary, copying the entries from the given dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to this dictionary.
+ *
+ * @return A newly initialized dictionary with the entries of the given dictionary.
+ **/
+- (instancetype)initWithDictionary:(GPBStringDoubleDictionary *)dictionary;
+
+/**
+ * Initializes this dictionary with the requested capacity.
+ *
+ * @param numItems Number of items needed for this dictionary.
+ *
+ * @return A newly initialized dictionary with the requested capacity.
+ **/
+- (instancetype)initWithCapacity:(NSUInteger)numItems;
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getDouble:(nullable double *)value forKey:(NSString *)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(NSString *key, double value, BOOL *stop))block;
+
+/**
+ * Adds the keys and values from another dictionary.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addEntriesFromDictionary:(GPBStringDoubleDictionary *)otherDictionary;
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setDouble:(double)value forKey:(NSString *)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeDoubleForKey:(NSString *)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+#pragma mark - String -> Enum
+
+/**
+ * Class used for map fields of <NSString, int32_t>
+ * values. This performs better than boxing into NSNumbers in NSDictionaries.
+ *
+ * @note This class is not meant to be subclassed.
+ **/
+@interface GPBStringEnumDictionary : NSObject <NSCopying>
+
+/** Number of entries stored in this dictionary. */
+@property(nonatomic, readonly) NSUInteger count;
+/** The validation function to check if the enums are valid. */
+@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+
+/**
+ * Initializes a dictionary with the given validation function.
+ *
+ * @param func The enum validation function for the dictionary.
+ *
+ * @return A newly initialized dictionary.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+
+/**
+ * Initializes a dictionary with the entries given.
+ *
+ * @param func   The enum validation function for the dictionary.
+ * @param values The raw enum values values to be placed in the dictionary.
+ * @param keys   The keys under which to store the values.
+ * @param count  The number of entries to store in the dictionary.
+ *
+ * @return A newly initialized dictionary with the keys and values in it.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [__nullable])values
+                                   forKeys:(const NSString * __nonnull GPB_UNSAFE_UNRETAINED [__nullable])keys
+                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initializes a dictionary with the entries from the given.
+ * dictionary.
+ *
+ * @param dictionary Dictionary containing the entries to add to the dictionary.
+ *
+ * @return A newly initialized dictionary with the entries from the given
+ *         dictionary in it.
+ **/
+- (instancetype)initWithDictionary:(GPBStringEnumDictionary *)dictionary;
+
+/**
+ * Initializes a dictionary with the given capacity.
+ *
+ * @param func     The enum validation function for the dictionary.
+ * @param numItems Capacity needed for the dictionary.
+ *
+ * @return A newly initialized dictionary with the given capacity.
+ **/
+- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems;
+
+// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+// is not a valid enumerator as defined by validationFunc. If the actual value is
+// desired, use "raw" version of the method.
+
+/**
+ * Gets the value for the given key.
+ *
+ * @param value Pointer into which the value will be set, if found.
+ * @param key   Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getEnum:(nullable int32_t *)value forKey:(NSString *)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:        The key for the current entry.
+ *   **value**:      The value for the current entry
+ *   **stop**:       A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(NSString *key, int32_t value, BOOL *stop))block;
+
+/**
+ * Gets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue Pointer into which the value will be set, if found.
+ * @param key      Key under which the value is stored, if present.
+ *
+ * @return YES if the key was found and the value was copied, NO otherwise.
+ **/
+- (BOOL)getRawValue:(nullable int32_t *)rawValue forKey:(NSString *)key;
+
+/**
+ * Enumerates the keys and values on this dictionary with the given block.
+ *
+ * @note This method bypass the validationFunc to enable the access of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param block The block to enumerate with.
+ *   **key**:      The key for the current entry.
+ *   **rawValue**: The value for the current entry
+ *   **stop**:     A pointer to a boolean that when set stops the enumeration.
+ **/
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(NSString *key, int32_t rawValue, BOOL *stop))block;
+
+/**
+ * Adds the keys and raw enum values from another dictionary.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param otherDictionary Dictionary containing entries to be added to this
+ *                        dictionary.
+ **/
+- (void)addRawEntriesFromDictionary:(GPBStringEnumDictionary *)otherDictionary;
+
+// If value is not a valid enumerator as defined by validationFunc, these
+// methods will assert in debug, and will log in release and assign the value
+// to the default value. Use the rawValue methods below to assign non enumerator
+// values.
+
+/**
+ * Sets the value for the given key.
+ *
+ * @param value     The value to set.
+ * @param key       The key under which to store the value.
+ **/
+- (void)setEnum:(int32_t)value forKey:(NSString *)key;
+
+/**
+ * Sets the raw enum value for the given key.
+ *
+ * @note This method bypass the validationFunc to enable the setting of values that
+ *       were not known at the time the binary was compiled.
+ *
+ * @param rawValue The raw enum value to set.
+ * @param key      The key under which to store the raw enum value.
+ **/
+- (void)setRawValue:(int32_t)rawValue forKey:(NSString *)key;
+
+/**
+ * Removes the entry for the given key.
+ *
+ * @param aKey Key to be removed from this dictionary.
+ **/
+- (void)removeEnumForKey:(NSString *)aKey;
+
+/**
+ * Removes all entries in this dictionary.
+ **/
+- (void)removeAll;
+
+@end
+
+//%PDDM-EXPAND-END DECLARE_DICTIONARIES()
+
+NS_ASSUME_NONNULL_END
+
+//%PDDM-DEFINE DECLARE_DICTIONARIES()
+//%DICTIONARY_INTERFACES_FOR_POD_KEY(UInt32, uint32_t)
+//%DICTIONARY_INTERFACES_FOR_POD_KEY(Int32, int32_t)
+//%DICTIONARY_INTERFACES_FOR_POD_KEY(UInt64, uint64_t)
+//%DICTIONARY_INTERFACES_FOR_POD_KEY(Int64, int64_t)
+//%DICTIONARY_INTERFACES_FOR_POD_KEY(Bool, BOOL)
+//%DICTIONARY_POD_INTERFACES_FOR_KEY(String, NSString, *, OBJECT)
+//%PDDM-DEFINE DICTIONARY_INTERFACES_FOR_POD_KEY(KEY_NAME, KEY_TYPE)
+//%DICTIONARY_POD_INTERFACES_FOR_KEY(KEY_NAME, KEY_TYPE, , POD)
+//%DICTIONARY_POD_KEY_TO_OBJECT_INTERFACE(KEY_NAME, KEY_TYPE, Object, ObjectType)
+//%PDDM-DEFINE DICTIONARY_POD_INTERFACES_FOR_KEY(KEY_NAME, KEY_TYPE, KisP, KHELPER)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, UInt32, uint32_t)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Int32, int32_t)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, UInt64, uint64_t)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Int64, int64_t)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Bool, BOOL)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Float, float)
+//%DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Double, double)
+//%DICTIONARY_KEY_TO_ENUM_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, Enum, int32_t)
+//%PDDM-DEFINE DICTIONARY_KEY_TO_POD_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_COMMON_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE, POD, VALUE_NAME, value)
+//%PDDM-DEFINE DICTIONARY_POD_KEY_TO_OBJECT_INTERFACE(KEY_NAME, KEY_TYPE, VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_COMMON_INTERFACE(KEY_NAME, KEY_TYPE, , POD, VALUE_NAME, VALUE_TYPE, OBJECT, Object, object)
+//%PDDM-DEFINE VALUE_FOR_KEY_POD(KEY_TYPE, VALUE_TYPE, VNAME)
+//%/**
+//% * Gets the value for the given key.
+//% *
+//% * @param value Pointer into which the value will be set, if found.
+//% * @param key   Key under which the value is stored, if present.
+//% *
+//% * @return YES if the key was found and the value was copied, NO otherwise.
+//% **/
+//%- (BOOL)get##VNAME##:(nullable VALUE_TYPE *)value forKey:(KEY_TYPE)key;
+//%PDDM-DEFINE VALUE_FOR_KEY_OBJECT(KEY_TYPE, VALUE_TYPE, VNAME)
+//%/**
+//% * Fetches the object stored under the given key.
+//% *
+//% * @param key Key under which the value is stored, if present.
+//% *
+//% * @return The object if found, nil otherwise.
+//% **/
+//%- (VALUE_TYPE)objectForKey:(KEY_TYPE)key;
+//%PDDM-DEFINE VALUE_FOR_KEY_Enum(KEY_TYPE, VALUE_TYPE, VNAME)
+//%VALUE_FOR_KEY_POD(KEY_TYPE, VALUE_TYPE, VNAME)
+//%PDDM-DEFINE ARRAY_ARG_MODIFIERPOD()
+// Nothing
+//%PDDM-DEFINE ARRAY_ARG_MODIFIEREnum()
+// Nothing
+//%PDDM-DEFINE ARRAY_ARG_MODIFIEROBJECT()
+//%__nonnull GPB_UNSAFE_UNRETAINED ##
+//%PDDM-DEFINE DICTIONARY_CLASS_DECLPOD(KEY_NAME, VALUE_NAME, VALUE_TYPE)
+//%GPB##KEY_NAME##VALUE_NAME##Dictionary
+//%PDDM-DEFINE DICTIONARY_CLASS_DECLEnum(KEY_NAME, VALUE_NAME, VALUE_TYPE)
+//%GPB##KEY_NAME##VALUE_NAME##Dictionary
+//%PDDM-DEFINE DICTIONARY_CLASS_DECLOBJECT(KEY_NAME, VALUE_NAME, VALUE_TYPE)
+//%GPB##KEY_NAME##VALUE_NAME##Dictionary<__covariant VALUE_TYPE>
+//%PDDM-DEFINE DICTIONARY_COMMON_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE, VHELPER, VNAME, VNAME_VAR)
+//%#pragma mark - KEY_NAME -> VALUE_NAME
+//%
+//%/**
+//% * Class used for map fields of <##KEY_TYPE##, ##VALUE_TYPE##>
+//% * values. This performs better than boxing into NSNumbers in NSDictionaries.
+//% *
+//% * @note This class is not meant to be subclassed.
+//% **/
+//%@interface DICTIONARY_CLASS_DECL##VHELPER(KEY_NAME, VALUE_NAME, VALUE_TYPE) : NSObject <NSCopying>
+//%
+//%/** Number of entries stored in this dictionary. */
+//%@property(nonatomic, readonly) NSUInteger count;
+//%
+//%/**
+//% * Initializes this dictionary, copying the given values and keys.
+//% *
+//% * @param ##VNAME_VAR##s      The values to be placed in this dictionary.
+//% * @param keys ##VNAME_VAR$S##  The keys under which to store the values.
+//% * @param count ##VNAME_VAR$S## The number of elements to copy into the dictionary.
+//% *
+//% * @return A newly initialized dictionary with a copy of the values and keys.
+//% **/
+//%- (instancetype)initWith##VNAME##s:(const VALUE_TYPE ARRAY_ARG_MODIFIER##VHELPER()[__nullable])##VNAME_VAR##s
+//%                ##VNAME$S##  forKeys:(const KEY_TYPE##KisP$S##KisP ARRAY_ARG_MODIFIER##KHELPER()[__nullable])keys
+//%                ##VNAME$S##    count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+//%
+//%/**
+//% * Initializes this dictionary, copying the entries from the given dictionary.
+//% *
+//% * @param dictionary Dictionary containing the entries to add to this dictionary.
+//% *
+//% * @return A newly initialized dictionary with the entries of the given dictionary.
+//% **/
+//%- (instancetype)initWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary;
+//%
+//%/**
+//% * Initializes this dictionary with the requested capacity.
+//% *
+//% * @param numItems Number of items needed for this dictionary.
+//% *
+//% * @return A newly initialized dictionary with the requested capacity.
+//% **/
+//%- (instancetype)initWithCapacity:(NSUInteger)numItems;
+//%
+//%DICTIONARY_IMMUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER, VNAME, VNAME_VAR)
+//%
+//%/**
+//% * Adds the keys and values from another dictionary.
+//% *
+//% * @param otherDictionary Dictionary containing entries to be added to this
+//% *                        dictionary.
+//% **/
+//%- (void)addEntriesFromDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)otherDictionary;
+//%
+//%DICTIONARY_MUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER, VNAME, VNAME_VAR)
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE DICTIONARY_KEY_TO_ENUM_INTERFACE(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_KEY_TO_ENUM_INTERFACE2(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE, Enum)
+//%PDDM-DEFINE DICTIONARY_KEY_TO_ENUM_INTERFACE2(KEY_NAME, KEY_TYPE, KisP, KHELPER, VALUE_NAME, VALUE_TYPE, VHELPER)
+//%#pragma mark - KEY_NAME -> VALUE_NAME
+//%
+//%/**
+//% * Class used for map fields of <##KEY_TYPE##, ##VALUE_TYPE##>
+//% * values. This performs better than boxing into NSNumbers in NSDictionaries.
+//% *
+//% * @note This class is not meant to be subclassed.
+//% **/
+//%@interface GPB##KEY_NAME##VALUE_NAME##Dictionary : NSObject <NSCopying>
+//%
+//%/** Number of entries stored in this dictionary. */
+//%@property(nonatomic, readonly) NSUInteger count;
+//%/** The validation function to check if the enums are valid. */
+//%@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+//%
+//%/**
+//% * Initializes a dictionary with the given validation function.
+//% *
+//% * @param func The enum validation function for the dictionary.
+//% *
+//% * @return A newly initialized dictionary.
+//% **/
+//%- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+//%
+//%/**
+//% * Initializes a dictionary with the entries given.
+//% *
+//% * @param func   The enum validation function for the dictionary.
+//% * @param values The raw enum values values to be placed in the dictionary.
+//% * @param keys   The keys under which to store the values.
+//% * @param count  The number of entries to store in the dictionary.
+//% *
+//% * @return A newly initialized dictionary with the keys and values in it.
+//% **/
+//%- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+//%                                 rawValues:(const VALUE_TYPE ARRAY_ARG_MODIFIER##VHELPER()[__nullable])values
+//%                                   forKeys:(const KEY_TYPE##KisP$S##KisP ARRAY_ARG_MODIFIER##KHELPER()[__nullable])keys
+//%                                     count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
+//%
+//%/**
+//% * Initializes a dictionary with the entries from the given.
+//% * dictionary.
+//% *
+//% * @param dictionary Dictionary containing the entries to add to the dictionary.
+//% *
+//% * @return A newly initialized dictionary with the entries from the given
+//% *         dictionary in it.
+//% **/
+//%- (instancetype)initWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary;
+//%
+//%/**
+//% * Initializes a dictionary with the given capacity.
+//% *
+//% * @param func     The enum validation function for the dictionary.
+//% * @param numItems Capacity needed for the dictionary.
+//% *
+//% * @return A newly initialized dictionary with the given capacity.
+//% **/
+//%- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
+//%                                  capacity:(NSUInteger)numItems;
+//%
+//%// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
+//%// is not a valid enumerator as defined by validationFunc. If the actual value is
+//%// desired, use "raw" version of the method.
+//%
+//%DICTIONARY_IMMUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER, Enum, value)
+//%
+//%/**
+//% * Gets the raw enum value for the given key.
+//% *
+//% * @note This method bypass the validationFunc to enable the access of values that
+//% *       were not known at the time the binary was compiled.
+//% *
+//% * @param rawValue Pointer into which the value will be set, if found.
+//% * @param key      Key under which the value is stored, if present.
+//% *
+//% * @return YES if the key was found and the value was copied, NO otherwise.
+//% **/
+//%- (BOOL)getRawValue:(nullable VALUE_TYPE *)rawValue forKey:(KEY_TYPE##KisP$S##KisP)key;
+//%
+//%/**
+//% * Enumerates the keys and values on this dictionary with the given block.
+//% *
+//% * @note This method bypass the validationFunc to enable the access of values that
+//% *       were not known at the time the binary was compiled.
+//% *
+//% * @param block The block to enumerate with.
+//% *   **key**:      The key for the current entry.
+//% *   **rawValue**: The value for the current entry
+//% *   **stop**:     A pointer to a boolean that when set stops the enumeration.
+//% **/
+//%- (void)enumerateKeysAndRawValuesUsingBlock:
+//%    (void (^)(KEY_TYPE KisP##key, VALUE_TYPE rawValue, BOOL *stop))block;
+//%
+//%/**
+//% * Adds the keys and raw enum values from another dictionary.
+//% *
+//% * @note This method bypass the validationFunc to enable the setting of values that
+//% *       were not known at the time the binary was compiled.
+//% *
+//% * @param otherDictionary Dictionary containing entries to be added to this
+//% *                        dictionary.
+//% **/
+//%- (void)addRawEntriesFromDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)otherDictionary;
+//%
+//%// If value is not a valid enumerator as defined by validationFunc, these
+//%// methods will assert in debug, and will log in release and assign the value
+//%// to the default value. Use the rawValue methods below to assign non enumerator
+//%// values.
+//%
+//%DICTIONARY_MUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER, Enum, value)
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE DICTIONARY_IMMUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER, VNAME, VNAME_VAR)
+//%VALUE_FOR_KEY_##VHELPER(KEY_TYPE##KisP$S##KisP, VALUE_TYPE, VNAME)
+//%
+//%/**
+//% * Enumerates the keys and values on this dictionary with the given block.
+//% *
+//% * @param block The block to enumerate with.
+//% *   **key**: ##VNAME_VAR$S##  The key for the current entry.
+//% *   **VNAME_VAR**:      The value for the current entry
+//% *   **stop**: ##VNAME_VAR$S## A pointer to a boolean that when set stops the enumeration.
+//% **/
+//%- (void)enumerateKeysAnd##VNAME##sUsingBlock:
+//%    (void (^)(KEY_TYPE KisP##key, VALUE_TYPE VNAME_VAR, BOOL *stop))block;
+
+//%PDDM-DEFINE DICTIONARY_MUTABLE_INTERFACE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, VHELPER, VNAME, VNAME_VAR)
+//%/**
+//% * Sets the value for the given key.
+//% *
+//% * @param ##VNAME_VAR     The value to set.
+//% * @param key ##VNAME_VAR$S## The key under which to store the value.
+//% **/
+//%- (void)set##VNAME##:(VALUE_TYPE)##VNAME_VAR forKey:(KEY_TYPE##KisP$S##KisP)key;
+//%DICTIONARY_EXTRA_MUTABLE_METHODS_##VHELPER(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE)
+//%/**
+//% * Removes the entry for the given key.
+//% *
+//% * @param aKey Key to be removed from this dictionary.
+//% **/
+//%- (void)remove##VNAME##ForKey:(KEY_TYPE##KisP$S##KisP)aKey;
+//%
+//%/**
+//% * Removes all entries in this dictionary.
+//% **/
+//%- (void)removeAll;
+
+//%PDDM-DEFINE DICTIONARY_EXTRA_MUTABLE_METHODS_POD(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE)
+// Empty
+//%PDDM-DEFINE DICTIONARY_EXTRA_MUTABLE_METHODS_OBJECT(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE)
+// Empty
+//%PDDM-DEFINE DICTIONARY_EXTRA_MUTABLE_METHODS_Enum(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE)
+//%
+//%/**
+//% * Sets the raw enum value for the given key.
+//% *
+//% * @note This method bypass the validationFunc to enable the setting of values that
+//% *       were not known at the time the binary was compiled.
+//% *
+//% * @param rawValue The raw enum value to set.
+//% * @param key      The key under which to store the raw enum value.
+//% **/
+//%- (void)setRawValue:(VALUE_TYPE)rawValue forKey:(KEY_TYPE##KisP$S##KisP)key;
+//%
diff --git a/iOS/Pods/Protobuf/objectivec/GPBDictionary.m b/iOS/Pods/Protobuf/objectivec/GPBDictionary.m
new file mode 100644 (file)
index 0000000..cedf5e5
--- /dev/null
@@ -0,0 +1,12120 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBDictionary_PackagePrivate.h"
+
+#import "GPBCodedInputStream_PackagePrivate.h"
+#import "GPBCodedOutputStream_PackagePrivate.h"
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+
+// ------------------------------ NOTE ------------------------------
+// At the moment, this is all using NSNumbers in NSDictionaries under
+// the hood, but it is all hidden so we can come back and optimize
+// with direct CFDictionary usage later.  The reason that wasn't
+// done yet is needing to support 32bit iOS builds.  Otherwise
+// it would be pretty simple to store all this data in CFDictionaries
+// directly.
+// ------------------------------------------------------------------
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+// Used to include code only visible to specific versions of the static
+// analyzer. Useful for wrapping code that only exists to silence the analyzer.
+// Determine the values you want to use for BEGIN_APPLE_BUILD_VERSION,
+// END_APPLE_BUILD_VERSION using:
+//   xcrun clang -dM -E -x c /dev/null | grep __apple_build_version__
+// Example usage:
+//  #if GPB_STATIC_ANALYZER_ONLY(5621, 5623) ... #endif
+#define GPB_STATIC_ANALYZER_ONLY(BEGIN_APPLE_BUILD_VERSION, END_APPLE_BUILD_VERSION) \
+    (defined(__clang_analyzer__) && \
+     (__apple_build_version__ >= BEGIN_APPLE_BUILD_VERSION && \
+      __apple_build_version__ <= END_APPLE_BUILD_VERSION))
+
+enum {
+  kMapKeyFieldNumber = 1,
+  kMapValueFieldNumber = 2,
+};
+
+static BOOL DictDefault_IsValidValue(int32_t value) {
+  // Anything but the bad value marker is allowed.
+  return (value != kGPBUnrecognizedEnumeratorValue);
+}
+
+//%PDDM-DEFINE SERIALIZE_SUPPORT_2_TYPE(VALUE_NAME, VALUE_TYPE, GPBDATATYPE_NAME1, GPBDATATYPE_NAME2)
+//%static size_t ComputeDict##VALUE_NAME##FieldSize(VALUE_TYPE value, uint32_t fieldNum, GPBDataType dataType) {
+//%  if (dataType == GPBDataType##GPBDATATYPE_NAME1) {
+//%    return GPBCompute##GPBDATATYPE_NAME1##Size(fieldNum, value);
+//%  } else if (dataType == GPBDataType##GPBDATATYPE_NAME2) {
+//%    return GPBCompute##GPBDATATYPE_NAME2##Size(fieldNum, value);
+//%  } else {
+//%    NSCAssert(NO, @"Unexpected type %d", dataType);
+//%    return 0;
+//%  }
+//%}
+//%
+//%static void WriteDict##VALUE_NAME##Field(GPBCodedOutputStream *stream, VALUE_TYPE value, uint32_t fieldNum, GPBDataType dataType) {
+//%  if (dataType == GPBDataType##GPBDATATYPE_NAME1) {
+//%    [stream write##GPBDATATYPE_NAME1##:fieldNum value:value];
+//%  } else if (dataType == GPBDataType##GPBDATATYPE_NAME2) {
+//%    [stream write##GPBDATATYPE_NAME2##:fieldNum value:value];
+//%  } else {
+//%    NSCAssert(NO, @"Unexpected type %d", dataType);
+//%  }
+//%}
+//%
+//%PDDM-DEFINE SERIALIZE_SUPPORT_3_TYPE(VALUE_NAME, VALUE_TYPE, GPBDATATYPE_NAME1, GPBDATATYPE_NAME2, GPBDATATYPE_NAME3)
+//%static size_t ComputeDict##VALUE_NAME##FieldSize(VALUE_TYPE value, uint32_t fieldNum, GPBDataType dataType) {
+//%  if (dataType == GPBDataType##GPBDATATYPE_NAME1) {
+//%    return GPBCompute##GPBDATATYPE_NAME1##Size(fieldNum, value);
+//%  } else if (dataType == GPBDataType##GPBDATATYPE_NAME2) {
+//%    return GPBCompute##GPBDATATYPE_NAME2##Size(fieldNum, value);
+//%  } else if (dataType == GPBDataType##GPBDATATYPE_NAME3) {
+//%    return GPBCompute##GPBDATATYPE_NAME3##Size(fieldNum, value);
+//%  } else {
+//%    NSCAssert(NO, @"Unexpected type %d", dataType);
+//%    return 0;
+//%  }
+//%}
+//%
+//%static void WriteDict##VALUE_NAME##Field(GPBCodedOutputStream *stream, VALUE_TYPE value, uint32_t fieldNum, GPBDataType dataType) {
+//%  if (dataType == GPBDataType##GPBDATATYPE_NAME1) {
+//%    [stream write##GPBDATATYPE_NAME1##:fieldNum value:value];
+//%  } else if (dataType == GPBDataType##GPBDATATYPE_NAME2) {
+//%    [stream write##GPBDATATYPE_NAME2##:fieldNum value:value];
+//%  } else if (dataType == GPBDataType##GPBDATATYPE_NAME3) {
+//%    [stream write##GPBDATATYPE_NAME3##:fieldNum value:value];
+//%  } else {
+//%    NSCAssert(NO, @"Unexpected type %d", dataType);
+//%  }
+//%}
+//%
+//%PDDM-DEFINE SIMPLE_SERIALIZE_SUPPORT(VALUE_NAME, VALUE_TYPE, VisP)
+//%static size_t ComputeDict##VALUE_NAME##FieldSize(VALUE_TYPE VisP##value, uint32_t fieldNum, GPBDataType dataType) {
+//%  NSCAssert(dataType == GPBDataType##VALUE_NAME, @"bad type: %d", dataType);
+//%  #pragma unused(dataType)  // For when asserts are off in release.
+//%  return GPBCompute##VALUE_NAME##Size(fieldNum, value);
+//%}
+//%
+//%static void WriteDict##VALUE_NAME##Field(GPBCodedOutputStream *stream, VALUE_TYPE VisP##value, uint32_t fieldNum, GPBDataType dataType) {
+//%  NSCAssert(dataType == GPBDataType##VALUE_NAME, @"bad type: %d", dataType);
+//%  #pragma unused(dataType)  // For when asserts are off in release.
+//%  [stream write##VALUE_NAME##:fieldNum value:value];
+//%}
+//%
+//%PDDM-DEFINE SERIALIZE_SUPPORT_HELPERS()
+//%SERIALIZE_SUPPORT_3_TYPE(Int32, int32_t, Int32, SInt32, SFixed32)
+//%SERIALIZE_SUPPORT_2_TYPE(UInt32, uint32_t, UInt32, Fixed32)
+//%SERIALIZE_SUPPORT_3_TYPE(Int64, int64_t, Int64, SInt64, SFixed64)
+//%SERIALIZE_SUPPORT_2_TYPE(UInt64, uint64_t, UInt64, Fixed64)
+//%SIMPLE_SERIALIZE_SUPPORT(Bool, BOOL, )
+//%SIMPLE_SERIALIZE_SUPPORT(Enum, int32_t, )
+//%SIMPLE_SERIALIZE_SUPPORT(Float, float, )
+//%SIMPLE_SERIALIZE_SUPPORT(Double, double, )
+//%SIMPLE_SERIALIZE_SUPPORT(String, NSString, *)
+//%SERIALIZE_SUPPORT_3_TYPE(Object, id, Message, String, Bytes)
+//%PDDM-EXPAND SERIALIZE_SUPPORT_HELPERS()
+// This block of code is generated, do not edit it directly.
+
+static size_t ComputeDictInt32FieldSize(int32_t value, uint32_t fieldNum, GPBDataType dataType) {
+  if (dataType == GPBDataTypeInt32) {
+    return GPBComputeInt32Size(fieldNum, value);
+  } else if (dataType == GPBDataTypeSInt32) {
+    return GPBComputeSInt32Size(fieldNum, value);
+  } else if (dataType == GPBDataTypeSFixed32) {
+    return GPBComputeSFixed32Size(fieldNum, value);
+  } else {
+    NSCAssert(NO, @"Unexpected type %d", dataType);
+    return 0;
+  }
+}
+
+static void WriteDictInt32Field(GPBCodedOutputStream *stream, int32_t value, uint32_t fieldNum, GPBDataType dataType) {
+  if (dataType == GPBDataTypeInt32) {
+    [stream writeInt32:fieldNum value:value];
+  } else if (dataType == GPBDataTypeSInt32) {
+    [stream writeSInt32:fieldNum value:value];
+  } else if (dataType == GPBDataTypeSFixed32) {
+    [stream writeSFixed32:fieldNum value:value];
+  } else {
+    NSCAssert(NO, @"Unexpected type %d", dataType);
+  }
+}
+
+static size_t ComputeDictUInt32FieldSize(uint32_t value, uint32_t fieldNum, GPBDataType dataType) {
+  if (dataType == GPBDataTypeUInt32) {
+    return GPBComputeUInt32Size(fieldNum, value);
+  } else if (dataType == GPBDataTypeFixed32) {
+    return GPBComputeFixed32Size(fieldNum, value);
+  } else {
+    NSCAssert(NO, @"Unexpected type %d", dataType);
+    return 0;
+  }
+}
+
+static void WriteDictUInt32Field(GPBCodedOutputStream *stream, uint32_t value, uint32_t fieldNum, GPBDataType dataType) {
+  if (dataType == GPBDataTypeUInt32) {
+    [stream writeUInt32:fieldNum value:value];
+  } else if (dataType == GPBDataTypeFixed32) {
+    [stream writeFixed32:fieldNum value:value];
+  } else {
+    NSCAssert(NO, @"Unexpected type %d", dataType);
+  }
+}
+
+static size_t ComputeDictInt64FieldSize(int64_t value, uint32_t fieldNum, GPBDataType dataType) {
+  if (dataType == GPBDataTypeInt64) {
+    return GPBComputeInt64Size(fieldNum, value);
+  } else if (dataType == GPBDataTypeSInt64) {
+    return GPBComputeSInt64Size(fieldNum, value);
+  } else if (dataType == GPBDataTypeSFixed64) {
+    return GPBComputeSFixed64Size(fieldNum, value);
+  } else {
+    NSCAssert(NO, @"Unexpected type %d", dataType);
+    return 0;
+  }
+}
+
+static void WriteDictInt64Field(GPBCodedOutputStream *stream, int64_t value, uint32_t fieldNum, GPBDataType dataType) {
+  if (dataType == GPBDataTypeInt64) {
+    [stream writeInt64:fieldNum value:value];
+  } else if (dataType == GPBDataTypeSInt64) {
+    [stream writeSInt64:fieldNum value:value];
+  } else if (dataType == GPBDataTypeSFixed64) {
+    [stream writeSFixed64:fieldNum value:value];
+  } else {
+    NSCAssert(NO, @"Unexpected type %d", dataType);
+  }
+}
+
+static size_t ComputeDictUInt64FieldSize(uint64_t value, uint32_t fieldNum, GPBDataType dataType) {
+  if (dataType == GPBDataTypeUInt64) {
+    return GPBComputeUInt64Size(fieldNum, value);
+  } else if (dataType == GPBDataTypeFixed64) {
+    return GPBComputeFixed64Size(fieldNum, value);
+  } else {
+    NSCAssert(NO, @"Unexpected type %d", dataType);
+    return 0;
+  }
+}
+
+static void WriteDictUInt64Field(GPBCodedOutputStream *stream, uint64_t value, uint32_t fieldNum, GPBDataType dataType) {
+  if (dataType == GPBDataTypeUInt64) {
+    [stream writeUInt64:fieldNum value:value];
+  } else if (dataType == GPBDataTypeFixed64) {
+    [stream writeFixed64:fieldNum value:value];
+  } else {
+    NSCAssert(NO, @"Unexpected type %d", dataType);
+  }
+}
+
+static size_t ComputeDictBoolFieldSize(BOOL value, uint32_t fieldNum, GPBDataType dataType) {
+  NSCAssert(dataType == GPBDataTypeBool, @"bad type: %d", dataType);
+  #pragma unused(dataType)  // For when asserts are off in release.
+  return GPBComputeBoolSize(fieldNum, value);
+}
+
+static void WriteDictBoolField(GPBCodedOutputStream *stream, BOOL value, uint32_t fieldNum, GPBDataType dataType) {
+  NSCAssert(dataType == GPBDataTypeBool, @"bad type: %d", dataType);
+  #pragma unused(dataType)  // For when asserts are off in release.
+  [stream writeBool:fieldNum value:value];
+}
+
+static size_t ComputeDictEnumFieldSize(int32_t value, uint32_t fieldNum, GPBDataType dataType) {
+  NSCAssert(dataType == GPBDataTypeEnum, @"bad type: %d", dataType);
+  #pragma unused(dataType)  // For when asserts are off in release.
+  return GPBComputeEnumSize(fieldNum, value);
+}
+
+static void WriteDictEnumField(GPBCodedOutputStream *stream, int32_t value, uint32_t fieldNum, GPBDataType dataType) {
+  NSCAssert(dataType == GPBDataTypeEnum, @"bad type: %d", dataType);
+  #pragma unused(dataType)  // For when asserts are off in release.
+  [stream writeEnum:fieldNum value:value];
+}
+
+static size_t ComputeDictFloatFieldSize(float value, uint32_t fieldNum, GPBDataType dataType) {
+  NSCAssert(dataType == GPBDataTypeFloat, @"bad type: %d", dataType);
+  #pragma unused(dataType)  // For when asserts are off in release.
+  return GPBComputeFloatSize(fieldNum, value);
+}
+
+static void WriteDictFloatField(GPBCodedOutputStream *stream, float value, uint32_t fieldNum, GPBDataType dataType) {
+  NSCAssert(dataType == GPBDataTypeFloat, @"bad type: %d", dataType);
+  #pragma unused(dataType)  // For when asserts are off in release.
+  [stream writeFloat:fieldNum value:value];
+}
+
+static size_t ComputeDictDoubleFieldSize(double value, uint32_t fieldNum, GPBDataType dataType) {
+  NSCAssert(dataType == GPBDataTypeDouble, @"bad type: %d", dataType);
+  #pragma unused(dataType)  // For when asserts are off in release.
+  return GPBComputeDoubleSize(fieldNum, value);
+}
+
+static void WriteDictDoubleField(GPBCodedOutputStream *stream, double value, uint32_t fieldNum, GPBDataType dataType) {
+  NSCAssert(dataType == GPBDataTypeDouble, @"bad type: %d", dataType);
+  #pragma unused(dataType)  // For when asserts are off in release.
+  [stream writeDouble:fieldNum value:value];
+}
+
+static size_t ComputeDictStringFieldSize(NSString *value, uint32_t fieldNum, GPBDataType dataType) {
+  NSCAssert(dataType == GPBDataTypeString, @"bad type: %d", dataType);
+  #pragma unused(dataType)  // For when asserts are off in release.
+  return GPBComputeStringSize(fieldNum, value);
+}
+
+static void WriteDictStringField(GPBCodedOutputStream *stream, NSString *value, uint32_t fieldNum, GPBDataType dataType) {
+  NSCAssert(dataType == GPBDataTypeString, @"bad type: %d", dataType);
+  #pragma unused(dataType)  // For when asserts are off in release.
+  [stream writeString:fieldNum value:value];
+}
+
+static size_t ComputeDictObjectFieldSize(id value, uint32_t fieldNum, GPBDataType dataType) {
+  if (dataType == GPBDataTypeMessage) {
+    return GPBComputeMessageSize(fieldNum, value);
+  } else if (dataType == GPBDataTypeString) {
+    return GPBComputeStringSize(fieldNum, value);
+  } else if (dataType == GPBDataTypeBytes) {
+    return GPBComputeBytesSize(fieldNum, value);
+  } else {
+    NSCAssert(NO, @"Unexpected type %d", dataType);
+    return 0;
+  }
+}
+
+static void WriteDictObjectField(GPBCodedOutputStream *stream, id value, uint32_t fieldNum, GPBDataType dataType) {
+  if (dataType == GPBDataTypeMessage) {
+    [stream writeMessage:fieldNum value:value];
+  } else if (dataType == GPBDataTypeString) {
+    [stream writeString:fieldNum value:value];
+  } else if (dataType == GPBDataTypeBytes) {
+    [stream writeBytes:fieldNum value:value];
+  } else {
+    NSCAssert(NO, @"Unexpected type %d", dataType);
+  }
+}
+
+//%PDDM-EXPAND-END SERIALIZE_SUPPORT_HELPERS()
+
+size_t GPBDictionaryComputeSizeInternalHelper(NSDictionary *dict, GPBFieldDescriptor *field) {
+  GPBDataType mapValueType = GPBGetFieldDataType(field);
+  size_t result = 0;
+  NSString *key;
+  NSEnumerator *keys = [dict keyEnumerator];
+  while ((key = [keys nextObject])) {
+    id obj = dict[key];
+    size_t msgSize = GPBComputeStringSize(kMapKeyFieldNumber, key);
+    msgSize += ComputeDictObjectFieldSize(obj, kMapValueFieldNumber, mapValueType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * dict.count;
+  return result;
+}
+
+void GPBDictionaryWriteToStreamInternalHelper(GPBCodedOutputStream *outputStream,
+                                              NSDictionary *dict,
+                                              GPBFieldDescriptor *field) {
+  NSCAssert(field.mapKeyDataType == GPBDataTypeString, @"Unexpected key type");
+  GPBDataType mapValueType = GPBGetFieldDataType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSString *key;
+  NSEnumerator *keys = [dict keyEnumerator];
+  while ((key = [keys nextObject])) {
+    id obj = dict[key];
+    // Write the tag.
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    size_t msgSize = GPBComputeStringSize(kMapKeyFieldNumber, key);
+    msgSize += ComputeDictObjectFieldSize(obj, kMapValueFieldNumber, mapValueType);
+
+    // Write the size and fields.
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    [outputStream writeString:kMapKeyFieldNumber value:key];
+    WriteDictObjectField(outputStream, obj, kMapValueFieldNumber, mapValueType);
+  }
+}
+
+BOOL GPBDictionaryIsInitializedInternalHelper(NSDictionary *dict, GPBFieldDescriptor *field) {
+  NSCAssert(field.mapKeyDataType == GPBDataTypeString, @"Unexpected key type");
+  NSCAssert(GPBGetFieldDataType(field) == GPBDataTypeMessage, @"Unexpected value type");
+  #pragma unused(field)  // For when asserts are off in release.
+  GPBMessage *msg;
+  NSEnumerator *objects = [dict objectEnumerator];
+  while ((msg = [objects nextObject])) {
+    if (!msg.initialized) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+// Note: if the type is an object, it the retain pass back to the caller.
+static void ReadValue(GPBCodedInputStream *stream,
+                      GPBGenericValue *valueToFill,
+                      GPBDataType type,
+                      GPBExtensionRegistry *registry,
+                      GPBFieldDescriptor *field) {
+  switch (type) {
+    case GPBDataTypeBool:
+      valueToFill->valueBool = GPBCodedInputStreamReadBool(&stream->state_);
+      break;
+    case GPBDataTypeFixed32:
+      valueToFill->valueUInt32 = GPBCodedInputStreamReadFixed32(&stream->state_);
+      break;
+    case GPBDataTypeSFixed32:
+      valueToFill->valueInt32 = GPBCodedInputStreamReadSFixed32(&stream->state_);
+      break;
+    case GPBDataTypeFloat:
+      valueToFill->valueFloat = GPBCodedInputStreamReadFloat(&stream->state_);
+      break;
+    case GPBDataTypeFixed64:
+      valueToFill->valueUInt64 = GPBCodedInputStreamReadFixed64(&stream->state_);
+      break;
+    case GPBDataTypeSFixed64:
+      valueToFill->valueInt64 = GPBCodedInputStreamReadSFixed64(&stream->state_);
+      break;
+    case GPBDataTypeDouble:
+      valueToFill->valueDouble = GPBCodedInputStreamReadDouble(&stream->state_);
+      break;
+    case GPBDataTypeInt32:
+      valueToFill->valueInt32 = GPBCodedInputStreamReadInt32(&stream->state_);
+      break;
+    case GPBDataTypeInt64:
+      valueToFill->valueInt64 = GPBCodedInputStreamReadInt64(&stream->state_);
+      break;
+    case GPBDataTypeSInt32:
+      valueToFill->valueInt32 = GPBCodedInputStreamReadSInt32(&stream->state_);
+      break;
+    case GPBDataTypeSInt64:
+      valueToFill->valueInt64 = GPBCodedInputStreamReadSInt64(&stream->state_);
+      break;
+    case GPBDataTypeUInt32:
+      valueToFill->valueUInt32 = GPBCodedInputStreamReadUInt32(&stream->state_);
+      break;
+    case GPBDataTypeUInt64:
+      valueToFill->valueUInt64 = GPBCodedInputStreamReadUInt64(&stream->state_);
+      break;
+    case GPBDataTypeBytes:
+      [valueToFill->valueData release];
+      valueToFill->valueData = GPBCodedInputStreamReadRetainedBytes(&stream->state_);
+      break;
+    case GPBDataTypeString:
+      [valueToFill->valueString release];
+      valueToFill->valueString = GPBCodedInputStreamReadRetainedString(&stream->state_);
+      break;
+    case GPBDataTypeMessage: {
+      GPBMessage *message = [[field.msgClass alloc] init];
+      [stream readMessage:message extensionRegistry:registry];
+      [valueToFill->valueMessage release];
+      valueToFill->valueMessage = message;
+      break;
+    }
+    case GPBDataTypeGroup:
+      NSCAssert(NO, @"Can't happen");
+      break;
+    case GPBDataTypeEnum:
+      valueToFill->valueEnum = GPBCodedInputStreamReadEnum(&stream->state_);
+      break;
+  }
+}
+
+void GPBDictionaryReadEntry(id mapDictionary,
+                            GPBCodedInputStream *stream,
+                            GPBExtensionRegistry *registry,
+                            GPBFieldDescriptor *field,
+                            GPBMessage *parentMessage) {
+  GPBDataType keyDataType = field.mapKeyDataType;
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+
+  GPBGenericValue key;
+  GPBGenericValue value;
+  // Zero them (but pick up any enum default for proto2).
+  key.valueString = value.valueString = nil;
+  if (valueDataType == GPBDataTypeEnum) {
+    value = field.defaultValue;
+  }
+
+  GPBCodedInputStreamState *state = &stream->state_;
+  uint32_t keyTag =
+      GPBWireFormatMakeTag(kMapKeyFieldNumber, GPBWireFormatForType(keyDataType, NO));
+  uint32_t valueTag =
+      GPBWireFormatMakeTag(kMapValueFieldNumber, GPBWireFormatForType(valueDataType, NO));
+
+  BOOL hitError = NO;
+  while (YES) {
+    uint32_t tag = GPBCodedInputStreamReadTag(state);
+    if (tag == keyTag) {
+      ReadValue(stream, &key, keyDataType, registry, field);
+    } else if (tag == valueTag) {
+      ReadValue(stream, &value, valueDataType, registry, field);
+    } else if (tag == 0) {
+      // zero signals EOF / limit reached
+      break;
+    } else {  // Unknown
+      if (![stream skipField:tag]){
+        hitError = YES;
+        break;
+      }
+    }
+  }
+
+  if (!hitError) {
+    // Handle the special defaults and/or missing key/value.
+    if ((keyDataType == GPBDataTypeString) && (key.valueString == nil)) {
+      key.valueString = [@"" retain];
+    }
+    if (GPBDataTypeIsObject(valueDataType) && value.valueString == nil) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wswitch-enum"
+      switch (valueDataType) {
+        case GPBDataTypeString:
+          value.valueString = [@"" retain];
+          break;
+        case GPBDataTypeBytes:
+          value.valueData = [GPBEmptyNSData() retain];
+          break;
+#if defined(__clang_analyzer__)
+        case GPBDataTypeGroup:
+          // Maps can't really have Groups as the value type, but this case is needed
+          // so the analyzer won't report the posibility of send nil in for the value
+          // in the NSMutableDictionary case below.
+#endif
+        case GPBDataTypeMessage: {
+          value.valueMessage = [[field.msgClass alloc] init];
+          break;
+        }
+        default:
+          // Nothing
+          break;
+      }
+#pragma clang diagnostic pop
+    }
+
+    if ((keyDataType == GPBDataTypeString) && GPBDataTypeIsObject(valueDataType)) {
+#if GPB_STATIC_ANALYZER_ONLY(6020053, 7000181)
+     // Limited to Xcode 6.4 - 7.2, are known to fail here. The upper end can
+     // be raised as needed for new Xcodes.
+     //
+     // This is only needed on a "shallow" analyze; on a "deep" analyze, the
+     // existing code path gets this correct. In shallow, the analyzer decides
+     // GPBDataTypeIsObject(valueDataType) is both false and true on a single
+     // path through this function, allowing nil to be used for the
+     // setObject:forKey:.
+     if (value.valueString == nil) {
+       value.valueString = [@"" retain];
+     }
+#endif
+      // mapDictionary is an NSMutableDictionary
+      [(NSMutableDictionary *)mapDictionary setObject:value.valueString
+                                               forKey:key.valueString];
+    } else {
+      if (valueDataType == GPBDataTypeEnum) {
+        if (GPBHasPreservingUnknownEnumSemantics([parentMessage descriptor].file.syntax) ||
+            [field isValidEnumValue:value.valueEnum]) {
+          [mapDictionary setGPBGenericValue:&value forGPBGenericValueKey:&key];
+        } else {
+          NSData *data = [mapDictionary serializedDataForUnknownValue:value.valueEnum
+                                                               forKey:&key
+                                                          keyDataType:keyDataType];
+          [parentMessage addUnknownMapEntry:GPBFieldNumber(field) value:data];
+        }
+      } else {
+        [mapDictionary setGPBGenericValue:&value forGPBGenericValueKey:&key];
+      }
+    }
+  }
+
+  if (GPBDataTypeIsObject(keyDataType)) {
+    [key.valueString release];
+  }
+  if (GPBDataTypeIsObject(valueDataType)) {
+    [value.valueString release];
+  }
+}
+
+//
+// Macros for the common basic cases.
+//
+
+//%PDDM-DEFINE DICTIONARY_IMPL_FOR_POD_KEY(KEY_NAME, KEY_TYPE)
+//%DICTIONARY_POD_IMPL_FOR_KEY(KEY_NAME, KEY_TYPE, , POD)
+//%DICTIONARY_POD_KEY_TO_OBJECT_IMPL(KEY_NAME, KEY_TYPE, Object, id)
+
+//%PDDM-DEFINE DICTIONARY_POD_IMPL_FOR_KEY(KEY_NAME, KEY_TYPE, KisP, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, UInt32, uint32_t, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, Int32, int32_t, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, UInt64, uint64_t, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, Int64, int64_t, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, Bool, BOOL, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, Float, float, KHELPER)
+//%DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, Double, double, KHELPER)
+//%DICTIONARY_KEY_TO_ENUM_IMPL(KEY_NAME, KEY_TYPE, KisP, Enum, int32_t, KHELPER)
+
+//%PDDM-DEFINE DICTIONARY_KEY_TO_POD_IMPL(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER)
+//%DICTIONARY_COMMON_IMPL(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, POD, VALUE_NAME, value)
+
+//%PDDM-DEFINE DICTIONARY_POD_KEY_TO_OBJECT_IMPL(KEY_NAME, KEY_TYPE, VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_COMMON_IMPL(KEY_NAME, KEY_TYPE, , VALUE_NAME, VALUE_TYPE, POD, OBJECT, Object, object)
+
+//%PDDM-DEFINE DICTIONARY_COMMON_IMPL(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, VNAME, VNAME_VAR)
+//%#pragma mark - KEY_NAME -> VALUE_NAME
+//%
+//%@implementation GPB##KEY_NAME##VALUE_NAME##Dictionary {
+//% @package
+//%  NSMutableDictionary *_dictionary;
+//%}
+//%
+//%- (instancetype)init {
+//%  return [self initWith##VNAME##s:NULL forKeys:NULL count:0];
+//%}
+//%
+//%- (instancetype)initWith##VNAME##s:(const VALUE_TYPE [])##VNAME_VAR##s
+//%                ##VNAME$S##  forKeys:(const KEY_TYPE##KisP$S##KisP [])keys
+//%                ##VNAME$S##    count:(NSUInteger)count {
+//%  self = [super init];
+//%  if (self) {
+//%    _dictionary = [[NSMutableDictionary alloc] init];
+//%    if (count && VNAME_VAR##s && keys) {
+//%      for (NSUInteger i = 0; i < count; ++i) {
+//%DICTIONARY_VALIDATE_VALUE_##VHELPER(VNAME_VAR##s[i], ______)##DICTIONARY_VALIDATE_KEY_##KHELPER(keys[i], ______)        [_dictionary setObject:WRAPPED##VHELPER(VNAME_VAR##s[i]) forKey:WRAPPED##KHELPER(keys[i])];
+//%      }
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary {
+//%  self = [self initWith##VNAME##s:NULL forKeys:NULL count:0];
+//%  if (self) {
+//%    if (dictionary) {
+//%      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithCapacity:(NSUInteger)numItems {
+//%  #pragma unused(numItems)
+//%  return [self initWith##VNAME##s:NULL forKeys:NULL count:0];
+//%}
+//%
+//%DICTIONARY_IMMUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, VNAME, VNAME_VAR, )
+//%
+//%VALUE_FOR_KEY_##VHELPER(KEY_TYPE##KisP$S##KisP, VALUE_NAME, VALUE_TYPE, KHELPER)
+//%
+//%DICTIONARY_MUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, VNAME, VNAME_VAR, )
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE DICTIONARY_KEY_TO_ENUM_IMPL(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER)
+//%DICTIONARY_KEY_TO_ENUM_IMPL2(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, POD)
+//%PDDM-DEFINE DICTIONARY_KEY_TO_ENUM_IMPL2(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER)
+//%#pragma mark - KEY_NAME -> VALUE_NAME
+//%
+//%@implementation GPB##KEY_NAME##VALUE_NAME##Dictionary {
+//% @package
+//%  NSMutableDictionary *_dictionary;
+//%  GPBEnumValidationFunc _validationFunc;
+//%}
+//%
+//%@synthesize validationFunc = _validationFunc;
+//%
+//%- (instancetype)init {
+//%  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+//%}
+//%
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+//%  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+//%}
+//%
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                 rawValues:(const VALUE_TYPE [])rawValues
+//%                                   forKeys:(const KEY_TYPE##KisP$S##KisP [])keys
+//%                                     count:(NSUInteger)count {
+//%  self = [super init];
+//%  if (self) {
+//%    _dictionary = [[NSMutableDictionary alloc] init];
+//%    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+//%    if (count && rawValues && keys) {
+//%      for (NSUInteger i = 0; i < count; ++i) {
+//%DICTIONARY_VALIDATE_KEY_##KHELPER(keys[i], ______)        [_dictionary setObject:WRAPPED##VHELPER(rawValues[i]) forKey:WRAPPED##KHELPER(keys[i])];
+//%      }
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)dictionary {
+//%  self = [self initWithValidationFunction:dictionary.validationFunc
+//%                                rawValues:NULL
+//%                                  forKeys:NULL
+//%                                    count:0];
+//%  if (self) {
+//%    if (dictionary) {
+//%      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+//%                                  capacity:(NSUInteger)numItems {
+//%  #pragma unused(numItems)
+//%  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+//%}
+//%
+//%DICTIONARY_IMMUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, Value, value, Raw)
+//%
+//%- (BOOL)getEnum:(VALUE_TYPE *)value forKey:(KEY_TYPE##KisP$S##KisP)key {
+//%  NSNumber *wrapped = [_dictionary objectForKey:WRAPPED##KHELPER(key)];
+//%  if (wrapped && value) {
+//%    VALUE_TYPE result = UNWRAP##VALUE_NAME(wrapped);
+//%    if (!_validationFunc(result)) {
+//%      result = kGPBUnrecognizedEnumeratorValue;
+//%    }
+//%    *value = result;
+//%  }
+//%  return (wrapped != NULL);
+//%}
+//%
+//%- (BOOL)getRawValue:(VALUE_TYPE *)rawValue forKey:(KEY_TYPE##KisP$S##KisP)key {
+//%  NSNumber *wrapped = [_dictionary objectForKey:WRAPPED##KHELPER(key)];
+//%  if (wrapped && rawValue) {
+//%    *rawValue = UNWRAP##VALUE_NAME(wrapped);
+//%  }
+//%  return (wrapped != NULL);
+//%}
+//%
+//%- (void)enumerateKeysAndEnumsUsingBlock:
+//%    (void (^)(KEY_TYPE KisP##key, VALUE_TYPE value, BOOL *stop))block {
+//%  GPBEnumValidationFunc func = _validationFunc;
+//%  BOOL stop = NO;
+//%  NSEnumerator *keys = [_dictionary keyEnumerator];
+//%  ENUM_TYPE##KHELPER(KEY_TYPE)##aKey;
+//%  while ((aKey = [keys nextObject])) {
+//%    ENUM_TYPE##VHELPER(VALUE_TYPE)##aValue = _dictionary[aKey];
+//%      VALUE_TYPE unwrapped = UNWRAP##VALUE_NAME(aValue);
+//%      if (!func(unwrapped)) {
+//%        unwrapped = kGPBUnrecognizedEnumeratorValue;
+//%      }
+//%    block(UNWRAP##KEY_NAME(aKey), unwrapped, &stop);
+//%    if (stop) {
+//%      break;
+//%    }
+//%  }
+//%}
+//%
+//%DICTIONARY_MUTABLE_CORE2(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, Value, Enum, value, Raw)
+//%
+//%- (void)setEnum:(VALUE_TYPE)value forKey:(KEY_TYPE##KisP$S##KisP)key {
+//%DICTIONARY_VALIDATE_KEY_##KHELPER(key, )  if (!_validationFunc(value)) {
+//%    [NSException raise:NSInvalidArgumentException
+//%                format:@"GPB##KEY_NAME##VALUE_NAME##Dictionary: Attempt to set an unknown enum value (%d)",
+//%                       value];
+//%  }
+//%
+//%  [_dictionary setObject:WRAPPED##VHELPER(value) forKey:WRAPPED##KHELPER(key)];
+//%  if (_autocreator) {
+//%    GPBAutocreatedDictionaryModified(_autocreator, self);
+//%  }
+//%}
+//%
+//%@end
+//%
+
+//%PDDM-DEFINE DICTIONARY_IMMUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, VNAME, VNAME_VAR, ACCESSOR_NAME)
+//%- (void)dealloc {
+//%  NSAssert(!_autocreator,
+//%           @"%@: Autocreator must be cleared before release, autocreator: %@",
+//%           [self class], _autocreator);
+//%  [_dictionary release];
+//%  [super dealloc];
+//%}
+//%
+//%- (instancetype)copyWithZone:(NSZone *)zone {
+//%  return [[GPB##KEY_NAME##VALUE_NAME##Dictionary allocWithZone:zone] initWithDictionary:self];
+//%}
+//%
+//%- (BOOL)isEqual:(id)other {
+//%  if (self == other) {
+//%    return YES;
+//%  }
+//%  if (![other isKindOfClass:[GPB##KEY_NAME##VALUE_NAME##Dictionary class]]) {
+//%    return NO;
+//%  }
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *otherDictionary = other;
+//%  return [_dictionary isEqual:otherDictionary->_dictionary];
+//%}
+//%
+//%- (NSUInteger)hash {
+//%  return _dictionary.count;
+//%}
+//%
+//%- (NSString *)description {
+//%  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+//%}
+//%
+//%- (NSUInteger)count {
+//%  return _dictionary.count;
+//%}
+//%
+//%- (void)enumerateKeysAnd##ACCESSOR_NAME##VNAME##sUsingBlock:
+//%    (void (^)(KEY_TYPE KisP##key, VALUE_TYPE VNAME_VAR, BOOL *stop))block {
+//%  BOOL stop = NO;
+//%  NSDictionary *internal = _dictionary;
+//%  NSEnumerator *keys = [internal keyEnumerator];
+//%  ENUM_TYPE##KHELPER(KEY_TYPE)##aKey;
+//%  while ((aKey = [keys nextObject])) {
+//%    ENUM_TYPE##VHELPER(VALUE_TYPE)##a##VNAME_VAR$u = internal[aKey];
+//%    block(UNWRAP##KEY_NAME(aKey), UNWRAP##VALUE_NAME(a##VNAME_VAR$u), &stop);
+//%    if (stop) {
+//%      break;
+//%    }
+//%  }
+//%}
+//%
+//%EXTRA_METHODS_##VHELPER(KEY_NAME, VALUE_NAME)- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+//%  NSDictionary *internal = _dictionary;
+//%  NSUInteger count = internal.count;
+//%  if (count == 0) {
+//%    return 0;
+//%  }
+//%
+//%  GPBDataType valueDataType = GPBGetFieldDataType(field);
+//%  GPBDataType keyDataType = field.mapKeyDataType;
+//%  size_t result = 0;
+//%  NSEnumerator *keys = [internal keyEnumerator];
+//%  ENUM_TYPE##KHELPER(KEY_TYPE)##aKey;
+//%  while ((aKey = [keys nextObject])) {
+//%    ENUM_TYPE##VHELPER(VALUE_TYPE)##a##VNAME_VAR$u = internal[aKey];
+//%    size_t msgSize = ComputeDict##KEY_NAME##FieldSize(UNWRAP##KEY_NAME(aKey), kMapKeyFieldNumber, keyDataType);
+//%    msgSize += ComputeDict##VALUE_NAME##FieldSize(UNWRAP##VALUE_NAME(a##VNAME_VAR$u), kMapValueFieldNumber, valueDataType);
+//%    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+//%  }
+//%  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+//%  result += tagSize * count;
+//%  return result;
+//%}
+//%
+//%- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+//%                         asField:(GPBFieldDescriptor *)field {
+//%  GPBDataType valueDataType = GPBGetFieldDataType(field);
+//%  GPBDataType keyDataType = field.mapKeyDataType;
+//%  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+//%  NSDictionary *internal = _dictionary;
+//%  NSEnumerator *keys = [internal keyEnumerator];
+//%  ENUM_TYPE##KHELPER(KEY_TYPE)##aKey;
+//%  while ((aKey = [keys nextObject])) {
+//%    ENUM_TYPE##VHELPER(VALUE_TYPE)##a##VNAME_VAR$u = internal[aKey];
+//%    [outputStream writeInt32NoTag:tag];
+//%    // Write the size of the message.
+//%    KEY_TYPE KisP##unwrappedKey = UNWRAP##KEY_NAME(aKey);
+//%    VALUE_TYPE unwrappedValue = UNWRAP##VALUE_NAME(a##VNAME_VAR$u);
+//%    size_t msgSize = ComputeDict##KEY_NAME##FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+//%    msgSize += ComputeDict##VALUE_NAME##FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+//%    [outputStream writeInt32NoTag:(int32_t)msgSize];
+//%    // Write the fields.
+//%    WriteDict##KEY_NAME##Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+//%    WriteDict##VALUE_NAME##Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+//%  }
+//%}
+//%
+//%SERIAL_DATA_FOR_ENTRY_##VHELPER(KEY_NAME, VALUE_NAME)- (void)setGPBGenericValue:(GPBGenericValue *)value
+//%     forGPBGenericValueKey:(GPBGenericValue *)key {
+//%  [_dictionary setObject:WRAPPED##VHELPER(value->##GPBVALUE_##VHELPER(VALUE_NAME)##) forKey:WRAPPED##KHELPER(key->value##KEY_NAME)];
+//%}
+//%
+//%- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+//%  [self enumerateKeysAnd##ACCESSOR_NAME##VNAME##sUsingBlock:^(KEY_TYPE KisP##key, VALUE_TYPE VNAME_VAR, BOOL *stop) {
+//%      #pragma unused(stop)
+//%      block(TEXT_FORMAT_OBJ##KEY_NAME(key), TEXT_FORMAT_OBJ##VALUE_NAME(VNAME_VAR));
+//%  }];
+//%}
+//%PDDM-DEFINE DICTIONARY_MUTABLE_CORE(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, VNAME, VNAME_VAR, ACCESSOR_NAME)
+//%DICTIONARY_MUTABLE_CORE2(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, VNAME, VNAME, VNAME_VAR, ACCESSOR_NAME)
+//%PDDM-DEFINE DICTIONARY_MUTABLE_CORE2(KEY_NAME, KEY_TYPE, KisP, VALUE_NAME, VALUE_TYPE, KHELPER, VHELPER, VNAME, VNAME_REMOVE, VNAME_VAR, ACCESSOR_NAME)
+//%- (void)add##ACCESSOR_NAME##EntriesFromDictionary:(GPB##KEY_NAME##VALUE_NAME##Dictionary *)otherDictionary {
+//%  if (otherDictionary) {
+//%    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+//%    if (_autocreator) {
+//%      GPBAutocreatedDictionaryModified(_autocreator, self);
+//%    }
+//%  }
+//%}
+//%
+//%- (void)set##ACCESSOR_NAME##VNAME##:(VALUE_TYPE)VNAME_VAR forKey:(KEY_TYPE##KisP$S##KisP)key {
+//%DICTIONARY_VALIDATE_VALUE_##VHELPER(VNAME_VAR, )##DICTIONARY_VALIDATE_KEY_##KHELPER(key, )  [_dictionary setObject:WRAPPED##VHELPER(VNAME_VAR) forKey:WRAPPED##KHELPER(key)];
+//%  if (_autocreator) {
+//%    GPBAutocreatedDictionaryModified(_autocreator, self);
+//%  }
+//%}
+//%
+//%- (void)remove##VNAME_REMOVE##ForKey:(KEY_TYPE##KisP$S##KisP)aKey {
+//%  [_dictionary removeObjectForKey:WRAPPED##KHELPER(aKey)];
+//%}
+//%
+//%- (void)removeAll {
+//%  [_dictionary removeAllObjects];
+//%}
+
+//
+// Custom Generation for Bool keys
+//
+
+//%PDDM-DEFINE DICTIONARY_BOOL_KEY_TO_POD_IMPL(VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_BOOL_KEY_TO_VALUE_IMPL(VALUE_NAME, VALUE_TYPE, POD, VALUE_NAME, value)
+//%PDDM-DEFINE DICTIONARY_BOOL_KEY_TO_OBJECT_IMPL(VALUE_NAME, VALUE_TYPE)
+//%DICTIONARY_BOOL_KEY_TO_VALUE_IMPL(VALUE_NAME, VALUE_TYPE, OBJECT, Object, object)
+
+//%PDDM-DEFINE DICTIONARY_BOOL_KEY_TO_VALUE_IMPL(VALUE_NAME, VALUE_TYPE, HELPER, VNAME, VNAME_VAR)
+//%#pragma mark - Bool -> VALUE_NAME
+//%
+//%@implementation GPBBool##VALUE_NAME##Dictionary {
+//% @package
+//%  VALUE_TYPE _values[2];
+//%BOOL_DICT_HAS_STORAGE_##HELPER()}
+//%
+//%- (instancetype)init {
+//%  return [self initWith##VNAME##s:NULL forKeys:NULL count:0];
+//%}
+//%
+//%BOOL_DICT_INITS_##HELPER(VALUE_NAME, VALUE_TYPE)
+//%
+//%- (instancetype)initWithCapacity:(NSUInteger)numItems {
+//%  #pragma unused(numItems)
+//%  return [self initWith##VNAME##s:NULL forKeys:NULL count:0];
+//%}
+//%
+//%BOOL_DICT_DEALLOC##HELPER()
+//%
+//%- (instancetype)copyWithZone:(NSZone *)zone {
+//%  return [[GPBBool##VALUE_NAME##Dictionary allocWithZone:zone] initWithDictionary:self];
+//%}
+//%
+//%- (BOOL)isEqual:(id)other {
+//%  if (self == other) {
+//%    return YES;
+//%  }
+//%  if (![other isKindOfClass:[GPBBool##VALUE_NAME##Dictionary class]]) {
+//%    return NO;
+//%  }
+//%  GPBBool##VALUE_NAME##Dictionary *otherDictionary = other;
+//%  if ((BOOL_DICT_W_HAS##HELPER(0, ) != BOOL_DICT_W_HAS##HELPER(0, otherDictionary->)) ||
+//%      (BOOL_DICT_W_HAS##HELPER(1, ) != BOOL_DICT_W_HAS##HELPER(1, otherDictionary->))) {
+//%    return NO;
+//%  }
+//%  if ((BOOL_DICT_W_HAS##HELPER(0, ) && (NEQ_##HELPER(_values[0], otherDictionary->_values[0]))) ||
+//%      (BOOL_DICT_W_HAS##HELPER(1, ) && (NEQ_##HELPER(_values[1], otherDictionary->_values[1])))) {
+//%    return NO;
+//%  }
+//%  return YES;
+//%}
+//%
+//%- (NSUInteger)hash {
+//%  return (BOOL_DICT_W_HAS##HELPER(0, ) ? 1 : 0) + (BOOL_DICT_W_HAS##HELPER(1, ) ? 1 : 0);
+//%}
+//%
+//%- (NSString *)description {
+//%  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+//%  if (BOOL_DICT_W_HAS##HELPER(0, )) {
+//%    [result appendFormat:@"NO: STR_FORMAT_##HELPER(VALUE_NAME)", _values[0]];
+//%  }
+//%  if (BOOL_DICT_W_HAS##HELPER(1, )) {
+//%    [result appendFormat:@"YES: STR_FORMAT_##HELPER(VALUE_NAME)", _values[1]];
+//%  }
+//%  [result appendString:@" }"];
+//%  return result;
+//%}
+//%
+//%- (NSUInteger)count {
+//%  return (BOOL_DICT_W_HAS##HELPER(0, ) ? 1 : 0) + (BOOL_DICT_W_HAS##HELPER(1, ) ? 1 : 0);
+//%}
+//%
+//%BOOL_VALUE_FOR_KEY_##HELPER(VALUE_NAME, VALUE_TYPE)
+//%
+//%BOOL_SET_GPBVALUE_FOR_KEY_##HELPER(VALUE_NAME, VALUE_TYPE, VisP)
+//%
+//%- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+//%  if (BOOL_DICT_HAS##HELPER(0, )) {
+//%    block(@"false", TEXT_FORMAT_OBJ##VALUE_NAME(_values[0]));
+//%  }
+//%  if (BOOL_DICT_W_HAS##HELPER(1, )) {
+//%    block(@"true", TEXT_FORMAT_OBJ##VALUE_NAME(_values[1]));
+//%  }
+//%}
+//%
+//%- (void)enumerateKeysAnd##VNAME##sUsingBlock:
+//%    (void (^)(BOOL key, VALUE_TYPE VNAME_VAR, BOOL *stop))block {
+//%  BOOL stop = NO;
+//%  if (BOOL_DICT_HAS##HELPER(0, )) {
+//%    block(NO, _values[0], &stop);
+//%  }
+//%  if (!stop && BOOL_DICT_W_HAS##HELPER(1, )) {
+//%    block(YES, _values[1], &stop);
+//%  }
+//%}
+//%
+//%BOOL_EXTRA_METHODS_##HELPER(Bool, VALUE_NAME)- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+//%  GPBDataType valueDataType = GPBGetFieldDataType(field);
+//%  NSUInteger count = 0;
+//%  size_t result = 0;
+//%  for (int i = 0; i < 2; ++i) {
+//%    if (BOOL_DICT_HAS##HELPER(i, )) {
+//%      ++count;
+//%      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+//%      msgSize += ComputeDict##VALUE_NAME##FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+//%      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+//%    }
+//%  }
+//%  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+//%  result += tagSize * count;
+//%  return result;
+//%}
+//%
+//%- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+//%                         asField:(GPBFieldDescriptor *)field {
+//%  GPBDataType valueDataType = GPBGetFieldDataType(field);
+//%  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+//%  for (int i = 0; i < 2; ++i) {
+//%    if (BOOL_DICT_HAS##HELPER(i, )) {
+//%      // Write the tag.
+//%      [outputStream writeInt32NoTag:tag];
+//%      // Write the size of the message.
+//%      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+//%      msgSize += ComputeDict##VALUE_NAME##FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+//%      [outputStream writeInt32NoTag:(int32_t)msgSize];
+//%      // Write the fields.
+//%      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+//%      WriteDict##VALUE_NAME##Field(outputStream, _values[i], kMapValueFieldNumber, valueDataType);
+//%    }
+//%  }
+//%}
+//%
+//%BOOL_DICT_MUTATIONS_##HELPER(VALUE_NAME, VALUE_TYPE)
+//%
+//%@end
+//%
+
+
+//
+// Helpers for PODs
+//
+
+//%PDDM-DEFINE VALUE_FOR_KEY_POD(KEY_TYPE, VALUE_NAME, VALUE_TYPE, KHELPER)
+//%- (BOOL)get##VALUE_NAME##:(nullable VALUE_TYPE *)value forKey:(KEY_TYPE)key {
+//%  NSNumber *wrapped = [_dictionary objectForKey:WRAPPED##KHELPER(key)];
+//%  if (wrapped && value) {
+//%    *value = UNWRAP##VALUE_NAME(wrapped);
+//%  }
+//%  return (wrapped != NULL);
+//%}
+//%PDDM-DEFINE WRAPPEDPOD(VALUE)
+//%@(VALUE)
+//%PDDM-DEFINE UNWRAPUInt32(VALUE)
+//%[VALUE unsignedIntValue]
+//%PDDM-DEFINE UNWRAPInt32(VALUE)
+//%[VALUE intValue]
+//%PDDM-DEFINE UNWRAPUInt64(VALUE)
+//%[VALUE unsignedLongLongValue]
+//%PDDM-DEFINE UNWRAPInt64(VALUE)
+//%[VALUE longLongValue]
+//%PDDM-DEFINE UNWRAPBool(VALUE)
+//%[VALUE boolValue]
+//%PDDM-DEFINE UNWRAPFloat(VALUE)
+//%[VALUE floatValue]
+//%PDDM-DEFINE UNWRAPDouble(VALUE)
+//%[VALUE doubleValue]
+//%PDDM-DEFINE UNWRAPEnum(VALUE)
+//%[VALUE intValue]
+//%PDDM-DEFINE TEXT_FORMAT_OBJUInt32(VALUE)
+//%[NSString stringWithFormat:@"%u", VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJInt32(VALUE)
+//%[NSString stringWithFormat:@"%d", VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJUInt64(VALUE)
+//%[NSString stringWithFormat:@"%llu", VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJInt64(VALUE)
+//%[NSString stringWithFormat:@"%lld", VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJBool(VALUE)
+//%(VALUE ? @"true" : @"false")
+//%PDDM-DEFINE TEXT_FORMAT_OBJFloat(VALUE)
+//%[NSString stringWithFormat:@"%.*g", FLT_DIG, VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJDouble(VALUE)
+//%[NSString stringWithFormat:@"%.*lg", DBL_DIG, VALUE]
+//%PDDM-DEFINE TEXT_FORMAT_OBJEnum(VALUE)
+//%@(VALUE)
+//%PDDM-DEFINE ENUM_TYPEPOD(TYPE)
+//%NSNumber *
+//%PDDM-DEFINE NEQ_POD(VAL1, VAL2)
+//%VAL1 != VAL2
+//%PDDM-DEFINE EXTRA_METHODS_POD(KEY_NAME, VALUE_NAME)
+// Empty
+//%PDDM-DEFINE BOOL_EXTRA_METHODS_POD(KEY_NAME, VALUE_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD(KEY_NAME, VALUE_NAME)
+//%SERIAL_DATA_FOR_ENTRY_POD_##VALUE_NAME(KEY_NAME)
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_UInt32(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Int32(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_UInt64(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Int64(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Bool(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Float(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Double(KEY_NAME)
+// Empty
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_POD_Enum(KEY_NAME)
+//%- (NSData *)serializedDataForUnknownValue:(int32_t)value
+//%                                   forKey:(GPBGenericValue *)key
+//%                              keyDataType:(GPBDataType)keyDataType {
+//%  size_t msgSize = ComputeDict##KEY_NAME##FieldSize(key->value##KEY_NAME, kMapKeyFieldNumber, keyDataType);
+//%  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBDataTypeEnum);
+//%  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+//%  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+//%  WriteDict##KEY_NAME##Field(outputStream, key->value##KEY_NAME, kMapKeyFieldNumber, keyDataType);
+//%  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBDataTypeEnum);
+//%  [outputStream release];
+//%  return data;
+//%}
+//%
+//%PDDM-DEFINE GPBVALUE_POD(VALUE_NAME)
+//%value##VALUE_NAME
+//%PDDM-DEFINE DICTIONARY_VALIDATE_VALUE_POD(VALUE_NAME, EXTRA_INDENT)
+// Empty
+//%PDDM-DEFINE DICTIONARY_VALIDATE_KEY_POD(KEY_NAME, EXTRA_INDENT)
+// Empty
+
+//%PDDM-DEFINE BOOL_DICT_HAS_STORAGE_POD()
+//%  BOOL _valueSet[2];
+//%
+//%PDDM-DEFINE BOOL_DICT_INITS_POD(VALUE_NAME, VALUE_TYPE)
+//%- (instancetype)initWith##VALUE_NAME##s:(const VALUE_TYPE [])values
+//%                 ##VALUE_NAME$S## forKeys:(const BOOL [])keys
+//%                 ##VALUE_NAME$S##   count:(NSUInteger)count {
+//%  self = [super init];
+//%  if (self) {
+//%    for (NSUInteger i = 0; i < count; ++i) {
+//%      int idx = keys[i] ? 1 : 0;
+//%      _values[idx] = values[i];
+//%      _valueSet[idx] = YES;
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithDictionary:(GPBBool##VALUE_NAME##Dictionary *)dictionary {
+//%  self = [self initWith##VALUE_NAME##s:NULL forKeys:NULL count:0];
+//%  if (self) {
+//%    if (dictionary) {
+//%      for (int i = 0; i < 2; ++i) {
+//%        if (dictionary->_valueSet[i]) {
+//%          _values[i] = dictionary->_values[i];
+//%          _valueSet[i] = YES;
+//%        }
+//%      }
+//%    }
+//%  }
+//%  return self;
+//%}
+//%PDDM-DEFINE BOOL_DICT_DEALLOCPOD()
+//%#if !defined(NS_BLOCK_ASSERTIONS)
+//%- (void)dealloc {
+//%  NSAssert(!_autocreator,
+//%           @"%@: Autocreator must be cleared before release, autocreator: %@",
+//%           [self class], _autocreator);
+//%  [super dealloc];
+//%}
+//%#endif  // !defined(NS_BLOCK_ASSERTIONS)
+//%PDDM-DEFINE BOOL_DICT_W_HASPOD(IDX, REF)
+//%BOOL_DICT_HASPOD(IDX, REF)
+//%PDDM-DEFINE BOOL_DICT_HASPOD(IDX, REF)
+//%REF##_valueSet[IDX]
+//%PDDM-DEFINE BOOL_VALUE_FOR_KEY_POD(VALUE_NAME, VALUE_TYPE)
+//%- (BOOL)get##VALUE_NAME##:(VALUE_TYPE *)value forKey:(BOOL)key {
+//%  int idx = (key ? 1 : 0);
+//%  if (_valueSet[idx]) {
+//%    if (value) {
+//%      *value = _values[idx];
+//%    }
+//%    return YES;
+//%  }
+//%  return NO;
+//%}
+//%PDDM-DEFINE BOOL_SET_GPBVALUE_FOR_KEY_POD(VALUE_NAME, VALUE_TYPE, VisP)
+//%- (void)setGPBGenericValue:(GPBGenericValue *)value
+//%     forGPBGenericValueKey:(GPBGenericValue *)key {
+//%  int idx = (key->valueBool ? 1 : 0);
+//%  _values[idx] = value->value##VALUE_NAME;
+//%  _valueSet[idx] = YES;
+//%}
+//%PDDM-DEFINE BOOL_DICT_MUTATIONS_POD(VALUE_NAME, VALUE_TYPE)
+//%- (void)addEntriesFromDictionary:(GPBBool##VALUE_NAME##Dictionary *)otherDictionary {
+//%  if (otherDictionary) {
+//%    for (int i = 0; i < 2; ++i) {
+//%      if (otherDictionary->_valueSet[i]) {
+//%        _valueSet[i] = YES;
+//%        _values[i] = otherDictionary->_values[i];
+//%      }
+//%    }
+//%    if (_autocreator) {
+//%      GPBAutocreatedDictionaryModified(_autocreator, self);
+//%    }
+//%  }
+//%}
+//%
+//%- (void)set##VALUE_NAME:(VALUE_TYPE)value forKey:(BOOL)key {
+//%  int idx = (key ? 1 : 0);
+//%  _values[idx] = value;
+//%  _valueSet[idx] = YES;
+//%  if (_autocreator) {
+//%    GPBAutocreatedDictionaryModified(_autocreator, self);
+//%  }
+//%}
+//%
+//%- (void)remove##VALUE_NAME##ForKey:(BOOL)aKey {
+//%  _valueSet[aKey ? 1 : 0] = NO;
+//%}
+//%
+//%- (void)removeAll {
+//%  _valueSet[0] = NO;
+//%  _valueSet[1] = NO;
+//%}
+//%PDDM-DEFINE STR_FORMAT_POD(VALUE_NAME)
+//%STR_FORMAT_##VALUE_NAME()
+//%PDDM-DEFINE STR_FORMAT_UInt32()
+//%%u
+//%PDDM-DEFINE STR_FORMAT_Int32()
+//%%d
+//%PDDM-DEFINE STR_FORMAT_UInt64()
+//%%llu
+//%PDDM-DEFINE STR_FORMAT_Int64()
+//%%lld
+//%PDDM-DEFINE STR_FORMAT_Bool()
+//%%d
+//%PDDM-DEFINE STR_FORMAT_Float()
+//%%f
+//%PDDM-DEFINE STR_FORMAT_Double()
+//%%lf
+
+//
+// Helpers for Objects
+//
+
+//%PDDM-DEFINE VALUE_FOR_KEY_OBJECT(KEY_TYPE, VALUE_NAME, VALUE_TYPE, KHELPER)
+//%- (VALUE_TYPE)objectForKey:(KEY_TYPE)key {
+//%  VALUE_TYPE result = [_dictionary objectForKey:WRAPPED##KHELPER(key)];
+//%  return result;
+//%}
+//%PDDM-DEFINE WRAPPEDOBJECT(VALUE)
+//%VALUE
+//%PDDM-DEFINE UNWRAPString(VALUE)
+//%VALUE
+//%PDDM-DEFINE UNWRAPObject(VALUE)
+//%VALUE
+//%PDDM-DEFINE TEXT_FORMAT_OBJString(VALUE)
+//%VALUE
+//%PDDM-DEFINE TEXT_FORMAT_OBJObject(VALUE)
+//%VALUE
+//%PDDM-DEFINE ENUM_TYPEOBJECT(TYPE)
+//%ENUM_TYPEOBJECT_##TYPE()
+//%PDDM-DEFINE ENUM_TYPEOBJECT_NSString()
+//%NSString *
+//%PDDM-DEFINE ENUM_TYPEOBJECT_id()
+//%id ##
+//%PDDM-DEFINE NEQ_OBJECT(VAL1, VAL2)
+//%![VAL1 isEqual:VAL2]
+//%PDDM-DEFINE EXTRA_METHODS_OBJECT(KEY_NAME, VALUE_NAME)
+//%- (BOOL)isInitialized {
+//%  for (GPBMessage *msg in [_dictionary objectEnumerator]) {
+//%    if (!msg.initialized) {
+//%      return NO;
+//%    }
+//%  }
+//%  return YES;
+//%}
+//%
+//%- (instancetype)deepCopyWithZone:(NSZone *)zone {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *newDict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] init];
+//%  NSEnumerator *keys = [_dictionary keyEnumerator];
+//%  id aKey;
+//%  NSMutableDictionary *internalDict = newDict->_dictionary;
+//%  while ((aKey = [keys nextObject])) {
+//%    GPBMessage *msg = _dictionary[aKey];
+//%    GPBMessage *copiedMsg = [msg copyWithZone:zone];
+//%    [internalDict setObject:copiedMsg forKey:aKey];
+//%    [copiedMsg release];
+//%  }
+//%  return newDict;
+//%}
+//%
+//%
+//%PDDM-DEFINE BOOL_EXTRA_METHODS_OBJECT(KEY_NAME, VALUE_NAME)
+//%- (BOOL)isInitialized {
+//%  if (_values[0] && ![_values[0] isInitialized]) {
+//%    return NO;
+//%  }
+//%  if (_values[1] && ![_values[1] isInitialized]) {
+//%    return NO;
+//%  }
+//%  return YES;
+//%}
+//%
+//%- (instancetype)deepCopyWithZone:(NSZone *)zone {
+//%  GPB##KEY_NAME##VALUE_NAME##Dictionary *newDict =
+//%      [[GPB##KEY_NAME##VALUE_NAME##Dictionary alloc] init];
+//%  for (int i = 0; i < 2; ++i) {
+//%    if (_values[i] != nil) {
+//%      newDict->_values[i] = [_values[i] copyWithZone:zone];
+//%    }
+//%  }
+//%  return newDict;
+//%}
+//%
+//%
+//%PDDM-DEFINE SERIAL_DATA_FOR_ENTRY_OBJECT(KEY_NAME, VALUE_NAME)
+// Empty
+//%PDDM-DEFINE GPBVALUE_OBJECT(VALUE_NAME)
+//%valueString
+//%PDDM-DEFINE DICTIONARY_VALIDATE_VALUE_OBJECT(VALUE_NAME, EXTRA_INDENT)
+//%##EXTRA_INDENT$S##  if (!##VALUE_NAME) {
+//%##EXTRA_INDENT$S##    [NSException raise:NSInvalidArgumentException
+//%##EXTRA_INDENT$S##                format:@"Attempting to add nil object to a Dictionary"];
+//%##EXTRA_INDENT$S##  }
+//%
+//%PDDM-DEFINE DICTIONARY_VALIDATE_KEY_OBJECT(KEY_NAME, EXTRA_INDENT)
+//%##EXTRA_INDENT$S##  if (!##KEY_NAME) {
+//%##EXTRA_INDENT$S##    [NSException raise:NSInvalidArgumentException
+//%##EXTRA_INDENT$S##                format:@"Attempting to add nil key to a Dictionary"];
+//%##EXTRA_INDENT$S##  }
+//%
+
+//%PDDM-DEFINE BOOL_DICT_HAS_STORAGE_OBJECT()
+// Empty
+//%PDDM-DEFINE BOOL_DICT_INITS_OBJECT(VALUE_NAME, VALUE_TYPE)
+//%- (instancetype)initWithObjects:(const VALUE_TYPE [])objects
+//%                        forKeys:(const BOOL [])keys
+//%                          count:(NSUInteger)count {
+//%  self = [super init];
+//%  if (self) {
+//%    for (NSUInteger i = 0; i < count; ++i) {
+//%      if (!objects[i]) {
+//%        [NSException raise:NSInvalidArgumentException
+//%                    format:@"Attempting to add nil object to a Dictionary"];
+//%      }
+//%      int idx = keys[i] ? 1 : 0;
+//%      [_values[idx] release];
+//%      _values[idx] = (VALUE_TYPE)[objects[i] retain];
+//%    }
+//%  }
+//%  return self;
+//%}
+//%
+//%- (instancetype)initWithDictionary:(GPBBool##VALUE_NAME##Dictionary *)dictionary {
+//%  self = [self initWithObjects:NULL forKeys:NULL count:0];
+//%  if (self) {
+//%    if (dictionary) {
+//%      _values[0] = [dictionary->_values[0] retain];
+//%      _values[1] = [dictionary->_values[1] retain];
+//%    }
+//%  }
+//%  return self;
+//%}
+//%PDDM-DEFINE BOOL_DICT_DEALLOCOBJECT()
+//%- (void)dealloc {
+//%  NSAssert(!_autocreator,
+//%           @"%@: Autocreator must be cleared before release, autocreator: %@",
+//%           [self class], _autocreator);
+//%  [_values[0] release];
+//%  [_values[1] release];
+//%  [super dealloc];
+//%}
+//%PDDM-DEFINE BOOL_DICT_W_HASOBJECT(IDX, REF)
+//%(BOOL_DICT_HASOBJECT(IDX, REF))
+//%PDDM-DEFINE BOOL_DICT_HASOBJECT(IDX, REF)
+//%REF##_values[IDX] != nil
+//%PDDM-DEFINE BOOL_VALUE_FOR_KEY_OBJECT(VALUE_NAME, VALUE_TYPE)
+//%- (VALUE_TYPE)objectForKey:(BOOL)key {
+//%  return _values[key ? 1 : 0];
+//%}
+//%PDDM-DEFINE BOOL_SET_GPBVALUE_FOR_KEY_OBJECT(VALUE_NAME, VALUE_TYPE, VisP)
+//%- (void)setGPBGenericValue:(GPBGenericValue *)value
+//%     forGPBGenericValueKey:(GPBGenericValue *)key {
+//%  int idx = (key->valueBool ? 1 : 0);
+//%  [_values[idx] release];
+//%  _values[idx] = [value->valueString retain];
+//%}
+
+//%PDDM-DEFINE BOOL_DICT_MUTATIONS_OBJECT(VALUE_NAME, VALUE_TYPE)
+//%- (void)addEntriesFromDictionary:(GPBBool##VALUE_NAME##Dictionary *)otherDictionary {
+//%  if (otherDictionary) {
+//%    for (int i = 0; i < 2; ++i) {
+//%      if (otherDictionary->_values[i] != nil) {
+//%        [_values[i] release];
+//%        _values[i] = [otherDictionary->_values[i] retain];
+//%      }
+//%    }
+//%    if (_autocreator) {
+//%      GPBAutocreatedDictionaryModified(_autocreator, self);
+//%    }
+//%  }
+//%}
+//%
+//%- (void)setObject:(VALUE_TYPE)object forKey:(BOOL)key {
+//%  if (!object) {
+//%    [NSException raise:NSInvalidArgumentException
+//%                format:@"Attempting to add nil object to a Dictionary"];
+//%  }
+//%  int idx = (key ? 1 : 0);
+//%  [_values[idx] release];
+//%  _values[idx] = [object retain];
+//%  if (_autocreator) {
+//%    GPBAutocreatedDictionaryModified(_autocreator, self);
+//%  }
+//%}
+//%
+//%- (void)removeObjectForKey:(BOOL)aKey {
+//%  int idx = (aKey ? 1 : 0);
+//%  [_values[idx] release];
+//%  _values[idx] = nil;
+//%}
+//%
+//%- (void)removeAll {
+//%  for (int i = 0; i < 2; ++i) {
+//%    [_values[i] release];
+//%    _values[i] = nil;
+//%  }
+//%}
+//%PDDM-DEFINE STR_FORMAT_OBJECT(VALUE_NAME)
+//%%@
+
+
+//%PDDM-EXPAND DICTIONARY_IMPL_FOR_POD_KEY(UInt32, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt32 -> UInt32
+
+@implementation GPBUInt32UInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt32s:(const uint32_t [])values
+                        forKeys:(const uint32_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32UInt32Dictionary *)dictionary {
+  self = [self initWithUInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32UInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32UInt32Dictionary class]]) {
+    return NO;
+  }
+  GPBUInt32UInt32Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(uint32_t key, uint32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedIntValue], [aValue unsignedIntValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint32_t unwrappedKey = [aKey unsignedIntValue];
+    uint32_t unwrappedValue = [aValue unsignedIntValue];
+    size_t msgSize = ComputeDictUInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt32FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictUInt32Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueUInt32) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndUInt32sUsingBlock:^(uint32_t key, uint32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%u", value]);
+  }];
+}
+
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(uint32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedIntValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32UInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt32:(uint32_t)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt32ForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Int32
+
+@implementation GPBUInt32Int32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt32s:(const int32_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32Int32Dictionary *)dictionary {
+  self = [self initWithInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32Int32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32Int32Dictionary class]]) {
+    return NO;
+  }
+  GPBUInt32Int32Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(uint32_t key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedIntValue], [aValue intValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint32_t unwrappedKey = [aKey unsignedIntValue];
+    int32_t unwrappedValue = [aValue intValue];
+    size_t msgSize = ComputeDictUInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt32FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictInt32Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueInt32) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndInt32sUsingBlock:^(uint32_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%d", value]);
+  }];
+}
+
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(uint32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32Int32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt32:(int32_t)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt32ForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> UInt64
+
+@implementation GPBUInt32UInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt64s:(const uint64_t [])values
+                        forKeys:(const uint32_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32UInt64Dictionary *)dictionary {
+  self = [self initWithUInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32UInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32UInt64Dictionary class]]) {
+    return NO;
+  }
+  GPBUInt32UInt64Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(uint32_t key, uint64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedIntValue], [aValue unsignedLongLongValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint32_t unwrappedKey = [aKey unsignedIntValue];
+    uint64_t unwrappedValue = [aValue unsignedLongLongValue];
+    size_t msgSize = ComputeDictUInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt64FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictUInt64Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueUInt64) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndUInt64sUsingBlock:^(uint32_t key, uint64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%llu", value]);
+  }];
+}
+
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(uint32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedLongLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32UInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt64:(uint64_t)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt64ForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Int64
+
+@implementation GPBUInt32Int64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt64s:(const int64_t [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32Int64Dictionary *)dictionary {
+  self = [self initWithInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32Int64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32Int64Dictionary class]]) {
+    return NO;
+  }
+  GPBUInt32Int64Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(uint32_t key, int64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedIntValue], [aValue longLongValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint32_t unwrappedKey = [aKey unsignedIntValue];
+    int64_t unwrappedValue = [aValue longLongValue];
+    size_t msgSize = ComputeDictUInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt64FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictInt64Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueInt64) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndInt64sUsingBlock:^(uint32_t key, int64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%lld", value]);
+  }];
+}
+
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(uint32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped longLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32Int64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt64:(int64_t)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt64ForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Bool
+
+@implementation GPBUInt32BoolDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithBools:(const BOOL [])values
+                      forKeys:(const uint32_t [])keys
+                        count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32BoolDictionary *)dictionary {
+  self = [self initWithBools:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32BoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32BoolDictionary class]]) {
+    return NO;
+  }
+  GPBUInt32BoolDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(uint32_t key, BOOL value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedIntValue], [aValue boolValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint32_t unwrappedKey = [aKey unsignedIntValue];
+    BOOL unwrappedValue = [aValue boolValue];
+    size_t msgSize = ComputeDictUInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictBoolFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictBoolField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueBool) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndBoolsUsingBlock:^(uint32_t key, BOOL value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], (value ? @"true" : @"false"));
+  }];
+}
+
+- (BOOL)getBool:(nullable BOOL *)value forKey:(uint32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped boolValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32BoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setBool:(BOOL)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeBoolForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Float
+
+@implementation GPBUInt32FloatDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithFloats:(const float [])values
+                       forKeys:(const uint32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32FloatDictionary *)dictionary {
+  self = [self initWithFloats:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32FloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32FloatDictionary class]]) {
+    return NO;
+  }
+  GPBUInt32FloatDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(uint32_t key, float value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedIntValue], [aValue floatValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint32_t unwrappedKey = [aKey unsignedIntValue];
+    float unwrappedValue = [aValue floatValue];
+    size_t msgSize = ComputeDictUInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictFloatFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictFloatField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueFloat) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndFloatsUsingBlock:^(uint32_t key, float value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%.*g", FLT_DIG, value]);
+  }];
+}
+
+- (BOOL)getFloat:(nullable float *)value forKey:(uint32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped floatValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32FloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setFloat:(float)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeFloatForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Double
+
+@implementation GPBUInt32DoubleDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithDoubles:(const double [])values
+                        forKeys:(const uint32_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32DoubleDictionary *)dictionary {
+  self = [self initWithDoubles:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32DoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32DoubleDictionary class]]) {
+    return NO;
+  }
+  GPBUInt32DoubleDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(uint32_t key, double value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedIntValue], [aValue doubleValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint32_t unwrappedKey = [aKey unsignedIntValue];
+    double unwrappedValue = [aValue doubleValue];
+    size_t msgSize = ComputeDictUInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictDoubleFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictDoubleField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueDouble) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndDoublesUsingBlock:^(uint32_t key, double value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], [NSString stringWithFormat:@"%.*lg", DBL_DIG, value]);
+  }];
+}
+
+- (BOOL)getDouble:(nullable double *)value forKey:(uint32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped doubleValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32DoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setDouble:(double)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeDoubleForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt32 -> Enum
+
+@implementation GPBUInt32EnumDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+  GPBEnumValidationFunc _validationFunc;
+}
+
+@synthesize validationFunc = _validationFunc;
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])rawValues
+                                   forKeys:(const uint32_t [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    if (count && rawValues && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(rawValues[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32EnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32EnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32EnumDictionary class]]) {
+    return NO;
+  }
+  GPBUInt32EnumDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(uint32_t key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedIntValue], [aValue intValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint32_t unwrappedKey = [aKey unsignedIntValue];
+    int32_t unwrappedValue = [aValue intValue];
+    size_t msgSize = ComputeDictUInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictEnumFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictEnumField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType {
+  size_t msgSize = ComputeDictUInt32FieldSize(key->valueUInt32, kMapKeyFieldNumber, keyDataType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBDataTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictUInt32Field(outputStream, key->valueUInt32, kMapKeyFieldNumber, keyDataType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBDataTypeEnum);
+  [outputStream release];
+  return data;
+}
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueEnum) forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndRawValuesUsingBlock:^(uint32_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], @(value));
+  }];
+}
+
+- (BOOL)getEnum:(int32_t *)value forKey:(uint32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    int32_t result = [wrapped intValue];
+    if (!_validationFunc(result)) {
+      result = kGPBUnrecognizedEnumeratorValue;
+    }
+    *value = result;
+  }
+  return (wrapped != NULL);
+}
+
+- (BOOL)getRawValue:(int32_t *)rawValue forKey:(uint32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && rawValue) {
+    *rawValue = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(uint32_t key, int32_t value, BOOL *stop))block {
+  GPBEnumValidationFunc func = _validationFunc;
+  BOOL stop = NO;
+  NSEnumerator *keys = [_dictionary keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = _dictionary[aKey];
+      int32_t unwrapped = [aValue intValue];
+      if (!func(unwrapped)) {
+        unwrapped = kGPBUnrecognizedEnumeratorValue;
+      }
+    block([aKey unsignedIntValue], unwrapped, &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (void)addRawEntriesFromDictionary:(GPBUInt32EnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setRawValue:(int32_t)value forKey:(uint32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeEnumForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+- (void)setEnum:(int32_t)value forKey:(uint32_t)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBUInt32EnumDictionary: Attempt to set an unknown enum value (%d)",
+                       value];
+  }
+
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+@end
+
+#pragma mark - UInt32 -> Object
+
+@implementation GPBUInt32ObjectDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithObjects:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithObjects:(const id [])objects
+                        forKeys:(const uint32_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && objects && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!objects[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil object to a Dictionary"];
+        }
+        [_dictionary setObject:objects[i] forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt32ObjectDictionary *)dictionary {
+  self = [self initWithObjects:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithObjects:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt32ObjectDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt32ObjectDictionary class]]) {
+    return NO;
+  }
+  GPBUInt32ObjectDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndObjectsUsingBlock:
+    (void (^)(uint32_t key, id object, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    block([aKey unsignedIntValue], aObject, &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (BOOL)isInitialized {
+  for (GPBMessage *msg in [_dictionary objectEnumerator]) {
+    if (!msg.initialized) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+- (instancetype)deepCopyWithZone:(NSZone *)zone {
+  GPBUInt32ObjectDictionary *newDict =
+      [[GPBUInt32ObjectDictionary alloc] init];
+  NSEnumerator *keys = [_dictionary keyEnumerator];
+  id aKey;
+  NSMutableDictionary *internalDict = newDict->_dictionary;
+  while ((aKey = [keys nextObject])) {
+    GPBMessage *msg = _dictionary[aKey];
+    GPBMessage *copiedMsg = [msg copyWithZone:zone];
+    [internalDict setObject:copiedMsg forKey:aKey];
+    [copiedMsg release];
+  }
+  return newDict;
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    size_t msgSize = ComputeDictUInt32FieldSize([aKey unsignedIntValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictObjectFieldSize(aObject, kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint32_t unwrappedKey = [aKey unsignedIntValue];
+    id unwrappedValue = aObject;
+    size_t msgSize = ComputeDictUInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictObjectFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictObjectField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:value->valueString forKey:@(key->valueUInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndObjectsUsingBlock:^(uint32_t key, id object, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%u", key], object);
+  }];
+}
+
+- (id)objectForKey:(uint32_t)key {
+  id result = [_dictionary objectForKey:@(key)];
+  return result;
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt32ObjectDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setObject:(id)object forKey:(uint32_t)key {
+  if (!object) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil object to a Dictionary"];
+  }
+  [_dictionary setObject:object forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeObjectForKey:(uint32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_IMPL_FOR_POD_KEY(Int32, int32_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int32 -> UInt32
+
+@implementation GPBInt32UInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt32s:(const uint32_t [])values
+                        forKeys:(const int32_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32UInt32Dictionary *)dictionary {
+  self = [self initWithUInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32UInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32UInt32Dictionary class]]) {
+    return NO;
+  }
+  GPBInt32UInt32Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(int32_t key, uint32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey intValue], [aValue unsignedIntValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int32_t unwrappedKey = [aKey intValue];
+    uint32_t unwrappedValue = [aValue unsignedIntValue];
+    size_t msgSize = ComputeDictInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt32FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictUInt32Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueUInt32) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndUInt32sUsingBlock:^(int32_t key, uint32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%u", value]);
+  }];
+}
+
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(int32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedIntValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32UInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt32:(uint32_t)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt32ForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Int32
+
+@implementation GPBInt32Int32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt32s:(const int32_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32Int32Dictionary *)dictionary {
+  self = [self initWithInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32Int32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32Int32Dictionary class]]) {
+    return NO;
+  }
+  GPBInt32Int32Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(int32_t key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey intValue], [aValue intValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int32_t unwrappedKey = [aKey intValue];
+    int32_t unwrappedValue = [aValue intValue];
+    size_t msgSize = ComputeDictInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt32FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictInt32Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueInt32) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndInt32sUsingBlock:^(int32_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%d", value]);
+  }];
+}
+
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(int32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32Int32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt32:(int32_t)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt32ForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> UInt64
+
+@implementation GPBInt32UInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt64s:(const uint64_t [])values
+                        forKeys:(const int32_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32UInt64Dictionary *)dictionary {
+  self = [self initWithUInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32UInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32UInt64Dictionary class]]) {
+    return NO;
+  }
+  GPBInt32UInt64Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(int32_t key, uint64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey intValue], [aValue unsignedLongLongValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int32_t unwrappedKey = [aKey intValue];
+    uint64_t unwrappedValue = [aValue unsignedLongLongValue];
+    size_t msgSize = ComputeDictInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt64FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictUInt64Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueUInt64) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndUInt64sUsingBlock:^(int32_t key, uint64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%llu", value]);
+  }];
+}
+
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(int32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedLongLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32UInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt64:(uint64_t)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt64ForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Int64
+
+@implementation GPBInt32Int64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt64s:(const int64_t [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32Int64Dictionary *)dictionary {
+  self = [self initWithInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32Int64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32Int64Dictionary class]]) {
+    return NO;
+  }
+  GPBInt32Int64Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(int32_t key, int64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey intValue], [aValue longLongValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int32_t unwrappedKey = [aKey intValue];
+    int64_t unwrappedValue = [aValue longLongValue];
+    size_t msgSize = ComputeDictInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt64FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictInt64Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueInt64) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndInt64sUsingBlock:^(int32_t key, int64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%lld", value]);
+  }];
+}
+
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(int32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped longLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32Int64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt64:(int64_t)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt64ForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Bool
+
+@implementation GPBInt32BoolDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithBools:(const BOOL [])values
+                      forKeys:(const int32_t [])keys
+                        count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32BoolDictionary *)dictionary {
+  self = [self initWithBools:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32BoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32BoolDictionary class]]) {
+    return NO;
+  }
+  GPBInt32BoolDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(int32_t key, BOOL value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey intValue], [aValue boolValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int32_t unwrappedKey = [aKey intValue];
+    BOOL unwrappedValue = [aValue boolValue];
+    size_t msgSize = ComputeDictInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictBoolFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictBoolField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueBool) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndBoolsUsingBlock:^(int32_t key, BOOL value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], (value ? @"true" : @"false"));
+  }];
+}
+
+- (BOOL)getBool:(nullable BOOL *)value forKey:(int32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped boolValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32BoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setBool:(BOOL)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeBoolForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Float
+
+@implementation GPBInt32FloatDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithFloats:(const float [])values
+                       forKeys:(const int32_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32FloatDictionary *)dictionary {
+  self = [self initWithFloats:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32FloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32FloatDictionary class]]) {
+    return NO;
+  }
+  GPBInt32FloatDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(int32_t key, float value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey intValue], [aValue floatValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int32_t unwrappedKey = [aKey intValue];
+    float unwrappedValue = [aValue floatValue];
+    size_t msgSize = ComputeDictInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictFloatFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictFloatField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueFloat) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndFloatsUsingBlock:^(int32_t key, float value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%.*g", FLT_DIG, value]);
+  }];
+}
+
+- (BOOL)getFloat:(nullable float *)value forKey:(int32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped floatValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32FloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setFloat:(float)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeFloatForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Double
+
+@implementation GPBInt32DoubleDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithDoubles:(const double [])values
+                        forKeys:(const int32_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32DoubleDictionary *)dictionary {
+  self = [self initWithDoubles:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32DoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32DoubleDictionary class]]) {
+    return NO;
+  }
+  GPBInt32DoubleDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(int32_t key, double value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey intValue], [aValue doubleValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int32_t unwrappedKey = [aKey intValue];
+    double unwrappedValue = [aValue doubleValue];
+    size_t msgSize = ComputeDictInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictDoubleFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictDoubleField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueDouble) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndDoublesUsingBlock:^(int32_t key, double value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], [NSString stringWithFormat:@"%.*lg", DBL_DIG, value]);
+  }];
+}
+
+- (BOOL)getDouble:(nullable double *)value forKey:(int32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped doubleValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32DoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setDouble:(double)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeDoubleForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int32 -> Enum
+
+@implementation GPBInt32EnumDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+  GPBEnumValidationFunc _validationFunc;
+}
+
+@synthesize validationFunc = _validationFunc;
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])rawValues
+                                   forKeys:(const int32_t [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    if (count && rawValues && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(rawValues[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32EnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32EnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32EnumDictionary class]]) {
+    return NO;
+  }
+  GPBInt32EnumDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(int32_t key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey intValue], [aValue intValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int32_t unwrappedKey = [aKey intValue];
+    int32_t unwrappedValue = [aValue intValue];
+    size_t msgSize = ComputeDictInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictEnumFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictEnumField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType {
+  size_t msgSize = ComputeDictInt32FieldSize(key->valueInt32, kMapKeyFieldNumber, keyDataType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBDataTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictInt32Field(outputStream, key->valueInt32, kMapKeyFieldNumber, keyDataType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBDataTypeEnum);
+  [outputStream release];
+  return data;
+}
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueEnum) forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndRawValuesUsingBlock:^(int32_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], @(value));
+  }];
+}
+
+- (BOOL)getEnum:(int32_t *)value forKey:(int32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    int32_t result = [wrapped intValue];
+    if (!_validationFunc(result)) {
+      result = kGPBUnrecognizedEnumeratorValue;
+    }
+    *value = result;
+  }
+  return (wrapped != NULL);
+}
+
+- (BOOL)getRawValue:(int32_t *)rawValue forKey:(int32_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && rawValue) {
+    *rawValue = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(int32_t key, int32_t value, BOOL *stop))block {
+  GPBEnumValidationFunc func = _validationFunc;
+  BOOL stop = NO;
+  NSEnumerator *keys = [_dictionary keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = _dictionary[aKey];
+      int32_t unwrapped = [aValue intValue];
+      if (!func(unwrapped)) {
+        unwrapped = kGPBUnrecognizedEnumeratorValue;
+      }
+    block([aKey intValue], unwrapped, &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (void)addRawEntriesFromDictionary:(GPBInt32EnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setRawValue:(int32_t)value forKey:(int32_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeEnumForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+- (void)setEnum:(int32_t)value forKey:(int32_t)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBInt32EnumDictionary: Attempt to set an unknown enum value (%d)",
+                       value];
+  }
+
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+@end
+
+#pragma mark - Int32 -> Object
+
+@implementation GPBInt32ObjectDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithObjects:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithObjects:(const id [])objects
+                        forKeys:(const int32_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && objects && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!objects[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil object to a Dictionary"];
+        }
+        [_dictionary setObject:objects[i] forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt32ObjectDictionary *)dictionary {
+  self = [self initWithObjects:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithObjects:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt32ObjectDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt32ObjectDictionary class]]) {
+    return NO;
+  }
+  GPBInt32ObjectDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndObjectsUsingBlock:
+    (void (^)(int32_t key, id object, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    block([aKey intValue], aObject, &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (BOOL)isInitialized {
+  for (GPBMessage *msg in [_dictionary objectEnumerator]) {
+    if (!msg.initialized) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+- (instancetype)deepCopyWithZone:(NSZone *)zone {
+  GPBInt32ObjectDictionary *newDict =
+      [[GPBInt32ObjectDictionary alloc] init];
+  NSEnumerator *keys = [_dictionary keyEnumerator];
+  id aKey;
+  NSMutableDictionary *internalDict = newDict->_dictionary;
+  while ((aKey = [keys nextObject])) {
+    GPBMessage *msg = _dictionary[aKey];
+    GPBMessage *copiedMsg = [msg copyWithZone:zone];
+    [internalDict setObject:copiedMsg forKey:aKey];
+    [copiedMsg release];
+  }
+  return newDict;
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    size_t msgSize = ComputeDictInt32FieldSize([aKey intValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictObjectFieldSize(aObject, kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int32_t unwrappedKey = [aKey intValue];
+    id unwrappedValue = aObject;
+    size_t msgSize = ComputeDictInt32FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictObjectFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt32Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictObjectField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:value->valueString forKey:@(key->valueInt32)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndObjectsUsingBlock:^(int32_t key, id object, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%d", key], object);
+  }];
+}
+
+- (id)objectForKey:(int32_t)key {
+  id result = [_dictionary objectForKey:@(key)];
+  return result;
+}
+
+- (void)addEntriesFromDictionary:(GPBInt32ObjectDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setObject:(id)object forKey:(int32_t)key {
+  if (!object) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil object to a Dictionary"];
+  }
+  [_dictionary setObject:object forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeObjectForKey:(int32_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_IMPL_FOR_POD_KEY(UInt64, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - UInt64 -> UInt32
+
+@implementation GPBUInt64UInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt32s:(const uint32_t [])values
+                        forKeys:(const uint64_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64UInt32Dictionary *)dictionary {
+  self = [self initWithUInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64UInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64UInt32Dictionary class]]) {
+    return NO;
+  }
+  GPBUInt64UInt32Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(uint64_t key, uint32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedLongLongValue], [aValue unsignedIntValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint64_t unwrappedKey = [aKey unsignedLongLongValue];
+    uint32_t unwrappedValue = [aValue unsignedIntValue];
+    size_t msgSize = ComputeDictUInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt32FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictUInt32Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueUInt32) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndUInt32sUsingBlock:^(uint64_t key, uint32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%u", value]);
+  }];
+}
+
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(uint64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedIntValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64UInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt32:(uint32_t)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt32ForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Int32
+
+@implementation GPBUInt64Int32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt32s:(const int32_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64Int32Dictionary *)dictionary {
+  self = [self initWithInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64Int32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64Int32Dictionary class]]) {
+    return NO;
+  }
+  GPBUInt64Int32Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(uint64_t key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedLongLongValue], [aValue intValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint64_t unwrappedKey = [aKey unsignedLongLongValue];
+    int32_t unwrappedValue = [aValue intValue];
+    size_t msgSize = ComputeDictUInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt32FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictInt32Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueInt32) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndInt32sUsingBlock:^(uint64_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%d", value]);
+  }];
+}
+
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(uint64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64Int32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt32:(int32_t)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt32ForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> UInt64
+
+@implementation GPBUInt64UInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt64s:(const uint64_t [])values
+                        forKeys:(const uint64_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64UInt64Dictionary *)dictionary {
+  self = [self initWithUInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64UInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64UInt64Dictionary class]]) {
+    return NO;
+  }
+  GPBUInt64UInt64Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(uint64_t key, uint64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedLongLongValue], [aValue unsignedLongLongValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint64_t unwrappedKey = [aKey unsignedLongLongValue];
+    uint64_t unwrappedValue = [aValue unsignedLongLongValue];
+    size_t msgSize = ComputeDictUInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt64FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictUInt64Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueUInt64) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndUInt64sUsingBlock:^(uint64_t key, uint64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%llu", value]);
+  }];
+}
+
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(uint64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedLongLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64UInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt64:(uint64_t)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt64ForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Int64
+
+@implementation GPBUInt64Int64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt64s:(const int64_t [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64Int64Dictionary *)dictionary {
+  self = [self initWithInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64Int64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64Int64Dictionary class]]) {
+    return NO;
+  }
+  GPBUInt64Int64Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(uint64_t key, int64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedLongLongValue], [aValue longLongValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint64_t unwrappedKey = [aKey unsignedLongLongValue];
+    int64_t unwrappedValue = [aValue longLongValue];
+    size_t msgSize = ComputeDictUInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt64FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictInt64Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueInt64) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndInt64sUsingBlock:^(uint64_t key, int64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%lld", value]);
+  }];
+}
+
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(uint64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped longLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64Int64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt64:(int64_t)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt64ForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Bool
+
+@implementation GPBUInt64BoolDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithBools:(const BOOL [])values
+                      forKeys:(const uint64_t [])keys
+                        count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64BoolDictionary *)dictionary {
+  self = [self initWithBools:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64BoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64BoolDictionary class]]) {
+    return NO;
+  }
+  GPBUInt64BoolDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(uint64_t key, BOOL value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedLongLongValue], [aValue boolValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint64_t unwrappedKey = [aKey unsignedLongLongValue];
+    BOOL unwrappedValue = [aValue boolValue];
+    size_t msgSize = ComputeDictUInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictBoolFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictBoolField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueBool) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndBoolsUsingBlock:^(uint64_t key, BOOL value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], (value ? @"true" : @"false"));
+  }];
+}
+
+- (BOOL)getBool:(nullable BOOL *)value forKey:(uint64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped boolValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64BoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setBool:(BOOL)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeBoolForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Float
+
+@implementation GPBUInt64FloatDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithFloats:(const float [])values
+                       forKeys:(const uint64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64FloatDictionary *)dictionary {
+  self = [self initWithFloats:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64FloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64FloatDictionary class]]) {
+    return NO;
+  }
+  GPBUInt64FloatDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(uint64_t key, float value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedLongLongValue], [aValue floatValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint64_t unwrappedKey = [aKey unsignedLongLongValue];
+    float unwrappedValue = [aValue floatValue];
+    size_t msgSize = ComputeDictUInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictFloatFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictFloatField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueFloat) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndFloatsUsingBlock:^(uint64_t key, float value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%.*g", FLT_DIG, value]);
+  }];
+}
+
+- (BOOL)getFloat:(nullable float *)value forKey:(uint64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped floatValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64FloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setFloat:(float)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeFloatForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Double
+
+@implementation GPBUInt64DoubleDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithDoubles:(const double [])values
+                        forKeys:(const uint64_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64DoubleDictionary *)dictionary {
+  self = [self initWithDoubles:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64DoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64DoubleDictionary class]]) {
+    return NO;
+  }
+  GPBUInt64DoubleDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(uint64_t key, double value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedLongLongValue], [aValue doubleValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint64_t unwrappedKey = [aKey unsignedLongLongValue];
+    double unwrappedValue = [aValue doubleValue];
+    size_t msgSize = ComputeDictUInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictDoubleFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictDoubleField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueDouble) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndDoublesUsingBlock:^(uint64_t key, double value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], [NSString stringWithFormat:@"%.*lg", DBL_DIG, value]);
+  }];
+}
+
+- (BOOL)getDouble:(nullable double *)value forKey:(uint64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped doubleValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64DoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setDouble:(double)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeDoubleForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - UInt64 -> Enum
+
+@implementation GPBUInt64EnumDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+  GPBEnumValidationFunc _validationFunc;
+}
+
+@synthesize validationFunc = _validationFunc;
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])rawValues
+                                   forKeys:(const uint64_t [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    if (count && rawValues && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(rawValues[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64EnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64EnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64EnumDictionary class]]) {
+    return NO;
+  }
+  GPBUInt64EnumDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(uint64_t key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey unsignedLongLongValue], [aValue intValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint64_t unwrappedKey = [aKey unsignedLongLongValue];
+    int32_t unwrappedValue = [aValue intValue];
+    size_t msgSize = ComputeDictUInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictEnumFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictEnumField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType {
+  size_t msgSize = ComputeDictUInt64FieldSize(key->valueUInt64, kMapKeyFieldNumber, keyDataType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBDataTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictUInt64Field(outputStream, key->valueUInt64, kMapKeyFieldNumber, keyDataType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBDataTypeEnum);
+  [outputStream release];
+  return data;
+}
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueEnum) forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndRawValuesUsingBlock:^(uint64_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], @(value));
+  }];
+}
+
+- (BOOL)getEnum:(int32_t *)value forKey:(uint64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    int32_t result = [wrapped intValue];
+    if (!_validationFunc(result)) {
+      result = kGPBUnrecognizedEnumeratorValue;
+    }
+    *value = result;
+  }
+  return (wrapped != NULL);
+}
+
+- (BOOL)getRawValue:(int32_t *)rawValue forKey:(uint64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && rawValue) {
+    *rawValue = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(uint64_t key, int32_t value, BOOL *stop))block {
+  GPBEnumValidationFunc func = _validationFunc;
+  BOOL stop = NO;
+  NSEnumerator *keys = [_dictionary keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = _dictionary[aKey];
+      int32_t unwrapped = [aValue intValue];
+      if (!func(unwrapped)) {
+        unwrapped = kGPBUnrecognizedEnumeratorValue;
+      }
+    block([aKey unsignedLongLongValue], unwrapped, &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (void)addRawEntriesFromDictionary:(GPBUInt64EnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setRawValue:(int32_t)value forKey:(uint64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeEnumForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+- (void)setEnum:(int32_t)value forKey:(uint64_t)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBUInt64EnumDictionary: Attempt to set an unknown enum value (%d)",
+                       value];
+  }
+
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+@end
+
+#pragma mark - UInt64 -> Object
+
+@implementation GPBUInt64ObjectDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithObjects:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithObjects:(const id [])objects
+                        forKeys:(const uint64_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && objects && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!objects[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil object to a Dictionary"];
+        }
+        [_dictionary setObject:objects[i] forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBUInt64ObjectDictionary *)dictionary {
+  self = [self initWithObjects:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithObjects:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBUInt64ObjectDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBUInt64ObjectDictionary class]]) {
+    return NO;
+  }
+  GPBUInt64ObjectDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndObjectsUsingBlock:
+    (void (^)(uint64_t key, id object, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    block([aKey unsignedLongLongValue], aObject, &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (BOOL)isInitialized {
+  for (GPBMessage *msg in [_dictionary objectEnumerator]) {
+    if (!msg.initialized) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+- (instancetype)deepCopyWithZone:(NSZone *)zone {
+  GPBUInt64ObjectDictionary *newDict =
+      [[GPBUInt64ObjectDictionary alloc] init];
+  NSEnumerator *keys = [_dictionary keyEnumerator];
+  id aKey;
+  NSMutableDictionary *internalDict = newDict->_dictionary;
+  while ((aKey = [keys nextObject])) {
+    GPBMessage *msg = _dictionary[aKey];
+    GPBMessage *copiedMsg = [msg copyWithZone:zone];
+    [internalDict setObject:copiedMsg forKey:aKey];
+    [copiedMsg release];
+  }
+  return newDict;
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    size_t msgSize = ComputeDictUInt64FieldSize([aKey unsignedLongLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictObjectFieldSize(aObject, kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    uint64_t unwrappedKey = [aKey unsignedLongLongValue];
+    id unwrappedValue = aObject;
+    size_t msgSize = ComputeDictUInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictObjectFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictUInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictObjectField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:value->valueString forKey:@(key->valueUInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndObjectsUsingBlock:^(uint64_t key, id object, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%llu", key], object);
+  }];
+}
+
+- (id)objectForKey:(uint64_t)key {
+  id result = [_dictionary objectForKey:@(key)];
+  return result;
+}
+
+- (void)addEntriesFromDictionary:(GPBUInt64ObjectDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setObject:(id)object forKey:(uint64_t)key {
+  if (!object) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil object to a Dictionary"];
+  }
+  [_dictionary setObject:object forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeObjectForKey:(uint64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_IMPL_FOR_POD_KEY(Int64, int64_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Int64 -> UInt32
+
+@implementation GPBInt64UInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt32s:(const uint32_t [])values
+                        forKeys:(const int64_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64UInt32Dictionary *)dictionary {
+  self = [self initWithUInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64UInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64UInt32Dictionary class]]) {
+    return NO;
+  }
+  GPBInt64UInt32Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(int64_t key, uint32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey longLongValue], [aValue unsignedIntValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int64_t unwrappedKey = [aKey longLongValue];
+    uint32_t unwrappedValue = [aValue unsignedIntValue];
+    size_t msgSize = ComputeDictInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt32FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictUInt32Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueUInt32) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndUInt32sUsingBlock:^(int64_t key, uint32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%u", value]);
+  }];
+}
+
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(int64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedIntValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64UInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt32:(uint32_t)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt32ForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Int32
+
+@implementation GPBInt64Int32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt32s:(const int32_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64Int32Dictionary *)dictionary {
+  self = [self initWithInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64Int32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64Int32Dictionary class]]) {
+    return NO;
+  }
+  GPBInt64Int32Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(int64_t key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey longLongValue], [aValue intValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int64_t unwrappedKey = [aKey longLongValue];
+    int32_t unwrappedValue = [aValue intValue];
+    size_t msgSize = ComputeDictInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt32FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictInt32Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueInt32) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndInt32sUsingBlock:^(int64_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%d", value]);
+  }];
+}
+
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(int64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64Int32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt32:(int32_t)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt32ForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> UInt64
+
+@implementation GPBInt64UInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt64s:(const uint64_t [])values
+                        forKeys:(const int64_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64UInt64Dictionary *)dictionary {
+  self = [self initWithUInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64UInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64UInt64Dictionary class]]) {
+    return NO;
+  }
+  GPBInt64UInt64Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(int64_t key, uint64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey longLongValue], [aValue unsignedLongLongValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int64_t unwrappedKey = [aKey longLongValue];
+    uint64_t unwrappedValue = [aValue unsignedLongLongValue];
+    size_t msgSize = ComputeDictInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt64FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictUInt64Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueUInt64) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndUInt64sUsingBlock:^(int64_t key, uint64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%llu", value]);
+  }];
+}
+
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(int64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped unsignedLongLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64UInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt64:(uint64_t)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt64ForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Int64
+
+@implementation GPBInt64Int64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt64s:(const int64_t [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64Int64Dictionary *)dictionary {
+  self = [self initWithInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64Int64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64Int64Dictionary class]]) {
+    return NO;
+  }
+  GPBInt64Int64Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(int64_t key, int64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey longLongValue], [aValue longLongValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int64_t unwrappedKey = [aKey longLongValue];
+    int64_t unwrappedValue = [aValue longLongValue];
+    size_t msgSize = ComputeDictInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt64FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictInt64Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueInt64) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndInt64sUsingBlock:^(int64_t key, int64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%lld", value]);
+  }];
+}
+
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(int64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped longLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64Int64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt64:(int64_t)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt64ForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Bool
+
+@implementation GPBInt64BoolDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithBools:(const BOOL [])values
+                      forKeys:(const int64_t [])keys
+                        count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64BoolDictionary *)dictionary {
+  self = [self initWithBools:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64BoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64BoolDictionary class]]) {
+    return NO;
+  }
+  GPBInt64BoolDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(int64_t key, BOOL value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey longLongValue], [aValue boolValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int64_t unwrappedKey = [aKey longLongValue];
+    BOOL unwrappedValue = [aValue boolValue];
+    size_t msgSize = ComputeDictInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictBoolFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictBoolField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueBool) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndBoolsUsingBlock:^(int64_t key, BOOL value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], (value ? @"true" : @"false"));
+  }];
+}
+
+- (BOOL)getBool:(nullable BOOL *)value forKey:(int64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped boolValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64BoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setBool:(BOOL)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeBoolForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Float
+
+@implementation GPBInt64FloatDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithFloats:(const float [])values
+                       forKeys:(const int64_t [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64FloatDictionary *)dictionary {
+  self = [self initWithFloats:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64FloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64FloatDictionary class]]) {
+    return NO;
+  }
+  GPBInt64FloatDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(int64_t key, float value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey longLongValue], [aValue floatValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int64_t unwrappedKey = [aKey longLongValue];
+    float unwrappedValue = [aValue floatValue];
+    size_t msgSize = ComputeDictInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictFloatFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictFloatField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueFloat) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndFloatsUsingBlock:^(int64_t key, float value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%.*g", FLT_DIG, value]);
+  }];
+}
+
+- (BOOL)getFloat:(nullable float *)value forKey:(int64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped floatValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64FloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setFloat:(float)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeFloatForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Double
+
+@implementation GPBInt64DoubleDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithDoubles:(const double [])values
+                        forKeys:(const int64_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(values[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64DoubleDictionary *)dictionary {
+  self = [self initWithDoubles:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64DoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64DoubleDictionary class]]) {
+    return NO;
+  }
+  GPBInt64DoubleDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(int64_t key, double value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey longLongValue], [aValue doubleValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int64_t unwrappedKey = [aKey longLongValue];
+    double unwrappedValue = [aValue doubleValue];
+    size_t msgSize = ComputeDictInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictDoubleFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictDoubleField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueDouble) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndDoublesUsingBlock:^(int64_t key, double value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], [NSString stringWithFormat:@"%.*lg", DBL_DIG, value]);
+  }];
+}
+
+- (BOOL)getDouble:(nullable double *)value forKey:(int64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    *value = [wrapped doubleValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64DoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setDouble:(double)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeDoubleForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - Int64 -> Enum
+
+@implementation GPBInt64EnumDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+  GPBEnumValidationFunc _validationFunc;
+}
+
+@synthesize validationFunc = _validationFunc;
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])rawValues
+                                   forKeys:(const int64_t [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    if (count && rawValues && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        [_dictionary setObject:@(rawValues[i]) forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64EnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64EnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64EnumDictionary class]]) {
+    return NO;
+  }
+  GPBInt64EnumDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(int64_t key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block([aKey longLongValue], [aValue intValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int64_t unwrappedKey = [aKey longLongValue];
+    int32_t unwrappedValue = [aValue intValue];
+    size_t msgSize = ComputeDictInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictEnumFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictEnumField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType {
+  size_t msgSize = ComputeDictInt64FieldSize(key->valueInt64, kMapKeyFieldNumber, keyDataType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBDataTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictInt64Field(outputStream, key->valueInt64, kMapKeyFieldNumber, keyDataType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBDataTypeEnum);
+  [outputStream release];
+  return data;
+}
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueEnum) forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndRawValuesUsingBlock:^(int64_t key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], @(value));
+  }];
+}
+
+- (BOOL)getEnum:(int32_t *)value forKey:(int64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && value) {
+    int32_t result = [wrapped intValue];
+    if (!_validationFunc(result)) {
+      result = kGPBUnrecognizedEnumeratorValue;
+    }
+    *value = result;
+  }
+  return (wrapped != NULL);
+}
+
+- (BOOL)getRawValue:(int32_t *)rawValue forKey:(int64_t)key {
+  NSNumber *wrapped = [_dictionary objectForKey:@(key)];
+  if (wrapped && rawValue) {
+    *rawValue = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(int64_t key, int32_t value, BOOL *stop))block {
+  GPBEnumValidationFunc func = _validationFunc;
+  BOOL stop = NO;
+  NSEnumerator *keys = [_dictionary keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = _dictionary[aKey];
+      int32_t unwrapped = [aValue intValue];
+      if (!func(unwrapped)) {
+        unwrapped = kGPBUnrecognizedEnumeratorValue;
+      }
+    block([aKey longLongValue], unwrapped, &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (void)addRawEntriesFromDictionary:(GPBInt64EnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setRawValue:(int32_t)value forKey:(int64_t)key {
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeEnumForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+- (void)setEnum:(int32_t)value forKey:(int64_t)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBInt64EnumDictionary: Attempt to set an unknown enum value (%d)",
+                       value];
+  }
+
+  [_dictionary setObject:@(value) forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+@end
+
+#pragma mark - Int64 -> Object
+
+@implementation GPBInt64ObjectDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithObjects:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithObjects:(const id [])objects
+                        forKeys:(const int64_t [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && objects && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!objects[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil object to a Dictionary"];
+        }
+        [_dictionary setObject:objects[i] forKey:@(keys[i])];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBInt64ObjectDictionary *)dictionary {
+  self = [self initWithObjects:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithObjects:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBInt64ObjectDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBInt64ObjectDictionary class]]) {
+    return NO;
+  }
+  GPBInt64ObjectDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndObjectsUsingBlock:
+    (void (^)(int64_t key, id object, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    block([aKey longLongValue], aObject, &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (BOOL)isInitialized {
+  for (GPBMessage *msg in [_dictionary objectEnumerator]) {
+    if (!msg.initialized) {
+      return NO;
+    }
+  }
+  return YES;
+}
+
+- (instancetype)deepCopyWithZone:(NSZone *)zone {
+  GPBInt64ObjectDictionary *newDict =
+      [[GPBInt64ObjectDictionary alloc] init];
+  NSEnumerator *keys = [_dictionary keyEnumerator];
+  id aKey;
+  NSMutableDictionary *internalDict = newDict->_dictionary;
+  while ((aKey = [keys nextObject])) {
+    GPBMessage *msg = _dictionary[aKey];
+    GPBMessage *copiedMsg = [msg copyWithZone:zone];
+    [internalDict setObject:copiedMsg forKey:aKey];
+    [copiedMsg release];
+  }
+  return newDict;
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    size_t msgSize = ComputeDictInt64FieldSize([aKey longLongValue], kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictObjectFieldSize(aObject, kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSNumber *aKey;
+  while ((aKey = [keys nextObject])) {
+    id aObject = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    int64_t unwrappedKey = [aKey longLongValue];
+    id unwrappedValue = aObject;
+    size_t msgSize = ComputeDictInt64FieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictObjectFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictInt64Field(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictObjectField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:value->valueString forKey:@(key->valueInt64)];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndObjectsUsingBlock:^(int64_t key, id object, BOOL *stop) {
+      #pragma unused(stop)
+      block([NSString stringWithFormat:@"%lld", key], object);
+  }];
+}
+
+- (id)objectForKey:(int64_t)key {
+  id result = [_dictionary objectForKey:@(key)];
+  return result;
+}
+
+- (void)addEntriesFromDictionary:(GPBInt64ObjectDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setObject:(id)object forKey:(int64_t)key {
+  if (!object) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil object to a Dictionary"];
+  }
+  [_dictionary setObject:object forKey:@(key)];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeObjectForKey:(int64_t)aKey {
+  [_dictionary removeObjectForKey:@(aKey)];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_POD_IMPL_FOR_KEY(String, NSString, *, OBJECT)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - String -> UInt32
+
+@implementation GPBStringUInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt32s:(const uint32_t [])values
+                        forKeys:(const NSString * [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!keys[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil key to a Dictionary"];
+        }
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringUInt32Dictionary *)dictionary {
+  self = [self initWithUInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringUInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringUInt32Dictionary class]]) {
+    return NO;
+  }
+  GPBStringUInt32Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(NSString *key, uint32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block(aKey, [aValue unsignedIntValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt32FieldSize([aValue unsignedIntValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    NSString *unwrappedKey = aKey;
+    uint32_t unwrappedValue = [aValue unsignedIntValue];
+    size_t msgSize = ComputeDictStringFieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt32FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictUInt32Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueUInt32) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndUInt32sUsingBlock:^(NSString *key, uint32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%u", value]);
+  }];
+}
+
+- (BOOL)getUInt32:(nullable uint32_t *)value forKey:(NSString *)key {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped unsignedIntValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringUInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt32:(uint32_t)value forKey:(NSString *)key {
+  if (!key) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil key to a Dictionary"];
+  }
+  [_dictionary setObject:@(value) forKey:key];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt32ForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Int32
+
+@implementation GPBStringInt32Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt32s:(const int32_t [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!keys[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil key to a Dictionary"];
+        }
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringInt32Dictionary *)dictionary {
+  self = [self initWithInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringInt32Dictionary class]]) {
+    return NO;
+  }
+  GPBStringInt32Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(NSString *key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block(aKey, [aValue intValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt32FieldSize([aValue intValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    NSString *unwrappedKey = aKey;
+    int32_t unwrappedValue = [aValue intValue];
+    size_t msgSize = ComputeDictStringFieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt32FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictInt32Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueInt32) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndInt32sUsingBlock:^(NSString *key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%d", value]);
+  }];
+}
+
+- (BOOL)getInt32:(nullable int32_t *)value forKey:(NSString *)key {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt32:(int32_t)value forKey:(NSString *)key {
+  if (!key) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil key to a Dictionary"];
+  }
+  [_dictionary setObject:@(value) forKey:key];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt32ForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> UInt64
+
+@implementation GPBStringUInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt64s:(const uint64_t [])values
+                        forKeys:(const NSString * [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!keys[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil key to a Dictionary"];
+        }
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringUInt64Dictionary *)dictionary {
+  self = [self initWithUInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringUInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringUInt64Dictionary class]]) {
+    return NO;
+  }
+  GPBStringUInt64Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(NSString *key, uint64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block(aKey, [aValue unsignedLongLongValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt64FieldSize([aValue unsignedLongLongValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    NSString *unwrappedKey = aKey;
+    uint64_t unwrappedValue = [aValue unsignedLongLongValue];
+    size_t msgSize = ComputeDictStringFieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictUInt64FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictUInt64Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueUInt64) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndUInt64sUsingBlock:^(NSString *key, uint64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%llu", value]);
+  }];
+}
+
+- (BOOL)getUInt64:(nullable uint64_t *)value forKey:(NSString *)key {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped unsignedLongLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringUInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt64:(uint64_t)value forKey:(NSString *)key {
+  if (!key) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil key to a Dictionary"];
+  }
+  [_dictionary setObject:@(value) forKey:key];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt64ForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Int64
+
+@implementation GPBStringInt64Dictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt64s:(const int64_t [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!keys[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil key to a Dictionary"];
+        }
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringInt64Dictionary *)dictionary {
+  self = [self initWithInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringInt64Dictionary class]]) {
+    return NO;
+  }
+  GPBStringInt64Dictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(NSString *key, int64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block(aKey, [aValue longLongValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt64FieldSize([aValue longLongValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    NSString *unwrappedKey = aKey;
+    int64_t unwrappedValue = [aValue longLongValue];
+    size_t msgSize = ComputeDictStringFieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictInt64FieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictInt64Field(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueInt64) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndInt64sUsingBlock:^(NSString *key, int64_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%lld", value]);
+  }];
+}
+
+- (BOOL)getInt64:(nullable int64_t *)value forKey:(NSString *)key {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped longLongValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt64:(int64_t)value forKey:(NSString *)key {
+  if (!key) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil key to a Dictionary"];
+  }
+  [_dictionary setObject:@(value) forKey:key];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt64ForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Bool
+
+@implementation GPBStringBoolDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithBools:(const BOOL [])values
+                      forKeys:(const NSString * [])keys
+                        count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!keys[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil key to a Dictionary"];
+        }
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringBoolDictionary *)dictionary {
+  self = [self initWithBools:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringBoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringBoolDictionary class]]) {
+    return NO;
+  }
+  GPBStringBoolDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(NSString *key, BOOL value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block(aKey, [aValue boolValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictBoolFieldSize([aValue boolValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    NSString *unwrappedKey = aKey;
+    BOOL unwrappedValue = [aValue boolValue];
+    size_t msgSize = ComputeDictStringFieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictBoolFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictBoolField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueBool) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndBoolsUsingBlock:^(NSString *key, BOOL value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, (value ? @"true" : @"false"));
+  }];
+}
+
+- (BOOL)getBool:(nullable BOOL *)value forKey:(NSString *)key {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped boolValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringBoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setBool:(BOOL)value forKey:(NSString *)key {
+  if (!key) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil key to a Dictionary"];
+  }
+  [_dictionary setObject:@(value) forKey:key];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeBoolForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Float
+
+@implementation GPBStringFloatDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithFloats:(const float [])values
+                       forKeys:(const NSString * [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!keys[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil key to a Dictionary"];
+        }
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringFloatDictionary *)dictionary {
+  self = [self initWithFloats:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringFloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringFloatDictionary class]]) {
+    return NO;
+  }
+  GPBStringFloatDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(NSString *key, float value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block(aKey, [aValue floatValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictFloatFieldSize([aValue floatValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    NSString *unwrappedKey = aKey;
+    float unwrappedValue = [aValue floatValue];
+    size_t msgSize = ComputeDictStringFieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictFloatFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictFloatField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueFloat) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndFloatsUsingBlock:^(NSString *key, float value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%.*g", FLT_DIG, value]);
+  }];
+}
+
+- (BOOL)getFloat:(nullable float *)value forKey:(NSString *)key {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped floatValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringFloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setFloat:(float)value forKey:(NSString *)key {
+  if (!key) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil key to a Dictionary"];
+  }
+  [_dictionary setObject:@(value) forKey:key];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeFloatForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Double
+
+@implementation GPBStringDoubleDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+}
+
+- (instancetype)init {
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithDoubles:(const double [])values
+                        forKeys:(const NSString * [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    if (count && values && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!keys[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil key to a Dictionary"];
+        }
+        [_dictionary setObject:@(values[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringDoubleDictionary *)dictionary {
+  self = [self initWithDoubles:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringDoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringDoubleDictionary class]]) {
+    return NO;
+  }
+  GPBStringDoubleDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(NSString *key, double value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block(aKey, [aValue doubleValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictDoubleFieldSize([aValue doubleValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    NSString *unwrappedKey = aKey;
+    double unwrappedValue = [aValue doubleValue];
+    size_t msgSize = ComputeDictStringFieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictDoubleFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictDoubleField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueDouble) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndDoublesUsingBlock:^(NSString *key, double value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, [NSString stringWithFormat:@"%.*lg", DBL_DIG, value]);
+  }];
+}
+
+- (BOOL)getDouble:(nullable double *)value forKey:(NSString *)key {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    *value = [wrapped doubleValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)addEntriesFromDictionary:(GPBStringDoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setDouble:(double)value forKey:(NSString *)key {
+  if (!key) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil key to a Dictionary"];
+  }
+  [_dictionary setObject:@(value) forKey:key];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeDoubleForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+@end
+
+#pragma mark - String -> Enum
+
+@implementation GPBStringEnumDictionary {
+ @package
+  NSMutableDictionary *_dictionary;
+  GPBEnumValidationFunc _validationFunc;
+}
+
+@synthesize validationFunc = _validationFunc;
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                 rawValues:(const int32_t [])rawValues
+                                   forKeys:(const NSString * [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    if (count && rawValues && keys) {
+      for (NSUInteger i = 0; i < count; ++i) {
+        if (!keys[i]) {
+          [NSException raise:NSInvalidArgumentException
+                      format:@"Attempting to add nil key to a Dictionary"];
+        }
+        [_dictionary setObject:@(rawValues[i]) forKey:keys[i]];
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBStringEnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      [_dictionary addEntriesFromDictionary:dictionary->_dictionary];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBStringEnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBStringEnumDictionary class]]) {
+    return NO;
+  }
+  GPBStringEnumDictionary *otherDictionary = other;
+  return [_dictionary isEqual:otherDictionary->_dictionary];
+}
+
+- (NSUInteger)hash {
+  return _dictionary.count;
+}
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<%@ %p> { %@ }", [self class], self, _dictionary];
+}
+
+- (NSUInteger)count {
+  return _dictionary.count;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(NSString *key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    block(aKey, [aValue intValue], &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  NSDictionary *internal = _dictionary;
+  NSUInteger count = internal.count;
+  if (count == 0) {
+    return 0;
+  }
+
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  size_t result = 0;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    size_t msgSize = ComputeDictStringFieldSize(aKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictEnumFieldSize([aValue intValue], kMapValueFieldNumber, valueDataType);
+    result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  GPBDataType keyDataType = field.mapKeyDataType;
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  NSDictionary *internal = _dictionary;
+  NSEnumerator *keys = [internal keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = internal[aKey];
+    [outputStream writeInt32NoTag:tag];
+    // Write the size of the message.
+    NSString *unwrappedKey = aKey;
+    int32_t unwrappedValue = [aValue intValue];
+    size_t msgSize = ComputeDictStringFieldSize(unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    msgSize += ComputeDictEnumFieldSize(unwrappedValue, kMapValueFieldNumber, valueDataType);
+    [outputStream writeInt32NoTag:(int32_t)msgSize];
+    // Write the fields.
+    WriteDictStringField(outputStream, unwrappedKey, kMapKeyFieldNumber, keyDataType);
+    WriteDictEnumField(outputStream, unwrappedValue, kMapValueFieldNumber, valueDataType);
+  }
+}
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType {
+  size_t msgSize = ComputeDictStringFieldSize(key->valueString, kMapKeyFieldNumber, keyDataType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBDataTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictStringField(outputStream, key->valueString, kMapKeyFieldNumber, keyDataType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBDataTypeEnum);
+  [outputStream release];
+  return data;
+}
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  [_dictionary setObject:@(value->valueEnum) forKey:key->valueString];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  [self enumerateKeysAndRawValuesUsingBlock:^(NSString *key, int32_t value, BOOL *stop) {
+      #pragma unused(stop)
+      block(key, @(value));
+  }];
+}
+
+- (BOOL)getEnum:(int32_t *)value forKey:(NSString *)key {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && value) {
+    int32_t result = [wrapped intValue];
+    if (!_validationFunc(result)) {
+      result = kGPBUnrecognizedEnumeratorValue;
+    }
+    *value = result;
+  }
+  return (wrapped != NULL);
+}
+
+- (BOOL)getRawValue:(int32_t *)rawValue forKey:(NSString *)key {
+  NSNumber *wrapped = [_dictionary objectForKey:key];
+  if (wrapped && rawValue) {
+    *rawValue = [wrapped intValue];
+  }
+  return (wrapped != NULL);
+}
+
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(NSString *key, int32_t value, BOOL *stop))block {
+  GPBEnumValidationFunc func = _validationFunc;
+  BOOL stop = NO;
+  NSEnumerator *keys = [_dictionary keyEnumerator];
+  NSString *aKey;
+  while ((aKey = [keys nextObject])) {
+    NSNumber *aValue = _dictionary[aKey];
+      int32_t unwrapped = [aValue intValue];
+      if (!func(unwrapped)) {
+        unwrapped = kGPBUnrecognizedEnumeratorValue;
+      }
+    block(aKey, unwrapped, &stop);
+    if (stop) {
+      break;
+    }
+  }
+}
+
+- (void)addRawEntriesFromDictionary:(GPBStringEnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    [_dictionary addEntriesFromDictionary:otherDictionary->_dictionary];
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setRawValue:(int32_t)value forKey:(NSString *)key {
+  if (!key) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil key to a Dictionary"];
+  }
+  [_dictionary setObject:@(value) forKey:key];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeEnumForKey:(NSString *)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+- (void)removeAll {
+  [_dictionary removeAllObjects];
+}
+
+- (void)setEnum:(int32_t)value forKey:(NSString *)key {
+  if (!key) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil key to a Dictionary"];
+  }
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBStringEnumDictionary: Attempt to set an unknown enum value (%d)",
+                       value];
+  }
+
+  [_dictionary setObject:@(value) forKey:key];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+@end
+
+//%PDDM-EXPAND-END (5 expansions)
+
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(UInt32, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> UInt32
+
+@implementation GPBBoolUInt32Dictionary {
+ @package
+  uint32_t _values[2];
+  BOOL _valueSet[2];
+}
+
+- (instancetype)init {
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt32s:(const uint32_t [])values
+                        forKeys:(const BOOL [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolUInt32Dictionary *)dictionary {
+  self = [self initWithUInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt32s:NULL forKeys:NULL count:0];
+}
+
+#if !defined(NS_BLOCK_ASSERTIONS)
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [super dealloc];
+}
+#endif  // !defined(NS_BLOCK_ASSERTIONS)
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolUInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolUInt32Dictionary class]]) {
+    return NO;
+  }
+  GPBBoolUInt32Dictionary *otherDictionary = other;
+  if ((_valueSet[0] != otherDictionary->_valueSet[0]) ||
+      (_valueSet[1] != otherDictionary->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != otherDictionary->_values[0])) ||
+      (_valueSet[1] && (_values[1] != otherDictionary->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %u", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %u", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)getUInt32:(uint32_t *)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueUInt32;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%u", _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%u", _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndUInt32sUsingBlock:
+    (void (^)(BOOL key, uint32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictUInt32FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictUInt32FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      WriteDictUInt32Field(outputStream, _values[i], kMapValueFieldNumber, valueDataType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolUInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt32:(uint32_t)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt32ForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(Int32, int32_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Int32
+
+@implementation GPBBoolInt32Dictionary {
+ @package
+  int32_t _values[2];
+  BOOL _valueSet[2];
+}
+
+- (instancetype)init {
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt32s:(const int32_t [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolInt32Dictionary *)dictionary {
+  self = [self initWithInt32s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt32s:NULL forKeys:NULL count:0];
+}
+
+#if !defined(NS_BLOCK_ASSERTIONS)
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [super dealloc];
+}
+#endif  // !defined(NS_BLOCK_ASSERTIONS)
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolInt32Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolInt32Dictionary class]]) {
+    return NO;
+  }
+  GPBBoolInt32Dictionary *otherDictionary = other;
+  if ((_valueSet[0] != otherDictionary->_valueSet[0]) ||
+      (_valueSet[1] != otherDictionary->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != otherDictionary->_values[0])) ||
+      (_valueSet[1] && (_values[1] != otherDictionary->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %d", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %d", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)getInt32:(int32_t *)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueInt32;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%d", _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%d", _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndInt32sUsingBlock:
+    (void (^)(BOOL key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictInt32FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictInt32FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      WriteDictInt32Field(outputStream, _values[i], kMapValueFieldNumber, valueDataType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolInt32Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt32:(int32_t)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt32ForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(UInt64, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> UInt64
+
+@implementation GPBBoolUInt64Dictionary {
+ @package
+  uint64_t _values[2];
+  BOOL _valueSet[2];
+}
+
+- (instancetype)init {
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithUInt64s:(const uint64_t [])values
+                        forKeys:(const BOOL [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolUInt64Dictionary *)dictionary {
+  self = [self initWithUInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithUInt64s:NULL forKeys:NULL count:0];
+}
+
+#if !defined(NS_BLOCK_ASSERTIONS)
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [super dealloc];
+}
+#endif  // !defined(NS_BLOCK_ASSERTIONS)
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolUInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolUInt64Dictionary class]]) {
+    return NO;
+  }
+  GPBBoolUInt64Dictionary *otherDictionary = other;
+  if ((_valueSet[0] != otherDictionary->_valueSet[0]) ||
+      (_valueSet[1] != otherDictionary->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != otherDictionary->_values[0])) ||
+      (_valueSet[1] && (_values[1] != otherDictionary->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %llu", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %llu", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)getUInt64:(uint64_t *)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueUInt64;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%llu", _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%llu", _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndUInt64sUsingBlock:
+    (void (^)(BOOL key, uint64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictUInt64FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictUInt64FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      WriteDictUInt64Field(outputStream, _values[i], kMapValueFieldNumber, valueDataType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolUInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setUInt64:(uint64_t)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeUInt64ForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(Int64, int64_t)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Int64
+
+@implementation GPBBoolInt64Dictionary {
+ @package
+  int64_t _values[2];
+  BOOL _valueSet[2];
+}
+
+- (instancetype)init {
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithInt64s:(const int64_t [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolInt64Dictionary *)dictionary {
+  self = [self initWithInt64s:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithInt64s:NULL forKeys:NULL count:0];
+}
+
+#if !defined(NS_BLOCK_ASSERTIONS)
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [super dealloc];
+}
+#endif  // !defined(NS_BLOCK_ASSERTIONS)
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolInt64Dictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolInt64Dictionary class]]) {
+    return NO;
+  }
+  GPBBoolInt64Dictionary *otherDictionary = other;
+  if ((_valueSet[0] != otherDictionary->_valueSet[0]) ||
+      (_valueSet[1] != otherDictionary->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != otherDictionary->_values[0])) ||
+      (_valueSet[1] && (_values[1] != otherDictionary->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %lld", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %lld", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)getInt64:(int64_t *)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueInt64;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%lld", _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%lld", _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndInt64sUsingBlock:
+    (void (^)(BOOL key, int64_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictInt64FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictInt64FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      WriteDictInt64Field(outputStream, _values[i], kMapValueFieldNumber, valueDataType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolInt64Dictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setInt64:(int64_t)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeInt64ForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(Bool, BOOL)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Bool
+
+@implementation GPBBoolBoolDictionary {
+ @package
+  BOOL _values[2];
+  BOOL _valueSet[2];
+}
+
+- (instancetype)init {
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithBools:(const BOOL [])values
+                      forKeys:(const BOOL [])keys
+                        count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolBoolDictionary *)dictionary {
+  self = [self initWithBools:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithBools:NULL forKeys:NULL count:0];
+}
+
+#if !defined(NS_BLOCK_ASSERTIONS)
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [super dealloc];
+}
+#endif  // !defined(NS_BLOCK_ASSERTIONS)
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolBoolDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolBoolDictionary class]]) {
+    return NO;
+  }
+  GPBBoolBoolDictionary *otherDictionary = other;
+  if ((_valueSet[0] != otherDictionary->_valueSet[0]) ||
+      (_valueSet[1] != otherDictionary->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != otherDictionary->_values[0])) ||
+      (_valueSet[1] && (_values[1] != otherDictionary->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %d", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %d", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)getBool:(BOOL *)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueBool;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", (_values[0] ? @"true" : @"false"));
+  }
+  if (_valueSet[1]) {
+    block(@"true", (_values[1] ? @"true" : @"false"));
+  }
+}
+
+- (void)enumerateKeysAndBoolsUsingBlock:
+    (void (^)(BOOL key, BOOL value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictBoolFieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictBoolFieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      WriteDictBoolField(outputStream, _values[i], kMapValueFieldNumber, valueDataType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolBoolDictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setBool:(BOOL)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeBoolForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(Float, float)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Float
+
+@implementation GPBBoolFloatDictionary {
+ @package
+  float _values[2];
+  BOOL _valueSet[2];
+}
+
+- (instancetype)init {
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithFloats:(const float [])values
+                       forKeys:(const BOOL [])keys
+                         count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolFloatDictionary *)dictionary {
+  self = [self initWithFloats:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithFloats:NULL forKeys:NULL count:0];
+}
+
+#if !defined(NS_BLOCK_ASSERTIONS)
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [super dealloc];
+}
+#endif  // !defined(NS_BLOCK_ASSERTIONS)
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolFloatDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolFloatDictionary class]]) {
+    return NO;
+  }
+  GPBBoolFloatDictionary *otherDictionary = other;
+  if ((_valueSet[0] != otherDictionary->_valueSet[0]) ||
+      (_valueSet[1] != otherDictionary->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != otherDictionary->_values[0])) ||
+      (_valueSet[1] && (_values[1] != otherDictionary->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %f", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %f", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)getFloat:(float *)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueFloat;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%.*g", FLT_DIG, _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%.*g", FLT_DIG, _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndFloatsUsingBlock:
+    (void (^)(BOOL key, float value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictFloatFieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictFloatFieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      WriteDictFloatField(outputStream, _values[i], kMapValueFieldNumber, valueDataType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolFloatDictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setFloat:(float)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeFloatForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_POD_IMPL(Double, double)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Double
+
+@implementation GPBBoolDoubleDictionary {
+ @package
+  double _values[2];
+  BOOL _valueSet[2];
+}
+
+- (instancetype)init {
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithDoubles:(const double [])values
+                        forKeys:(const BOOL [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = values[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolDoubleDictionary *)dictionary {
+  self = [self initWithDoubles:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithDoubles:NULL forKeys:NULL count:0];
+}
+
+#if !defined(NS_BLOCK_ASSERTIONS)
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [super dealloc];
+}
+#endif  // !defined(NS_BLOCK_ASSERTIONS)
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolDoubleDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolDoubleDictionary class]]) {
+    return NO;
+  }
+  GPBBoolDoubleDictionary *otherDictionary = other;
+  if ((_valueSet[0] != otherDictionary->_valueSet[0]) ||
+      (_valueSet[1] != otherDictionary->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != otherDictionary->_values[0])) ||
+      (_valueSet[1] && (_values[1] != otherDictionary->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %lf", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %lf", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)getDouble:(double *)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      *value = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueDouble;
+  _valueSet[idx] = YES;
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", [NSString stringWithFormat:@"%.*lg", DBL_DIG, _values[0]]);
+  }
+  if (_valueSet[1]) {
+    block(@"true", [NSString stringWithFormat:@"%.*lg", DBL_DIG, _values[1]]);
+  }
+}
+
+- (void)enumerateKeysAndDoublesUsingBlock:
+    (void (^)(BOOL key, double value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictDoubleFieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictDoubleFieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      WriteDictDoubleField(outputStream, _values[i], kMapValueFieldNumber, valueDataType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolDoubleDictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setDouble:(double)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeDoubleForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+//%PDDM-EXPAND DICTIONARY_BOOL_KEY_TO_OBJECT_IMPL(Object, id)
+// This block of code is generated, do not edit it directly.
+
+#pragma mark - Bool -> Object
+
+@implementation GPBBoolObjectDictionary {
+ @package
+  id _values[2];
+}
+
+- (instancetype)init {
+  return [self initWithObjects:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithObjects:(const id [])objects
+                        forKeys:(const BOOL [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    for (NSUInteger i = 0; i < count; ++i) {
+      if (!objects[i]) {
+        [NSException raise:NSInvalidArgumentException
+                    format:@"Attempting to add nil object to a Dictionary"];
+      }
+      int idx = keys[i] ? 1 : 0;
+      [_values[idx] release];
+      _values[idx] = (id)[objects[i] retain];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolObjectDictionary *)dictionary {
+  self = [self initWithObjects:NULL forKeys:NULL count:0];
+  if (self) {
+    if (dictionary) {
+      _values[0] = [dictionary->_values[0] retain];
+      _values[1] = [dictionary->_values[1] retain];
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithCapacity:(NSUInteger)numItems {
+  #pragma unused(numItems)
+  return [self initWithObjects:NULL forKeys:NULL count:0];
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_values[0] release];
+  [_values[1] release];
+  [super dealloc];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolObjectDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolObjectDictionary class]]) {
+    return NO;
+  }
+  GPBBoolObjectDictionary *otherDictionary = other;
+  if (((_values[0] != nil) != (otherDictionary->_values[0] != nil)) ||
+      ((_values[1] != nil) != (otherDictionary->_values[1] != nil))) {
+    return NO;
+  }
+  if (((_values[0] != nil) && (![_values[0] isEqual:otherDictionary->_values[0]])) ||
+      ((_values[1] != nil) && (![_values[1] isEqual:otherDictionary->_values[1]]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return ((_values[0] != nil) ? 1 : 0) + ((_values[1] != nil) ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if ((_values[0] != nil)) {
+    [result appendFormat:@"NO: %@", _values[0]];
+  }
+  if ((_values[1] != nil)) {
+    [result appendFormat:@"YES: %@", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return ((_values[0] != nil) ? 1 : 0) + ((_values[1] != nil) ? 1 : 0);
+}
+
+- (id)objectForKey:(BOOL)key {
+  return _values[key ? 1 : 0];
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  [_values[idx] release];
+  _values[idx] = [value->valueString retain];
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_values[0] != nil) {
+    block(@"false", _values[0]);
+  }
+  if ((_values[1] != nil)) {
+    block(@"true", _values[1]);
+  }
+}
+
+- (void)enumerateKeysAndObjectsUsingBlock:
+    (void (^)(BOOL key, id object, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_values[0] != nil) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && (_values[1] != nil)) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (BOOL)isInitialized {
+  if (_values[0] && ![_values[0] isInitialized]) {
+    return NO;
+  }
+  if (_values[1] && ![_values[1] isInitialized]) {
+    return NO;
+  }
+  return YES;
+}
+
+- (instancetype)deepCopyWithZone:(NSZone *)zone {
+  GPBBoolObjectDictionary *newDict =
+      [[GPBBoolObjectDictionary alloc] init];
+  for (int i = 0; i < 2; ++i) {
+    if (_values[i] != nil) {
+      newDict->_values[i] = [_values[i] copyWithZone:zone];
+    }
+  }
+  return newDict;
+}
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_values[i] != nil) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictObjectFieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_values[i] != nil) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictObjectFieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      WriteDictObjectField(outputStream, _values[i], kMapValueFieldNumber, valueDataType);
+    }
+  }
+}
+
+- (void)addEntriesFromDictionary:(GPBBoolObjectDictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_values[i] != nil) {
+        [_values[i] release];
+        _values[i] = [otherDictionary->_values[i] retain];
+      }
+    }
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setObject:(id)object forKey:(BOOL)key {
+  if (!object) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Attempting to add nil object to a Dictionary"];
+  }
+  int idx = (key ? 1 : 0);
+  [_values[idx] release];
+  _values[idx] = [object retain];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeObjectForKey:(BOOL)aKey {
+  int idx = (aKey ? 1 : 0);
+  [_values[idx] release];
+  _values[idx] = nil;
+}
+
+- (void)removeAll {
+  for (int i = 0; i < 2; ++i) {
+    [_values[i] release];
+    _values[i] = nil;
+  }
+}
+
+@end
+
+//%PDDM-EXPAND-END (8 expansions)
+
+#pragma mark - Bool -> Enum
+
+@implementation GPBBoolEnumDictionary {
+ @package
+  GPBEnumValidationFunc _validationFunc;
+  int32_t _values[2];
+  BOOL _valueSet[2];
+}
+
+@synthesize validationFunc = _validationFunc;
+
+- (instancetype)init {
+  return [self initWithValidationFunction:NULL rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func {
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                rawValues:(const int32_t [])rawValues
+                                   forKeys:(const BOOL [])keys
+                                     count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _validationFunc = (func != NULL ? func : DictDefault_IsValidValue);
+    for (NSUInteger i = 0; i < count; ++i) {
+      int idx = keys[i] ? 1 : 0;
+      _values[idx] = rawValues[i];
+      _valueSet[idx] = YES;
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithDictionary:(GPBBoolEnumDictionary *)dictionary {
+  self = [self initWithValidationFunction:dictionary.validationFunc
+                                rawValues:NULL
+                                  forKeys:NULL
+                                    count:0];
+  if (self) {
+    if (dictionary) {
+      for (int i = 0; i < 2; ++i) {
+        if (dictionary->_valueSet[i]) {
+          _values[i] = dictionary->_values[i];
+          _valueSet[i] = YES;
+        }
+      }
+    }
+  }
+  return self;
+}
+
+- (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func
+                                  capacity:(NSUInteger)numItems {
+#pragma unused(numItems)
+  return [self initWithValidationFunction:func rawValues:NULL forKeys:NULL count:0];
+}
+
+#if !defined(NS_BLOCK_ASSERTIONS)
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [super dealloc];
+}
+#endif  // !defined(NS_BLOCK_ASSERTIONS)
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  return [[GPBBoolEnumDictionary allocWithZone:zone] initWithDictionary:self];
+}
+
+- (BOOL)isEqual:(id)other {
+  if (self == other) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBBoolEnumDictionary class]]) {
+    return NO;
+  }
+  GPBBoolEnumDictionary *otherDictionary = other;
+  if ((_valueSet[0] != otherDictionary->_valueSet[0]) ||
+      (_valueSet[1] != otherDictionary->_valueSet[1])) {
+    return NO;
+  }
+  if ((_valueSet[0] && (_values[0] != otherDictionary->_values[0])) ||
+      (_valueSet[1] && (_values[1] != otherDictionary->_values[1]))) {
+    return NO;
+  }
+  return YES;
+}
+
+- (NSUInteger)hash {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (NSString *)description {
+  NSMutableString *result = [NSMutableString stringWithFormat:@"<%@ %p> {", [self class], self];
+  if (_valueSet[0]) {
+    [result appendFormat:@"NO: %d", _values[0]];
+  }
+  if (_valueSet[1]) {
+    [result appendFormat:@"YES: %d", _values[1]];
+  }
+  [result appendString:@" }"];
+  return result;
+}
+
+- (NSUInteger)count {
+  return (_valueSet[0] ? 1 : 0) + (_valueSet[1] ? 1 : 0);
+}
+
+- (BOOL)getEnum:(int32_t*)value forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (value) {
+      int32_t result = _values[idx];
+      if (!_validationFunc(result)) {
+        result = kGPBUnrecognizedEnumeratorValue;
+      }
+      *value = result;
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (BOOL)getRawValue:(int32_t*)rawValue forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  if (_valueSet[idx]) {
+    if (rawValue) {
+      *rawValue = _values[idx];
+    }
+    return YES;
+  }
+  return NO;
+}
+
+- (void)enumerateKeysAndRawValuesUsingBlock:
+    (void (^)(BOOL key, int32_t value, BOOL *stop))block {
+  BOOL stop = NO;
+  if (_valueSet[0]) {
+    block(NO, _values[0], &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    block(YES, _values[1], &stop);
+  }
+}
+
+- (void)enumerateKeysAndEnumsUsingBlock:
+    (void (^)(BOOL key, int32_t rawValue, BOOL *stop))block {
+  BOOL stop = NO;
+  GPBEnumValidationFunc func = _validationFunc;
+  int32_t validatedValue;
+  if (_valueSet[0]) {
+    validatedValue = _values[0];
+    if (!func(validatedValue)) {
+      validatedValue = kGPBUnrecognizedEnumeratorValue;
+    }
+    block(NO, validatedValue, &stop);
+  }
+  if (!stop && _valueSet[1]) {
+    validatedValue = _values[1];
+    if (!func(validatedValue)) {
+      validatedValue = kGPBUnrecognizedEnumeratorValue;
+    }
+    block(YES, validatedValue, &stop);
+  }
+}
+
+//%PDDM-EXPAND SERIAL_DATA_FOR_ENTRY_POD_Enum(Bool)
+// This block of code is generated, do not edit it directly.
+
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType {
+  size_t msgSize = ComputeDictBoolFieldSize(key->valueBool, kMapKeyFieldNumber, keyDataType);
+  msgSize += ComputeDictEnumFieldSize(value, kMapValueFieldNumber, GPBDataTypeEnum);
+  NSMutableData *data = [NSMutableData dataWithLength:msgSize];
+  GPBCodedOutputStream *outputStream = [[GPBCodedOutputStream alloc] initWithData:data];
+  WriteDictBoolField(outputStream, key->valueBool, kMapKeyFieldNumber, keyDataType);
+  WriteDictEnumField(outputStream, value, kMapValueFieldNumber, GPBDataTypeEnum);
+  [outputStream release];
+  return data;
+}
+
+//%PDDM-EXPAND-END SERIAL_DATA_FOR_ENTRY_POD_Enum(Bool)
+
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  NSUInteger count = 0;
+  size_t result = 0;
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      ++count;
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictInt32FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      result += GPBComputeRawVarint32SizeForInteger(msgSize) + msgSize;
+    }
+  }
+  size_t tagSize = GPBComputeWireFormatTagSize(GPBFieldNumber(field), GPBDataTypeMessage);
+  result += tagSize * count;
+  return result;
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field {
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  uint32_t tag = GPBWireFormatMakeTag(GPBFieldNumber(field), GPBWireFormatLengthDelimited);
+  for (int i = 0; i < 2; ++i) {
+    if (_valueSet[i]) {
+      // Write the tag.
+      [outputStream writeInt32NoTag:tag];
+      // Write the size of the message.
+      size_t msgSize = ComputeDictBoolFieldSize((i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      msgSize += ComputeDictInt32FieldSize(_values[i], kMapValueFieldNumber, valueDataType);
+      [outputStream writeInt32NoTag:(int32_t)msgSize];
+      // Write the fields.
+      WriteDictBoolField(outputStream, (i == 1), kMapKeyFieldNumber, GPBDataTypeBool);
+      WriteDictInt32Field(outputStream, _values[i], kMapValueFieldNumber, valueDataType);
+    }
+  }
+}
+
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block {
+  if (_valueSet[0]) {
+    block(@"false", @(_values[0]));
+  }
+  if (_valueSet[1]) {
+    block(@"true", @(_values[1]));
+  }
+}
+
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key {
+  int idx = (key->valueBool ? 1 : 0);
+  _values[idx] = value->valueInt32;
+  _valueSet[idx] = YES;
+}
+
+- (void)addRawEntriesFromDictionary:(GPBBoolEnumDictionary *)otherDictionary {
+  if (otherDictionary) {
+    for (int i = 0; i < 2; ++i) {
+      if (otherDictionary->_valueSet[i]) {
+        _valueSet[i] = YES;
+        _values[i] = otherDictionary->_values[i];
+      }
+    }
+    if (_autocreator) {
+      GPBAutocreatedDictionaryModified(_autocreator, self);
+    }
+  }
+}
+
+- (void)setEnum:(int32_t)value forKey:(BOOL)key {
+  if (!_validationFunc(value)) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"GPBBoolEnumDictionary: Attempt to set an unknown enum value (%d)",
+     value];
+  }
+  int idx = (key ? 1 : 0);
+  _values[idx] = value;
+  _valueSet[idx] = YES;
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)setRawValue:(int32_t)rawValue forKey:(BOOL)key {
+  int idx = (key ? 1 : 0);
+  _values[idx] = rawValue;
+  _valueSet[idx] = YES;
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeEnumForKey:(BOOL)aKey {
+  _valueSet[aKey ? 1 : 0] = NO;
+}
+
+- (void)removeAll {
+  _valueSet[0] = NO;
+  _valueSet[1] = NO;
+}
+
+@end
+
+#pragma mark - NSDictionary Subclass
+
+@implementation GPBAutocreatedDictionary {
+  NSMutableDictionary *_dictionary;
+}
+
+- (void)dealloc {
+  NSAssert(!_autocreator,
+           @"%@: Autocreator must be cleared before release, autocreator: %@",
+           [self class], _autocreator);
+  [_dictionary release];
+  [super dealloc];
+}
+
+#pragma mark Required NSDictionary overrides
+
+- (instancetype)initWithObjects:(const id [])objects
+                        forKeys:(const id<NSCopying> [])keys
+                          count:(NSUInteger)count {
+  self = [super init];
+  if (self) {
+    _dictionary = [[NSMutableDictionary alloc] initWithObjects:objects
+                                                       forKeys:keys
+                                                         count:count];
+  }
+  return self;
+}
+
+- (NSUInteger)count {
+  return [_dictionary count];
+}
+
+- (id)objectForKey:(id)aKey {
+  return [_dictionary objectForKey:aKey];
+}
+
+- (NSEnumerator *)keyEnumerator {
+  if (_dictionary == nil) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+  }
+  return [_dictionary keyEnumerator];
+}
+
+#pragma mark Required NSMutableDictionary overrides
+
+// Only need to call GPBAutocreatedDictionaryModified() when adding things
+// since we only autocreate empty dictionaries.
+
+- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey {
+  if (_dictionary == nil) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+  }
+  [_dictionary setObject:anObject forKey:aKey];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)removeObjectForKey:(id)aKey {
+  [_dictionary removeObjectForKey:aKey];
+}
+
+#pragma mark Extra things hooked
+
+- (id)copyWithZone:(NSZone *)zone {
+  if (_dictionary == nil) {
+    return [[NSMutableDictionary allocWithZone:zone] init];
+  }
+  return [_dictionary copyWithZone:zone];
+}
+
+- (id)mutableCopyWithZone:(NSZone *)zone {
+  if (_dictionary == nil) {
+    return [[NSMutableDictionary allocWithZone:zone] init];
+  }
+  return [_dictionary mutableCopyWithZone:zone];
+}
+
+// Not really needed, but subscripting is likely common enough it doesn't hurt
+// to ensure it goes directly to the real NSMutableDictionary.
+- (id)objectForKeyedSubscript:(id)key {
+  return [_dictionary objectForKeyedSubscript:key];
+}
+
+// Not really needed, but subscripting is likely common enough it doesn't hurt
+// to ensure it goes directly to the real NSMutableDictionary.
+- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
+  if (_dictionary == nil) {
+    _dictionary = [[NSMutableDictionary alloc] init];
+  }
+  [_dictionary setObject:obj forKeyedSubscript:key];
+  if (_autocreator) {
+    GPBAutocreatedDictionaryModified(_autocreator, self);
+  }
+}
+
+- (void)enumerateKeysAndObjectsUsingBlock:(void (NS_NOESCAPE ^)(id key,
+                                                    id obj,
+                                                    BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsUsingBlock:block];
+}
+
+- (void)enumerateKeysAndObjectsWithOptions:(NSEnumerationOptions)opts
+                                usingBlock:(void (NS_NOESCAPE ^)(id key,
+                                                     id obj,
+                                                     BOOL *stop))block {
+  [_dictionary enumerateKeysAndObjectsWithOptions:opts usingBlock:block];
+}
+
+@end
+
+#pragma clang diagnostic pop
diff --git a/iOS/Pods/Protobuf/objectivec/GPBDictionary_PackagePrivate.h b/iOS/Pods/Protobuf/objectivec/GPBDictionary_PackagePrivate.h
new file mode 100644 (file)
index 0000000..7b921e8
--- /dev/null
@@ -0,0 +1,488 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBDictionary.h"
+
+@class GPBCodedInputStream;
+@class GPBCodedOutputStream;
+@class GPBExtensionRegistry;
+@class GPBFieldDescriptor;
+
+@protocol GPBDictionaryInternalsProtocol
+- (size_t)computeSerializedSizeAsField:(GPBFieldDescriptor *)field;
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)outputStream
+                         asField:(GPBFieldDescriptor *)field;
+- (void)setGPBGenericValue:(GPBGenericValue *)value
+     forGPBGenericValueKey:(GPBGenericValue *)key;
+- (void)enumerateForTextFormat:(void (^)(id keyObj, id valueObj))block;
+@end
+
+//%PDDM-DEFINE DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(KEY_NAME)
+//%DICTIONARY_POD_PRIV_INTERFACES_FOR_KEY(KEY_NAME)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Object, Object)
+//%PDDM-DEFINE DICTIONARY_POD_PRIV_INTERFACES_FOR_KEY(KEY_NAME)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, UInt32, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Int32, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, UInt64, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Int64, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Bool, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Float, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Double, Basic)
+//%DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, Enum, Enum)
+
+//%PDDM-DEFINE DICTIONARY_PRIVATE_INTERFACES(KEY_NAME, VALUE_NAME, HELPER)
+//%@interface GPB##KEY_NAME##VALUE_NAME##Dictionary () <GPBDictionaryInternalsProtocol> {
+//% @package
+//%  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+//%}
+//%EXTRA_DICTIONARY_PRIVATE_INTERFACES_##HELPER()@end
+//%
+
+//%PDDM-DEFINE EXTRA_DICTIONARY_PRIVATE_INTERFACES_Basic()
+// Empty
+//%PDDM-DEFINE EXTRA_DICTIONARY_PRIVATE_INTERFACES_Object()
+//%- (BOOL)isInitialized;
+//%- (instancetype)deepCopyWithZone:(NSZone *)zone
+//%    __attribute__((ns_returns_retained));
+//%
+//%PDDM-DEFINE EXTRA_DICTIONARY_PRIVATE_INTERFACES_Enum()
+//%- (NSData *)serializedDataForUnknownValue:(int32_t)value
+//%                                   forKey:(GPBGenericValue *)key
+//%                              keyDataType:(GPBDataType)keyDataType;
+//%
+
+//%PDDM-EXPAND DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(UInt32)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBUInt32UInt32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt32Int32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt32UInt64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt32Int64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt32BoolDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt32FloatDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt32DoubleDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt32EnumDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType;
+@end
+
+@interface GPBUInt32ObjectDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (BOOL)isInitialized;
+- (instancetype)deepCopyWithZone:(NSZone *)zone
+    __attribute__((ns_returns_retained));
+@end
+
+//%PDDM-EXPAND DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(Int32)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBInt32UInt32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt32Int32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt32UInt64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt32Int64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt32BoolDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt32FloatDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt32DoubleDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt32EnumDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType;
+@end
+
+@interface GPBInt32ObjectDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (BOOL)isInitialized;
+- (instancetype)deepCopyWithZone:(NSZone *)zone
+    __attribute__((ns_returns_retained));
+@end
+
+//%PDDM-EXPAND DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(UInt64)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBUInt64UInt32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt64Int32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt64UInt64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt64Int64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt64BoolDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt64FloatDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt64DoubleDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBUInt64EnumDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType;
+@end
+
+@interface GPBUInt64ObjectDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (BOOL)isInitialized;
+- (instancetype)deepCopyWithZone:(NSZone *)zone
+    __attribute__((ns_returns_retained));
+@end
+
+//%PDDM-EXPAND DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(Int64)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBInt64UInt32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt64Int32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt64UInt64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt64Int64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt64BoolDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt64FloatDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt64DoubleDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBInt64EnumDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType;
+@end
+
+@interface GPBInt64ObjectDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (BOOL)isInitialized;
+- (instancetype)deepCopyWithZone:(NSZone *)zone
+    __attribute__((ns_returns_retained));
+@end
+
+//%PDDM-EXPAND DICTIONARY_PRIV_INTERFACES_FOR_POD_KEY(Bool)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBBoolUInt32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBBoolInt32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBBoolUInt64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBBoolInt64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBBoolBoolDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBBoolFloatDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBBoolDoubleDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBBoolEnumDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType;
+@end
+
+@interface GPBBoolObjectDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (BOOL)isInitialized;
+- (instancetype)deepCopyWithZone:(NSZone *)zone
+    __attribute__((ns_returns_retained));
+@end
+
+//%PDDM-EXPAND DICTIONARY_POD_PRIV_INTERFACES_FOR_KEY(String)
+// This block of code is generated, do not edit it directly.
+
+@interface GPBStringUInt32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBStringInt32Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBStringUInt64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBStringInt64Dictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBStringBoolDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBStringFloatDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBStringDoubleDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+@interface GPBStringEnumDictionary () <GPBDictionaryInternalsProtocol> {
+ @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+- (NSData *)serializedDataForUnknownValue:(int32_t)value
+                                   forKey:(GPBGenericValue *)key
+                              keyDataType:(GPBDataType)keyDataType;
+@end
+
+//%PDDM-EXPAND-END (6 expansions)
+
+#pragma mark - NSDictionary Subclass
+
+@interface GPBAutocreatedDictionary : NSMutableDictionary {
+  @package
+  GPB_UNSAFE_UNRETAINED GPBMessage *_autocreator;
+}
+@end
+
+#pragma mark - Helpers
+
+CF_EXTERN_C_BEGIN
+
+// Helper to compute size when an NSDictionary is used for the map instead
+// of a custom type.
+size_t GPBDictionaryComputeSizeInternalHelper(NSDictionary *dict,
+                                              GPBFieldDescriptor *field);
+
+// Helper to write out when an NSDictionary is used for the map instead
+// of a custom type.
+void GPBDictionaryWriteToStreamInternalHelper(
+    GPBCodedOutputStream *outputStream, NSDictionary *dict,
+    GPBFieldDescriptor *field);
+
+// Helper to check message initialization when an NSDictionary is used for
+// the map instead of a custom type.
+BOOL GPBDictionaryIsInitializedInternalHelper(NSDictionary *dict,
+                                              GPBFieldDescriptor *field);
+
+// Helper to read a map instead.
+void GPBDictionaryReadEntry(id mapDictionary, GPBCodedInputStream *stream,
+                            GPBExtensionRegistry *registry,
+                            GPBFieldDescriptor *field,
+                            GPBMessage *parentMessage);
+
+CF_EXTERN_C_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBExtensionInternals.h b/iOS/Pods/Protobuf/objectivec/GPBExtensionInternals.h
new file mode 100644 (file)
index 0000000..2b980ae
--- /dev/null
@@ -0,0 +1,50 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBDescriptor.h"
+
+@class GPBCodedInputStream;
+@class GPBCodedOutputStream;
+@class GPBExtensionRegistry;
+
+void GPBExtensionMergeFromInputStream(GPBExtensionDescriptor *extension,
+                                      BOOL isPackedOnStream,
+                                      GPBCodedInputStream *input,
+                                      GPBExtensionRegistry *extensionRegistry,
+                                      GPBMessage *message);
+
+size_t GPBComputeExtensionSerializedSizeIncludingTag(
+    GPBExtensionDescriptor *extension, id value);
+
+void GPBWriteExtensionValueToOutputStream(GPBExtensionDescriptor *extension,
+                                          id value,
+                                          GPBCodedOutputStream *output);
diff --git a/iOS/Pods/Protobuf/objectivec/GPBExtensionInternals.m b/iOS/Pods/Protobuf/objectivec/GPBExtensionInternals.m
new file mode 100644 (file)
index 0000000..290c82a
--- /dev/null
@@ -0,0 +1,391 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBExtensionInternals.h"
+
+#import <objc/runtime.h>
+
+#import "GPBCodedInputStream_PackagePrivate.h"
+#import "GPBCodedOutputStream_PackagePrivate.h"
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+
+static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
+                                        GPBCodedInputStream *input,
+                                        GPBExtensionRegistry *extensionRegistry,
+                                        GPBMessage *existingValue)
+    __attribute__((ns_returns_retained));
+
+GPB_INLINE size_t DataTypeSize(GPBDataType dataType) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wswitch-enum"
+  switch (dataType) {
+    case GPBDataTypeBool:
+      return 1;
+    case GPBDataTypeFixed32:
+    case GPBDataTypeSFixed32:
+    case GPBDataTypeFloat:
+      return 4;
+    case GPBDataTypeFixed64:
+    case GPBDataTypeSFixed64:
+    case GPBDataTypeDouble:
+      return 8;
+    default:
+      return 0;
+  }
+#pragma clang diagnostic pop
+}
+
+static size_t ComputePBSerializedSizeNoTagOfObject(GPBDataType dataType, id object) {
+#define FIELD_CASE(TYPE, ACCESSOR)                                     \
+  case GPBDataType##TYPE:                                              \
+    return GPBCompute##TYPE##SizeNoTag([(NSNumber *)object ACCESSOR]);
+#define FIELD_CASE2(TYPE)                                              \
+  case GPBDataType##TYPE:                                              \
+    return GPBCompute##TYPE##SizeNoTag(object);
+  switch (dataType) {
+    FIELD_CASE(Bool, boolValue)
+    FIELD_CASE(Float, floatValue)
+    FIELD_CASE(Double, doubleValue)
+    FIELD_CASE(Int32, intValue)
+    FIELD_CASE(SFixed32, intValue)
+    FIELD_CASE(SInt32, intValue)
+    FIELD_CASE(Enum, intValue)
+    FIELD_CASE(Int64, longLongValue)
+    FIELD_CASE(SInt64, longLongValue)
+    FIELD_CASE(SFixed64, longLongValue)
+    FIELD_CASE(UInt32, unsignedIntValue)
+    FIELD_CASE(Fixed32, unsignedIntValue)
+    FIELD_CASE(UInt64, unsignedLongLongValue)
+    FIELD_CASE(Fixed64, unsignedLongLongValue)
+    FIELD_CASE2(Bytes)
+    FIELD_CASE2(String)
+    FIELD_CASE2(Message)
+    FIELD_CASE2(Group)
+  }
+#undef FIELD_CASE
+#undef FIELD_CASE2
+}
+
+static size_t ComputeSerializedSizeIncludingTagOfObject(
+    GPBExtensionDescription *description, id object) {
+#define FIELD_CASE(TYPE, ACCESSOR)                                   \
+  case GPBDataType##TYPE:                                            \
+    return GPBCompute##TYPE##Size(description->fieldNumber,          \
+                                  [(NSNumber *)object ACCESSOR]);
+#define FIELD_CASE2(TYPE)                                            \
+  case GPBDataType##TYPE:                                            \
+    return GPBCompute##TYPE##Size(description->fieldNumber, object);
+  switch (description->dataType) {
+    FIELD_CASE(Bool, boolValue)
+    FIELD_CASE(Float, floatValue)
+    FIELD_CASE(Double, doubleValue)
+    FIELD_CASE(Int32, intValue)
+    FIELD_CASE(SFixed32, intValue)
+    FIELD_CASE(SInt32, intValue)
+    FIELD_CASE(Enum, intValue)
+    FIELD_CASE(Int64, longLongValue)
+    FIELD_CASE(SInt64, longLongValue)
+    FIELD_CASE(SFixed64, longLongValue)
+    FIELD_CASE(UInt32, unsignedIntValue)
+    FIELD_CASE(Fixed32, unsignedIntValue)
+    FIELD_CASE(UInt64, unsignedLongLongValue)
+    FIELD_CASE(Fixed64, unsignedLongLongValue)
+    FIELD_CASE2(Bytes)
+    FIELD_CASE2(String)
+    FIELD_CASE2(Group)
+    case GPBDataTypeMessage:
+      if (GPBExtensionIsWireFormat(description)) {
+        return GPBComputeMessageSetExtensionSize(description->fieldNumber,
+                                                 object);
+      } else {
+        return GPBComputeMessageSize(description->fieldNumber, object);
+      }
+  }
+#undef FIELD_CASE
+#undef FIELD_CASE2
+}
+
+static size_t ComputeSerializedSizeIncludingTagOfArray(
+    GPBExtensionDescription *description, NSArray *values) {
+  if (GPBExtensionIsPacked(description)) {
+    size_t size = 0;
+    size_t typeSize = DataTypeSize(description->dataType);
+    if (typeSize != 0) {
+      size = values.count * typeSize;
+    } else {
+      for (id value in values) {
+        size +=
+            ComputePBSerializedSizeNoTagOfObject(description->dataType, value);
+      }
+    }
+    return size + GPBComputeTagSize(description->fieldNumber) +
+           GPBComputeRawVarint32SizeForInteger(size);
+  } else {
+    size_t size = 0;
+    for (id value in values) {
+      size += ComputeSerializedSizeIncludingTagOfObject(description, value);
+    }
+    return size;
+  }
+}
+
+static void WriteObjectIncludingTagToCodedOutputStream(
+    id object, GPBExtensionDescription *description,
+    GPBCodedOutputStream *output) {
+#define FIELD_CASE(TYPE, ACCESSOR)                      \
+  case GPBDataType##TYPE:                               \
+    [output write##TYPE:description->fieldNumber        \
+                  value:[(NSNumber *)object ACCESSOR]]; \
+    return;
+#define FIELD_CASE2(TYPE)                                       \
+  case GPBDataType##TYPE:                                       \
+    [output write##TYPE:description->fieldNumber value:object]; \
+    return;
+  switch (description->dataType) {
+    FIELD_CASE(Bool, boolValue)
+    FIELD_CASE(Float, floatValue)
+    FIELD_CASE(Double, doubleValue)
+    FIELD_CASE(Int32, intValue)
+    FIELD_CASE(SFixed32, intValue)
+    FIELD_CASE(SInt32, intValue)
+    FIELD_CASE(Enum, intValue)
+    FIELD_CASE(Int64, longLongValue)
+    FIELD_CASE(SInt64, longLongValue)
+    FIELD_CASE(SFixed64, longLongValue)
+    FIELD_CASE(UInt32, unsignedIntValue)
+    FIELD_CASE(Fixed32, unsignedIntValue)
+    FIELD_CASE(UInt64, unsignedLongLongValue)
+    FIELD_CASE(Fixed64, unsignedLongLongValue)
+    FIELD_CASE2(Bytes)
+    FIELD_CASE2(String)
+    FIELD_CASE2(Group)
+    case GPBDataTypeMessage:
+      if (GPBExtensionIsWireFormat(description)) {
+        [output writeMessageSetExtension:description->fieldNumber value:object];
+      } else {
+        [output writeMessage:description->fieldNumber value:object];
+      }
+      return;
+  }
+#undef FIELD_CASE
+#undef FIELD_CASE2
+}
+
+static void WriteObjectNoTagToCodedOutputStream(
+    id object, GPBExtensionDescription *description,
+    GPBCodedOutputStream *output) {
+#define FIELD_CASE(TYPE, ACCESSOR)                             \
+  case GPBDataType##TYPE:                                      \
+    [output write##TYPE##NoTag:[(NSNumber *)object ACCESSOR]]; \
+    return;
+#define FIELD_CASE2(TYPE)               \
+  case GPBDataType##TYPE:               \
+    [output write##TYPE##NoTag:object]; \
+    return;
+  switch (description->dataType) {
+    FIELD_CASE(Bool, boolValue)
+    FIELD_CASE(Float, floatValue)
+    FIELD_CASE(Double, doubleValue)
+    FIELD_CASE(Int32, intValue)
+    FIELD_CASE(SFixed32, intValue)
+    FIELD_CASE(SInt32, intValue)
+    FIELD_CASE(Enum, intValue)
+    FIELD_CASE(Int64, longLongValue)
+    FIELD_CASE(SInt64, longLongValue)
+    FIELD_CASE(SFixed64, longLongValue)
+    FIELD_CASE(UInt32, unsignedIntValue)
+    FIELD_CASE(Fixed32, unsignedIntValue)
+    FIELD_CASE(UInt64, unsignedLongLongValue)
+    FIELD_CASE(Fixed64, unsignedLongLongValue)
+    FIELD_CASE2(Bytes)
+    FIELD_CASE2(String)
+    FIELD_CASE2(Message)
+    case GPBDataTypeGroup:
+      [output writeGroupNoTag:description->fieldNumber value:object];
+      return;
+  }
+#undef FIELD_CASE
+#undef FIELD_CASE2
+}
+
+static void WriteArrayIncludingTagsToCodedOutputStream(
+    NSArray *values, GPBExtensionDescription *description,
+    GPBCodedOutputStream *output) {
+  if (GPBExtensionIsPacked(description)) {
+    [output writeTag:description->fieldNumber
+              format:GPBWireFormatLengthDelimited];
+    size_t dataSize = 0;
+    size_t typeSize = DataTypeSize(description->dataType);
+    if (typeSize != 0) {
+      dataSize = values.count * typeSize;
+    } else {
+      for (id value in values) {
+        dataSize +=
+            ComputePBSerializedSizeNoTagOfObject(description->dataType, value);
+      }
+    }
+    [output writeRawVarintSizeTAs32:dataSize];
+    for (id value in values) {
+      WriteObjectNoTagToCodedOutputStream(value, description, output);
+    }
+  } else {
+    for (id value in values) {
+      WriteObjectIncludingTagToCodedOutputStream(value, description, output);
+    }
+  }
+}
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+void GPBExtensionMergeFromInputStream(GPBExtensionDescriptor *extension,
+                                      BOOL isPackedOnStream,
+                                      GPBCodedInputStream *input,
+                                      GPBExtensionRegistry *extensionRegistry,
+                                      GPBMessage *message) {
+  GPBExtensionDescription *description = extension->description_;
+  GPBCodedInputStreamState *state = &input->state_;
+  if (isPackedOnStream) {
+    NSCAssert(GPBExtensionIsRepeated(description),
+              @"How was it packed if it isn't repeated?");
+    int32_t length = GPBCodedInputStreamReadInt32(state);
+    size_t limit = GPBCodedInputStreamPushLimit(state, length);
+    while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+      id value = NewSingleValueFromInputStream(extension,
+                                               input,
+                                               extensionRegistry,
+                                               nil);
+      [message addExtension:extension value:value];
+      [value release];
+    }
+    GPBCodedInputStreamPopLimit(state, limit);
+  } else {
+    id existingValue = nil;
+    BOOL isRepeated = GPBExtensionIsRepeated(description);
+    if (!isRepeated && GPBDataTypeIsMessage(description->dataType)) {
+      existingValue = [message getExistingExtension:extension];
+    }
+    id value = NewSingleValueFromInputStream(extension,
+                                             input,
+                                             extensionRegistry,
+                                             existingValue);
+    if (isRepeated) {
+      [message addExtension:extension value:value];
+    } else {
+      [message setExtension:extension value:value];
+    }
+    [value release];
+  }
+}
+
+void GPBWriteExtensionValueToOutputStream(GPBExtensionDescriptor *extension,
+                                          id value,
+                                          GPBCodedOutputStream *output) {
+  GPBExtensionDescription *description = extension->description_;
+  if (GPBExtensionIsRepeated(description)) {
+    WriteArrayIncludingTagsToCodedOutputStream(value, description, output);
+  } else {
+    WriteObjectIncludingTagToCodedOutputStream(value, description, output);
+  }
+}
+
+size_t GPBComputeExtensionSerializedSizeIncludingTag(
+    GPBExtensionDescriptor *extension, id value) {
+  GPBExtensionDescription *description = extension->description_;
+  if (GPBExtensionIsRepeated(description)) {
+    return ComputeSerializedSizeIncludingTagOfArray(description, value);
+  } else {
+    return ComputeSerializedSizeIncludingTagOfObject(description, value);
+  }
+}
+
+// Note that this returns a retained value intentionally.
+static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
+                                        GPBCodedInputStream *input,
+                                        GPBExtensionRegistry *extensionRegistry,
+                                        GPBMessage *existingValue) {
+  GPBExtensionDescription *description = extension->description_;
+  GPBCodedInputStreamState *state = &input->state_;
+  switch (description->dataType) {
+    case GPBDataTypeBool:     return [[NSNumber alloc] initWithBool:GPBCodedInputStreamReadBool(state)];
+    case GPBDataTypeFixed32:  return [[NSNumber alloc] initWithUnsignedInt:GPBCodedInputStreamReadFixed32(state)];
+    case GPBDataTypeSFixed32: return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadSFixed32(state)];
+    case GPBDataTypeFloat:    return [[NSNumber alloc] initWithFloat:GPBCodedInputStreamReadFloat(state)];
+    case GPBDataTypeFixed64:  return [[NSNumber alloc] initWithUnsignedLongLong:GPBCodedInputStreamReadFixed64(state)];
+    case GPBDataTypeSFixed64: return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadSFixed64(state)];
+    case GPBDataTypeDouble:   return [[NSNumber alloc] initWithDouble:GPBCodedInputStreamReadDouble(state)];
+    case GPBDataTypeInt32:    return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadInt32(state)];
+    case GPBDataTypeInt64:    return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadInt64(state)];
+    case GPBDataTypeSInt32:   return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadSInt32(state)];
+    case GPBDataTypeSInt64:   return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadSInt64(state)];
+    case GPBDataTypeUInt32:   return [[NSNumber alloc] initWithUnsignedInt:GPBCodedInputStreamReadUInt32(state)];
+    case GPBDataTypeUInt64:   return [[NSNumber alloc] initWithUnsignedLongLong:GPBCodedInputStreamReadUInt64(state)];
+    case GPBDataTypeBytes:    return GPBCodedInputStreamReadRetainedBytes(state);
+    case GPBDataTypeString:   return GPBCodedInputStreamReadRetainedString(state);
+    case GPBDataTypeEnum:     return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadEnum(state)];
+    case GPBDataTypeGroup:
+    case GPBDataTypeMessage: {
+      GPBMessage *message;
+      if (existingValue) {
+        message = [existingValue retain];
+      } else {
+        GPBDescriptor *decriptor = [extension.msgClass descriptor];
+        message = [[decriptor.messageClass alloc] init];
+      }
+
+      if (description->dataType == GPBDataTypeGroup) {
+        [input readGroup:description->fieldNumber
+                 message:message
+            extensionRegistry:extensionRegistry];
+      } else {
+        // description->dataType == GPBDataTypeMessage
+        if (GPBExtensionIsWireFormat(description)) {
+          // For MessageSet fields the message length will have already been
+          // read.
+          [message mergeFromCodedInputStream:input
+                           extensionRegistry:extensionRegistry];
+        } else {
+          [input readMessage:message extensionRegistry:extensionRegistry];
+        }
+      }
+
+      return message;
+    }
+  }
+
+  return nil;
+}
+
+#pragma clang diagnostic pop
diff --git a/iOS/Pods/Protobuf/objectivec/GPBExtensionRegistry.h b/iOS/Pods/Protobuf/objectivec/GPBExtensionRegistry.h
new file mode 100644 (file)
index 0000000..d79632d
--- /dev/null
@@ -0,0 +1,87 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+@class GPBDescriptor;
+@class GPBExtensionDescriptor;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * A table of known extensions, searchable by name or field number.  When
+ * parsing a protocol message that might have extensions, you must provide a
+ * GPBExtensionRegistry in which you have registered any extensions that you
+ * want to be able to parse. Otherwise, those extensions will just be treated
+ * like unknown fields.
+ *
+ * The *Root classes provide `+extensionRegistry` for the extensions defined
+ * in a given file *and* all files it imports. You can also create a
+ * GPBExtensionRegistry, and merge those registries to handle parsing
+ * extensions defined from non overlapping files.
+ *
+ * ```
+ * GPBExtensionRegistry *registry = [[MyProtoFileRoot extensionRegistry] copy];
+ * [registry addExtension:[OtherMessage neededExtension]]; // Not in MyProtoFile
+ * NSError *parseError;
+ * MyMessage *msg = [MyMessage parseData:data extensionRegistry:registry error:&parseError];
+ * ```
+ **/
+@interface GPBExtensionRegistry : NSObject<NSCopying>
+
+/**
+ * Adds the given GPBExtensionDescriptor to this registry.
+ *
+ * @param extension The extension description to add.
+ **/
+- (void)addExtension:(GPBExtensionDescriptor *)extension;
+
+/**
+ * Adds all the extensions from another registry to this registry.
+ *
+ * @param registry The registry to merge into this registry.
+ **/
+- (void)addExtensions:(GPBExtensionRegistry *)registry;
+
+/**
+ * Looks for the extension registered for the given field number on a given
+ * GPBDescriptor.
+ *
+ * @param descriptor  The descriptor to look for a registered extension on.
+ * @param fieldNumber The field number of the extension to look for.
+ *
+ * @return The registered GPBExtensionDescriptor or nil if none was found.
+ **/
+- (nullable GPBExtensionDescriptor *)extensionForDescriptor:(GPBDescriptor *)descriptor
+                                                fieldNumber:(NSInteger)fieldNumber;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBExtensionRegistry.m b/iOS/Pods/Protobuf/objectivec/GPBExtensionRegistry.m
new file mode 100644 (file)
index 0000000..b056a52
--- /dev/null
@@ -0,0 +1,130 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBExtensionRegistry.h"
+
+#import "GPBBootstrap.h"
+#import "GPBDescriptor.h"
+
+@implementation GPBExtensionRegistry {
+  NSMutableDictionary *mutableClassMap_;
+}
+
+- (instancetype)init {
+  if ((self = [super init])) {
+    mutableClassMap_ = [[NSMutableDictionary alloc] init];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [mutableClassMap_ release];
+  [super dealloc];
+}
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+  GPBExtensionRegistry *result = [[[self class] allocWithZone:zone] init];
+  [result addExtensions:self];
+  return result;
+}
+
+- (void)addExtension:(GPBExtensionDescriptor *)extension {
+  if (extension == nil) {
+    return;
+  }
+
+  Class containingMessageClass = extension.containingMessageClass;
+  CFMutableDictionaryRef extensionMap = (CFMutableDictionaryRef)
+      [mutableClassMap_ objectForKey:containingMessageClass];
+  if (extensionMap == nil) {
+    // Use a custom dictionary here because the keys are numbers and conversion
+    // back and forth from NSNumber isn't worth the cost.
+    extensionMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL,
+                                             &kCFTypeDictionaryValueCallBacks);
+    [mutableClassMap_ setObject:(id)extensionMap
+                         forKey:(id<NSCopying>)containingMessageClass];
+    CFRelease(extensionMap);
+  }
+
+  ssize_t key = extension.fieldNumber;
+  CFDictionarySetValue(extensionMap, (const void *)key, extension);
+}
+
+- (GPBExtensionDescriptor *)extensionForDescriptor:(GPBDescriptor *)descriptor
+                                       fieldNumber:(NSInteger)fieldNumber {
+  Class messageClass = descriptor.messageClass;
+  CFMutableDictionaryRef extensionMap = (CFMutableDictionaryRef)
+      [mutableClassMap_ objectForKey:messageClass];
+  ssize_t key = fieldNumber;
+  GPBExtensionDescriptor *result =
+      (extensionMap
+       ? CFDictionaryGetValue(extensionMap, (const void *)key)
+       : nil);
+  return result;
+}
+
+static void CopyKeyValue(const void *key, const void *value, void *context) {
+  CFMutableDictionaryRef extensionMap = (CFMutableDictionaryRef)context;
+  CFDictionarySetValue(extensionMap, key, value);
+}
+
+- (void)addExtensions:(GPBExtensionRegistry *)registry {
+  if (registry == nil) {
+    // In the case where there are no extensions just ignore.
+    return;
+  }
+  NSMutableDictionary *otherClassMap = registry->mutableClassMap_;
+  [otherClassMap enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL * stop) {
+#pragma unused(stop)
+    Class containingMessageClass = key;
+    CFMutableDictionaryRef otherExtensionMap = (CFMutableDictionaryRef)value;
+
+    CFMutableDictionaryRef extensionMap = (CFMutableDictionaryRef)
+        [mutableClassMap_ objectForKey:containingMessageClass];
+    if (extensionMap == nil) {
+      extensionMap = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, otherExtensionMap);
+      [mutableClassMap_ setObject:(id)extensionMap
+                           forKey:(id<NSCopying>)containingMessageClass];
+      CFRelease(extensionMap);
+    } else {
+      CFDictionaryApplyFunction(otherExtensionMap, CopyKeyValue, extensionMap);
+    }
+  }];
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/iOS/Pods/Protobuf/objectivec/GPBMessage.h b/iOS/Pods/Protobuf/objectivec/GPBMessage.h
new file mode 100644 (file)
index 0000000..276740d
--- /dev/null
@@ -0,0 +1,470 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBBootstrap.h"
+
+@class GPBDescriptor;
+@class GPBCodedInputStream;
+@class GPBCodedOutputStream;
+@class GPBExtensionDescriptor;
+@class GPBExtensionRegistry;
+@class GPBFieldDescriptor;
+@class GPBUnknownFieldSet;
+
+NS_ASSUME_NONNULL_BEGIN
+
+CF_EXTERN_C_BEGIN
+
+/** NSError domain used for errors. */
+extern NSString *const GPBMessageErrorDomain;
+
+/** Error codes for NSErrors originated in GPBMessage. */
+typedef NS_ENUM(NSInteger, GPBMessageErrorCode) {
+  /** Uncategorized error. */
+  GPBMessageErrorCodeOther = -100,
+  /** Message couldn't be serialized because it is missing required fields. */
+  GPBMessageErrorCodeMissingRequiredField = -101,
+};
+
+/**
+ * Key under which the GPBMessage error's reason is stored inside the userInfo
+ * dictionary.
+ **/
+extern NSString *const GPBErrorReasonKey;
+
+CF_EXTERN_C_END
+
+/**
+ * Base class that each generated message subclasses from.
+ *
+ * @note @c NSCopying support is a "deep copy", in that all sub objects are
+ *       copied.  Just like you wouldn't want a UIView/NSView trying to
+ *       exist in two places, you don't want a sub message to be a property
+ *       property of two other messages.
+ *
+ * @note While the class support NSSecureCoding, if the message has any
+ *       extensions, they will end up reloaded in @c unknownFields as there is
+ *       no way for the @c NSCoding plumbing to pass through a
+ *       @c GPBExtensionRegistry. To support extensions, instead of passing the
+ *       calls off to the Message, simple store the result of @c data, and then
+ *       when loading, fetch the data and use
+ *       @c +parseFromData:extensionRegistry:error: to provide an extension
+ *       registry.
+ **/
+@interface GPBMessage : NSObject<NSSecureCoding, NSCopying>
+
+// If you add an instance method/property to this class that may conflict with
+// fields declared in protos, you need to update objective_helpers.cc. The main
+// cases are methods that take no arguments, or setFoo:/hasFoo: type methods.
+
+/**
+ * The set of unknown fields for this message.
+ *
+ * Only messages from proto files declared with "proto2" syntax support unknown
+ * fields. For "proto3" syntax, any unknown fields found while parsing are
+ * dropped.
+ **/
+@property(nonatomic, copy, nullable) GPBUnknownFieldSet *unknownFields;
+
+/**
+ * Whether the message, along with all submessages, have the required fields
+ * set. This is only applicable for files declared with "proto2" syntax, as
+ * there are no required fields for "proto3" syntax.
+ **/
+@property(nonatomic, readonly, getter=isInitialized) BOOL initialized;
+
+/**
+ * @return An autoreleased message with the default values set.
+ **/
++ (instancetype)message;
+
+/**
+ * Creates a new instance by parsing the provided data. This method should be
+ * sent to the generated message class that the data should be interpreted as.
+ * If there is an error the method returns nil and the error is returned in
+ * errorPtr (when provided).
+ *
+ * @note In DEBUG builds, the parsed message is checked to be sure all required
+ *       fields were provided, and the parse will fail if some are missing.
+ *
+ * @note The errors returned are likely coming from the domain and codes listed
+ *       at the top of this file and GPBCodedInputStream.h.
+ *
+ * @param data     The data to parse.
+ * @param errorPtr An optional error pointer to fill in with a failure reason if
+ *                 the data can not be parsed.
+ *
+ * @return A new instance of the generated class.
+ **/
++ (nullable instancetype)parseFromData:(NSData *)data error:(NSError **)errorPtr;
+
+/**
+ * Creates a new instance by parsing the data. This method should be sent to
+ * the generated message class that the data should be interpreted as. If
+ * there is an error the method returns nil and the error is returned in
+ * errorPtr (when provided).
+ *
+ * @note In DEBUG builds, the parsed message is checked to be sure all required
+ *       fields were provided, and the parse will fail if some are missing.
+ *
+ * @note The errors returned are likely coming from the domain and codes listed
+ *       at the top of this file and GPBCodedInputStream.h.
+ *
+ * @param data              The data to parse.
+ * @param extensionRegistry The extension registry to use to look up extensions.
+ * @param errorPtr          An optional error pointer to fill in with a failure
+ *                          reason if the data can not be parsed.
+ *
+ * @return A new instance of the generated class.
+ **/
++ (nullable instancetype)parseFromData:(NSData *)data
+                     extensionRegistry:(nullable GPBExtensionRegistry *)extensionRegistry
+                                 error:(NSError **)errorPtr;
+
+/**
+ * Creates a new instance by parsing the data from the given input stream. This
+ * method should be sent to the generated message class that the data should
+ * be interpreted as. If there is an error the method returns nil and the error
+ * is returned in errorPtr (when provided).
+ *
+ * @note In DEBUG builds, the parsed message is checked to be sure all required
+ *       fields were provided, and the parse will fail if some are missing.
+ *
+ * @note The errors returned are likely coming from the domain and codes listed
+ *       at the top of this file and GPBCodedInputStream.h.
+ *
+ * @param input             The stream to read data from.
+ * @param extensionRegistry The extension registry to use to look up extensions.
+ * @param errorPtr          An optional error pointer to fill in with a failure
+ *                          reason if the data can not be parsed.
+ *
+ * @return A new instance of the generated class.
+ **/
++ (nullable instancetype)parseFromCodedInputStream:(GPBCodedInputStream *)input
+                                 extensionRegistry:
+                                     (nullable GPBExtensionRegistry *)extensionRegistry
+                                             error:(NSError **)errorPtr;
+
+/**
+ * Creates a new instance by parsing the data from the given input stream. This
+ * method should be sent to the generated message class that the data should
+ * be interpreted as. If there is an error the method returns nil and the error
+ * is returned in errorPtr (when provided).
+ *
+ * @note Unlike the parseFrom... methods, this never checks to see if all of
+ *       the required fields are set. So this method can be used to reload
+ *       messages that may not be complete.
+ *
+ * @note The errors returned are likely coming from the domain and codes listed
+ *       at the top of this file and GPBCodedInputStream.h.
+ *
+ * @param input             The stream to read data from.
+ * @param extensionRegistry The extension registry to use to look up extensions.
+ * @param errorPtr          An optional error pointer to fill in with a failure
+ *                          reason if the data can not be parsed.
+ *
+ * @return A new instance of the generated class.
+ **/
++ (nullable instancetype)parseDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
+                                          extensionRegistry:
+                                              (nullable GPBExtensionRegistry *)extensionRegistry
+                                                      error:(NSError **)errorPtr;
+
+/**
+ * Initializes an instance by parsing the data. This method should be sent to
+ * the generated message class that the data should be interpreted as. If
+ * there is an error the method returns nil and the error is returned in
+ * errorPtr (when provided).
+ *
+ * @note In DEBUG builds, the parsed message is checked to be sure all required
+ *       fields were provided, and the parse will fail if some are missing.
+ *
+ * @note The errors returned are likely coming from the domain and codes listed
+ *       at the top of this file and GPBCodedInputStream.h.
+ *
+ * @param data     The data to parse.
+ * @param errorPtr An optional error pointer to fill in with a failure reason if
+ *                 the data can not be parsed.
+ *
+ * @return An initialized instance of the generated class.
+ **/
+- (nullable instancetype)initWithData:(NSData *)data error:(NSError **)errorPtr;
+
+/**
+ * Initializes an instance by parsing the data. This method should be sent to
+ * the generated message class that the data should be interpreted as. If
+ * there is an error the method returns nil and the error is returned in
+ * errorPtr (when provided).
+ *
+ * @note In DEBUG builds, the parsed message is checked to be sure all required
+ *       fields were provided, and the parse will fail if some are missing.
+ *
+ * @note The errors returned are likely coming from the domain and codes listed
+ *       at the top of this file and GPBCodedInputStream.h.
+ *
+ * @param data              The data to parse.
+ * @param extensionRegistry The extension registry to use to look up extensions.
+ * @param errorPtr          An optional error pointer to fill in with a failure
+ *                          reason if the data can not be parsed.
+ *
+ * @return An initialized instance of the generated class.
+ **/
+- (nullable instancetype)initWithData:(NSData *)data
+                    extensionRegistry:(nullable GPBExtensionRegistry *)extensionRegistry
+                                error:(NSError **)errorPtr;
+
+/**
+ * Initializes an instance by parsing the data from the given input stream. This
+ * method should be sent to the generated message class that the data should
+ * be interpreted as. If there is an error the method returns nil and the error
+ * is returned in errorPtr (when provided).
+ *
+ * @note Unlike the parseFrom... methods, this never checks to see if all of
+ *       the required fields are set. So this method can be used to reload
+ *       messages that may not be complete.
+ *
+ * @note The errors returned are likely coming from the domain and codes listed
+ *       at the top of this file and GPBCodedInputStream.h.
+ *
+ * @param input             The stream to read data from.
+ * @param extensionRegistry The extension registry to use to look up extensions.
+ * @param errorPtr          An optional error pointer to fill in with a failure
+ *                          reason if the data can not be parsed.
+ *
+ * @return An initialized instance of the generated class.
+ **/
+- (nullable instancetype)initWithCodedInputStream:(GPBCodedInputStream *)input
+                                extensionRegistry:
+                                    (nullable GPBExtensionRegistry *)extensionRegistry
+                                            error:(NSError **)errorPtr;
+
+/**
+ * Parses the given data as this message's class, and merges those values into
+ * this message.
+ *
+ * @param data              The binary representation of the message to merge.
+ * @param extensionRegistry The extension registry to use to look up extensions.
+ *
+ * @exception GPBCodedInputStreamException Exception thrown when parsing was
+ *                                         unsuccessful.
+ **/
+- (void)mergeFromData:(NSData *)data
+    extensionRegistry:(nullable GPBExtensionRegistry *)extensionRegistry;
+
+/**
+ * Merges the fields from another message (of the same type) into this
+ * message.
+ *
+ * @param other Message to merge into this message.
+ **/
+- (void)mergeFrom:(GPBMessage *)other;
+
+/**
+ * Writes out the message to the given coded output stream.
+ *
+ * @param output The coded output stream into which to write the message.
+ *
+ * @note This can raise the GPBCodedOutputStreamException_* exceptions.
+ *
+ **/
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output;
+
+/**
+ * Writes out the message to the given output stream.
+ *
+ * @param output The output stream into which to write the message.
+ *
+ * @note This can raise the GPBCodedOutputStreamException_* exceptions.
+ **/
+- (void)writeToOutputStream:(NSOutputStream *)output;
+
+/**
+ * Writes out a varint for the message size followed by the the message to
+ * the given output stream.
+ *
+ * @param output The coded output stream into which to write the message.
+ *
+ * @note This can raise the GPBCodedOutputStreamException_* exceptions.
+ **/
+- (void)writeDelimitedToCodedOutputStream:(GPBCodedOutputStream *)output;
+
+/**
+ * Writes out a varint for the message size followed by the the message to
+ * the given output stream.
+ *
+ * @param output The output stream into which to write the message.
+ *
+ * @note This can raise the GPBCodedOutputStreamException_* exceptions.
+ **/
+- (void)writeDelimitedToOutputStream:(NSOutputStream *)output;
+
+/**
+ * Serializes the message to an NSData.
+ *
+ * If there is an error while generating the data, nil is returned.
+ *
+ * @note This value is not cached, so if you are using it repeatedly, cache
+ *       it yourself.
+ *
+ * @note In DEBUG ONLY, the message is also checked for all required field,
+ *       if one is missing, nil will be returned.
+ *
+ * @return The binary representation of the message.
+ **/
+- (nullable NSData *)data;
+
+/**
+ * Serializes a varint with the message size followed by the message data,
+ * returning that as an NSData.
+ *
+ * @note This value is not cached, so if you are using it repeatedly, it is
+ *       recommended to keep a local copy.
+ *
+ * @return The binary representation of the size along with the message.
+ **/
+- (NSData *)delimitedData;
+
+/**
+ * Calculates the size of the object if it were serialized.
+ *
+ * This is not a cached value. If you are following a pattern like this:
+ *
+ * ```
+ * size_t size = [aMsg serializedSize];
+ * NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
+ * [foo writeSize:size];
+ * [foo appendData:[aMsg data]];
+ * ```
+ *
+ * you would be better doing:
+ *
+ * ```
+ * NSData *data = [aMsg data];
+ * NSUInteger size = [aMsg length];
+ * NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
+ * [foo writeSize:size];
+ * [foo appendData:data];
+ * ```
+ *
+ * @return The size of the message in it's binary representation.
+ **/
+- (size_t)serializedSize;
+
+/**
+ * @return The descriptor for the message class.
+ **/
++ (GPBDescriptor *)descriptor;
+
+/**
+ * Return the descriptor for the message.
+ **/
+- (GPBDescriptor *)descriptor;
+
+/**
+ * @return An array with the extension descriptors that are currently set on the
+ * message.
+ **/
+- (NSArray *)extensionsCurrentlySet;
+
+/**
+ * Checks whether there is an extension set on the message which matches the
+ * given extension descriptor.
+ *
+ * @param extension Extension descriptor to check if it's set on the message.
+ *
+ * @return Whether the extension is currently set on the message.
+ **/
+- (BOOL)hasExtension:(GPBExtensionDescriptor *)extension;
+
+/*
+ * Fetches the given extension's value for this message.
+ *
+ * Extensions use boxed values (NSNumbers) for PODs and NSMutableArrays for
+ * repeated fields. If the extension is a Message one will be auto created for
+ * you and returned similar to fields.
+ *
+ * @param extension The extension descriptor of the extension to fetch.
+ *
+ * @return The extension matching the given descriptor, or nil if none found.
+ **/
+- (nullable id)getExtension:(GPBExtensionDescriptor *)extension;
+
+/**
+ * Sets the given extension's value for this message. This only applies for
+ * single field extensions (i.e. - not repeated fields).
+ *
+ * Extensions use boxed values (NSNumbers).
+ *
+ * @param extension The extension descriptor under which to set the value.
+ * @param value     The value to be set as the extension.
+ **/
+- (void)setExtension:(GPBExtensionDescriptor *)extension
+               value:(nullable id)value;
+
+/**
+ * Adds the given value to the extension for this message. This only applies
+ * to repeated field extensions. If the field is a repeated POD type, the value
+ * should be an NSNumber.
+ *
+ * @param extension The extension descriptor under which to add the value.
+ * @param value     The value to be added to the repeated extension.
+ **/
+- (void)addExtension:(GPBExtensionDescriptor *)extension value:(id)value;
+
+/**
+ * Replaces the value at the given index with the given value for the extension
+ * on this message. This only applies to repeated field extensions. If the field
+ * is a repeated POD type, the value is should be an NSNumber.
+ *
+ * @param extension The extension descriptor under which to replace the value.
+ * @param index     The index of the extension to be replaced.
+ * @param value     The value to be replaced in the repeated extension.
+ **/
+- (void)setExtension:(GPBExtensionDescriptor *)extension
+               index:(NSUInteger)index
+               value:(id)value;
+
+/**
+ * Clears the given extension for this message.
+ *
+ * @param extension The extension descriptor to be cleared from this message.
+ **/
+- (void)clearExtension:(GPBExtensionDescriptor *)extension;
+
+/**
+ * Resets all of the fields of this message to their default values.
+ **/
+- (void)clear;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBMessage.m b/iOS/Pods/Protobuf/objectivec/GPBMessage.m
new file mode 100644 (file)
index 0000000..db5d3b6
--- /dev/null
@@ -0,0 +1,3317 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBMessage_PackagePrivate.h"
+
+#import <objc/runtime.h>
+#import <objc/message.h>
+#import <stdatomic.h>
+
+#import "GPBArray_PackagePrivate.h"
+#import "GPBCodedInputStream_PackagePrivate.h"
+#import "GPBCodedOutputStream_PackagePrivate.h"
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBDictionary_PackagePrivate.h"
+#import "GPBExtensionInternals.h"
+#import "GPBExtensionRegistry.h"
+#import "GPBRootObject_PackagePrivate.h"
+#import "GPBUnknownFieldSet_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+NSString *const GPBMessageErrorDomain =
+    GPBNSStringifySymbol(GPBMessageErrorDomain);
+
+NSString *const GPBErrorReasonKey = @"Reason";
+
+static NSString *const kGPBDataCoderKey = @"GPBData";
+
+//
+// PLEASE REMEMBER:
+//
+// This is the base class for *all* messages generated, so any selector defined,
+// *public* or *private* could end up colliding with a proto message field. So
+// avoid using selectors that could match a property, use C functions to hide
+// them, etc.
+//
+
+@interface GPBMessage () {
+ @package
+  GPBUnknownFieldSet *unknownFields_;
+  NSMutableDictionary *extensionMap_;
+  NSMutableDictionary *autocreatedExtensionMap_;
+
+  // If the object was autocreated, we remember the creator so that if we get
+  // mutated, we can inform the creator to make our field visible.
+  GPBMessage *autocreator_;
+  GPBFieldDescriptor *autocreatorField_;
+  GPBExtensionDescriptor *autocreatorExtension_;
+
+  // A lock to provide mutual exclusion from internal data that can be modified
+  // by *read* operations such as getters (autocreation of message fields and
+  // message extensions, not setting of values). Used to guarantee thread safety
+  // for concurrent reads on the message.
+  // NOTE: OSSpinLock may seem like a good fit here but Apple engineers have
+  // pointed out that they are vulnerable to live locking on iOS in cases of
+  // priority inversion:
+  //   http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
+  //   https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
+  // Use of readOnlySemaphore_ must be prefaced by a call to
+  // GPBPrepareReadOnlySemaphore to ensure it has been created. This allows
+  // readOnlySemaphore_ to be only created when actually needed.
+  _Atomic(dispatch_semaphore_t) readOnlySemaphore_;
+}
+@end
+
+static id CreateArrayForField(GPBFieldDescriptor *field,
+                              GPBMessage *autocreator)
+    __attribute__((ns_returns_retained));
+static id GetOrCreateArrayIvarWithField(GPBMessage *self,
+                                        GPBFieldDescriptor *field,
+                                        GPBFileSyntax syntax);
+static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field);
+static id CreateMapForField(GPBFieldDescriptor *field,
+                            GPBMessage *autocreator)
+    __attribute__((ns_returns_retained));
+static id GetOrCreateMapIvarWithField(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      GPBFileSyntax syntax);
+static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field);
+static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap,
+                                              NSZone *zone)
+    __attribute__((ns_returns_retained));
+
+#ifdef DEBUG
+static NSError *MessageError(NSInteger code, NSDictionary *userInfo) {
+  return [NSError errorWithDomain:GPBMessageErrorDomain
+                             code:code
+                         userInfo:userInfo];
+}
+#endif
+
+static NSError *ErrorFromException(NSException *exception) {
+  NSError *error = nil;
+
+  if ([exception.name isEqual:GPBCodedInputStreamException]) {
+    NSDictionary *exceptionInfo = exception.userInfo;
+    error = exceptionInfo[GPBCodedInputStreamUnderlyingErrorKey];
+  }
+
+  if (!error) {
+    NSString *reason = exception.reason;
+    NSDictionary *userInfo = nil;
+    if ([reason length]) {
+      userInfo = @{ GPBErrorReasonKey : reason };
+    }
+
+    error = [NSError errorWithDomain:GPBMessageErrorDomain
+                                code:GPBMessageErrorCodeOther
+                            userInfo:userInfo];
+  }
+  return error;
+}
+
+static void CheckExtension(GPBMessage *self,
+                           GPBExtensionDescriptor *extension) {
+  if (![self isKindOfClass:extension.containingMessageClass]) {
+    [NSException
+         raise:NSInvalidArgumentException
+        format:@"Extension %@ used on wrong class (%@ instead of %@)",
+               extension.singletonName,
+               [self class], extension.containingMessageClass];
+  }
+}
+
+static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap,
+                                              NSZone *zone) {
+  if (extensionMap.count == 0) {
+    return nil;
+  }
+  NSMutableDictionary *result = [[NSMutableDictionary allocWithZone:zone]
+      initWithCapacity:extensionMap.count];
+
+  for (GPBExtensionDescriptor *extension in extensionMap) {
+    id value = [extensionMap objectForKey:extension];
+    BOOL isMessageExtension = GPBExtensionIsMessage(extension);
+
+    if (extension.repeated) {
+      if (isMessageExtension) {
+        NSMutableArray *list =
+            [[NSMutableArray alloc] initWithCapacity:[value count]];
+        for (GPBMessage *listValue in value) {
+          GPBMessage *copiedValue = [listValue copyWithZone:zone];
+          [list addObject:copiedValue];
+          [copiedValue release];
+        }
+        [result setObject:list forKey:extension];
+        [list release];
+      } else {
+        NSMutableArray *copiedValue = [value mutableCopyWithZone:zone];
+        [result setObject:copiedValue forKey:extension];
+        [copiedValue release];
+      }
+    } else {
+      if (isMessageExtension) {
+        GPBMessage *copiedValue = [value copyWithZone:zone];
+        [result setObject:copiedValue forKey:extension];
+        [copiedValue release];
+      } else {
+        [result setObject:value forKey:extension];
+      }
+    }
+  }
+
+  return result;
+}
+
+static id CreateArrayForField(GPBFieldDescriptor *field,
+                              GPBMessage *autocreator) {
+  id result;
+  GPBDataType fieldDataType = GPBGetFieldDataType(field);
+  switch (fieldDataType) {
+    case GPBDataTypeBool:
+      result = [[GPBBoolArray alloc] init];
+      break;
+    case GPBDataTypeFixed32:
+    case GPBDataTypeUInt32:
+      result = [[GPBUInt32Array alloc] init];
+      break;
+    case GPBDataTypeInt32:
+    case GPBDataTypeSFixed32:
+    case GPBDataTypeSInt32:
+      result = [[GPBInt32Array alloc] init];
+      break;
+    case GPBDataTypeFixed64:
+    case GPBDataTypeUInt64:
+      result = [[GPBUInt64Array alloc] init];
+      break;
+    case GPBDataTypeInt64:
+    case GPBDataTypeSFixed64:
+    case GPBDataTypeSInt64:
+      result = [[GPBInt64Array alloc] init];
+      break;
+    case GPBDataTypeFloat:
+      result = [[GPBFloatArray alloc] init];
+      break;
+    case GPBDataTypeDouble:
+      result = [[GPBDoubleArray alloc] init];
+      break;
+
+    case GPBDataTypeEnum:
+      result = [[GPBEnumArray alloc]
+                  initWithValidationFunction:field.enumDescriptor.enumVerifier];
+      break;
+
+    case GPBDataTypeBytes:
+    case GPBDataTypeGroup:
+    case GPBDataTypeMessage:
+    case GPBDataTypeString:
+      if (autocreator) {
+        result = [[GPBAutocreatedArray alloc] init];
+      } else {
+        result = [[NSMutableArray alloc] init];
+      }
+      break;
+  }
+
+  if (autocreator) {
+    if (GPBDataTypeIsObject(fieldDataType)) {
+      GPBAutocreatedArray *autoArray = result;
+      autoArray->_autocreator =  autocreator;
+    } else {
+      GPBInt32Array *gpbArray = result;
+      gpbArray->_autocreator = autocreator;
+    }
+  }
+
+  return result;
+}
+
+static id CreateMapForField(GPBFieldDescriptor *field,
+                            GPBMessage *autocreator) {
+  id result;
+  GPBDataType keyDataType = field.mapKeyDataType;
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  switch (keyDataType) {
+    case GPBDataTypeBool:
+      switch (valueDataType) {
+        case GPBDataTypeBool:
+          result = [[GPBBoolBoolDictionary alloc] init];
+          break;
+        case GPBDataTypeFixed32:
+        case GPBDataTypeUInt32:
+          result = [[GPBBoolUInt32Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt32:
+        case GPBDataTypeSFixed32:
+        case GPBDataTypeSInt32:
+          result = [[GPBBoolInt32Dictionary alloc] init];
+          break;
+        case GPBDataTypeFixed64:
+        case GPBDataTypeUInt64:
+          result = [[GPBBoolUInt64Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt64:
+        case GPBDataTypeSFixed64:
+        case GPBDataTypeSInt64:
+          result = [[GPBBoolInt64Dictionary alloc] init];
+          break;
+        case GPBDataTypeFloat:
+          result = [[GPBBoolFloatDictionary alloc] init];
+          break;
+        case GPBDataTypeDouble:
+          result = [[GPBBoolDoubleDictionary alloc] init];
+          break;
+        case GPBDataTypeEnum:
+          result = [[GPBBoolEnumDictionary alloc]
+              initWithValidationFunction:field.enumDescriptor.enumVerifier];
+          break;
+        case GPBDataTypeBytes:
+        case GPBDataTypeMessage:
+        case GPBDataTypeString:
+          result = [[GPBBoolObjectDictionary alloc] init];
+          break;
+        case GPBDataTypeGroup:
+          NSCAssert(NO, @"shouldn't happen");
+          return nil;
+      }
+      break;
+    case GPBDataTypeFixed32:
+    case GPBDataTypeUInt32:
+      switch (valueDataType) {
+        case GPBDataTypeBool:
+          result = [[GPBUInt32BoolDictionary alloc] init];
+          break;
+        case GPBDataTypeFixed32:
+        case GPBDataTypeUInt32:
+          result = [[GPBUInt32UInt32Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt32:
+        case GPBDataTypeSFixed32:
+        case GPBDataTypeSInt32:
+          result = [[GPBUInt32Int32Dictionary alloc] init];
+          break;
+        case GPBDataTypeFixed64:
+        case GPBDataTypeUInt64:
+          result = [[GPBUInt32UInt64Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt64:
+        case GPBDataTypeSFixed64:
+        case GPBDataTypeSInt64:
+          result = [[GPBUInt32Int64Dictionary alloc] init];
+          break;
+        case GPBDataTypeFloat:
+          result = [[GPBUInt32FloatDictionary alloc] init];
+          break;
+        case GPBDataTypeDouble:
+          result = [[GPBUInt32DoubleDictionary alloc] init];
+          break;
+        case GPBDataTypeEnum:
+          result = [[GPBUInt32EnumDictionary alloc]
+              initWithValidationFunction:field.enumDescriptor.enumVerifier];
+          break;
+        case GPBDataTypeBytes:
+        case GPBDataTypeMessage:
+        case GPBDataTypeString:
+          result = [[GPBUInt32ObjectDictionary alloc] init];
+          break;
+        case GPBDataTypeGroup:
+          NSCAssert(NO, @"shouldn't happen");
+          return nil;
+      }
+      break;
+    case GPBDataTypeInt32:
+    case GPBDataTypeSFixed32:
+    case GPBDataTypeSInt32:
+      switch (valueDataType) {
+        case GPBDataTypeBool:
+          result = [[GPBInt32BoolDictionary alloc] init];
+          break;
+        case GPBDataTypeFixed32:
+        case GPBDataTypeUInt32:
+          result = [[GPBInt32UInt32Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt32:
+        case GPBDataTypeSFixed32:
+        case GPBDataTypeSInt32:
+          result = [[GPBInt32Int32Dictionary alloc] init];
+          break;
+        case GPBDataTypeFixed64:
+        case GPBDataTypeUInt64:
+          result = [[GPBInt32UInt64Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt64:
+        case GPBDataTypeSFixed64:
+        case GPBDataTypeSInt64:
+          result = [[GPBInt32Int64Dictionary alloc] init];
+          break;
+        case GPBDataTypeFloat:
+          result = [[GPBInt32FloatDictionary alloc] init];
+          break;
+        case GPBDataTypeDouble:
+          result = [[GPBInt32DoubleDictionary alloc] init];
+          break;
+        case GPBDataTypeEnum:
+          result = [[GPBInt32EnumDictionary alloc]
+              initWithValidationFunction:field.enumDescriptor.enumVerifier];
+          break;
+        case GPBDataTypeBytes:
+        case GPBDataTypeMessage:
+        case GPBDataTypeString:
+          result = [[GPBInt32ObjectDictionary alloc] init];
+          break;
+        case GPBDataTypeGroup:
+          NSCAssert(NO, @"shouldn't happen");
+          return nil;
+      }
+      break;
+    case GPBDataTypeFixed64:
+    case GPBDataTypeUInt64:
+      switch (valueDataType) {
+        case GPBDataTypeBool:
+          result = [[GPBUInt64BoolDictionary alloc] init];
+          break;
+        case GPBDataTypeFixed32:
+        case GPBDataTypeUInt32:
+          result = [[GPBUInt64UInt32Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt32:
+        case GPBDataTypeSFixed32:
+        case GPBDataTypeSInt32:
+          result = [[GPBUInt64Int32Dictionary alloc] init];
+          break;
+        case GPBDataTypeFixed64:
+        case GPBDataTypeUInt64:
+          result = [[GPBUInt64UInt64Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt64:
+        case GPBDataTypeSFixed64:
+        case GPBDataTypeSInt64:
+          result = [[GPBUInt64Int64Dictionary alloc] init];
+          break;
+        case GPBDataTypeFloat:
+          result = [[GPBUInt64FloatDictionary alloc] init];
+          break;
+        case GPBDataTypeDouble:
+          result = [[GPBUInt64DoubleDictionary alloc] init];
+          break;
+        case GPBDataTypeEnum:
+          result = [[GPBUInt64EnumDictionary alloc]
+              initWithValidationFunction:field.enumDescriptor.enumVerifier];
+          break;
+        case GPBDataTypeBytes:
+        case GPBDataTypeMessage:
+        case GPBDataTypeString:
+          result = [[GPBUInt64ObjectDictionary alloc] init];
+          break;
+        case GPBDataTypeGroup:
+          NSCAssert(NO, @"shouldn't happen");
+          return nil;
+      }
+      break;
+    case GPBDataTypeInt64:
+    case GPBDataTypeSFixed64:
+    case GPBDataTypeSInt64:
+      switch (valueDataType) {
+        case GPBDataTypeBool:
+          result = [[GPBInt64BoolDictionary alloc] init];
+          break;
+        case GPBDataTypeFixed32:
+        case GPBDataTypeUInt32:
+          result = [[GPBInt64UInt32Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt32:
+        case GPBDataTypeSFixed32:
+        case GPBDataTypeSInt32:
+          result = [[GPBInt64Int32Dictionary alloc] init];
+          break;
+        case GPBDataTypeFixed64:
+        case GPBDataTypeUInt64:
+          result = [[GPBInt64UInt64Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt64:
+        case GPBDataTypeSFixed64:
+        case GPBDataTypeSInt64:
+          result = [[GPBInt64Int64Dictionary alloc] init];
+          break;
+        case GPBDataTypeFloat:
+          result = [[GPBInt64FloatDictionary alloc] init];
+          break;
+        case GPBDataTypeDouble:
+          result = [[GPBInt64DoubleDictionary alloc] init];
+          break;
+        case GPBDataTypeEnum:
+          result = [[GPBInt64EnumDictionary alloc]
+              initWithValidationFunction:field.enumDescriptor.enumVerifier];
+          break;
+        case GPBDataTypeBytes:
+        case GPBDataTypeMessage:
+        case GPBDataTypeString:
+          result = [[GPBInt64ObjectDictionary alloc] init];
+          break;
+        case GPBDataTypeGroup:
+          NSCAssert(NO, @"shouldn't happen");
+          return nil;
+      }
+      break;
+    case GPBDataTypeString:
+      switch (valueDataType) {
+        case GPBDataTypeBool:
+          result = [[GPBStringBoolDictionary alloc] init];
+          break;
+        case GPBDataTypeFixed32:
+        case GPBDataTypeUInt32:
+          result = [[GPBStringUInt32Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt32:
+        case GPBDataTypeSFixed32:
+        case GPBDataTypeSInt32:
+          result = [[GPBStringInt32Dictionary alloc] init];
+          break;
+        case GPBDataTypeFixed64:
+        case GPBDataTypeUInt64:
+          result = [[GPBStringUInt64Dictionary alloc] init];
+          break;
+        case GPBDataTypeInt64:
+        case GPBDataTypeSFixed64:
+        case GPBDataTypeSInt64:
+          result = [[GPBStringInt64Dictionary alloc] init];
+          break;
+        case GPBDataTypeFloat:
+          result = [[GPBStringFloatDictionary alloc] init];
+          break;
+        case GPBDataTypeDouble:
+          result = [[GPBStringDoubleDictionary alloc] init];
+          break;
+        case GPBDataTypeEnum:
+          result = [[GPBStringEnumDictionary alloc]
+              initWithValidationFunction:field.enumDescriptor.enumVerifier];
+          break;
+        case GPBDataTypeBytes:
+        case GPBDataTypeMessage:
+        case GPBDataTypeString:
+          if (autocreator) {
+            result = [[GPBAutocreatedDictionary alloc] init];
+          } else {
+            result = [[NSMutableDictionary alloc] init];
+          }
+          break;
+        case GPBDataTypeGroup:
+          NSCAssert(NO, @"shouldn't happen");
+          return nil;
+      }
+      break;
+
+    case GPBDataTypeFloat:
+    case GPBDataTypeDouble:
+    case GPBDataTypeEnum:
+    case GPBDataTypeBytes:
+    case GPBDataTypeGroup:
+    case GPBDataTypeMessage:
+      NSCAssert(NO, @"shouldn't happen");
+      return nil;
+  }
+
+  if (autocreator) {
+    if ((keyDataType == GPBDataTypeString) &&
+        GPBDataTypeIsObject(valueDataType)) {
+      GPBAutocreatedDictionary *autoDict = result;
+      autoDict->_autocreator =  autocreator;
+    } else {
+      GPBInt32Int32Dictionary *gpbDict = result;
+      gpbDict->_autocreator = autocreator;
+    }
+  }
+
+  return result;
+}
+
+#if !defined(__clang_analyzer__)
+// These functions are blocked from the analyzer because the analyzer sees the
+// GPBSetRetainedObjectIvarWithFieldInternal() call as consuming the array/map,
+// so use of the array/map after the call returns is flagged as a use after
+// free.
+// But GPBSetRetainedObjectIvarWithFieldInternal() is "consuming" the retain
+// count be holding onto the object (it is transfering it), the object is
+// still valid after returning from the call.  The other way to avoid this
+// would be to add a -retain/-autorelease, but that would force every
+// repeated/map field parsed into the autorelease pool which is both a memory
+// and performance hit.
+
+static id GetOrCreateArrayIvarWithField(GPBMessage *self,
+                                        GPBFieldDescriptor *field,
+                                        GPBFileSyntax syntax) {
+  id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+  if (!array) {
+    // No lock needed, this is called from places expecting to mutate
+    // so no threading protection is needed.
+    array = CreateArrayForField(field, nil);
+    GPBSetRetainedObjectIvarWithFieldInternal(self, field, array, syntax);
+  }
+  return array;
+}
+
+// This is like GPBGetObjectIvarWithField(), but for arrays, it should
+// only be used to wire the method into the class.
+static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
+  id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+  if (!array) {
+    // Check again after getting the lock.
+    GPBPrepareReadOnlySemaphore(self);
+    dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
+    array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+    if (!array) {
+      array = CreateArrayForField(field, self);
+      GPBSetAutocreatedRetainedObjectIvarWithField(self, field, array);
+    }
+    dispatch_semaphore_signal(self->readOnlySemaphore_);
+  }
+  return array;
+}
+
+static id GetOrCreateMapIvarWithField(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      GPBFileSyntax syntax) {
+  id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+  if (!dict) {
+    // No lock needed, this is called from places expecting to mutate
+    // so no threading protection is needed.
+    dict = CreateMapForField(field, nil);
+    GPBSetRetainedObjectIvarWithFieldInternal(self, field, dict, syntax);
+  }
+  return dict;
+}
+
+// This is like GPBGetObjectIvarWithField(), but for maps, it should
+// only be used to wire the method into the class.
+static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
+  id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+  if (!dict) {
+    // Check again after getting the lock.
+    GPBPrepareReadOnlySemaphore(self);
+    dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
+    dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+    if (!dict) {
+      dict = CreateMapForField(field, self);
+      GPBSetAutocreatedRetainedObjectIvarWithField(self, field, dict);
+    }
+    dispatch_semaphore_signal(self->readOnlySemaphore_);
+  }
+  return dict;
+}
+
+#endif  // !defined(__clang_analyzer__)
+
+GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass,
+                                            GPBMessage *autocreator,
+                                            GPBFieldDescriptor *field) {
+  GPBMessage *message = [[msgClass alloc] init];
+  message->autocreator_ = autocreator;
+  message->autocreatorField_ = [field retain];
+  return message;
+}
+
+static GPBMessage *CreateMessageWithAutocreatorForExtension(
+    Class msgClass, GPBMessage *autocreator, GPBExtensionDescriptor *extension)
+    __attribute__((ns_returns_retained));
+
+static GPBMessage *CreateMessageWithAutocreatorForExtension(
+    Class msgClass, GPBMessage *autocreator,
+    GPBExtensionDescriptor *extension) {
+  GPBMessage *message = [[msgClass alloc] init];
+  message->autocreator_ = autocreator;
+  message->autocreatorExtension_ = [extension retain];
+  return message;
+}
+
+BOOL GPBWasMessageAutocreatedBy(GPBMessage *message, GPBMessage *parent) {
+  return (message->autocreator_ == parent);
+}
+
+void GPBBecomeVisibleToAutocreator(GPBMessage *self) {
+  // Message objects that are implicitly created by accessing a message field
+  // are initially not visible via the hasX selector. This method makes them
+  // visible.
+  if (self->autocreator_) {
+    // This will recursively make all parent messages visible until it reaches a
+    // super-creator that's visible.
+    if (self->autocreatorField_) {
+      GPBFileSyntax syntax = [self->autocreator_ descriptor].file.syntax;
+      GPBSetObjectIvarWithFieldInternal(self->autocreator_,
+                                        self->autocreatorField_, self, syntax);
+    } else {
+      [self->autocreator_ setExtension:self->autocreatorExtension_ value:self];
+    }
+  }
+}
+
+void GPBAutocreatedArrayModified(GPBMessage *self, id array) {
+  // When one of our autocreated arrays adds elements, make it visible.
+  GPBDescriptor *descriptor = [[self class] descriptor];
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (field.fieldType == GPBFieldTypeRepeated) {
+      id curArray = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      if (curArray == array) {
+        if (GPBFieldDataTypeIsObject(field)) {
+          GPBAutocreatedArray *autoArray = array;
+          autoArray->_autocreator = nil;
+        } else {
+          GPBInt32Array *gpbArray = array;
+          gpbArray->_autocreator = nil;
+        }
+        GPBBecomeVisibleToAutocreator(self);
+        return;
+      }
+    }
+  }
+  NSCAssert(NO, @"Unknown autocreated %@ for %@.", [array class], self);
+}
+
+void GPBAutocreatedDictionaryModified(GPBMessage *self, id dictionary) {
+  // When one of our autocreated dicts adds elements, make it visible.
+  GPBDescriptor *descriptor = [[self class] descriptor];
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (field.fieldType == GPBFieldTypeMap) {
+      id curDict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      if (curDict == dictionary) {
+        if ((field.mapKeyDataType == GPBDataTypeString) &&
+            GPBFieldDataTypeIsObject(field)) {
+          GPBAutocreatedDictionary *autoDict = dictionary;
+          autoDict->_autocreator = nil;
+        } else {
+          GPBInt32Int32Dictionary *gpbDict = dictionary;
+          gpbDict->_autocreator = nil;
+        }
+        GPBBecomeVisibleToAutocreator(self);
+        return;
+      }
+    }
+  }
+  NSCAssert(NO, @"Unknown autocreated %@ for %@.", [dictionary class], self);
+}
+
+void GPBClearMessageAutocreator(GPBMessage *self) {
+  if ((self == nil) || !self->autocreator_) {
+    return;
+  }
+
+#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS)
+  // Either the autocreator must have its "has" flag set to YES, or it must be
+  // NO and not equal to ourselves.
+  BOOL autocreatorHas =
+      (self->autocreatorField_
+           ? GPBGetHasIvarField(self->autocreator_, self->autocreatorField_)
+           : [self->autocreator_ hasExtension:self->autocreatorExtension_]);
+  GPBMessage *autocreatorFieldValue =
+      (self->autocreatorField_
+           ? GPBGetObjectIvarWithFieldNoAutocreate(self->autocreator_,
+                                                   self->autocreatorField_)
+           : [self->autocreator_->autocreatedExtensionMap_
+                 objectForKey:self->autocreatorExtension_]);
+  NSCAssert(autocreatorHas || autocreatorFieldValue != self,
+            @"Cannot clear autocreator because it still refers to self, self: %@.",
+            self);
+
+#endif  // DEBUG && !defined(NS_BLOCK_ASSERTIONS)
+
+  self->autocreator_ = nil;
+  [self->autocreatorField_ release];
+  self->autocreatorField_ = nil;
+  [self->autocreatorExtension_ release];
+  self->autocreatorExtension_ = nil;
+}
+
+// Call this before using the readOnlySemaphore_. This ensures it is created only once.
+void GPBPrepareReadOnlySemaphore(GPBMessage *self) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+  // Create the semaphore on demand (rather than init) as developers might not cause them
+  // to be needed, and the heap usage can add up.  The atomic swap is used to avoid needing
+  // another lock around creating it.
+  if (self->readOnlySemaphore_ == nil) {
+    dispatch_semaphore_t worker = dispatch_semaphore_create(1);
+    dispatch_semaphore_t expected = nil;
+    if (!atomic_compare_exchange_strong(&self->readOnlySemaphore_, &expected, worker)) {
+      dispatch_release(worker);
+    }
+#if defined(__clang_analyzer__)
+    // The Xcode 9.2 (and 9.3 beta) static analyzer thinks worker is leaked
+    // (doesn't seem to know about atomic_compare_exchange_strong); so just
+    // for the analyzer, let it think worker is also released in this case.
+    else { dispatch_release(worker); }
+#endif
+  }
+
+#pragma clang diagnostic pop
+}
+
+static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
+  if (!self->unknownFields_) {
+    self->unknownFields_ = [[GPBUnknownFieldSet alloc] init];
+    GPBBecomeVisibleToAutocreator(self);
+  }
+  return self->unknownFields_;
+}
+
+@implementation GPBMessage
+
++ (void)initialize {
+  Class pbMessageClass = [GPBMessage class];
+  if ([self class] == pbMessageClass) {
+    // This is here to start up the "base" class descriptor.
+    [self descriptor];
+    // Message shares extension method resolving with GPBRootObject so insure
+    // it is started up at the same time.
+    (void)[GPBRootObject class];
+  } else if ([self superclass] == pbMessageClass) {
+    // This is here to start up all the "message" subclasses. Just needs to be
+    // done for the messages, not any of the subclasses.
+    // This must be done in initialize to enforce thread safety of start up of
+    // the protocol buffer library.
+    // Note: The generated code for -descriptor calls
+    // +[GPBDescriptor allocDescriptorForClass:...], passing the GPBRootObject
+    // subclass for the file.  That call chain is what ensures that *Root class
+    // is started up to support extension resolution off the message class
+    // (+resolveClassMethod: below) in a thread safe manner.
+    [self descriptor];
+  }
+}
+
++ (instancetype)allocWithZone:(NSZone *)zone {
+  // Override alloc to allocate our classes with the additional storage
+  // required for the instance variables.
+  GPBDescriptor *descriptor = [self descriptor];
+  return NSAllocateObject(self, descriptor->storageSize_, zone);
+}
+
++ (instancetype)alloc {
+  return [self allocWithZone:nil];
+}
+
++ (GPBDescriptor *)descriptor {
+  // This is thread safe because it is called from +initialize.
+  static GPBDescriptor *descriptor = NULL;
+  static GPBFileDescriptor *fileDescriptor = NULL;
+  if (!descriptor) {
+    // Use a dummy file that marks it as proto2 syntax so when used generically
+    // it supports unknowns/etc.
+    fileDescriptor =
+        [[GPBFileDescriptor alloc] initWithPackage:@"internal"
+                                            syntax:GPBFileSyntaxProto2];
+
+    descriptor = [GPBDescriptor allocDescriptorForClass:[GPBMessage class]
+                                              rootClass:Nil
+                                                   file:fileDescriptor
+                                                 fields:NULL
+                                             fieldCount:0
+                                            storageSize:0
+                                                  flags:0];
+  }
+  return descriptor;
+}
+
++ (instancetype)message {
+  return [[[self alloc] init] autorelease];
+}
+
+- (instancetype)init {
+  if ((self = [super init])) {
+    messageStorage_ = (GPBMessage_StoragePtr)(
+        ((uint8_t *)self) + class_getInstanceSize([self class]));
+  }
+
+  return self;
+}
+
+- (instancetype)initWithData:(NSData *)data error:(NSError **)errorPtr {
+  return [self initWithData:data extensionRegistry:nil error:errorPtr];
+}
+
+- (instancetype)initWithData:(NSData *)data
+           extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                       error:(NSError **)errorPtr {
+  if ((self = [self init])) {
+    @try {
+      [self mergeFromData:data extensionRegistry:extensionRegistry];
+      if (errorPtr) {
+        *errorPtr = nil;
+      }
+    }
+    @catch (NSException *exception) {
+      [self release];
+      self = nil;
+      if (errorPtr) {
+        *errorPtr = ErrorFromException(exception);
+      }
+    }
+#ifdef DEBUG
+    if (self && !self.initialized) {
+      [self release];
+      self = nil;
+      if (errorPtr) {
+        *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil);
+      }
+    }
+#endif
+  }
+  return self;
+}
+
+- (instancetype)initWithCodedInputStream:(GPBCodedInputStream *)input
+                       extensionRegistry:
+                           (GPBExtensionRegistry *)extensionRegistry
+                                   error:(NSError **)errorPtr {
+  if ((self = [self init])) {
+    @try {
+      [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry];
+      if (errorPtr) {
+        *errorPtr = nil;
+      }
+    }
+    @catch (NSException *exception) {
+      [self release];
+      self = nil;
+      if (errorPtr) {
+        *errorPtr = ErrorFromException(exception);
+      }
+    }
+#ifdef DEBUG
+    if (self && !self.initialized) {
+      [self release];
+      self = nil;
+      if (errorPtr) {
+        *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil);
+      }
+    }
+#endif
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [self internalClear:NO];
+  NSCAssert(!autocreator_, @"Autocreator was not cleared before dealloc.");
+  if (readOnlySemaphore_) {
+    dispatch_release(readOnlySemaphore_);
+  }
+  [super dealloc];
+}
+
+- (void)copyFieldsInto:(GPBMessage *)message
+                  zone:(NSZone *)zone
+            descriptor:(GPBDescriptor *)descriptor {
+  // Copy all the storage...
+  memcpy(message->messageStorage_, messageStorage_, descriptor->storageSize_);
+
+  GPBFileSyntax syntax = descriptor.file.syntax;
+
+  // Loop over the fields doing fixup...
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (GPBFieldIsMapOrArray(field)) {
+      id value = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      if (value) {
+        // We need to copy the array/map, but the catch is for message fields,
+        // we also need to ensure all the messages as those need copying also.
+        id newValue;
+        if (GPBFieldDataTypeIsMessage(field)) {
+          if (field.fieldType == GPBFieldTypeRepeated) {
+            NSArray *existingArray = (NSArray *)value;
+            NSMutableArray *newArray =
+                [[NSMutableArray alloc] initWithCapacity:existingArray.count];
+            newValue = newArray;
+            for (GPBMessage *msg in existingArray) {
+              GPBMessage *copiedMsg = [msg copyWithZone:zone];
+              [newArray addObject:copiedMsg];
+              [copiedMsg release];
+            }
+          } else {
+            if (field.mapKeyDataType == GPBDataTypeString) {
+              // Map is an NSDictionary.
+              NSDictionary *existingDict = value;
+              NSMutableDictionary *newDict = [[NSMutableDictionary alloc]
+                  initWithCapacity:existingDict.count];
+              newValue = newDict;
+              [existingDict enumerateKeysAndObjectsUsingBlock:^(NSString *key,
+                                                                GPBMessage *msg,
+                                                                BOOL *stop) {
+#pragma unused(stop)
+                GPBMessage *copiedMsg = [msg copyWithZone:zone];
+                [newDict setObject:copiedMsg forKey:key];
+                [copiedMsg release];
+              }];
+            } else {
+              // Is one of the GPB*ObjectDictionary classes.  Type doesn't
+              // matter, just need one to invoke the selector.
+              GPBInt32ObjectDictionary *existingDict = value;
+              newValue = [existingDict deepCopyWithZone:zone];
+            }
+          }
+        } else {
+          // Not messages (but is a map/array)...
+          if (field.fieldType == GPBFieldTypeRepeated) {
+            if (GPBFieldDataTypeIsObject(field)) {
+              // NSArray
+              newValue = [value mutableCopyWithZone:zone];
+            } else {
+              // GPB*Array
+              newValue = [value copyWithZone:zone];
+            }
+          } else {
+            if ((field.mapKeyDataType == GPBDataTypeString) &&
+                GPBFieldDataTypeIsObject(field)) {
+              // NSDictionary
+              newValue = [value mutableCopyWithZone:zone];
+            } else {
+              // Is one of the GPB*Dictionary classes.  Type doesn't matter,
+              // just need one to invoke the selector.
+              GPBInt32Int32Dictionary *existingDict = value;
+              newValue = [existingDict copyWithZone:zone];
+            }
+          }
+        }
+        // We retain here because the memcpy picked up the pointer value and
+        // the next call to SetRetainedObject... will release the current value.
+        [value retain];
+        GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue,
+                                                  syntax);
+      }
+    } else if (GPBFieldDataTypeIsMessage(field)) {
+      // For object types, if we have a value, copy it.  If we don't,
+      // zero it to remove the pointer to something that was autocreated
+      // (and the ptr just got memcpyed).
+      if (GPBGetHasIvarField(self, field)) {
+        GPBMessage *value = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBMessage *newValue = [value copyWithZone:zone];
+        // We retain here because the memcpy picked up the pointer value and
+        // the next call to SetRetainedObject... will release the current value.
+        [value retain];
+        GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue,
+                                                  syntax);
+      } else {
+        uint8_t *storage = (uint8_t *)message->messageStorage_;
+        id *typePtr = (id *)&storage[field->description_->offset];
+        *typePtr = NULL;
+      }
+    } else if (GPBFieldDataTypeIsObject(field) &&
+               GPBGetHasIvarField(self, field)) {
+      // A set string/data value (message picked off above), copy it.
+      id value = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      id newValue = [value copyWithZone:zone];
+      // We retain here because the memcpy picked up the pointer value and
+      // the next call to SetRetainedObject... will release the current value.
+      [value retain];
+      GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue,
+                                                syntax);
+    } else {
+      // memcpy took care of the rest of the primitive fields if they were set.
+    }
+  }  // for (field in descriptor->fields_)
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+  GPBDescriptor *descriptor = [self descriptor];
+  GPBMessage *result = [[descriptor.messageClass allocWithZone:zone] init];
+
+  [self copyFieldsInto:result zone:zone descriptor:descriptor];
+  // Make immutable copies of the extra bits.
+  result->unknownFields_ = [unknownFields_ copyWithZone:zone];
+  result->extensionMap_ = CloneExtensionMap(extensionMap_, zone);
+  return result;
+}
+
+- (void)clear {
+  [self internalClear:YES];
+}
+
+- (void)internalClear:(BOOL)zeroStorage {
+  GPBDescriptor *descriptor = [self descriptor];
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (GPBFieldIsMapOrArray(field)) {
+      id arrayOrMap = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      if (arrayOrMap) {
+        if (field.fieldType == GPBFieldTypeRepeated) {
+          if (GPBFieldDataTypeIsObject(field)) {
+            if ([arrayOrMap isKindOfClass:[GPBAutocreatedArray class]]) {
+              GPBAutocreatedArray *autoArray = arrayOrMap;
+              if (autoArray->_autocreator == self) {
+                autoArray->_autocreator = nil;
+              }
+            }
+          } else {
+            // Type doesn't matter, it is a GPB*Array.
+            GPBInt32Array *gpbArray = arrayOrMap;
+            if (gpbArray->_autocreator == self) {
+              gpbArray->_autocreator = nil;
+            }
+          }
+        } else {
+          if ((field.mapKeyDataType == GPBDataTypeString) &&
+              GPBFieldDataTypeIsObject(field)) {
+            if ([arrayOrMap isKindOfClass:[GPBAutocreatedDictionary class]]) {
+              GPBAutocreatedDictionary *autoDict = arrayOrMap;
+              if (autoDict->_autocreator == self) {
+                autoDict->_autocreator = nil;
+              }
+            }
+          } else {
+            // Type doesn't matter, it is a GPB*Dictionary.
+            GPBInt32Int32Dictionary *gpbDict = arrayOrMap;
+            if (gpbDict->_autocreator == self) {
+              gpbDict->_autocreator = nil;
+            }
+          }
+        }
+        [arrayOrMap release];
+      }
+    } else if (GPBFieldDataTypeIsMessage(field)) {
+      GPBClearAutocreatedMessageIvarWithField(self, field);
+      GPBMessage *value = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      [value release];
+    } else if (GPBFieldDataTypeIsObject(field) &&
+               GPBGetHasIvarField(self, field)) {
+      id value = GPBGetObjectIvarWithField(self, field);
+      [value release];
+    }
+  }
+
+  // GPBClearMessageAutocreator() expects that its caller has already been
+  // removed from autocreatedExtensionMap_ so we set to nil first.
+  NSArray *autocreatedValues = [autocreatedExtensionMap_ allValues];
+  [autocreatedExtensionMap_ release];
+  autocreatedExtensionMap_ = nil;
+
+  // Since we're clearing all of our extensions, make sure that we clear the
+  // autocreator on any that we've created so they no longer refer to us.
+  for (GPBMessage *value in autocreatedValues) {
+    NSCAssert(GPBWasMessageAutocreatedBy(value, self),
+              @"Autocreated extension does not refer back to self.");
+    GPBClearMessageAutocreator(value);
+  }
+
+  [extensionMap_ release];
+  extensionMap_ = nil;
+  [unknownFields_ release];
+  unknownFields_ = nil;
+
+  // Note that clearing does not affect autocreator_. If we are being cleared
+  // because of a dealloc, then autocreator_ should be nil anyway. If we are
+  // being cleared because someone explicitly clears us, we don't want to
+  // sever our relationship with our autocreator.
+
+  if (zeroStorage) {
+    memset(messageStorage_, 0, descriptor->storageSize_);
+  }
+}
+
+- (BOOL)isInitialized {
+  GPBDescriptor *descriptor = [self descriptor];
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (field.isRequired) {
+      if (!GPBGetHasIvarField(self, field)) {
+        return NO;
+      }
+    }
+    if (GPBFieldDataTypeIsMessage(field)) {
+      GPBFieldType fieldType = field.fieldType;
+      if (fieldType == GPBFieldTypeSingle) {
+        if (field.isRequired) {
+          GPBMessage *message = GPBGetMessageMessageField(self, field);
+          if (!message.initialized) {
+            return NO;
+          }
+        } else {
+          NSAssert(field.isOptional,
+                   @"%@: Single message field %@ not required or optional?",
+                   [self class], field.name);
+          if (GPBGetHasIvarField(self, field)) {
+            GPBMessage *message = GPBGetMessageMessageField(self, field);
+            if (!message.initialized) {
+              return NO;
+            }
+          }
+        }
+      } else if (fieldType == GPBFieldTypeRepeated) {
+        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        for (GPBMessage *message in array) {
+          if (!message.initialized) {
+            return NO;
+          }
+        }
+      } else {  // fieldType == GPBFieldTypeMap
+        if (field.mapKeyDataType == GPBDataTypeString) {
+          NSDictionary *map =
+              GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+          if (map && !GPBDictionaryIsInitializedInternalHelper(map, field)) {
+            return NO;
+          }
+        } else {
+          // Real type is GPB*ObjectDictionary, exact type doesn't matter.
+          GPBInt32ObjectDictionary *map =
+              GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+          if (map && ![map isInitialized]) {
+            return NO;
+          }
+        }
+      }
+    }
+  }
+
+  __block BOOL result = YES;
+  [extensionMap_
+      enumerateKeysAndObjectsUsingBlock:^(GPBExtensionDescriptor *extension,
+                                          id obj,
+                                          BOOL *stop) {
+        if (GPBExtensionIsMessage(extension)) {
+          if (extension.isRepeated) {
+            for (GPBMessage *msg in obj) {
+              if (!msg.initialized) {
+                result = NO;
+                *stop = YES;
+                break;
+              }
+            }
+          } else {
+            GPBMessage *asMsg = obj;
+            if (!asMsg.initialized) {
+              result = NO;
+              *stop = YES;
+            }
+          }
+        }
+      }];
+  return result;
+}
+
+- (GPBDescriptor *)descriptor {
+  return [[self class] descriptor];
+}
+
+- (NSData *)data {
+#ifdef DEBUG
+  if (!self.initialized) {
+    return nil;
+  }
+#endif
+  NSMutableData *data = [NSMutableData dataWithLength:[self serializedSize]];
+  GPBCodedOutputStream *stream =
+      [[GPBCodedOutputStream alloc] initWithData:data];
+  @try {
+    [self writeToCodedOutputStream:stream];
+  }
+  @catch (NSException *exception) {
+    // This really shouldn't happen. The only way writeToCodedOutputStream:
+    // could throw is if something in the library has a bug and the
+    // serializedSize was wrong.
+#ifdef DEBUG
+    NSLog(@"%@: Internal exception while building message data: %@",
+          [self class], exception);
+#endif
+    data = nil;
+  }
+  [stream release];
+  return data;
+}
+
+- (NSData *)delimitedData {
+  size_t serializedSize = [self serializedSize];
+  size_t varintSize = GPBComputeRawVarint32SizeForInteger(serializedSize);
+  NSMutableData *data =
+      [NSMutableData dataWithLength:(serializedSize + varintSize)];
+  GPBCodedOutputStream *stream =
+      [[GPBCodedOutputStream alloc] initWithData:data];
+  @try {
+    [self writeDelimitedToCodedOutputStream:stream];
+  }
+  @catch (NSException *exception) {
+    // This really shouldn't happen.  The only way writeToCodedOutputStream:
+    // could throw is if something in the library has a bug and the
+    // serializedSize was wrong.
+#ifdef DEBUG
+    NSLog(@"%@: Internal exception while building message delimitedData: %@",
+          [self class], exception);
+#endif
+    // If it happens, truncate.
+    data.length = 0;
+  }
+  [stream release];
+  return data;
+}
+
+- (void)writeToOutputStream:(NSOutputStream *)output {
+  GPBCodedOutputStream *stream =
+      [[GPBCodedOutputStream alloc] initWithOutputStream:output];
+  [self writeToCodedOutputStream:stream];
+  [stream release];
+}
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output {
+  GPBDescriptor *descriptor = [self descriptor];
+  NSArray *fieldsArray = descriptor->fields_;
+  NSUInteger fieldCount = fieldsArray.count;
+  const GPBExtensionRange *extensionRanges = descriptor.extensionRanges;
+  NSUInteger extensionRangesCount = descriptor.extensionRangesCount;
+  for (NSUInteger i = 0, j = 0; i < fieldCount || j < extensionRangesCount;) {
+    if (i == fieldCount) {
+      [self writeExtensionsToCodedOutputStream:output
+                                         range:extensionRanges[j++]];
+    } else if (j == extensionRangesCount ||
+               GPBFieldNumber(fieldsArray[i]) < extensionRanges[j].start) {
+      [self writeField:fieldsArray[i++] toCodedOutputStream:output];
+    } else {
+      [self writeExtensionsToCodedOutputStream:output
+                                         range:extensionRanges[j++]];
+    }
+  }
+  if (descriptor.isWireFormat) {
+    [unknownFields_ writeAsMessageSetTo:output];
+  } else {
+    [unknownFields_ writeToCodedOutputStream:output];
+  }
+}
+
+- (void)writeDelimitedToOutputStream:(NSOutputStream *)output {
+  GPBCodedOutputStream *codedOutput =
+      [[GPBCodedOutputStream alloc] initWithOutputStream:output];
+  [self writeDelimitedToCodedOutputStream:codedOutput];
+  [codedOutput release];
+}
+
+- (void)writeDelimitedToCodedOutputStream:(GPBCodedOutputStream *)output {
+  [output writeRawVarintSizeTAs32:[self serializedSize]];
+  [self writeToCodedOutputStream:output];
+}
+
+- (void)writeField:(GPBFieldDescriptor *)field
+    toCodedOutputStream:(GPBCodedOutputStream *)output {
+  GPBFieldType fieldType = field.fieldType;
+  if (fieldType == GPBFieldTypeSingle) {
+    BOOL has = GPBGetHasIvarField(self, field);
+    if (!has) {
+      return;
+    }
+  }
+  uint32_t fieldNumber = GPBFieldNumber(field);
+
+//%PDDM-DEFINE FIELD_CASE(TYPE, REAL_TYPE)
+//%FIELD_CASE_FULL(TYPE, REAL_TYPE, REAL_TYPE)
+//%PDDM-DEFINE FIELD_CASE_FULL(TYPE, REAL_TYPE, ARRAY_TYPE)
+//%    case GPBDataType##TYPE:
+//%      if (fieldType == GPBFieldTypeRepeated) {
+//%        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+//%        GPB##ARRAY_TYPE##Array *array =
+//%            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+//%        [output write##TYPE##Array:fieldNumber values:array tag:tag];
+//%      } else if (fieldType == GPBFieldTypeSingle) {
+//%        [output write##TYPE:fieldNumber
+//%              TYPE$S  value:GPBGetMessage##REAL_TYPE##Field(self, field)];
+//%      } else {  // fieldType == GPBFieldTypeMap
+//%        // Exact type here doesn't matter.
+//%        GPBInt32##ARRAY_TYPE##Dictionary *dict =
+//%            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+//%        [dict writeToCodedOutputStream:output asField:field];
+//%      }
+//%      break;
+//%
+//%PDDM-DEFINE FIELD_CASE2(TYPE)
+//%    case GPBDataType##TYPE:
+//%      if (fieldType == GPBFieldTypeRepeated) {
+//%        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+//%        [output write##TYPE##Array:fieldNumber values:array];
+//%      } else if (fieldType == GPBFieldTypeSingle) {
+//%        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+//%        // again.
+//%        [output write##TYPE:fieldNumber
+//%              TYPE$S  value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)];
+//%      } else {  // fieldType == GPBFieldTypeMap
+//%        // Exact type here doesn't matter.
+//%        id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+//%        GPBDataType mapKeyDataType = field.mapKeyDataType;
+//%        if (mapKeyDataType == GPBDataTypeString) {
+//%          GPBDictionaryWriteToStreamInternalHelper(output, dict, field);
+//%        } else {
+//%          [dict writeToCodedOutputStream:output asField:field];
+//%        }
+//%      }
+//%      break;
+//%
+
+  switch (GPBGetFieldDataType(field)) {
+
+//%PDDM-EXPAND FIELD_CASE(Bool, Bool)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeBool:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBBoolArray *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeBoolArray:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeBool:fieldNumber
+                    value:GPBGetMessageBoolField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32BoolDictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Fixed32, UInt32)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeFixed32:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBUInt32Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeFixed32Array:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeFixed32:fieldNumber
+                       value:GPBGetMessageUInt32Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32UInt32Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(SFixed32, Int32)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeSFixed32:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt32Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeSFixed32Array:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeSFixed32:fieldNumber
+                        value:GPBGetMessageInt32Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int32Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Float, Float)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeFloat:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBFloatArray *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeFloatArray:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeFloat:fieldNumber
+                     value:GPBGetMessageFloatField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32FloatDictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Fixed64, UInt64)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeFixed64:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBUInt64Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeFixed64Array:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeFixed64:fieldNumber
+                       value:GPBGetMessageUInt64Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32UInt64Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(SFixed64, Int64)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeSFixed64:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt64Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeSFixed64Array:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeSFixed64:fieldNumber
+                        value:GPBGetMessageInt64Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int64Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Double, Double)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeDouble:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBDoubleArray *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeDoubleArray:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeDouble:fieldNumber
+                      value:GPBGetMessageDoubleField(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32DoubleDictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Int32, Int32)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeInt32:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt32Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeInt32Array:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeInt32:fieldNumber
+                     value:GPBGetMessageInt32Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int32Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(Int64, Int64)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeInt64:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt64Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeInt64Array:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeInt64:fieldNumber
+                     value:GPBGetMessageInt64Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int64Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(SInt32, Int32)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeSInt32:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt32Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeSInt32Array:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeSInt32:fieldNumber
+                      value:GPBGetMessageInt32Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int32Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(SInt64, Int64)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeSInt64:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBInt64Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeSInt64Array:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeSInt64:fieldNumber
+                      value:GPBGetMessageInt64Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32Int64Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(UInt32, UInt32)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeUInt32:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBUInt32Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeUInt32Array:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeUInt32:fieldNumber
+                      value:GPBGetMessageUInt32Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32UInt32Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE(UInt64, UInt64)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeUInt64:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBUInt64Array *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeUInt64Array:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeUInt64:fieldNumber
+                      value:GPBGetMessageUInt64Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32UInt64Dictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE_FULL(Enum, Int32, Enum)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeEnum:
+      if (fieldType == GPBFieldTypeRepeated) {
+        uint32_t tag = field.isPackable ? GPBFieldTag(field) : 0;
+        GPBEnumArray *array =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeEnumArray:fieldNumber values:array tag:tag];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        [output writeEnum:fieldNumber
+                    value:GPBGetMessageInt32Field(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        GPBInt32EnumDictionary *dict =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [dict writeToCodedOutputStream:output asField:field];
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE2(Bytes)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeBytes:
+      if (fieldType == GPBFieldTypeRepeated) {
+        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeBytesArray:fieldNumber values:array];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+        // again.
+        [output writeBytes:fieldNumber
+                     value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBDataType mapKeyDataType = field.mapKeyDataType;
+        if (mapKeyDataType == GPBDataTypeString) {
+          GPBDictionaryWriteToStreamInternalHelper(output, dict, field);
+        } else {
+          [dict writeToCodedOutputStream:output asField:field];
+        }
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE2(String)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeString:
+      if (fieldType == GPBFieldTypeRepeated) {
+        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeStringArray:fieldNumber values:array];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+        // again.
+        [output writeString:fieldNumber
+                      value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBDataType mapKeyDataType = field.mapKeyDataType;
+        if (mapKeyDataType == GPBDataTypeString) {
+          GPBDictionaryWriteToStreamInternalHelper(output, dict, field);
+        } else {
+          [dict writeToCodedOutputStream:output asField:field];
+        }
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE2(Message)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeMessage:
+      if (fieldType == GPBFieldTypeRepeated) {
+        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeMessageArray:fieldNumber values:array];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+        // again.
+        [output writeMessage:fieldNumber
+                       value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBDataType mapKeyDataType = field.mapKeyDataType;
+        if (mapKeyDataType == GPBDataTypeString) {
+          GPBDictionaryWriteToStreamInternalHelper(output, dict, field);
+        } else {
+          [dict writeToCodedOutputStream:output asField:field];
+        }
+      }
+      break;
+
+//%PDDM-EXPAND FIELD_CASE2(Group)
+// This block of code is generated, do not edit it directly.
+
+    case GPBDataTypeGroup:
+      if (fieldType == GPBFieldTypeRepeated) {
+        NSArray *array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [output writeGroupArray:fieldNumber values:array];
+      } else if (fieldType == GPBFieldTypeSingle) {
+        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has check
+        // again.
+        [output writeGroup:fieldNumber
+                     value:GPBGetObjectIvarWithFieldNoAutocreate(self, field)];
+      } else {  // fieldType == GPBFieldTypeMap
+        // Exact type here doesn't matter.
+        id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBDataType mapKeyDataType = field.mapKeyDataType;
+        if (mapKeyDataType == GPBDataTypeString) {
+          GPBDictionaryWriteToStreamInternalHelper(output, dict, field);
+        } else {
+          [dict writeToCodedOutputStream:output asField:field];
+        }
+      }
+      break;
+
+//%PDDM-EXPAND-END (18 expansions)
+  }
+}
+
+#pragma mark - Extensions
+
+- (id)getExtension:(GPBExtensionDescriptor *)extension {
+  CheckExtension(self, extension);
+  id value = [extensionMap_ objectForKey:extension];
+  if (value != nil) {
+    return value;
+  }
+
+  // No default for repeated.
+  if (extension.isRepeated) {
+    return nil;
+  }
+  // Non messages get their default.
+  if (!GPBExtensionIsMessage(extension)) {
+    return extension.defaultValue;
+  }
+
+  // Check for an autocreated value.
+  GPBPrepareReadOnlySemaphore(self);
+  dispatch_semaphore_wait(readOnlySemaphore_, DISPATCH_TIME_FOREVER);
+  value = [autocreatedExtensionMap_ objectForKey:extension];
+  if (!value) {
+    // Auto create the message extensions to match normal fields.
+    value = CreateMessageWithAutocreatorForExtension(extension.msgClass, self,
+                                                     extension);
+
+    if (autocreatedExtensionMap_ == nil) {
+      autocreatedExtensionMap_ = [[NSMutableDictionary alloc] init];
+    }
+
+    // We can't simply call setExtension here because that would clear the new
+    // value's autocreator.
+    [autocreatedExtensionMap_ setObject:value forKey:extension];
+    [value release];
+  }
+
+  dispatch_semaphore_signal(readOnlySemaphore_);
+  return value;
+}
+
+- (id)getExistingExtension:(GPBExtensionDescriptor *)extension {
+  // This is an internal method so we don't need to call CheckExtension().
+  return [extensionMap_ objectForKey:extension];
+}
+
+- (BOOL)hasExtension:(GPBExtensionDescriptor *)extension {
+#if defined(DEBUG) && DEBUG
+  CheckExtension(self, extension);
+#endif  // DEBUG
+  return nil != [extensionMap_ objectForKey:extension];
+}
+
+- (NSArray *)extensionsCurrentlySet {
+  return [extensionMap_ allKeys];
+}
+
+- (void)writeExtensionsToCodedOutputStream:(GPBCodedOutputStream *)output
+                                     range:(GPBExtensionRange)range {
+  NSArray *sortedExtensions = [[extensionMap_ allKeys]
+      sortedArrayUsingSelector:@selector(compareByFieldNumber:)];
+  uint32_t start = range.start;
+  uint32_t end = range.end;
+  for (GPBExtensionDescriptor *extension in sortedExtensions) {
+    uint32_t fieldNumber = extension.fieldNumber;
+    if (fieldNumber >= start && fieldNumber < end) {
+      id value = [extensionMap_ objectForKey:extension];
+      GPBWriteExtensionValueToOutputStream(extension, value, output);
+    }
+  }
+}
+
+- (void)setExtension:(GPBExtensionDescriptor *)extension value:(id)value {
+  if (!value) {
+    [self clearExtension:extension];
+    return;
+  }
+
+  CheckExtension(self, extension);
+
+  if (extension.repeated) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Must call addExtension() for repeated types."];
+  }
+
+  if (extensionMap_ == nil) {
+    extensionMap_ = [[NSMutableDictionary alloc] init];
+  }
+
+  // This pointless cast is for CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION.
+  // Without it, the compiler complains we're passing an id nullable when
+  // setObject:forKey: requires a id nonnull for the value. The check for
+  // !value at the start of the method ensures it isn't nil, but the check
+  // isn't smart enough to realize that.
+  [extensionMap_ setObject:(id)value forKey:extension];
+
+  GPBExtensionDescriptor *descriptor = extension;
+
+  if (GPBExtensionIsMessage(descriptor) && !descriptor.isRepeated) {
+    GPBMessage *autocreatedValue =
+        [[autocreatedExtensionMap_ objectForKey:extension] retain];
+    // Must remove from the map before calling GPBClearMessageAutocreator() so
+    // that GPBClearMessageAutocreator() knows its safe to clear.
+    [autocreatedExtensionMap_ removeObjectForKey:extension];
+    GPBClearMessageAutocreator(autocreatedValue);
+    [autocreatedValue release];
+  }
+
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+- (void)addExtension:(GPBExtensionDescriptor *)extension value:(id)value {
+  CheckExtension(self, extension);
+
+  if (!extension.repeated) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Must call setExtension() for singular types."];
+  }
+
+  if (extensionMap_ == nil) {
+    extensionMap_ = [[NSMutableDictionary alloc] init];
+  }
+  NSMutableArray *list = [extensionMap_ objectForKey:extension];
+  if (list == nil) {
+    list = [NSMutableArray array];
+    [extensionMap_ setObject:list forKey:extension];
+  }
+
+  [list addObject:value];
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+- (void)setExtension:(GPBExtensionDescriptor *)extension
+               index:(NSUInteger)idx
+               value:(id)value {
+  CheckExtension(self, extension);
+
+  if (!extension.repeated) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Must call setExtension() for singular types."];
+  }
+
+  if (extensionMap_ == nil) {
+    extensionMap_ = [[NSMutableDictionary alloc] init];
+  }
+
+  NSMutableArray *list = [extensionMap_ objectForKey:extension];
+
+  [list replaceObjectAtIndex:idx withObject:value];
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+- (void)clearExtension:(GPBExtensionDescriptor *)extension {
+  CheckExtension(self, extension);
+
+  // Only become visible if there was actually a value to clear.
+  if ([extensionMap_ objectForKey:extension]) {
+    [extensionMap_ removeObjectForKey:extension];
+    GPBBecomeVisibleToAutocreator(self);
+  }
+}
+
+#pragma mark - mergeFrom
+
+- (void)mergeFromData:(NSData *)data
+    extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data];
+  [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry];
+  [input checkLastTagWas:0];
+  [input release];
+}
+
+#pragma mark - mergeDelimitedFrom
+
+- (void)mergeDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
+                         extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  GPBCodedInputStreamState *state = &input->state_;
+  if (GPBCodedInputStreamIsAtEnd(state)) {
+    return;
+  }
+  NSData *data = GPBCodedInputStreamReadRetainedBytesNoCopy(state);
+  if (data == nil) {
+    return;
+  }
+  [self mergeFromData:data extensionRegistry:extensionRegistry];
+  [data release];
+}
+
+#pragma mark - Parse From Data Support
+
++ (instancetype)parseFromData:(NSData *)data error:(NSError **)errorPtr {
+  return [self parseFromData:data extensionRegistry:nil error:errorPtr];
+}
+
++ (instancetype)parseFromData:(NSData *)data
+            extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                        error:(NSError **)errorPtr {
+  return [[[self alloc] initWithData:data
+                   extensionRegistry:extensionRegistry
+                               error:errorPtr] autorelease];
+}
+
++ (instancetype)parseFromCodedInputStream:(GPBCodedInputStream *)input
+                        extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                                    error:(NSError **)errorPtr {
+  return
+      [[[self alloc] initWithCodedInputStream:input
+                            extensionRegistry:extensionRegistry
+                                        error:errorPtr] autorelease];
+}
+
+#pragma mark - Parse Delimited From Data Support
+
++ (instancetype)parseDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
+                                 extensionRegistry:
+                                     (GPBExtensionRegistry *)extensionRegistry
+                                             error:(NSError **)errorPtr {
+  GPBMessage *message = [[[self alloc] init] autorelease];
+  @try {
+    [message mergeDelimitedFromCodedInputStream:input
+                              extensionRegistry:extensionRegistry];
+    if (errorPtr) {
+      *errorPtr = nil;
+    }
+  }
+  @catch (NSException *exception) {
+    message = nil;
+    if (errorPtr) {
+      *errorPtr = ErrorFromException(exception);
+    }
+  }
+#ifdef DEBUG
+  if (message && !message.initialized) {
+    message = nil;
+    if (errorPtr) {
+      *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil);
+    }
+  }
+#endif
+  return message;
+}
+
+#pragma mark - Unknown Field Support
+
+- (GPBUnknownFieldSet *)unknownFields {
+  return unknownFields_;
+}
+
+- (void)setUnknownFields:(GPBUnknownFieldSet *)unknownFields {
+  if (unknownFields != unknownFields_) {
+    [unknownFields_ release];
+    unknownFields_ = [unknownFields copy];
+    GPBBecomeVisibleToAutocreator(self);
+  }
+}
+
+- (void)parseMessageSet:(GPBCodedInputStream *)input
+      extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  uint32_t typeId = 0;
+  NSData *rawBytes = nil;
+  GPBExtensionDescriptor *extension = nil;
+  GPBCodedInputStreamState *state = &input->state_;
+  while (true) {
+    uint32_t tag = GPBCodedInputStreamReadTag(state);
+    if (tag == 0) {
+      break;
+    }
+
+    if (tag == GPBWireFormatMessageSetTypeIdTag) {
+      typeId = GPBCodedInputStreamReadUInt32(state);
+      if (typeId != 0) {
+        extension = [extensionRegistry extensionForDescriptor:[self descriptor]
+                                                  fieldNumber:typeId];
+      }
+    } else if (tag == GPBWireFormatMessageSetMessageTag) {
+      rawBytes =
+          [GPBCodedInputStreamReadRetainedBytesNoCopy(state) autorelease];
+    } else {
+      if (![input skipField:tag]) {
+        break;
+      }
+    }
+  }
+
+  [input checkLastTagWas:GPBWireFormatMessageSetItemEndTag];
+
+  if (rawBytes != nil && typeId != 0) {
+    if (extension != nil) {
+      GPBCodedInputStream *newInput =
+          [[GPBCodedInputStream alloc] initWithData:rawBytes];
+      GPBExtensionMergeFromInputStream(extension,
+                                       extension.packable,
+                                       newInput,
+                                       extensionRegistry,
+                                       self);
+      [newInput release];
+    } else {
+      GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
+      // rawBytes was created via a NoCopy, so it can be reusing a
+      // subrange of another NSData that might go out of scope as things
+      // unwind, so a copy is needed to ensure what is saved in the
+      // unknown fields stays valid.
+      NSData *cloned = [NSData dataWithData:rawBytes];
+      [unknownFields mergeMessageSetMessage:typeId data:cloned];
+    }
+  }
+}
+
+- (BOOL)parseUnknownField:(GPBCodedInputStream *)input
+        extensionRegistry:(GPBExtensionRegistry *)extensionRegistry
+                      tag:(uint32_t)tag {
+  GPBWireFormat wireType = GPBWireFormatGetTagWireType(tag);
+  int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag);
+
+  GPBDescriptor *descriptor = [self descriptor];
+  GPBExtensionDescriptor *extension =
+      [extensionRegistry extensionForDescriptor:descriptor
+                                    fieldNumber:fieldNumber];
+  if (extension == nil) {
+    if (descriptor.wireFormat && GPBWireFormatMessageSetItemTag == tag) {
+      [self parseMessageSet:input extensionRegistry:extensionRegistry];
+      return YES;
+    }
+  } else {
+    if (extension.wireType == wireType) {
+      GPBExtensionMergeFromInputStream(extension,
+                                       extension.packable,
+                                       input,
+                                       extensionRegistry,
+                                       self);
+      return YES;
+    }
+    // Primitive, repeated types can be packed on unpacked on the wire, and are
+    // parsed either way.
+    if ([extension isRepeated] &&
+        !GPBDataTypeIsObject(extension->description_->dataType) &&
+        (extension.alternateWireType == wireType)) {
+      GPBExtensionMergeFromInputStream(extension,
+                                       !extension.packable,
+                                       input,
+                                       extensionRegistry,
+                                       self);
+      return YES;
+    }
+  }
+  if ([GPBUnknownFieldSet isFieldTag:tag]) {
+    GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
+    return [unknownFields mergeFieldFrom:tag input:input];
+  } else {
+    return NO;
+  }
+}
+
+- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data {
+  GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
+  [unknownFields addUnknownMapEntry:fieldNum value:data];
+}
+
+#pragma mark - MergeFromCodedInputStream Support
+
+static void MergeSingleFieldFromCodedInputStream(
+    GPBMessage *self, GPBFieldDescriptor *field, GPBFileSyntax syntax,
+    GPBCodedInputStream *input, GPBExtensionRegistry *extensionRegistry) {
+  GPBDataType fieldDataType = GPBGetFieldDataType(field);
+  switch (fieldDataType) {
+#define CASE_SINGLE_POD(NAME, TYPE, FUNC_TYPE)                             \
+    case GPBDataType##NAME: {                                              \
+      TYPE val = GPBCodedInputStreamRead##NAME(&input->state_);            \
+      GPBSet##FUNC_TYPE##IvarWithFieldInternal(self, field, val, syntax);  \
+      break;                                                               \
+            }
+#define CASE_SINGLE_OBJECT(NAME)                                           \
+    case GPBDataType##NAME: {                                              \
+      id val = GPBCodedInputStreamReadRetained##NAME(&input->state_);      \
+      GPBSetRetainedObjectIvarWithFieldInternal(self, field, val, syntax); \
+      break;                                                               \
+    }
+      CASE_SINGLE_POD(Bool, BOOL, Bool)
+      CASE_SINGLE_POD(Fixed32, uint32_t, UInt32)
+      CASE_SINGLE_POD(SFixed32, int32_t, Int32)
+      CASE_SINGLE_POD(Float, float, Float)
+      CASE_SINGLE_POD(Fixed64, uint64_t, UInt64)
+      CASE_SINGLE_POD(SFixed64, int64_t, Int64)
+      CASE_SINGLE_POD(Double, double, Double)
+      CASE_SINGLE_POD(Int32, int32_t, Int32)
+      CASE_SINGLE_POD(Int64, int64_t, Int64)
+      CASE_SINGLE_POD(SInt32, int32_t, Int32)
+      CASE_SINGLE_POD(SInt64, int64_t, Int64)
+      CASE_SINGLE_POD(UInt32, uint32_t, UInt32)
+      CASE_SINGLE_POD(UInt64, uint64_t, UInt64)
+      CASE_SINGLE_OBJECT(Bytes)
+      CASE_SINGLE_OBJECT(String)
+#undef CASE_SINGLE_POD
+#undef CASE_SINGLE_OBJECT
+
+    case GPBDataTypeMessage: {
+      if (GPBGetHasIvarField(self, field)) {
+        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has
+        // check again.
+        GPBMessage *message =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [input readMessage:message extensionRegistry:extensionRegistry];
+      } else {
+        GPBMessage *message = [[field.msgClass alloc] init];
+        [input readMessage:message extensionRegistry:extensionRegistry];
+        GPBSetRetainedObjectIvarWithFieldInternal(self, field, message, syntax);
+      }
+      break;
+    }
+
+    case GPBDataTypeGroup: {
+      if (GPBGetHasIvarField(self, field)) {
+        // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has
+        // check again.
+        GPBMessage *message =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [input readGroup:GPBFieldNumber(field)
+                      message:message
+            extensionRegistry:extensionRegistry];
+      } else {
+        GPBMessage *message = [[field.msgClass alloc] init];
+        [input readGroup:GPBFieldNumber(field)
+                      message:message
+            extensionRegistry:extensionRegistry];
+        GPBSetRetainedObjectIvarWithFieldInternal(self, field, message, syntax);
+      }
+      break;
+    }
+
+    case GPBDataTypeEnum: {
+      int32_t val = GPBCodedInputStreamReadEnum(&input->state_);
+      if (GPBHasPreservingUnknownEnumSemantics(syntax) ||
+          [field isValidEnumValue:val]) {
+        GPBSetInt32IvarWithFieldInternal(self, field, val, syntax);
+      } else {
+        GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
+        [unknownFields mergeVarintField:GPBFieldNumber(field) value:val];
+      }
+    }
+  }  // switch
+}
+
+static void MergeRepeatedPackedFieldFromCodedInputStream(
+    GPBMessage *self, GPBFieldDescriptor *field, GPBFileSyntax syntax,
+    GPBCodedInputStream *input) {
+  GPBDataType fieldDataType = GPBGetFieldDataType(field);
+  GPBCodedInputStreamState *state = &input->state_;
+  id genericArray = GetOrCreateArrayIvarWithField(self, field, syntax);
+  int32_t length = GPBCodedInputStreamReadInt32(state);
+  size_t limit = GPBCodedInputStreamPushLimit(state, length);
+  while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
+    switch (fieldDataType) {
+#define CASE_REPEATED_PACKED_POD(NAME, TYPE, ARRAY_TYPE)      \
+     case GPBDataType##NAME: {                                \
+       TYPE val = GPBCodedInputStreamRead##NAME(state);       \
+       [(GPB##ARRAY_TYPE##Array *)genericArray addValue:val]; \
+       break;                                                 \
+     }
+        CASE_REPEATED_PACKED_POD(Bool, BOOL, Bool)
+        CASE_REPEATED_PACKED_POD(Fixed32, uint32_t, UInt32)
+        CASE_REPEATED_PACKED_POD(SFixed32, int32_t, Int32)
+        CASE_REPEATED_PACKED_POD(Float, float, Float)
+        CASE_REPEATED_PACKED_POD(Fixed64, uint64_t, UInt64)
+        CASE_REPEATED_PACKED_POD(SFixed64, int64_t, Int64)
+        CASE_REPEATED_PACKED_POD(Double, double, Double)
+        CASE_REPEATED_PACKED_POD(Int32, int32_t, Int32)
+        CASE_REPEATED_PACKED_POD(Int64, int64_t, Int64)
+        CASE_REPEATED_PACKED_POD(SInt32, int32_t, Int32)
+        CASE_REPEATED_PACKED_POD(SInt64, int64_t, Int64)
+        CASE_REPEATED_PACKED_POD(UInt32, uint32_t, UInt32)
+        CASE_REPEATED_PACKED_POD(UInt64, uint64_t, UInt64)
+#undef CASE_REPEATED_PACKED_POD
+
+      case GPBDataTypeBytes:
+      case GPBDataTypeString:
+      case GPBDataTypeMessage:
+      case GPBDataTypeGroup:
+        NSCAssert(NO, @"Non primitive types can't be packed");
+        break;
+
+      case GPBDataTypeEnum: {
+        int32_t val = GPBCodedInputStreamReadEnum(state);
+        if (GPBHasPreservingUnknownEnumSemantics(syntax) ||
+            [field isValidEnumValue:val]) {
+          [(GPBEnumArray*)genericArray addRawValue:val];
+        } else {
+          GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
+          [unknownFields mergeVarintField:GPBFieldNumber(field) value:val];
+        }
+        break;
+      }
+    }  // switch
+  }  // while(BytesUntilLimit() > 0)
+  GPBCodedInputStreamPopLimit(state, limit);
+}
+
+static void MergeRepeatedNotPackedFieldFromCodedInputStream(
+    GPBMessage *self, GPBFieldDescriptor *field, GPBFileSyntax syntax,
+    GPBCodedInputStream *input, GPBExtensionRegistry *extensionRegistry) {
+  GPBCodedInputStreamState *state = &input->state_;
+  id genericArray = GetOrCreateArrayIvarWithField(self, field, syntax);
+  switch (GPBGetFieldDataType(field)) {
+#define CASE_REPEATED_NOT_PACKED_POD(NAME, TYPE, ARRAY_TYPE) \
+   case GPBDataType##NAME: {                                 \
+     TYPE val = GPBCodedInputStreamRead##NAME(state);        \
+     [(GPB##ARRAY_TYPE##Array *)genericArray addValue:val];  \
+     break;                                                  \
+   }
+#define CASE_REPEATED_NOT_PACKED_OBJECT(NAME)                \
+   case GPBDataType##NAME: {                                 \
+     id val = GPBCodedInputStreamReadRetained##NAME(state);  \
+     [(NSMutableArray*)genericArray addObject:val];          \
+     [val release];                                          \
+     break;                                                  \
+   }
+      CASE_REPEATED_NOT_PACKED_POD(Bool, BOOL, Bool)
+      CASE_REPEATED_NOT_PACKED_POD(Fixed32, uint32_t, UInt32)
+      CASE_REPEATED_NOT_PACKED_POD(SFixed32, int32_t, Int32)
+      CASE_REPEATED_NOT_PACKED_POD(Float, float, Float)
+      CASE_REPEATED_NOT_PACKED_POD(Fixed64, uint64_t, UInt64)
+      CASE_REPEATED_NOT_PACKED_POD(SFixed64, int64_t, Int64)
+      CASE_REPEATED_NOT_PACKED_POD(Double, double, Double)
+      CASE_REPEATED_NOT_PACKED_POD(Int32, int32_t, Int32)
+      CASE_REPEATED_NOT_PACKED_POD(Int64, int64_t, Int64)
+      CASE_REPEATED_NOT_PACKED_POD(SInt32, int32_t, Int32)
+      CASE_REPEATED_NOT_PACKED_POD(SInt64, int64_t, Int64)
+      CASE_REPEATED_NOT_PACKED_POD(UInt32, uint32_t, UInt32)
+      CASE_REPEATED_NOT_PACKED_POD(UInt64, uint64_t, UInt64)
+      CASE_REPEATED_NOT_PACKED_OBJECT(Bytes)
+      CASE_REPEATED_NOT_PACKED_OBJECT(String)
+#undef CASE_REPEATED_NOT_PACKED_POD
+#undef CASE_NOT_PACKED_OBJECT
+    case GPBDataTypeMessage: {
+      GPBMessage *message = [[field.msgClass alloc] init];
+      [input readMessage:message extensionRegistry:extensionRegistry];
+      [(NSMutableArray*)genericArray addObject:message];
+      [message release];
+      break;
+    }
+    case GPBDataTypeGroup: {
+      GPBMessage *message = [[field.msgClass alloc] init];
+      [input readGroup:GPBFieldNumber(field)
+                    message:message
+          extensionRegistry:extensionRegistry];
+      [(NSMutableArray*)genericArray addObject:message];
+      [message release];
+      break;
+    }
+    case GPBDataTypeEnum: {
+      int32_t val = GPBCodedInputStreamReadEnum(state);
+      if (GPBHasPreservingUnknownEnumSemantics(syntax) ||
+          [field isValidEnumValue:val]) {
+        [(GPBEnumArray*)genericArray addRawValue:val];
+      } else {
+        GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
+        [unknownFields mergeVarintField:GPBFieldNumber(field) value:val];
+      }
+      break;
+    }
+  }  // switch
+}
+
+- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
+                extensionRegistry:(GPBExtensionRegistry *)extensionRegistry {
+  GPBDescriptor *descriptor = [self descriptor];
+  GPBFileSyntax syntax = descriptor.file.syntax;
+  GPBCodedInputStreamState *state = &input->state_;
+  uint32_t tag = 0;
+  NSUInteger startingIndex = 0;
+  NSArray *fields = descriptor->fields_;
+  NSUInteger numFields = fields.count;
+  while (YES) {
+    BOOL merged = NO;
+    tag = GPBCodedInputStreamReadTag(state);
+    if (tag == 0) {
+      break;  // Reached end.
+    }
+    for (NSUInteger i = 0; i < numFields; ++i) {
+      if (startingIndex >= numFields) startingIndex = 0;
+      GPBFieldDescriptor *fieldDescriptor = fields[startingIndex];
+      if (GPBFieldTag(fieldDescriptor) == tag) {
+        GPBFieldType fieldType = fieldDescriptor.fieldType;
+        if (fieldType == GPBFieldTypeSingle) {
+          MergeSingleFieldFromCodedInputStream(self, fieldDescriptor, syntax,
+                                               input, extensionRegistry);
+          // Well formed protos will only have a single field once, advance
+          // the starting index to the next field.
+          startingIndex += 1;
+        } else if (fieldType == GPBFieldTypeRepeated) {
+          if (fieldDescriptor.isPackable) {
+            MergeRepeatedPackedFieldFromCodedInputStream(
+                self, fieldDescriptor, syntax, input);
+            // Well formed protos will only have a repeated field that is
+            // packed once, advance the starting index to the next field.
+            startingIndex += 1;
+          } else {
+            MergeRepeatedNotPackedFieldFromCodedInputStream(
+                self, fieldDescriptor, syntax, input, extensionRegistry);
+          }
+        } else {  // fieldType == GPBFieldTypeMap
+          // GPB*Dictionary or NSDictionary, exact type doesn't matter at this
+          // point.
+          id map = GetOrCreateMapIvarWithField(self, fieldDescriptor, syntax);
+          [input readMapEntry:map
+            extensionRegistry:extensionRegistry
+                        field:fieldDescriptor
+                parentMessage:self];
+        }
+        merged = YES;
+        break;
+      } else {
+        startingIndex += 1;
+      }
+    }  // for(i < numFields)
+
+    if (!merged && (tag != 0)) {
+      // Primitive, repeated types can be packed on unpacked on the wire, and
+      // are parsed either way.  The above loop covered tag in the preferred
+      // for, so this need to check the alternate form.
+      for (NSUInteger i = 0; i < numFields; ++i) {
+        if (startingIndex >= numFields) startingIndex = 0;
+        GPBFieldDescriptor *fieldDescriptor = fields[startingIndex];
+        if ((fieldDescriptor.fieldType == GPBFieldTypeRepeated) &&
+            !GPBFieldDataTypeIsObject(fieldDescriptor) &&
+            (GPBFieldAlternateTag(fieldDescriptor) == tag)) {
+          BOOL alternateIsPacked = !fieldDescriptor.isPackable;
+          if (alternateIsPacked) {
+            MergeRepeatedPackedFieldFromCodedInputStream(
+                self, fieldDescriptor, syntax, input);
+            // Well formed protos will only have a repeated field that is
+            // packed once, advance the starting index to the next field.
+            startingIndex += 1;
+          } else {
+            MergeRepeatedNotPackedFieldFromCodedInputStream(
+                self, fieldDescriptor, syntax, input, extensionRegistry);
+          }
+          merged = YES;
+          break;
+        } else {
+          startingIndex += 1;
+        }
+      }
+    }
+
+    if (!merged) {
+      if (tag == 0) {
+        // zero signals EOF / limit reached
+        return;
+      } else {
+        if (![self parseUnknownField:input
+                   extensionRegistry:extensionRegistry
+                                 tag:tag]) {
+          // it's an endgroup tag
+          return;
+        }
+      }
+    }  // if(!merged)
+
+  }  // while(YES)
+}
+
+#pragma mark - MergeFrom Support
+
+- (void)mergeFrom:(GPBMessage *)other {
+  Class selfClass = [self class];
+  Class otherClass = [other class];
+  if (!([selfClass isSubclassOfClass:otherClass] ||
+        [otherClass isSubclassOfClass:selfClass])) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Classes must match %@ != %@", selfClass, otherClass];
+  }
+
+  // We assume something will be done and become visible.
+  GPBBecomeVisibleToAutocreator(self);
+
+  GPBDescriptor *descriptor = [[self class] descriptor];
+  GPBFileSyntax syntax = descriptor.file.syntax;
+
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    GPBFieldType fieldType = field.fieldType;
+    if (fieldType == GPBFieldTypeSingle) {
+      int32_t hasIndex = GPBFieldHasIndex(field);
+      uint32_t fieldNumber = GPBFieldNumber(field);
+      if (!GPBGetHasIvar(other, hasIndex, fieldNumber)) {
+        // Other doesn't have the field set, on to the next.
+        continue;
+      }
+      GPBDataType fieldDataType = GPBGetFieldDataType(field);
+      switch (fieldDataType) {
+        case GPBDataTypeBool:
+          GPBSetBoolIvarWithFieldInternal(
+              self, field, GPBGetMessageBoolField(other, field), syntax);
+          break;
+        case GPBDataTypeSFixed32:
+        case GPBDataTypeEnum:
+        case GPBDataTypeInt32:
+        case GPBDataTypeSInt32:
+          GPBSetInt32IvarWithFieldInternal(
+              self, field, GPBGetMessageInt32Field(other, field), syntax);
+          break;
+        case GPBDataTypeFixed32:
+        case GPBDataTypeUInt32:
+          GPBSetUInt32IvarWithFieldInternal(
+              self, field, GPBGetMessageUInt32Field(other, field), syntax);
+          break;
+        case GPBDataTypeSFixed64:
+        case GPBDataTypeInt64:
+        case GPBDataTypeSInt64:
+          GPBSetInt64IvarWithFieldInternal(
+              self, field, GPBGetMessageInt64Field(other, field), syntax);
+          break;
+        case GPBDataTypeFixed64:
+        case GPBDataTypeUInt64:
+          GPBSetUInt64IvarWithFieldInternal(
+              self, field, GPBGetMessageUInt64Field(other, field), syntax);
+          break;
+        case GPBDataTypeFloat:
+          GPBSetFloatIvarWithFieldInternal(
+              self, field, GPBGetMessageFloatField(other, field), syntax);
+          break;
+        case GPBDataTypeDouble:
+          GPBSetDoubleIvarWithFieldInternal(
+              self, field, GPBGetMessageDoubleField(other, field), syntax);
+          break;
+        case GPBDataTypeBytes:
+        case GPBDataTypeString: {
+          id otherVal = GPBGetObjectIvarWithFieldNoAutocreate(other, field);
+          GPBSetObjectIvarWithFieldInternal(self, field, otherVal, syntax);
+          break;
+        }
+        case GPBDataTypeMessage:
+        case GPBDataTypeGroup: {
+          id otherVal = GPBGetObjectIvarWithFieldNoAutocreate(other, field);
+          if (GPBGetHasIvar(self, hasIndex, fieldNumber)) {
+            GPBMessage *message =
+                GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+            [message mergeFrom:otherVal];
+          } else {
+            GPBMessage *message = [otherVal copy];
+            GPBSetRetainedObjectIvarWithFieldInternal(self, field, message,
+                                                      syntax);
+          }
+          break;
+        }
+      } // switch()
+    } else if (fieldType == GPBFieldTypeRepeated) {
+      // In the case of a list, they need to be appended, and there is no
+      // _hasIvar to worry about setting.
+      id otherArray =
+          GPBGetObjectIvarWithFieldNoAutocreate(other, field);
+      if (otherArray) {
+        GPBDataType fieldDataType = field->description_->dataType;
+        if (GPBDataTypeIsObject(fieldDataType)) {
+          NSMutableArray *resultArray =
+              GetOrCreateArrayIvarWithField(self, field, syntax);
+          [resultArray addObjectsFromArray:otherArray];
+        } else if (fieldDataType == GPBDataTypeEnum) {
+          GPBEnumArray *resultArray =
+              GetOrCreateArrayIvarWithField(self, field, syntax);
+          [resultArray addRawValuesFromArray:otherArray];
+        } else {
+          // The array type doesn't matter, that all implment
+          // -addValuesFromArray:.
+          GPBInt32Array *resultArray =
+              GetOrCreateArrayIvarWithField(self, field, syntax);
+          [resultArray addValuesFromArray:otherArray];
+        }
+      }
+    } else {  // fieldType = GPBFieldTypeMap
+      // In the case of a map, they need to be merged, and there is no
+      // _hasIvar to worry about setting.
+      id otherDict = GPBGetObjectIvarWithFieldNoAutocreate(other, field);
+      if (otherDict) {
+        GPBDataType keyDataType = field.mapKeyDataType;
+        GPBDataType valueDataType = field->description_->dataType;
+        if (GPBDataTypeIsObject(keyDataType) &&
+            GPBDataTypeIsObject(valueDataType)) {
+          NSMutableDictionary *resultDict =
+              GetOrCreateMapIvarWithField(self, field, syntax);
+          [resultDict addEntriesFromDictionary:otherDict];
+        } else if (valueDataType == GPBDataTypeEnum) {
+          // The exact type doesn't matter, just need to know it is a
+          // GPB*EnumDictionary.
+          GPBInt32EnumDictionary *resultDict =
+              GetOrCreateMapIvarWithField(self, field, syntax);
+          [resultDict addRawEntriesFromDictionary:otherDict];
+        } else {
+          // The exact type doesn't matter, they all implement
+          // -addEntriesFromDictionary:.
+          GPBInt32Int32Dictionary *resultDict =
+              GetOrCreateMapIvarWithField(self, field, syntax);
+          [resultDict addEntriesFromDictionary:otherDict];
+        }
+      }
+    }  // if (fieldType)..else if...else
+  }  // for(fields)
+
+  // Unknown fields.
+  if (!unknownFields_) {
+    [self setUnknownFields:other.unknownFields];
+  } else {
+    [unknownFields_ mergeUnknownFields:other.unknownFields];
+  }
+
+  // Extensions
+
+  if (other->extensionMap_.count == 0) {
+    return;
+  }
+
+  if (extensionMap_ == nil) {
+    extensionMap_ =
+        CloneExtensionMap(other->extensionMap_, NSZoneFromPointer(self));
+  } else {
+    for (GPBExtensionDescriptor *extension in other->extensionMap_) {
+      id otherValue = [other->extensionMap_ objectForKey:extension];
+      id value = [extensionMap_ objectForKey:extension];
+      BOOL isMessageExtension = GPBExtensionIsMessage(extension);
+
+      if (extension.repeated) {
+        NSMutableArray *list = value;
+        if (list == nil) {
+          list = [[NSMutableArray alloc] init];
+          [extensionMap_ setObject:list forKey:extension];
+          [list release];
+        }
+        if (isMessageExtension) {
+          for (GPBMessage *otherListValue in otherValue) {
+            GPBMessage *copiedValue = [otherListValue copy];
+            [list addObject:copiedValue];
+            [copiedValue release];
+          }
+        } else {
+          [list addObjectsFromArray:otherValue];
+        }
+      } else {
+        if (isMessageExtension) {
+          if (value) {
+            [(GPBMessage *)value mergeFrom:(GPBMessage *)otherValue];
+          } else {
+            GPBMessage *copiedValue = [otherValue copy];
+            [extensionMap_ setObject:copiedValue forKey:extension];
+            [copiedValue release];
+          }
+        } else {
+          [extensionMap_ setObject:otherValue forKey:extension];
+        }
+      }
+
+      if (isMessageExtension && !extension.isRepeated) {
+        GPBMessage *autocreatedValue =
+            [[autocreatedExtensionMap_ objectForKey:extension] retain];
+        // Must remove from the map before calling GPBClearMessageAutocreator()
+        // so that GPBClearMessageAutocreator() knows its safe to clear.
+        [autocreatedExtensionMap_ removeObjectForKey:extension];
+        GPBClearMessageAutocreator(autocreatedValue);
+        [autocreatedValue release];
+      }
+    }
+  }
+}
+
+#pragma mark - isEqual: & hash Support
+
+- (BOOL)isEqual:(id)other {
+  if (other == self) {
+    return YES;
+  }
+  if (![other isKindOfClass:[GPBMessage class]]) {
+    return NO;
+  }
+  GPBMessage *otherMsg = other;
+  GPBDescriptor *descriptor = [[self class] descriptor];
+  if ([[otherMsg class] descriptor] != descriptor) {
+    return NO;
+  }
+  uint8_t *selfStorage = (uint8_t *)messageStorage_;
+  uint8_t *otherStorage = (uint8_t *)otherMsg->messageStorage_;
+
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (GPBFieldIsMapOrArray(field)) {
+      // In the case of a list or map, there is no _hasIvar to worry about.
+      // NOTE: These are NSArray/GPB*Array or NSDictionary/GPB*Dictionary, but
+      // the type doesn't really matter as the objects all support -count and
+      // -isEqual:.
+      NSArray *resultMapOrArray =
+          GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      NSArray *otherMapOrArray =
+          GPBGetObjectIvarWithFieldNoAutocreate(other, field);
+      // nil and empty are equal
+      if (resultMapOrArray.count != 0 || otherMapOrArray.count != 0) {
+        if (![resultMapOrArray isEqual:otherMapOrArray]) {
+          return NO;
+        }
+      }
+    } else {  // Single field
+      int32_t hasIndex = GPBFieldHasIndex(field);
+      uint32_t fieldNum = GPBFieldNumber(field);
+      BOOL selfHas = GPBGetHasIvar(self, hasIndex, fieldNum);
+      BOOL otherHas = GPBGetHasIvar(other, hasIndex, fieldNum);
+      if (selfHas != otherHas) {
+        return NO;  // Differing has values, not equal.
+      }
+      if (!selfHas) {
+        // Same has values, was no, nothing else to check for this field.
+        continue;
+      }
+      // Now compare the values.
+      GPBDataType fieldDataType = GPBGetFieldDataType(field);
+      size_t fieldOffset = field->description_->offset;
+      switch (fieldDataType) {
+        case GPBDataTypeBool: {
+          // Bools are stored in has_bits to avoid needing explicit space in
+          // the storage structure.
+          // (the field number passed to the HasIvar helper doesn't really
+          // matter since the offset is never negative)
+          BOOL selfValue = GPBGetHasIvar(self, (int32_t)(fieldOffset), 0);
+          BOOL otherValue = GPBGetHasIvar(other, (int32_t)(fieldOffset), 0);
+          if (selfValue != otherValue) {
+            return NO;
+          }
+          break;
+        }
+        case GPBDataTypeSFixed32:
+        case GPBDataTypeInt32:
+        case GPBDataTypeSInt32:
+        case GPBDataTypeEnum:
+        case GPBDataTypeFixed32:
+        case GPBDataTypeUInt32:
+        case GPBDataTypeFloat: {
+          GPBInternalCompileAssert(sizeof(float) == sizeof(uint32_t), float_not_32_bits);
+          // These are all 32bit, signed/unsigned doesn't matter for equality.
+          uint32_t *selfValPtr = (uint32_t *)&selfStorage[fieldOffset];
+          uint32_t *otherValPtr = (uint32_t *)&otherStorage[fieldOffset];
+          if (*selfValPtr != *otherValPtr) {
+            return NO;
+          }
+          break;
+        }
+        case GPBDataTypeSFixed64:
+        case GPBDataTypeInt64:
+        case GPBDataTypeSInt64:
+        case GPBDataTypeFixed64:
+        case GPBDataTypeUInt64:
+        case GPBDataTypeDouble: {
+          GPBInternalCompileAssert(sizeof(double) == sizeof(uint64_t), double_not_64_bits);
+          // These are all 64bit, signed/unsigned doesn't matter for equality.
+          uint64_t *selfValPtr = (uint64_t *)&selfStorage[fieldOffset];
+          uint64_t *otherValPtr = (uint64_t *)&otherStorage[fieldOffset];
+          if (*selfValPtr != *otherValPtr) {
+            return NO;
+          }
+          break;
+        }
+        case GPBDataTypeBytes:
+        case GPBDataTypeString:
+        case GPBDataTypeMessage:
+        case GPBDataTypeGroup: {
+          // Type doesn't matter here, they all implement -isEqual:.
+          id *selfValPtr = (id *)&selfStorage[fieldOffset];
+          id *otherValPtr = (id *)&otherStorage[fieldOffset];
+          if (![*selfValPtr isEqual:*otherValPtr]) {
+            return NO;
+          }
+          break;
+        }
+      } // switch()
+    }   // if(mapOrArray)...else
+  }  // for(fields)
+
+  // nil and empty are equal
+  if (extensionMap_.count != 0 || otherMsg->extensionMap_.count != 0) {
+    if (![extensionMap_ isEqual:otherMsg->extensionMap_]) {
+      return NO;
+    }
+  }
+
+  // nil and empty are equal
+  GPBUnknownFieldSet *otherUnknowns = otherMsg->unknownFields_;
+  if ([unknownFields_ countOfFields] != 0 ||
+      [otherUnknowns countOfFields] != 0) {
+    if (![unknownFields_ isEqual:otherUnknowns]) {
+      return NO;
+    }
+  }
+
+  return YES;
+}
+
+// It is very difficult to implement a generic hash for ProtoBuf messages that
+// will perform well. If you need hashing on your ProtoBufs (eg you are using
+// them as dictionary keys) you will probably want to implement a ProtoBuf
+// message specific hash as a category on your protobuf class. Do not make it a
+// category on GPBMessage as you will conflict with this hash, and will possibly
+// override hash for all generated protobufs. A good implementation of hash will
+// be really fast, so we would recommend only hashing protobufs that have an
+// identifier field of some kind that you can easily hash. If you implement
+// hash, we would strongly recommend overriding isEqual: in your category as
+// well, as the default implementation of isEqual: is extremely slow, and may
+// drastically affect performance in large sets.
+- (NSUInteger)hash {
+  GPBDescriptor *descriptor = [[self class] descriptor];
+  const NSUInteger prime = 19;
+  uint8_t *storage = (uint8_t *)messageStorage_;
+
+  // Start with the descriptor and then mix it with some instance info.
+  // Hopefully that will give a spread based on classes and what fields are set.
+  NSUInteger result = (NSUInteger)descriptor;
+
+  for (GPBFieldDescriptor *field in descriptor->fields_) {
+    if (GPBFieldIsMapOrArray(field)) {
+      // Exact type doesn't matter, just check if there are any elements.
+      NSArray *mapOrArray = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      NSUInteger count = mapOrArray.count;
+      if (count) {
+        // NSArray/NSDictionary use count, use the field number and the count.
+        result = prime * result + GPBFieldNumber(field);
+        result = prime * result + count;
+      }
+    } else if (GPBGetHasIvarField(self, field)) {
+      // Just using the field number seemed simple/fast, but then a small
+      // message class where all the same fields are always set (to different
+      // things would end up all with the same hash, so pull in some data).
+      GPBDataType fieldDataType = GPBGetFieldDataType(field);
+      size_t fieldOffset = field->description_->offset;
+      switch (fieldDataType) {
+        case GPBDataTypeBool: {
+          // Bools are stored in has_bits to avoid needing explicit space in
+          // the storage structure.
+          // (the field number passed to the HasIvar helper doesn't really
+          // matter since the offset is never negative)
+          BOOL value = GPBGetHasIvar(self, (int32_t)(fieldOffset), 0);
+          result = prime * result + value;
+          break;
+        }
+        case GPBDataTypeSFixed32:
+        case GPBDataTypeInt32:
+        case GPBDataTypeSInt32:
+        case GPBDataTypeEnum:
+        case GPBDataTypeFixed32:
+        case GPBDataTypeUInt32:
+        case GPBDataTypeFloat: {
+          GPBInternalCompileAssert(sizeof(float) == sizeof(uint32_t), float_not_32_bits);
+          // These are all 32bit, just mix it in.
+          uint32_t *valPtr = (uint32_t *)&storage[fieldOffset];
+          result = prime * result + *valPtr;
+          break;
+        }
+        case GPBDataTypeSFixed64:
+        case GPBDataTypeInt64:
+        case GPBDataTypeSInt64:
+        case GPBDataTypeFixed64:
+        case GPBDataTypeUInt64:
+        case GPBDataTypeDouble: {
+          GPBInternalCompileAssert(sizeof(double) == sizeof(uint64_t), double_not_64_bits);
+          // These are all 64bit, just mix what fits into an NSUInteger in.
+          uint64_t *valPtr = (uint64_t *)&storage[fieldOffset];
+          result = prime * result + (NSUInteger)(*valPtr);
+          break;
+        }
+        case GPBDataTypeBytes:
+        case GPBDataTypeString: {
+          // Type doesn't matter here, they both implement -hash:.
+          id *valPtr = (id *)&storage[fieldOffset];
+          result = prime * result + [*valPtr hash];
+          break;
+        }
+
+        case GPBDataTypeMessage:
+        case GPBDataTypeGroup: {
+          GPBMessage **valPtr = (GPBMessage **)&storage[fieldOffset];
+          // Could call -hash on the sub message, but that could recurse pretty
+          // deep; follow the lead of NSArray/NSDictionary and don't really
+          // recurse for hash, instead use the field number and the descriptor
+          // of the sub message.  Yes, this could suck for a bunch of messages
+          // where they all only differ in the sub messages, but if you are
+          // using a message with sub messages for something that needs -hash,
+          // odds are you are also copying them as keys, and that deep copy
+          // will also suck.
+          result = prime * result + GPBFieldNumber(field);
+          result = prime * result + (NSUInteger)[[*valPtr class] descriptor];
+          break;
+        }
+      } // switch()
+    }
+  }
+
+  // Unknowns and extensions are not included.
+
+  return result;
+}
+
+#pragma mark - Description Support
+
+- (NSString *)description {
+  NSString *textFormat = GPBTextFormatForMessage(self, @"    ");
+  NSString *description = [NSString
+      stringWithFormat:@"<%@ %p>: {\n%@}", [self class], self, textFormat];
+  return description;
+}
+
+#if defined(DEBUG) && DEBUG
+
+// Xcode 5.1 added support for custom quick look info.
+// https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/CustomClassDisplay_in_QuickLook/CH01-quick_look_for_custom_objects/CH01-quick_look_for_custom_objects.html#//apple_ref/doc/uid/TP40014001-CH2-SW1
+- (id)debugQuickLookObject {
+  return GPBTextFormatForMessage(self, nil);
+}
+
+#endif  // DEBUG
+
+#pragma mark - SerializedSize
+
+- (size_t)serializedSize {
+  GPBDescriptor *descriptor = [[self class] descriptor];
+  size_t result = 0;
+
+  // Has check is done explicitly, so GPBGetObjectIvarWithFieldNoAutocreate()
+  // avoids doing the has check again.
+
+  // Fields.
+  for (GPBFieldDescriptor *fieldDescriptor in descriptor->fields_) {
+    GPBFieldType fieldType = fieldDescriptor.fieldType;
+    GPBDataType fieldDataType = GPBGetFieldDataType(fieldDescriptor);
+
+    // Single Fields
+    if (fieldType == GPBFieldTypeSingle) {
+      BOOL selfHas = GPBGetHasIvarField(self, fieldDescriptor);
+      if (!selfHas) {
+        continue;  // Nothing to do.
+      }
+
+      uint32_t fieldNumber = GPBFieldNumber(fieldDescriptor);
+
+      switch (fieldDataType) {
+#define CASE_SINGLE_POD(NAME, TYPE, FUNC_TYPE)                                \
+        case GPBDataType##NAME: {                                             \
+          TYPE fieldVal = GPBGetMessage##FUNC_TYPE##Field(self, fieldDescriptor); \
+          result += GPBCompute##NAME##Size(fieldNumber, fieldVal);            \
+          break;                                                              \
+        }
+#define CASE_SINGLE_OBJECT(NAME)                                              \
+        case GPBDataType##NAME: {                                             \
+          id fieldVal = GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor); \
+          result += GPBCompute##NAME##Size(fieldNumber, fieldVal);            \
+          break;                                                              \
+        }
+          CASE_SINGLE_POD(Bool, BOOL, Bool)
+          CASE_SINGLE_POD(Fixed32, uint32_t, UInt32)
+          CASE_SINGLE_POD(SFixed32, int32_t, Int32)
+          CASE_SINGLE_POD(Float, float, Float)
+          CASE_SINGLE_POD(Fixed64, uint64_t, UInt64)
+          CASE_SINGLE_POD(SFixed64, int64_t, Int64)
+          CASE_SINGLE_POD(Double, double, Double)
+          CASE_SINGLE_POD(Int32, int32_t, Int32)
+          CASE_SINGLE_POD(Int64, int64_t, Int64)
+          CASE_SINGLE_POD(SInt32, int32_t, Int32)
+          CASE_SINGLE_POD(SInt64, int64_t, Int64)
+          CASE_SINGLE_POD(UInt32, uint32_t, UInt32)
+          CASE_SINGLE_POD(UInt64, uint64_t, UInt64)
+          CASE_SINGLE_OBJECT(Bytes)
+          CASE_SINGLE_OBJECT(String)
+          CASE_SINGLE_OBJECT(Message)
+          CASE_SINGLE_OBJECT(Group)
+          CASE_SINGLE_POD(Enum, int32_t, Int32)
+#undef CASE_SINGLE_POD
+#undef CASE_SINGLE_OBJECT
+      }
+
+    // Repeated Fields
+    } else if (fieldType == GPBFieldTypeRepeated) {
+      id genericArray =
+          GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor);
+      NSUInteger count = [genericArray count];
+      if (count == 0) {
+        continue;  // Nothing to add.
+      }
+      __block size_t dataSize = 0;
+
+      switch (fieldDataType) {
+#define CASE_REPEATED_POD(NAME, TYPE, ARRAY_TYPE)                             \
+    CASE_REPEATED_POD_EXTRA(NAME, TYPE, ARRAY_TYPE, )
+#define CASE_REPEATED_POD_EXTRA(NAME, TYPE, ARRAY_TYPE, ARRAY_ACCESSOR_NAME)  \
+        case GPBDataType##NAME: {                                             \
+          GPB##ARRAY_TYPE##Array *array = genericArray;                       \
+          [array enumerate##ARRAY_ACCESSOR_NAME##ValuesWithBlock:^(TYPE value, NSUInteger idx, BOOL *stop) { \
+            _Pragma("unused(idx, stop)");                                     \
+            dataSize += GPBCompute##NAME##SizeNoTag(value);                   \
+          }];                                                                 \
+          break;                                                              \
+        }
+#define CASE_REPEATED_OBJECT(NAME)                                            \
+        case GPBDataType##NAME: {                                             \
+          for (id value in genericArray) {                                    \
+            dataSize += GPBCompute##NAME##SizeNoTag(value);                   \
+          }                                                                   \
+          break;                                                              \
+        }
+          CASE_REPEATED_POD(Bool, BOOL, Bool)
+          CASE_REPEATED_POD(Fixed32, uint32_t, UInt32)
+          CASE_REPEATED_POD(SFixed32, int32_t, Int32)
+          CASE_REPEATED_POD(Float, float, Float)
+          CASE_REPEATED_POD(Fixed64, uint64_t, UInt64)
+          CASE_REPEATED_POD(SFixed64, int64_t, Int64)
+          CASE_REPEATED_POD(Double, double, Double)
+          CASE_REPEATED_POD(Int32, int32_t, Int32)
+          CASE_REPEATED_POD(Int64, int64_t, Int64)
+          CASE_REPEATED_POD(SInt32, int32_t, Int32)
+          CASE_REPEATED_POD(SInt64, int64_t, Int64)
+          CASE_REPEATED_POD(UInt32, uint32_t, UInt32)
+          CASE_REPEATED_POD(UInt64, uint64_t, UInt64)
+          CASE_REPEATED_OBJECT(Bytes)
+          CASE_REPEATED_OBJECT(String)
+          CASE_REPEATED_OBJECT(Message)
+          CASE_REPEATED_OBJECT(Group)
+          CASE_REPEATED_POD_EXTRA(Enum, int32_t, Enum, Raw)
+#undef CASE_REPEATED_POD
+#undef CASE_REPEATED_POD_EXTRA
+#undef CASE_REPEATED_OBJECT
+      }  // switch
+      result += dataSize;
+      size_t tagSize = GPBComputeTagSize(GPBFieldNumber(fieldDescriptor));
+      if (fieldDataType == GPBDataTypeGroup) {
+        // Groups have both a start and an end tag.
+        tagSize *= 2;
+      }
+      if (fieldDescriptor.isPackable) {
+        result += tagSize;
+        result += GPBComputeSizeTSizeAsInt32NoTag(dataSize);
+      } else {
+        result += count * tagSize;
+      }
+
+    // Map<> Fields
+    } else {  // fieldType == GPBFieldTypeMap
+      if (GPBDataTypeIsObject(fieldDataType) &&
+          (fieldDescriptor.mapKeyDataType == GPBDataTypeString)) {
+        // If key type was string, then the map is an NSDictionary.
+        NSDictionary *map =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor);
+        if (map) {
+          result += GPBDictionaryComputeSizeInternalHelper(map, fieldDescriptor);
+        }
+      } else {
+        // Type will be GPB*GroupDictionary, exact type doesn't matter.
+        GPBInt32Int32Dictionary *map =
+            GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor);
+        result += [map computeSerializedSizeAsField:fieldDescriptor];
+      }
+    }
+  }  // for(fields)
+
+  // Add any unknown fields.
+  if (descriptor.wireFormat) {
+    result += [unknownFields_ serializedSizeAsMessageSet];
+  } else {
+    result += [unknownFields_ serializedSize];
+  }
+
+  // Add any extensions.
+  for (GPBExtensionDescriptor *extension in extensionMap_) {
+    id value = [extensionMap_ objectForKey:extension];
+    result += GPBComputeExtensionSerializedSizeIncludingTag(extension, value);
+  }
+
+  return result;
+}
+
+#pragma mark - Resolve Methods Support
+
+typedef struct ResolveIvarAccessorMethodResult {
+  IMP impToAdd;
+  SEL encodingSelector;
+} ResolveIvarAccessorMethodResult;
+
+// |field| can be __unsafe_unretained because they are created at startup
+// and are essentially global. No need to pay for retain/release when
+// they are captured in blocks.
+static void ResolveIvarGet(__unsafe_unretained GPBFieldDescriptor *field,
+                           ResolveIvarAccessorMethodResult *result) {
+  GPBDataType fieldDataType = GPBGetFieldDataType(field);
+  switch (fieldDataType) {
+#define CASE_GET(NAME, TYPE, TRUE_NAME)                          \
+    case GPBDataType##NAME: {                                    \
+      result->impToAdd = imp_implementationWithBlock(^(id obj) { \
+        return GPBGetMessage##TRUE_NAME##Field(obj, field);      \
+       });                                                       \
+      result->encodingSelector = @selector(get##NAME);           \
+      break;                                                     \
+    }
+#define CASE_GET_OBJECT(NAME, TYPE, TRUE_NAME)                   \
+    case GPBDataType##NAME: {                                    \
+      result->impToAdd = imp_implementationWithBlock(^(id obj) { \
+        return GPBGetObjectIvarWithField(obj, field);            \
+       });                                                       \
+      result->encodingSelector = @selector(get##NAME);           \
+      break;                                                     \
+    }
+      CASE_GET(Bool, BOOL, Bool)
+      CASE_GET(Fixed32, uint32_t, UInt32)
+      CASE_GET(SFixed32, int32_t, Int32)
+      CASE_GET(Float, float, Float)
+      CASE_GET(Fixed64, uint64_t, UInt64)
+      CASE_GET(SFixed64, int64_t, Int64)
+      CASE_GET(Double, double, Double)
+      CASE_GET(Int32, int32_t, Int32)
+      CASE_GET(Int64, int64_t, Int64)
+      CASE_GET(SInt32, int32_t, Int32)
+      CASE_GET(SInt64, int64_t, Int64)
+      CASE_GET(UInt32, uint32_t, UInt32)
+      CASE_GET(UInt64, uint64_t, UInt64)
+      CASE_GET_OBJECT(Bytes, id, Object)
+      CASE_GET_OBJECT(String, id, Object)
+      CASE_GET_OBJECT(Message, id, Object)
+      CASE_GET_OBJECT(Group, id, Object)
+      CASE_GET(Enum, int32_t, Enum)
+#undef CASE_GET
+  }
+}
+
+// See comment about __unsafe_unretained on ResolveIvarGet.
+static void ResolveIvarSet(__unsafe_unretained GPBFieldDescriptor *field,
+                           GPBFileSyntax syntax,
+                           ResolveIvarAccessorMethodResult *result) {
+  GPBDataType fieldDataType = GPBGetFieldDataType(field);
+  switch (fieldDataType) {
+#define CASE_SET(NAME, TYPE, TRUE_NAME)                                       \
+    case GPBDataType##NAME: {                                                 \
+      result->impToAdd = imp_implementationWithBlock(^(id obj, TYPE value) {  \
+        return GPBSet##TRUE_NAME##IvarWithFieldInternal(obj, field, value, syntax); \
+      });                                                                     \
+      result->encodingSelector = @selector(set##NAME:);                       \
+      break;                                                                  \
+    }
+      CASE_SET(Bool, BOOL, Bool)
+      CASE_SET(Fixed32, uint32_t, UInt32)
+      CASE_SET(SFixed32, int32_t, Int32)
+      CASE_SET(Float, float, Float)
+      CASE_SET(Fixed64, uint64_t, UInt64)
+      CASE_SET(SFixed64, int64_t, Int64)
+      CASE_SET(Double, double, Double)
+      CASE_SET(Int32, int32_t, Int32)
+      CASE_SET(Int64, int64_t, Int64)
+      CASE_SET(SInt32, int32_t, Int32)
+      CASE_SET(SInt64, int64_t, Int64)
+      CASE_SET(UInt32, uint32_t, UInt32)
+      CASE_SET(UInt64, uint64_t, UInt64)
+      CASE_SET(Bytes, id, Object)
+      CASE_SET(String, id, Object)
+      CASE_SET(Message, id, Object)
+      CASE_SET(Group, id, Object)
+      CASE_SET(Enum, int32_t, Enum)
+#undef CASE_SET
+  }
+}
+
++ (BOOL)resolveInstanceMethod:(SEL)sel {
+  const GPBDescriptor *descriptor = [self descriptor];
+  if (!descriptor) {
+    return [super resolveInstanceMethod:sel];
+  }
+
+  // NOTE: hasOrCountSel_/setHasSel_ will be NULL if the field for the given
+  // message should not have has support (done in GPBDescriptor.m), so there is
+  // no need for checks here to see if has*/setHas* are allowed.
+  ResolveIvarAccessorMethodResult result = {NULL, NULL};
+
+  // See comment about __unsafe_unretained on ResolveIvarGet.
+  for (__unsafe_unretained GPBFieldDescriptor *field in descriptor->fields_) {
+    BOOL isMapOrArray = GPBFieldIsMapOrArray(field);
+    if (!isMapOrArray) {
+      // Single fields.
+      if (sel == field->getSel_) {
+        ResolveIvarGet(field, &result);
+        break;
+      } else if (sel == field->setSel_) {
+        ResolveIvarSet(field, descriptor.file.syntax, &result);
+        break;
+      } else if (sel == field->hasOrCountSel_) {
+        int32_t index = GPBFieldHasIndex(field);
+        uint32_t fieldNum = GPBFieldNumber(field);
+        result.impToAdd = imp_implementationWithBlock(^(id obj) {
+          return GPBGetHasIvar(obj, index, fieldNum);
+        });
+        result.encodingSelector = @selector(getBool);
+        break;
+      } else if (sel == field->setHasSel_) {
+        result.impToAdd = imp_implementationWithBlock(^(id obj, BOOL value) {
+          if (value) {
+            [NSException raise:NSInvalidArgumentException
+                        format:@"%@: %@ can only be set to NO (to clear field).",
+                               [obj class],
+                               NSStringFromSelector(field->setHasSel_)];
+          }
+          GPBClearMessageField(obj, field);
+        });
+        result.encodingSelector = @selector(setBool:);
+        break;
+      } else {
+        GPBOneofDescriptor *oneof = field->containingOneof_;
+        if (oneof && (sel == oneof->caseSel_)) {
+          int32_t index = GPBFieldHasIndex(field);
+          result.impToAdd = imp_implementationWithBlock(^(id obj) {
+            return GPBGetHasOneof(obj, index);
+          });
+          result.encodingSelector = @selector(getEnum);
+          break;
+        }
+      }
+    } else {
+      // map<>/repeated fields.
+      if (sel == field->getSel_) {
+        if (field.fieldType == GPBFieldTypeRepeated) {
+          result.impToAdd = imp_implementationWithBlock(^(id obj) {
+            return GetArrayIvarWithField(obj, field);
+          });
+        } else {
+          result.impToAdd = imp_implementationWithBlock(^(id obj) {
+            return GetMapIvarWithField(obj, field);
+          });
+        }
+        result.encodingSelector = @selector(getArray);
+        break;
+      } else if (sel == field->setSel_) {
+        // Local for syntax so the block can directly capture it and not the
+        // full lookup.
+        const GPBFileSyntax syntax = descriptor.file.syntax;
+        result.impToAdd = imp_implementationWithBlock(^(id obj, id value) {
+          return GPBSetObjectIvarWithFieldInternal(obj, field, value, syntax);
+        });
+        result.encodingSelector = @selector(setArray:);
+        break;
+      } else if (sel == field->hasOrCountSel_) {
+        result.impToAdd = imp_implementationWithBlock(^(id obj) {
+          // Type doesn't matter, all *Array and *Dictionary types support
+          // -count.
+          NSArray *arrayOrMap =
+              GPBGetObjectIvarWithFieldNoAutocreate(obj, field);
+          return [arrayOrMap count];
+        });
+        result.encodingSelector = @selector(getArrayCount);
+        break;
+      }
+    }
+  }
+  if (result.impToAdd) {
+    const char *encoding =
+        GPBMessageEncodingForSelector(result.encodingSelector, YES);
+    Class msgClass = descriptor.messageClass;
+    BOOL methodAdded = class_addMethod(msgClass, sel, result.impToAdd, encoding);
+    // class_addMethod() is documented as also failing if the method was already
+    // added; so we check if the method is already there and return success so
+    // the method dispatch will still happen.  Why would it already be added?
+    // Two threads could cause the same method to be bound at the same time,
+    // but only one will actually bind it; the other still needs to return true
+    // so things will dispatch.
+    if (!methodAdded) {
+      methodAdded = GPBClassHasSel(msgClass, sel);
+    }
+    return methodAdded;
+  }
+  return [super resolveInstanceMethod:sel];
+}
+
++ (BOOL)resolveClassMethod:(SEL)sel {
+  // Extensions scoped to a Message and looked up via class methods.
+  if (GPBResolveExtensionClassMethod([self descriptor].messageClass, sel)) {
+    return YES;
+  }
+  return [super resolveClassMethod:sel];
+}
+
+#pragma mark - NSCoding Support
+
++ (BOOL)supportsSecureCoding {
+  return YES;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+  self = [self init];
+  if (self) {
+    NSData *data =
+        [aDecoder decodeObjectOfClass:[NSData class] forKey:kGPBDataCoderKey];
+    if (data.length) {
+      [self mergeFromData:data extensionRegistry:nil];
+    }
+  }
+  return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+  NSData *data = [self data];
+  if (data.length) {
+    [aCoder encodeObject:data forKey:kGPBDataCoderKey];
+  }
+}
+
+#pragma mark - KVC Support
+
++ (BOOL)accessInstanceVariablesDirectly {
+  // Make sure KVC doesn't use instance variables.
+  return NO;
+}
+
+@end
+
+#pragma mark - Messages from GPBUtilities.h but defined here for access to helpers.
+
+// Only exists for public api, no core code should use this.
+id GPBGetMessageRepeatedField(GPBMessage *self, GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  if (field.fieldType != GPBFieldTypeRepeated) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@.%@ is not a repeated field.",
+     [self class], field.name];
+  }
+#endif
+  GPBDescriptor *descriptor = [[self class] descriptor];
+  GPBFileSyntax syntax = descriptor.file.syntax;
+  return GetOrCreateArrayIvarWithField(self, field, syntax);
+}
+
+// Only exists for public api, no core code should use this.
+id GPBGetMessageMapField(GPBMessage *self, GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  if (field.fieldType != GPBFieldTypeMap) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@.%@ is not a map<> field.",
+     [self class], field.name];
+  }
+#endif
+  GPBDescriptor *descriptor = [[self class] descriptor];
+  GPBFileSyntax syntax = descriptor.file.syntax;
+  return GetOrCreateMapIvarWithField(self, field, syntax);
+}
+
+id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
+  NSCAssert(!GPBFieldIsMapOrArray(field), @"Shouldn't get here");
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    id *typePtr = (id *)&storage[field->description_->offset];
+    return *typePtr;
+  }
+  // Not set...
+
+  // Non messages (string/data), get their default.
+  if (!GPBFieldDataTypeIsMessage(field)) {
+    return field.defaultValue.valueMessage;
+  }
+
+  GPBPrepareReadOnlySemaphore(self);
+  dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
+  GPBMessage *result = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+  if (!result) {
+    // For non repeated messages, create the object, set it and return it.
+    // This object will not initially be visible via GPBGetHasIvar, so
+    // we save its creator so it can become visible if it's mutated later.
+    result = GPBCreateMessageWithAutocreator(field.msgClass, self, field);
+    GPBSetAutocreatedRetainedObjectIvarWithField(self, field, result);
+  }
+  dispatch_semaphore_signal(self->readOnlySemaphore_);
+  return result;
+}
+
+#pragma clang diagnostic pop
diff --git a/iOS/Pods/Protobuf/objectivec/GPBMessage_PackagePrivate.h b/iOS/Pods/Protobuf/objectivec/GPBMessage_PackagePrivate.h
new file mode 100644 (file)
index 0000000..ca10983
--- /dev/null
@@ -0,0 +1,124 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+// This header is private to the ProtobolBuffers library and must NOT be
+// included by any sources outside this library. The contents of this file are
+// subject to change at any time without notice.
+
+#import "GPBMessage.h"
+
+// TODO: Remove this import. Older generated code use the OSAtomic* apis,
+// so anyone that hasn't regenerated says building by having this. After
+// enough time has passed, this likely can be removed as folks should have
+// regenerated.
+#import <libkern/OSAtomic.h>
+
+#import "GPBBootstrap.h"
+
+typedef struct GPBMessage_Storage {
+  uint32_t _has_storage_[0];
+} GPBMessage_Storage;
+
+typedef struct GPBMessage_Storage *GPBMessage_StoragePtr;
+
+@interface GPBMessage () {
+ @package
+  // NOTE: Because of the +allocWithZone code using NSAllocateObject(),
+  // this structure should ideally always be kept pointer aligned where the
+  // real storage starts is also pointer aligned. The compiler/runtime already
+  // do this, but it may not be documented.
+
+  // A pointer to the actual fields of the subclasses. The actual structure
+  // pointed to by this pointer will depend on the subclass.
+  // All of the actual structures will start the same as
+  // GPBMessage_Storage with _has_storage__ as the first field.
+  // Kept public because static functions need to access it.
+  GPBMessage_StoragePtr messageStorage_;
+}
+
+// Gets an extension value without autocreating the result if not found. (i.e.
+// returns nil if the extension is not set)
+- (id)getExistingExtension:(GPBExtensionDescriptor *)extension;
+
+// Parses a message of this type from the input and merges it with this
+// message.
+//
+// Warning:  This does not verify that all required fields are present in
+// the input message.
+// Note:  The caller should call
+// -[CodedInputStream checkLastTagWas:] after calling this to
+// verify that the last tag seen was the appropriate end-group tag,
+// or zero for EOF.
+// NOTE: This will throw if there is an error while parsing.
+- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
+                extensionRegistry:(GPBExtensionRegistry *)extensionRegistry;
+
+// Parses the next delimited message of this type from the input and merges it
+// with this message.
+- (void)mergeDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
+                         extensionRegistry:
+                             (GPBExtensionRegistry *)extensionRegistry;
+
+- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data;
+
+@end
+
+CF_EXTERN_C_BEGIN
+
+
+// Call this before using the readOnlySemaphore_. This ensures it is created only once.
+void GPBPrepareReadOnlySemaphore(GPBMessage *self);
+
+// Returns a new instance that was automatically created by |autocreator| for
+// its field |field|.
+GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass,
+                                            GPBMessage *autocreator,
+                                            GPBFieldDescriptor *field)
+    __attribute__((ns_returns_retained));
+
+// Returns whether |message| autocreated this message. This is NO if the message
+// was not autocreated by |message| or if it has been mutated since
+// autocreation.
+BOOL GPBWasMessageAutocreatedBy(GPBMessage *message, GPBMessage *parent);
+
+// Call this when you mutate a message. It will cause the message to become
+// visible to its autocreator.
+void GPBBecomeVisibleToAutocreator(GPBMessage *self);
+
+// Call this when an array/dictionary is mutated so the parent message that
+// autocreated it can react.
+void GPBAutocreatedArrayModified(GPBMessage *self, id array);
+void GPBAutocreatedDictionaryModified(GPBMessage *self, id dictionary);
+
+// Clear the autocreator, if any. Asserts if the autocreator still has an
+// autocreated reference to this message.
+void GPBClearMessageAutocreator(GPBMessage *self);
+
+CF_EXTERN_C_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBProtocolBuffers.h b/iOS/Pods/Protobuf/objectivec/GPBProtocolBuffers.h
new file mode 100644 (file)
index 0000000..68d8854
--- /dev/null
@@ -0,0 +1,76 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBBootstrap.h"
+
+#import "GPBArray.h"
+#import "GPBCodedInputStream.h"
+#import "GPBCodedOutputStream.h"
+#import "GPBDescriptor.h"
+#import "GPBDictionary.h"
+#import "GPBExtensionRegistry.h"
+#import "GPBMessage.h"
+#import "GPBRootObject.h"
+#import "GPBUnknownField.h"
+#import "GPBUnknownFieldSet.h"
+#import "GPBUtilities.h"
+#import "GPBWellKnownTypes.h"
+#import "GPBWireFormat.h"
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+// Well-known proto types
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/Any.pbobjc.h>
+ #import <Protobuf/Api.pbobjc.h>
+ #import <Protobuf/Duration.pbobjc.h>
+ #import <Protobuf/Empty.pbobjc.h>
+ #import <Protobuf/FieldMask.pbobjc.h>
+ #import <Protobuf/SourceContext.pbobjc.h>
+ #import <Protobuf/Struct.pbobjc.h>
+ #import <Protobuf/Timestamp.pbobjc.h>
+ #import <Protobuf/Type.pbobjc.h>
+ #import <Protobuf/Wrappers.pbobjc.h>
+#else
+ #import "google/protobuf/Any.pbobjc.h"
+ #import "google/protobuf/Api.pbobjc.h"
+ #import "google/protobuf/Duration.pbobjc.h"
+ #import "google/protobuf/Empty.pbobjc.h"
+ #import "google/protobuf/FieldMask.pbobjc.h"
+ #import "google/protobuf/SourceContext.pbobjc.h"
+ #import "google/protobuf/Struct.pbobjc.h"
+ #import "google/protobuf/Timestamp.pbobjc.h"
+ #import "google/protobuf/Type.pbobjc.h"
+ #import "google/protobuf/Wrappers.pbobjc.h"
+#endif
diff --git a/iOS/Pods/Protobuf/objectivec/GPBProtocolBuffers_RuntimeSupport.h b/iOS/Pods/Protobuf/objectivec/GPBProtocolBuffers_RuntimeSupport.h
new file mode 100644 (file)
index 0000000..04dde62
--- /dev/null
@@ -0,0 +1,40 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+// This header is meant to only be used by the generated source, it should not
+// be included in code using protocol buffers.
+
+#import "GPBBootstrap.h"
+
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBExtensionInternals.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBRootObject_PackagePrivate.h"
+#import "GPBUtilities_PackagePrivate.h"
diff --git a/iOS/Pods/Protobuf/objectivec/GPBRootObject.h b/iOS/Pods/Protobuf/objectivec/GPBRootObject.h
new file mode 100644 (file)
index 0000000..d2e2aeb
--- /dev/null
@@ -0,0 +1,52 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+@class GPBExtensionRegistry;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Every generated proto file defines a local "Root" class that exposes a
+ * GPBExtensionRegistry for all the extensions defined by that file and
+ * the files it depends on.
+ **/
+@interface GPBRootObject : NSObject
+
+/**
+ * @return An extension registry for the given file and all the files it depends
+ * on.
+ **/
++ (GPBExtensionRegistry *)extensionRegistry;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBRootObject.m b/iOS/Pods/Protobuf/objectivec/GPBRootObject.m
new file mode 100644 (file)
index 0000000..bad2f9a
--- /dev/null
@@ -0,0 +1,245 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBRootObject_PackagePrivate.h"
+
+#import <objc/runtime.h>
+
+#import <CoreFoundation/CoreFoundation.h>
+
+#import "GPBDescriptor.h"
+#import "GPBExtensionRegistry.h"
+#import "GPBUtilities_PackagePrivate.h"
+
+@interface GPBExtensionDescriptor (GPBRootObject)
+// Get singletonName as a c string.
+- (const char *)singletonNameC;
+@end
+
+// We need some object to conform to the MessageSignatureProtocol to make sure
+// the selectors in it are recorded in our Objective C runtime information.
+// GPBMessage is arguably the more "obvious" choice, but given that all messages
+// inherit from GPBMessage, conflicts seem likely, so we are using GPBRootObject
+// instead.
+@interface GPBRootObject () <GPBMessageSignatureProtocol>
+@end
+
+@implementation GPBRootObject
+
+// Taken from http://www.burtleburtle.net/bob/hash/doobs.html
+// Public Domain
+static uint32_t jenkins_one_at_a_time_hash(const char *key) {
+  uint32_t hash = 0;
+  for (uint32_t i = 0; key[i] != '\0'; ++i) {
+    hash += key[i];
+    hash += (hash << 10);
+    hash ^= (hash >> 6);
+  }
+  hash += (hash << 3);
+  hash ^= (hash >> 11);
+  hash += (hash << 15);
+  return hash;
+}
+
+// Key methods for our custom CFDictionary.
+// Note that the dictionary lasts for the lifetime of our app, so no need
+// to worry about deallocation. All of the items are added to it at
+// startup, and so the keys don't need to be retained/released.
+// Keys are NULL terminated char *.
+static const void *GPBRootExtensionKeyRetain(CFAllocatorRef allocator,
+                                             const void *value) {
+#pragma unused(allocator)
+  return value;
+}
+
+static void GPBRootExtensionKeyRelease(CFAllocatorRef allocator,
+                                       const void *value) {
+#pragma unused(allocator)
+#pragma unused(value)
+}
+
+static CFStringRef GPBRootExtensionCopyKeyDescription(const void *value) {
+  const char *key = (const char *)value;
+  return CFStringCreateWithCString(kCFAllocatorDefault, key,
+                                   kCFStringEncodingUTF8);
+}
+
+static Boolean GPBRootExtensionKeyEqual(const void *value1,
+                                        const void *value2) {
+  const char *key1 = (const char *)value1;
+  const char *key2 = (const char *)value2;
+  return strcmp(key1, key2) == 0;
+}
+
+static CFHashCode GPBRootExtensionKeyHash(const void *value) {
+  const char *key = (const char *)value;
+  return jenkins_one_at_a_time_hash(key);
+}
+
+// NOTE: OSSpinLock may seem like a good fit here but Apple engineers have
+// pointed out that they are vulnerable to live locking on iOS in cases of
+// priority inversion:
+//   http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
+//   https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
+static dispatch_semaphore_t gExtensionSingletonDictionarySemaphore;
+static CFMutableDictionaryRef gExtensionSingletonDictionary = NULL;
+static GPBExtensionRegistry *gDefaultExtensionRegistry = NULL;
+
++ (void)initialize {
+  // Ensure the global is started up.
+  if (!gExtensionSingletonDictionary) {
+    gExtensionSingletonDictionarySemaphore = dispatch_semaphore_create(1);
+    CFDictionaryKeyCallBacks keyCallBacks = {
+      // See description above for reason for using custom dictionary.
+      0,
+      GPBRootExtensionKeyRetain,
+      GPBRootExtensionKeyRelease,
+      GPBRootExtensionCopyKeyDescription,
+      GPBRootExtensionKeyEqual,
+      GPBRootExtensionKeyHash,
+    };
+    gExtensionSingletonDictionary =
+        CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallBacks,
+                                  &kCFTypeDictionaryValueCallBacks);
+    gDefaultExtensionRegistry = [[GPBExtensionRegistry alloc] init];
+  }
+
+  if ([self superclass] == [GPBRootObject class]) {
+    // This is here to start up all the per file "Root" subclasses.
+    // This must be done in initialize to enforce thread safety of start up of
+    // the protocol buffer library.
+    [self extensionRegistry];
+  }
+}
+
++ (GPBExtensionRegistry *)extensionRegistry {
+  // Is overridden in all the subclasses that provide extensions to provide the
+  // per class one.
+  return gDefaultExtensionRegistry;
+}
+
++ (void)globallyRegisterExtension:(GPBExtensionDescriptor *)field {
+  const char *key = [field singletonNameC];
+  dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
+                          DISPATCH_TIME_FOREVER);
+  CFDictionarySetValue(gExtensionSingletonDictionary, key, field);
+  dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
+}
+
+static id ExtensionForName(id self, SEL _cmd) {
+  // Really fast way of doing "classname_selName".
+  // This came up as a hotspot (creation of NSString *) when accessing a
+  // lot of extensions.
+  const char *selName = sel_getName(_cmd);
+  if (selName[0] == '_') {
+    return nil;  // Apple internal selector.
+  }
+  size_t selNameLen = 0;
+  while (1) {
+    char c = selName[selNameLen];
+    if (c == '\0') {  // String end.
+      break;
+    }
+    if (c == ':') {
+      return nil;  // Selector took an arg, not one of the runtime methods.
+    }
+    ++selNameLen;
+  }
+
+  const char *className = class_getName(self);
+  size_t classNameLen = strlen(className);
+  char key[classNameLen + selNameLen + 2];
+  memcpy(key, className, classNameLen);
+  key[classNameLen] = '_';
+  memcpy(&key[classNameLen + 1], selName, selNameLen);
+  key[classNameLen + 1 + selNameLen] = '\0';
+
+  // NOTE: Even though this method is called from another C function,
+  // gExtensionSingletonDictionarySemaphore and gExtensionSingletonDictionary
+  // will always be initialized. This is because this call flow is just to
+  // lookup the Extension, meaning the code is calling an Extension class
+  // message on a Message or Root class. This guarantees that the class was
+  // initialized and Message classes ensure their Root was also initialized.
+  NSAssert(gExtensionSingletonDictionary, @"Startup order broken!");
+
+  dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
+                          DISPATCH_TIME_FOREVER);
+  id extension = (id)CFDictionaryGetValue(gExtensionSingletonDictionary, key);
+  // We can't remove the key from the dictionary here (as an optimization),
+  // two threads could have gone into +resolveClassMethod: for the same method,
+  // and ended up here; there's no way to ensure both return YES without letting
+  // both try to wire in the method.
+  dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
+  return extension;
+}
+
+BOOL GPBResolveExtensionClassMethod(Class self, SEL sel) {
+  // Another option would be to register the extensions with the class at
+  // globallyRegisterExtension:
+  // Timing the two solutions, this solution turned out to be much faster
+  // and reduced startup time, and runtime memory.
+  // The advantage to globallyRegisterExtension is that it would reduce the
+  // size of the protos somewhat because the singletonNameC wouldn't need
+  // to include the class name. For a class with a lot of extensions it
+  // can add up. You could also significantly reduce the code complexity of this
+  // file.
+  id extension = ExtensionForName(self, sel);
+  if (extension != nil) {
+    const char *encoding =
+        GPBMessageEncodingForSelector(@selector(getClassValue), NO);
+    Class metaClass = objc_getMetaClass(class_getName(self));
+    IMP imp = imp_implementationWithBlock(^(id obj) {
+#pragma unused(obj)
+      return extension;
+    });
+    BOOL methodAdded = class_addMethod(metaClass, sel, imp, encoding);
+    // class_addMethod() is documented as also failing if the method was already
+    // added; so we check if the method is already there and return success so
+    // the method dispatch will still happen.  Why would it already be added?
+    // Two threads could cause the same method to be bound at the same time,
+    // but only one will actually bind it; the other still needs to return true
+    // so things will dispatch.
+    if (!methodAdded) {
+      methodAdded = GPBClassHasSel(metaClass, sel);
+    }
+    return methodAdded;
+  }
+  return NO;
+}
+
+
++ (BOOL)resolveClassMethod:(SEL)sel {
+  if (GPBResolveExtensionClassMethod(self, sel)) {
+    return YES;
+  }
+  return [super resolveClassMethod:sel];
+}
+
+@end
diff --git a/iOS/Pods/Protobuf/objectivec/GPBRootObject_PackagePrivate.h b/iOS/Pods/Protobuf/objectivec/GPBRootObject_PackagePrivate.h
new file mode 100644 (file)
index 0000000..3c8f09c
--- /dev/null
@@ -0,0 +1,46 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBRootObject.h"
+
+@class GPBExtensionDescriptor;
+
+@interface GPBRootObject ()
+
+// Globally register.
++ (void)globallyRegisterExtension:(GPBExtensionDescriptor *)field;
+
+@end
+
+// Returns YES if the selector was resolved and added to the class,
+// NO otherwise.
+BOOL GPBResolveExtensionClassMethod(Class self, SEL sel);
diff --git a/iOS/Pods/Protobuf/objectivec/GPBRuntimeTypes.h b/iOS/Pods/Protobuf/objectivec/GPBRuntimeTypes.h
new file mode 100644 (file)
index 0000000..4d55206
--- /dev/null
@@ -0,0 +1,144 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBBootstrap.h"
+
+@class GPBEnumDescriptor;
+@class GPBMessage;
+@class GPBInt32Array;
+
+/**
+ * Verifies that a given value can be represented by an enum type.
+ * */
+typedef BOOL (*GPBEnumValidationFunc)(int32_t);
+
+/**
+ * Fetches an EnumDescriptor.
+ * */
+typedef GPBEnumDescriptor *(*GPBEnumDescriptorFunc)(void);
+
+/**
+ * Magic value used at runtime to indicate an enum value that wasn't know at
+ * compile time.
+ * */
+enum {
+  kGPBUnrecognizedEnumeratorValue = (int32_t)0xFBADBEEF,
+};
+
+/**
+ * A union for storing all possible Protobuf values. Note that owner is
+ * responsible for memory management of object types.
+ * */
+typedef union {
+  BOOL valueBool;
+  int32_t valueInt32;
+  int64_t valueInt64;
+  uint32_t valueUInt32;
+  uint64_t valueUInt64;
+  float valueFloat;
+  double valueDouble;
+  GPB_UNSAFE_UNRETAINED NSData *valueData;
+  GPB_UNSAFE_UNRETAINED NSString *valueString;
+  GPB_UNSAFE_UNRETAINED GPBMessage *valueMessage;
+  int32_t valueEnum;
+} GPBGenericValue;
+
+/**
+ * Enum listing the possible data types that a field can contain.
+ * 
+ * @note Do not change the order of this enum (or add things to it) without
+ *       thinking about it very carefully. There are several things that depend
+ *       on the order.
+ * */
+typedef NS_ENUM(uint8_t, GPBDataType) {
+  /** Field contains boolean value(s). */
+  GPBDataTypeBool = 0,
+  /** Field contains unsigned 4 byte value(s). */
+  GPBDataTypeFixed32,
+  /** Field contains signed 4 byte value(s). */
+  GPBDataTypeSFixed32,
+  /** Field contains float value(s). */
+  GPBDataTypeFloat,
+  /** Field contains unsigned 8 byte value(s). */
+  GPBDataTypeFixed64,
+  /** Field contains signed 8 byte value(s). */
+  GPBDataTypeSFixed64,
+  /** Field contains double value(s). */
+  GPBDataTypeDouble,
+  /**
+   * Field contains variable length value(s). Inefficient for encoding negative
+   * numbers – if your field is likely to have negative values, use
+   * GPBDataTypeSInt32 instead.
+   **/
+  GPBDataTypeInt32,
+  /**
+   * Field contains variable length value(s). Inefficient for encoding negative
+   * numbers – if your field is likely to have negative values, use
+   * GPBDataTypeSInt64 instead.
+   **/
+  GPBDataTypeInt64,
+  /** Field contains signed variable length integer value(s). */
+  GPBDataTypeSInt32,
+  /** Field contains signed variable length integer value(s). */
+  GPBDataTypeSInt64,
+  /** Field contains unsigned variable length integer value(s). */
+  GPBDataTypeUInt32,
+  /** Field contains unsigned variable length integer value(s). */
+  GPBDataTypeUInt64,
+  /** Field contains an arbitrary sequence of bytes. */
+  GPBDataTypeBytes,
+  /** Field contains UTF-8 encoded or 7-bit ASCII text. */
+  GPBDataTypeString,
+  /** Field contains message type(s). */
+  GPBDataTypeMessage,
+  /** Field contains message type(s). */
+  GPBDataTypeGroup,
+  /** Field contains enum value(s). */
+  GPBDataTypeEnum,
+};
+
+enum {
+  /**
+   * A count of the number of types in GPBDataType. Separated out from the
+   * GPBDataType enum to avoid warnings regarding not handling GPBDataType_Count
+   * in switch statements.
+   **/
+  GPBDataType_Count = GPBDataTypeEnum + 1
+};
+
+/** An extension range. */
+typedef struct GPBExtensionRange {
+  /** Inclusive. */
+  uint32_t start;
+  /** Exclusive. */
+  uint32_t end;
+} GPBExtensionRange;
diff --git a/iOS/Pods/Protobuf/objectivec/GPBUnknownField.h b/iOS/Pods/Protobuf/objectivec/GPBUnknownField.h
new file mode 100644 (file)
index 0000000..5b96023
--- /dev/null
@@ -0,0 +1,99 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+@class GPBCodedOutputStream;
+@class GPBUInt32Array;
+@class GPBUInt64Array;
+@class GPBUnknownFieldSet;
+
+NS_ASSUME_NONNULL_BEGIN
+/**
+ * Store an unknown field. These are used in conjunction with
+ * GPBUnknownFieldSet.
+ **/
+@interface GPBUnknownField : NSObject<NSCopying>
+
+/** Initialize a field with the given number. */
+- (instancetype)initWithNumber:(int32_t)number;
+
+/** The field number the data is stored under. */
+@property(nonatomic, readonly, assign) int32_t number;
+
+/** An array of varint values for this field. */
+@property(nonatomic, readonly, strong) GPBUInt64Array *varintList;
+
+/** An array of fixed32 values for this field. */
+@property(nonatomic, readonly, strong) GPBUInt32Array *fixed32List;
+
+/** An array of fixed64 values for this field. */
+@property(nonatomic, readonly, strong) GPBUInt64Array *fixed64List;
+
+/** An array of data values for this field. */
+@property(nonatomic, readonly, strong) NSArray<NSData*> *lengthDelimitedList;
+
+/** An array of groups of values for this field. */
+@property(nonatomic, readonly, strong) NSArray<GPBUnknownFieldSet*> *groupList;
+
+/**
+ * Add a value to the varintList.
+ *
+ * @param value The value to add.
+ **/
+- (void)addVarint:(uint64_t)value;
+/**
+ * Add a value to the fixed32List.
+ *
+ * @param value The value to add.
+ **/
+- (void)addFixed32:(uint32_t)value;
+/**
+ * Add a value to the fixed64List.
+ *
+ * @param value The value to add.
+ **/
+- (void)addFixed64:(uint64_t)value;
+/**
+ * Add a value to the lengthDelimitedList.
+ *
+ * @param value The value to add.
+ **/
+- (void)addLengthDelimited:(NSData *)value;
+/**
+ * Add a value to the groupList.
+ *
+ * @param value The value to add.
+ **/
+- (void)addGroup:(GPBUnknownFieldSet *)value;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBUnknownField.m b/iOS/Pods/Protobuf/objectivec/GPBUnknownField.m
new file mode 100644 (file)
index 0000000..9d5c97f
--- /dev/null
@@ -0,0 +1,336 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBUnknownField_PackagePrivate.h"
+
+#import "GPBArray.h"
+#import "GPBCodedOutputStream_PackagePrivate.h"
+
+@implementation GPBUnknownField {
+ @protected
+  int32_t number_;
+  GPBUInt64Array *mutableVarintList_;
+  GPBUInt32Array *mutableFixed32List_;
+  GPBUInt64Array *mutableFixed64List_;
+  NSMutableArray<NSData*> *mutableLengthDelimitedList_;
+  NSMutableArray<GPBUnknownFieldSet*> *mutableGroupList_;
+}
+
+@synthesize number = number_;
+@synthesize varintList = mutableVarintList_;
+@synthesize fixed32List = mutableFixed32List_;
+@synthesize fixed64List = mutableFixed64List_;
+@synthesize lengthDelimitedList = mutableLengthDelimitedList_;
+@synthesize groupList = mutableGroupList_;
+
+- (instancetype)initWithNumber:(int32_t)number {
+  if ((self = [super init])) {
+    number_ = number;
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [mutableVarintList_ release];
+  [mutableFixed32List_ release];
+  [mutableFixed64List_ release];
+  [mutableLengthDelimitedList_ release];
+  [mutableGroupList_ release];
+
+  [super dealloc];
+}
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+- (id)copyWithZone:(NSZone *)zone {
+  GPBUnknownField *result =
+      [[GPBUnknownField allocWithZone:zone] initWithNumber:number_];
+  result->mutableFixed32List_ = [mutableFixed32List_ copyWithZone:zone];
+  result->mutableFixed64List_ = [mutableFixed64List_ copyWithZone:zone];
+  result->mutableLengthDelimitedList_ =
+      [mutableLengthDelimitedList_ mutableCopyWithZone:zone];
+  result->mutableVarintList_ = [mutableVarintList_ copyWithZone:zone];
+  if (mutableGroupList_.count) {
+    result->mutableGroupList_ = [[NSMutableArray allocWithZone:zone]
+        initWithCapacity:mutableGroupList_.count];
+    for (GPBUnknownFieldSet *group in mutableGroupList_) {
+      GPBUnknownFieldSet *copied = [group copyWithZone:zone];
+      [result->mutableGroupList_ addObject:copied];
+      [copied release];
+    }
+  }
+  return result;
+}
+
+- (BOOL)isEqual:(id)object {
+  if (self == object) return YES;
+  if (![object isKindOfClass:[GPBUnknownField class]]) return NO;
+  GPBUnknownField *field = (GPBUnknownField *)object;
+  if (number_ != field->number_) return NO;
+  BOOL equalVarint =
+      (mutableVarintList_.count == 0 && field->mutableVarintList_.count == 0) ||
+      [mutableVarintList_ isEqual:field->mutableVarintList_];
+  if (!equalVarint) return NO;
+  BOOL equalFixed32 = (mutableFixed32List_.count == 0 &&
+                       field->mutableFixed32List_.count == 0) ||
+                      [mutableFixed32List_ isEqual:field->mutableFixed32List_];
+  if (!equalFixed32) return NO;
+  BOOL equalFixed64 = (mutableFixed64List_.count == 0 &&
+                       field->mutableFixed64List_.count == 0) ||
+                      [mutableFixed64List_ isEqual:field->mutableFixed64List_];
+  if (!equalFixed64) return NO;
+  BOOL equalLDList =
+      (mutableLengthDelimitedList_.count == 0 &&
+       field->mutableLengthDelimitedList_.count == 0) ||
+      [mutableLengthDelimitedList_ isEqual:field->mutableLengthDelimitedList_];
+  if (!equalLDList) return NO;
+  BOOL equalGroupList =
+      (mutableGroupList_.count == 0 && field->mutableGroupList_.count == 0) ||
+      [mutableGroupList_ isEqual:field->mutableGroupList_];
+  if (!equalGroupList) return NO;
+  return YES;
+}
+
+- (NSUInteger)hash {
+  // Just mix the hashes of the possible sub arrays.
+  const int prime = 31;
+  NSUInteger result = prime + [mutableVarintList_ hash];
+  result = prime * result + [mutableFixed32List_ hash];
+  result = prime * result + [mutableFixed64List_ hash];
+  result = prime * result + [mutableLengthDelimitedList_ hash];
+  result = prime * result + [mutableGroupList_ hash];
+  return result;
+}
+
+- (void)writeToOutput:(GPBCodedOutputStream *)output {
+  NSUInteger count = mutableVarintList_.count;
+  if (count > 0) {
+    [output writeUInt64Array:number_ values:mutableVarintList_ tag:0];
+  }
+  count = mutableFixed32List_.count;
+  if (count > 0) {
+    [output writeFixed32Array:number_ values:mutableFixed32List_ tag:0];
+  }
+  count = mutableFixed64List_.count;
+  if (count > 0) {
+    [output writeFixed64Array:number_ values:mutableFixed64List_ tag:0];
+  }
+  count = mutableLengthDelimitedList_.count;
+  if (count > 0) {
+    [output writeBytesArray:number_ values:mutableLengthDelimitedList_];
+  }
+  count = mutableGroupList_.count;
+  if (count > 0) {
+    [output writeUnknownGroupArray:number_ values:mutableGroupList_];
+  }
+}
+
+- (size_t)serializedSize {
+  __block size_t result = 0;
+  int32_t number = number_;
+  [mutableVarintList_
+      enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        result += GPBComputeUInt64Size(number, value);
+      }];
+
+  [mutableFixed32List_
+      enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        result += GPBComputeFixed32Size(number, value);
+      }];
+
+  [mutableFixed64List_
+      enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        result += GPBComputeFixed64Size(number, value);
+      }];
+
+  for (NSData *data in mutableLengthDelimitedList_) {
+    result += GPBComputeBytesSize(number, data);
+  }
+
+  for (GPBUnknownFieldSet *set in mutableGroupList_) {
+    result += GPBComputeUnknownGroupSize(number, set);
+  }
+
+  return result;
+}
+
+- (void)writeAsMessageSetExtensionToOutput:(GPBCodedOutputStream *)output {
+  for (NSData *data in mutableLengthDelimitedList_) {
+    [output writeRawMessageSetExtension:number_ value:data];
+  }
+}
+
+- (size_t)serializedSizeAsMessageSetExtension {
+  size_t result = 0;
+  for (NSData *data in mutableLengthDelimitedList_) {
+    result += GPBComputeRawMessageSetExtensionSize(number_, data);
+  }
+  return result;
+}
+
+- (NSString *)description {
+  NSMutableString *description =
+      [NSMutableString stringWithFormat:@"<%@ %p>: Field: %d {\n",
+       [self class], self, number_];
+  [mutableVarintList_
+      enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        [description appendFormat:@"\t%llu\n", value];
+      }];
+
+  [mutableFixed32List_
+      enumerateValuesWithBlock:^(uint32_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        [description appendFormat:@"\t%u\n", value];
+      }];
+
+  [mutableFixed64List_
+      enumerateValuesWithBlock:^(uint64_t value, NSUInteger idx, BOOL *stop) {
+#pragma unused(idx, stop)
+        [description appendFormat:@"\t%llu\n", value];
+      }];
+
+  for (NSData *data in mutableLengthDelimitedList_) {
+    [description appendFormat:@"\t%@\n", data];
+  }
+
+  for (GPBUnknownFieldSet *set in mutableGroupList_) {
+    [description appendFormat:@"\t%@\n", set];
+  }
+  [description appendString:@"}"];
+  return description;
+}
+
+- (void)mergeFromField:(GPBUnknownField *)other {
+  GPBUInt64Array *otherVarintList = other.varintList;
+  if (otherVarintList.count > 0) {
+    if (mutableVarintList_ == nil) {
+      mutableVarintList_ = [otherVarintList copy];
+    } else {
+      [mutableVarintList_ addValuesFromArray:otherVarintList];
+    }
+  }
+
+  GPBUInt32Array *otherFixed32List = other.fixed32List;
+  if (otherFixed32List.count > 0) {
+    if (mutableFixed32List_ == nil) {
+      mutableFixed32List_ = [otherFixed32List copy];
+    } else {
+      [mutableFixed32List_ addValuesFromArray:otherFixed32List];
+    }
+  }
+
+  GPBUInt64Array *otherFixed64List = other.fixed64List;
+  if (otherFixed64List.count > 0) {
+    if (mutableFixed64List_ == nil) {
+      mutableFixed64List_ = [otherFixed64List copy];
+    } else {
+      [mutableFixed64List_ addValuesFromArray:otherFixed64List];
+    }
+  }
+
+  NSArray *otherLengthDelimitedList = other.lengthDelimitedList;
+  if (otherLengthDelimitedList.count > 0) {
+    if (mutableLengthDelimitedList_ == nil) {
+      mutableLengthDelimitedList_ = [otherLengthDelimitedList mutableCopy];
+    } else {
+      [mutableLengthDelimitedList_
+          addObjectsFromArray:otherLengthDelimitedList];
+    }
+  }
+
+  NSArray *otherGroupList = other.groupList;
+  if (otherGroupList.count > 0) {
+    if (mutableGroupList_ == nil) {
+      mutableGroupList_ =
+          [[NSMutableArray alloc] initWithCapacity:otherGroupList.count];
+    }
+    // Make our own mutable copies.
+    for (GPBUnknownFieldSet *group in otherGroupList) {
+      GPBUnknownFieldSet *copied = [group copy];
+      [mutableGroupList_ addObject:copied];
+      [copied release];
+    }
+  }
+}
+
+- (void)addVarint:(uint64_t)value {
+  if (mutableVarintList_ == nil) {
+    mutableVarintList_ = [[GPBUInt64Array alloc] initWithValues:&value count:1];
+  } else {
+    [mutableVarintList_ addValue:value];
+  }
+}
+
+- (void)addFixed32:(uint32_t)value {
+  if (mutableFixed32List_ == nil) {
+    mutableFixed32List_ =
+        [[GPBUInt32Array alloc] initWithValues:&value count:1];
+  } else {
+    [mutableFixed32List_ addValue:value];
+  }
+}
+
+- (void)addFixed64:(uint64_t)value {
+  if (mutableFixed64List_ == nil) {
+    mutableFixed64List_ =
+        [[GPBUInt64Array alloc] initWithValues:&value count:1];
+  } else {
+    [mutableFixed64List_ addValue:value];
+  }
+}
+
+- (void)addLengthDelimited:(NSData *)value {
+  if (mutableLengthDelimitedList_ == nil) {
+    mutableLengthDelimitedList_ =
+        [[NSMutableArray alloc] initWithObjects:&value count:1];
+  } else {
+    [mutableLengthDelimitedList_ addObject:value];
+  }
+}
+
+- (void)addGroup:(GPBUnknownFieldSet *)value {
+  if (mutableGroupList_ == nil) {
+    mutableGroupList_ = [[NSMutableArray alloc] initWithObjects:&value count:1];
+  } else {
+    [mutableGroupList_ addObject:value];
+  }
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/iOS/Pods/Protobuf/objectivec/GPBUnknownFieldSet.h b/iOS/Pods/Protobuf/objectivec/GPBUnknownFieldSet.h
new file mode 100644 (file)
index 0000000..1b5f24f
--- /dev/null
@@ -0,0 +1,82 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+@class GPBUnknownField;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * A collection of unknown fields. Fields parsed from the binary representation
+ * of a message that are unknown end up in an instance of this set. This only
+ * applies for files declared with the "proto2" syntax. Files declared with the
+ * "proto3" syntax discard the unknown values.
+ **/
+@interface GPBUnknownFieldSet : NSObject<NSCopying>
+
+/**
+ * Tests to see if the given field number has a value.
+ *
+ * @param number The field number to check.
+ *
+ * @return YES if there is an unknown field for the given field number.
+ **/
+- (BOOL)hasField:(int32_t)number;
+
+/**
+ * Fetches the GPBUnknownField for the given field number.
+ *
+ * @param number The field number to look up.
+ *
+ * @return The GPBUnknownField or nil if none found.
+ **/
+- (nullable GPBUnknownField *)getField:(int32_t)number;
+
+/**
+ * @return The number of fields in this set.
+ **/
+- (NSUInteger)countOfFields;
+
+/**
+ * Adds the given field to the set.
+ *
+ * @param field The field to add to the set.
+ **/
+- (void)addField:(GPBUnknownField *)field;
+
+/**
+ * @return An array of the GPBUnknownFields sorted by the field numbers.
+ **/
+- (NSArray<GPBUnknownField *> *)sortedFields;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBUnknownFieldSet.m b/iOS/Pods/Protobuf/objectivec/GPBUnknownFieldSet.m
new file mode 100644 (file)
index 0000000..a7335f0
--- /dev/null
@@ -0,0 +1,395 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBUnknownFieldSet_PackagePrivate.h"
+
+#import "GPBCodedInputStream_PackagePrivate.h"
+#import "GPBCodedOutputStream.h"
+#import "GPBUnknownField_PackagePrivate.h"
+#import "GPBUtilities.h"
+#import "GPBWireFormat.h"
+
+#pragma mark Helpers
+
+static void checkNumber(int32_t number) {
+  if (number == 0) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"Zero is not a valid field number."];
+  }
+}
+
+@implementation GPBUnknownFieldSet {
+ @package
+  CFMutableDictionaryRef fields_;
+}
+
+static void CopyWorker(const void *key, const void *value, void *context) {
+#pragma unused(key)
+  GPBUnknownField *field = value;
+  GPBUnknownFieldSet *result = context;
+
+  GPBUnknownField *copied = [field copy];
+  [result addField:copied];
+  [copied release];
+}
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+- (id)copyWithZone:(NSZone *)zone {
+  GPBUnknownFieldSet *result = [[GPBUnknownFieldSet allocWithZone:zone] init];
+  if (fields_) {
+    CFDictionaryApplyFunction(fields_, CopyWorker, result);
+  }
+  return result;
+}
+
+- (void)dealloc {
+  if (fields_) {
+    CFRelease(fields_);
+  }
+  [super dealloc];
+}
+
+- (BOOL)isEqual:(id)object {
+  BOOL equal = NO;
+  if ([object isKindOfClass:[GPBUnknownFieldSet class]]) {
+    GPBUnknownFieldSet *set = (GPBUnknownFieldSet *)object;
+    if ((fields_ == NULL) && (set->fields_ == NULL)) {
+      equal = YES;
+    } else if ((fields_ != NULL) && (set->fields_ != NULL)) {
+      equal = CFEqual(fields_, set->fields_);
+    }
+  }
+  return equal;
+}
+
+- (NSUInteger)hash {
+  // Return the hash of the fields dictionary (or just some value).
+  if (fields_) {
+    return CFHash(fields_);
+  }
+  return (NSUInteger)[GPBUnknownFieldSet class];
+}
+
+#pragma mark - Public Methods
+
+- (BOOL)hasField:(int32_t)number {
+  ssize_t key = number;
+  return fields_ ? (CFDictionaryGetValue(fields_, (void *)key) != nil) : NO;
+}
+
+- (GPBUnknownField *)getField:(int32_t)number {
+  ssize_t key = number;
+  GPBUnknownField *result =
+      fields_ ? CFDictionaryGetValue(fields_, (void *)key) : nil;
+  return result;
+}
+
+- (NSUInteger)countOfFields {
+  return fields_ ? CFDictionaryGetCount(fields_) : 0;
+}
+
+- (NSArray *)sortedFields {
+  if (!fields_) return [NSArray array];
+  size_t count = CFDictionaryGetCount(fields_);
+  ssize_t keys[count];
+  GPBUnknownField *values[count];
+  CFDictionaryGetKeysAndValues(fields_, (const void **)keys,
+                               (const void **)values);
+  struct GPBFieldPair {
+    ssize_t key;
+    GPBUnknownField *value;
+  } pairs[count];
+  for (size_t i = 0; i < count; ++i) {
+    pairs[i].key = keys[i];
+    pairs[i].value = values[i];
+  };
+  qsort_b(pairs, count, sizeof(struct GPBFieldPair),
+          ^(const void *first, const void *second) {
+            const struct GPBFieldPair *a = first;
+            const struct GPBFieldPair *b = second;
+            return (a->key > b->key) ? 1 : ((a->key == b->key) ? 0 : -1);
+          });
+  for (size_t i = 0; i < count; ++i) {
+    values[i] = pairs[i].value;
+  };
+  return [NSArray arrayWithObjects:values count:count];
+}
+
+#pragma mark - Internal Methods
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output {
+  if (!fields_) return;
+  size_t count = CFDictionaryGetCount(fields_);
+  ssize_t keys[count];
+  GPBUnknownField *values[count];
+  CFDictionaryGetKeysAndValues(fields_, (const void **)keys,
+                               (const void **)values);
+  if (count > 1) {
+    struct GPBFieldPair {
+      ssize_t key;
+      GPBUnknownField *value;
+    } pairs[count];
+
+    for (size_t i = 0; i < count; ++i) {
+      pairs[i].key = keys[i];
+      pairs[i].value = values[i];
+    };
+    qsort_b(pairs, count, sizeof(struct GPBFieldPair),
+            ^(const void *first, const void *second) {
+              const struct GPBFieldPair *a = first;
+              const struct GPBFieldPair *b = second;
+              return (a->key > b->key) ? 1 : ((a->key == b->key) ? 0 : -1);
+            });
+    for (size_t i = 0; i < count; ++i) {
+      GPBUnknownField *value = pairs[i].value;
+      [value writeToOutput:output];
+    }
+  } else {
+    [values[0] writeToOutput:output];
+  }
+}
+
+- (NSString *)description {
+  NSMutableString *description = [NSMutableString
+      stringWithFormat:@"<%@ %p>: TextFormat: {\n", [self class], self];
+  NSString *textFormat = GPBTextFormatForUnknownFieldSet(self, @"  ");
+  [description appendString:textFormat];
+  [description appendString:@"}"];
+  return description;
+}
+
+static void GPBUnknownFieldSetSerializedSize(const void *key, const void *value,
+                                             void *context) {
+#pragma unused(key)
+  GPBUnknownField *field = value;
+  size_t *result = context;
+  *result += [field serializedSize];
+}
+
+- (size_t)serializedSize {
+  size_t result = 0;
+  if (fields_) {
+    CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetSerializedSize,
+                              &result);
+  }
+  return result;
+}
+
+static void GPBUnknownFieldSetWriteAsMessageSetTo(const void *key,
+                                                  const void *value,
+                                                  void *context) {
+#pragma unused(key)
+  GPBUnknownField *field = value;
+  GPBCodedOutputStream *output = context;
+  [field writeAsMessageSetExtensionToOutput:output];
+}
+
+- (void)writeAsMessageSetTo:(GPBCodedOutputStream *)output {
+  if (fields_) {
+    CFDictionaryApplyFunction(fields_, GPBUnknownFieldSetWriteAsMessageSetTo,
+                              output);
+  }
+}
+
+static void GPBUnknownFieldSetSerializedSizeAsMessageSet(const void *key,
+                                                         const void *value,
+                                                         void *context) {
+#pragma unused(key)
+  GPBUnknownField *field = value;
+  size_t *result = context;
+  *result += [field serializedSizeAsMessageSetExtension];
+}
+
+- (size_t)serializedSizeAsMessageSet {
+  size_t result = 0;
+  if (fields_) {
+    CFDictionaryApplyFunction(
+        fields_, GPBUnknownFieldSetSerializedSizeAsMessageSet, &result);
+  }
+  return result;
+}
+
+- (NSData *)data {
+  NSMutableData *data = [NSMutableData dataWithLength:self.serializedSize];
+  GPBCodedOutputStream *output =
+      [[GPBCodedOutputStream alloc] initWithData:data];
+  [self writeToCodedOutputStream:output];
+  [output release];
+  return data;
+}
+
++ (BOOL)isFieldTag:(int32_t)tag {
+  return GPBWireFormatGetTagWireType(tag) != GPBWireFormatEndGroup;
+}
+
+- (void)addField:(GPBUnknownField *)field {
+  int32_t number = [field number];
+  checkNumber(number);
+  if (!fields_) {
+    // Use a custom dictionary here because the keys are numbers and conversion
+    // back and forth from NSNumber isn't worth the cost.
+    fields_ = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL,
+                                        &kCFTypeDictionaryValueCallBacks);
+  }
+  ssize_t key = number;
+  CFDictionarySetValue(fields_, (const void *)key, field);
+}
+
+- (GPBUnknownField *)mutableFieldForNumber:(int32_t)number create:(BOOL)create {
+  ssize_t key = number;
+  GPBUnknownField *existing =
+      fields_ ? CFDictionaryGetValue(fields_, (const void *)key) : nil;
+  if (!existing && create) {
+    existing = [[GPBUnknownField alloc] initWithNumber:number];
+    // This retains existing.
+    [self addField:existing];
+    [existing release];
+  }
+  return existing;
+}
+
+static void GPBUnknownFieldSetMergeUnknownFields(const void *key,
+                                                 const void *value,
+                                                 void *context) {
+#pragma unused(key)
+  GPBUnknownField *field = value;
+  GPBUnknownFieldSet *self = context;
+
+  int32_t number = [field number];
+  checkNumber(number);
+  GPBUnknownField *oldField = [self mutableFieldForNumber:number create:NO];
+  if (oldField) {
+    [oldField mergeFromField:field];
+  } else {
+    // Merge only comes from GPBMessage's mergeFrom:, so it means we are on
+    // mutable message and are an mutable instance, so make sure we need
+    // mutable fields.
+    GPBUnknownField *fieldCopy = [field copy];
+    [self addField:fieldCopy];
+    [fieldCopy release];
+  }
+}
+
+- (void)mergeUnknownFields:(GPBUnknownFieldSet *)other {
+  if (other && other->fields_) {
+    CFDictionaryApplyFunction(other->fields_,
+                              GPBUnknownFieldSetMergeUnknownFields, self);
+  }
+}
+
+- (void)mergeFromData:(NSData *)data {
+  GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data];
+  [self mergeFromCodedInputStream:input];
+  [input checkLastTagWas:0];
+  [input release];
+}
+
+- (void)mergeVarintField:(int32_t)number value:(int32_t)value {
+  checkNumber(number);
+  [[self mutableFieldForNumber:number create:YES] addVarint:value];
+}
+
+- (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input {
+  NSAssert(GPBWireFormatIsValidTag(tag), @"Got passed an invalid tag");
+  int32_t number = GPBWireFormatGetTagFieldNumber(tag);
+  GPBCodedInputStreamState *state = &input->state_;
+  switch (GPBWireFormatGetTagWireType(tag)) {
+    case GPBWireFormatVarint: {
+      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
+      [field addVarint:GPBCodedInputStreamReadInt64(state)];
+      return YES;
+    }
+    case GPBWireFormatFixed64: {
+      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
+      [field addFixed64:GPBCodedInputStreamReadFixed64(state)];
+      return YES;
+    }
+    case GPBWireFormatLengthDelimited: {
+      NSData *data = GPBCodedInputStreamReadRetainedBytes(state);
+      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
+      [field addLengthDelimited:data];
+      [data release];
+      return YES;
+    }
+    case GPBWireFormatStartGroup: {
+      GPBUnknownFieldSet *unknownFieldSet = [[GPBUnknownFieldSet alloc] init];
+      [input readUnknownGroup:number message:unknownFieldSet];
+      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
+      [field addGroup:unknownFieldSet];
+      [unknownFieldSet release];
+      return YES;
+    }
+    case GPBWireFormatEndGroup:
+      return NO;
+    case GPBWireFormatFixed32: {
+      GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
+      [field addFixed32:GPBCodedInputStreamReadFixed32(state)];
+      return YES;
+    }
+  }
+}
+
+- (void)mergeMessageSetMessage:(int32_t)number data:(NSData *)messageData {
+  [[self mutableFieldForNumber:number create:YES]
+      addLengthDelimited:messageData];
+}
+
+- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data {
+  GPBUnknownField *field = [self mutableFieldForNumber:fieldNum create:YES];
+  [field addLengthDelimited:data];
+}
+
+- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input {
+  while (YES) {
+    int32_t tag = GPBCodedInputStreamReadTag(&input->state_);
+    if (tag == 0 || ![self mergeFieldFrom:tag input:input]) {
+      break;
+    }
+  }
+}
+
+- (void)getTags:(int32_t *)tags {
+  if (!fields_) return;
+  size_t count = CFDictionaryGetCount(fields_);
+  ssize_t keys[count];
+  CFDictionaryGetKeysAndValues(fields_, (const void **)keys, NULL);
+  for (size_t i = 0; i < count; ++i) {
+    tags[i] = (int32_t)keys[i];
+  }
+}
+
+#pragma clang diagnostic pop
+
+@end
diff --git a/iOS/Pods/Protobuf/objectivec/GPBUnknownFieldSet_PackagePrivate.h b/iOS/Pods/Protobuf/objectivec/GPBUnknownFieldSet_PackagePrivate.h
new file mode 100644 (file)
index 0000000..e27127a
--- /dev/null
@@ -0,0 +1,61 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBUnknownFieldSet.h"
+
+@class GPBCodedOutputStream;
+@class GPBCodedInputStream;
+
+@interface GPBUnknownFieldSet ()
+
++ (BOOL)isFieldTag:(int32_t)tag;
+
+- (NSData *)data;
+
+- (size_t)serializedSize;
+- (size_t)serializedSizeAsMessageSet;
+
+- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output;
+- (void)writeAsMessageSetTo:(GPBCodedOutputStream *)output;
+
+- (void)mergeUnknownFields:(GPBUnknownFieldSet *)other;
+
+- (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input;
+- (void)mergeFromData:(NSData *)data;
+
+- (void)mergeVarintField:(int32_t)number value:(int32_t)value;
+- (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input;
+- (void)mergeMessageSetMessage:(int32_t)number data:(NSData *)messageData;
+
+- (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data;
+
+@end
diff --git a/iOS/Pods/Protobuf/objectivec/GPBUnknownField_PackagePrivate.h b/iOS/Pods/Protobuf/objectivec/GPBUnknownField_PackagePrivate.h
new file mode 100644 (file)
index 0000000..2b4c789
--- /dev/null
@@ -0,0 +1,47 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBUnknownField.h"
+
+@class GPBCodedOutputStream;
+
+@interface GPBUnknownField ()
+
+- (void)writeToOutput:(GPBCodedOutputStream *)output;
+- (size_t)serializedSize;
+
+- (void)writeAsMessageSetExtensionToOutput:(GPBCodedOutputStream *)output;
+- (size_t)serializedSizeAsMessageSetExtension;
+
+- (void)mergeFromField:(GPBUnknownField *)other;
+
+@end
diff --git a/iOS/Pods/Protobuf/objectivec/GPBUtilities.h b/iOS/Pods/Protobuf/objectivec/GPBUtilities.h
new file mode 100644 (file)
index 0000000..5464dfb
--- /dev/null
@@ -0,0 +1,539 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBArray.h"
+#import "GPBMessage.h"
+#import "GPBRuntimeTypes.h"
+
+CF_EXTERN_C_BEGIN
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Generates a string that should be a valid "TextFormat" for the C++ version
+ * of Protocol Buffers.
+ *
+ * @param message    The message to generate from.
+ * @param lineIndent A string to use as the prefix for all lines generated. Can
+ *                   be nil if no extra indent is needed.
+ *
+ * @return An NSString with the TextFormat of the message.
+ **/
+NSString *GPBTextFormatForMessage(GPBMessage *message,
+                                  NSString * __nullable lineIndent);
+
+/**
+ * Generates a string that should be a valid "TextFormat" for the C++ version
+ * of Protocol Buffers.
+ *
+ * @param unknownSet The unknown field set to generate from.
+ * @param lineIndent A string to use as the prefix for all lines generated. Can
+ *                   be nil if no extra indent is needed.
+ *
+ * @return An NSString with the TextFormat of the unknown field set.
+ **/
+NSString *GPBTextFormatForUnknownFieldSet(GPBUnknownFieldSet * __nullable unknownSet,
+                                          NSString * __nullable lineIndent);
+
+/**
+ * Checks if the given field number is set on a message.
+ *
+ * @param self        The message to check.
+ * @param fieldNumber The field number to check.
+ *
+ * @return YES if the field number is set on the given message.
+ **/
+BOOL GPBMessageHasFieldNumberSet(GPBMessage *self, uint32_t fieldNumber);
+
+/**
+ * Checks if the given field is set on a message.
+ *
+ * @param self  The message to check.
+ * @param field The field to check.
+ *
+ * @return YES if the field is set on the given message.
+ **/
+BOOL GPBMessageHasFieldSet(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Clears the given field for the given message.
+ *
+ * @param self  The message for which to clear the field.
+ * @param field The field to clear.
+ **/
+void GPBClearMessageField(GPBMessage *self, GPBFieldDescriptor *field);
+
+//%PDDM-EXPAND GPB_ACCESSORS()
+// This block of code is generated, do not edit it directly.
+
+
+//
+// Get/Set a given field from/to a message.
+//
+
+// Single Fields
+
+/**
+ * Gets the value of a bytes field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+NSData *GPBGetMessageBytesField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of a bytes field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageBytesField(GPBMessage *self, GPBFieldDescriptor *field, NSData *value);
+
+/**
+ * Gets the value of a string field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+NSString *GPBGetMessageStringField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of a string field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageStringField(GPBMessage *self, GPBFieldDescriptor *field, NSString *value);
+
+/**
+ * Gets the value of a message field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+GPBMessage *GPBGetMessageMessageField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of a message field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageMessageField(GPBMessage *self, GPBFieldDescriptor *field, GPBMessage *value);
+
+/**
+ * Gets the value of a group field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+GPBMessage *GPBGetMessageGroupField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of a group field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageGroupField(GPBMessage *self, GPBFieldDescriptor *field, GPBMessage *value);
+
+/**
+ * Gets the value of a bool field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+BOOL GPBGetMessageBoolField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of a bool field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageBoolField(GPBMessage *self, GPBFieldDescriptor *field, BOOL value);
+
+/**
+ * Gets the value of an int32 field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+int32_t GPBGetMessageInt32Field(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of an int32 field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageInt32Field(GPBMessage *self, GPBFieldDescriptor *field, int32_t value);
+
+/**
+ * Gets the value of an uint32 field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+uint32_t GPBGetMessageUInt32Field(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of an uint32 field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageUInt32Field(GPBMessage *self, GPBFieldDescriptor *field, uint32_t value);
+
+/**
+ * Gets the value of an int64 field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+int64_t GPBGetMessageInt64Field(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of an int64 field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageInt64Field(GPBMessage *self, GPBFieldDescriptor *field, int64_t value);
+
+/**
+ * Gets the value of an uint64 field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+uint64_t GPBGetMessageUInt64Field(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of an uint64 field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageUInt64Field(GPBMessage *self, GPBFieldDescriptor *field, uint64_t value);
+
+/**
+ * Gets the value of a float field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+float GPBGetMessageFloatField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of a float field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageFloatField(GPBMessage *self, GPBFieldDescriptor *field, float value);
+
+/**
+ * Gets the value of a double field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ **/
+double GPBGetMessageDoubleField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of a double field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The to set in the field.
+ **/
+void GPBSetMessageDoubleField(GPBMessage *self, GPBFieldDescriptor *field, double value);
+
+/**
+ * Gets the given enum field of a message. For proto3, if the value isn't a
+ * member of the enum, @c kGPBUnrecognizedEnumeratorValue will be returned.
+ * GPBGetMessageRawEnumField will bypass the check and return whatever value
+ * was set.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ *
+ * @return The enum value for the given field.
+ **/
+int32_t GPBGetMessageEnumField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Set the given enum field of a message. You can only set values that are
+ * members of the enum.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The enum value to set in the field.
+ **/
+void GPBSetMessageEnumField(GPBMessage *self,
+                            GPBFieldDescriptor *field,
+                            int32_t value);
+
+/**
+ * Get the given enum field of a message. No check is done to ensure the value
+ * was defined in the enum.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The field to get.
+ *
+ * @return The raw enum value for the given field.
+ **/
+int32_t GPBGetMessageRawEnumField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Set the given enum field of a message. You can set the value to anything,
+ * even a value that is not a member of the enum.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param value The raw enum value to set in the field.
+ **/
+void GPBSetMessageRawEnumField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               int32_t value);
+
+// Repeated Fields
+
+/**
+ * Gets the value of a repeated field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The repeated field to get.
+ *
+ * @return A GPB*Array or an NSMutableArray based on the field's type.
+ **/
+id GPBGetMessageRepeatedField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of a repeated field.
+ *
+ * @param self  The message into which to set the field.
+ * @param field The field to set.
+ * @param array A GPB*Array or NSMutableArray based on the field's type.
+ **/
+void GPBSetMessageRepeatedField(GPBMessage *self,
+                                GPBFieldDescriptor *field,
+                                id array);
+
+// Map Fields
+
+/**
+ * Gets the value of a map<> field.
+ *
+ * @param self  The message from which to get the field.
+ * @param field The repeated field to get.
+ *
+ * @return A GPB*Dictionary or NSMutableDictionary based on the field's type.
+ **/
+id GPBGetMessageMapField(GPBMessage *self, GPBFieldDescriptor *field);
+
+/**
+ * Sets the value of a map<> field.
+ *
+ * @param self       The message into which to set the field.
+ * @param field      The field to set.
+ * @param dictionary A GPB*Dictionary or NSMutableDictionary based on the
+ *                   field's type.
+ **/
+void GPBSetMessageMapField(GPBMessage *self,
+                           GPBFieldDescriptor *field,
+                           id dictionary);
+
+//%PDDM-EXPAND-END GPB_ACCESSORS()
+
+/**
+ * Returns an empty NSData to assign to byte fields when you wish to assign them
+ * to empty. Prevents allocating a lot of little [NSData data] objects.
+ **/
+NSData *GPBEmptyNSData(void) __attribute__((pure));
+
+/**
+ * Drops the `unknownFields` from the given message and from all sub message.
+ **/
+void GPBMessageDropUnknownFieldsRecursively(GPBMessage *message);
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+
+//%PDDM-DEFINE GPB_ACCESSORS()
+//%
+//%//
+//%// Get/Set a given field from/to a message.
+//%//
+//%
+//%// Single Fields
+//%
+//%GPB_ACCESSOR_SINGLE_FULL(Bytes, NSData, , *)
+//%GPB_ACCESSOR_SINGLE_FULL(String, NSString, , *)
+//%GPB_ACCESSOR_SINGLE_FULL(Message, GPBMessage, , *)
+//%GPB_ACCESSOR_SINGLE_FULL(Group, GPBMessage, , *)
+//%GPB_ACCESSOR_SINGLE(Bool, BOOL, )
+//%GPB_ACCESSOR_SINGLE(Int32, int32_t, n)
+//%GPB_ACCESSOR_SINGLE(UInt32, uint32_t, n)
+//%GPB_ACCESSOR_SINGLE(Int64, int64_t, n)
+//%GPB_ACCESSOR_SINGLE(UInt64, uint64_t, n)
+//%GPB_ACCESSOR_SINGLE(Float, float, )
+//%GPB_ACCESSOR_SINGLE(Double, double, )
+//%/**
+//% * Gets the given enum field of a message. For proto3, if the value isn't a
+//% * member of the enum, @c kGPBUnrecognizedEnumeratorValue will be returned.
+//% * GPBGetMessageRawEnumField will bypass the check and return whatever value
+//% * was set.
+//% *
+//% * @param self  The message from which to get the field.
+//% * @param field The field to get.
+//% *
+//% * @return The enum value for the given field.
+//% **/
+//%int32_t GPBGetMessageEnumField(GPBMessage *self, GPBFieldDescriptor *field);
+//%
+//%/**
+//% * Set the given enum field of a message. You can only set values that are
+//% * members of the enum.
+//% *
+//% * @param self  The message into which to set the field.
+//% * @param field The field to set.
+//% * @param value The enum value to set in the field.
+//% **/
+//%void GPBSetMessageEnumField(GPBMessage *self,
+//%                            GPBFieldDescriptor *field,
+//%                            int32_t value);
+//%
+//%/**
+//% * Get the given enum field of a message. No check is done to ensure the value
+//% * was defined in the enum.
+//% *
+//% * @param self  The message from which to get the field.
+//% * @param field The field to get.
+//% *
+//% * @return The raw enum value for the given field.
+//% **/
+//%int32_t GPBGetMessageRawEnumField(GPBMessage *self, GPBFieldDescriptor *field);
+//%
+//%/**
+//% * Set the given enum field of a message. You can set the value to anything,
+//% * even a value that is not a member of the enum.
+//% *
+//% * @param self  The message into which to set the field.
+//% * @param field The field to set.
+//% * @param value The raw enum value to set in the field.
+//% **/
+//%void GPBSetMessageRawEnumField(GPBMessage *self,
+//%                               GPBFieldDescriptor *field,
+//%                               int32_t value);
+//%
+//%// Repeated Fields
+//%
+//%/**
+//% * Gets the value of a repeated field.
+//% *
+//% * @param self  The message from which to get the field.
+//% * @param field The repeated field to get.
+//% *
+//% * @return A GPB*Array or an NSMutableArray based on the field's type.
+//% **/
+//%id GPBGetMessageRepeatedField(GPBMessage *self, GPBFieldDescriptor *field);
+//%
+//%/**
+//% * Sets the value of a repeated field.
+//% *
+//% * @param self  The message into which to set the field.
+//% * @param field The field to set.
+//% * @param array A GPB*Array or NSMutableArray based on the field's type.
+//% **/
+//%void GPBSetMessageRepeatedField(GPBMessage *self,
+//%                                GPBFieldDescriptor *field,
+//%                                id array);
+//%
+//%// Map Fields
+//%
+//%/**
+//% * Gets the value of a map<> field.
+//% *
+//% * @param self  The message from which to get the field.
+//% * @param field The repeated field to get.
+//% *
+//% * @return A GPB*Dictionary or NSMutableDictionary based on the field's type.
+//% **/
+//%id GPBGetMessageMapField(GPBMessage *self, GPBFieldDescriptor *field);
+//%
+//%/**
+//% * Sets the value of a map<> field.
+//% *
+//% * @param self       The message into which to set the field.
+//% * @param field      The field to set.
+//% * @param dictionary A GPB*Dictionary or NSMutableDictionary based on the
+//% *                   field's type.
+//% **/
+//%void GPBSetMessageMapField(GPBMessage *self,
+//%                           GPBFieldDescriptor *field,
+//%                           id dictionary);
+//%
+
+//%PDDM-DEFINE GPB_ACCESSOR_SINGLE(NAME, TYPE, AN)
+//%GPB_ACCESSOR_SINGLE_FULL(NAME, TYPE, AN, )
+//%PDDM-DEFINE GPB_ACCESSOR_SINGLE_FULL(NAME, TYPE, AN, TisP)
+//%/**
+//% * Gets the value of a##AN NAME$L field.
+//% *
+//% * @param self  The message from which to get the field.
+//% * @param field The field to get.
+//% **/
+//%TYPE TisP##GPBGetMessage##NAME##Field(GPBMessage *self, GPBFieldDescriptor *field);
+//%
+//%/**
+//% * Sets the value of a##AN NAME$L field.
+//% *
+//% * @param self  The message into which to set the field.
+//% * @param field The field to set.
+//% * @param value The to set in the field.
+//% **/
+//%void GPBSetMessage##NAME##Field(GPBMessage *self, GPBFieldDescriptor *field, TYPE TisP##value);
+//%
diff --git a/iOS/Pods/Protobuf/objectivec/GPBUtilities.m b/iOS/Pods/Protobuf/objectivec/GPBUtilities.m
new file mode 100644 (file)
index 0000000..e2a12ca
--- /dev/null
@@ -0,0 +1,2166 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBUtilities_PackagePrivate.h"
+
+#import <objc/runtime.h>
+
+#import "GPBArray_PackagePrivate.h"
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBDictionary_PackagePrivate.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBUnknownField.h"
+#import "GPBUnknownFieldSet.h"
+
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+static void AppendTextFormatForMessage(GPBMessage *message,
+                                       NSMutableString *toStr,
+                                       NSString *lineIndent);
+
+// Are two datatypes the same basic type representation (ex Int32 and SInt32).
+// Marked unused because currently only called from asserts/debug.
+static BOOL DataTypesEquivalent(GPBDataType type1,
+                                GPBDataType type2) __attribute__ ((unused));
+
+// Basic type representation for a type (ex: for SInt32 it is Int32).
+// Marked unused because currently only called from asserts/debug.
+static GPBDataType BaseDataType(GPBDataType type) __attribute__ ((unused));
+
+// String name for a data type.
+// Marked unused because currently only called from asserts/debug.
+static NSString *TypeToString(GPBDataType dataType) __attribute__ ((unused));
+
+NSData *GPBEmptyNSData(void) {
+  static dispatch_once_t onceToken;
+  static NSData *defaultNSData = nil;
+  dispatch_once(&onceToken, ^{
+    defaultNSData = [[NSData alloc] init];
+  });
+  return defaultNSData;
+}
+
+void GPBMessageDropUnknownFieldsRecursively(GPBMessage *initialMessage) {
+  if (!initialMessage) {
+    return;
+  }
+
+  // Use an array as a list to process to avoid recursion.
+  NSMutableArray *todo = [NSMutableArray arrayWithObject:initialMessage];
+
+  while (todo.count) {
+    GPBMessage *msg = todo.lastObject;
+    [todo removeLastObject];
+
+    // Clear unknowns.
+    msg.unknownFields = nil;
+
+    // Handle the message fields.
+    GPBDescriptor *descriptor = [[msg class] descriptor];
+    for (GPBFieldDescriptor *field in descriptor->fields_) {
+      if (!GPBFieldDataTypeIsMessage(field)) {
+        continue;
+      }
+      switch (field.fieldType) {
+        case GPBFieldTypeSingle:
+          if (GPBGetHasIvarField(msg, field)) {
+            GPBMessage *fieldMessage = GPBGetObjectIvarWithFieldNoAutocreate(msg, field);
+            [todo addObject:fieldMessage];
+          }
+          break;
+
+        case GPBFieldTypeRepeated: {
+          NSArray *fieldMessages = GPBGetObjectIvarWithFieldNoAutocreate(msg, field);
+          if (fieldMessages.count) {
+            [todo addObjectsFromArray:fieldMessages];
+          }
+          break;
+        }
+
+        case GPBFieldTypeMap: {
+          id rawFieldMap = GPBGetObjectIvarWithFieldNoAutocreate(msg, field);
+          switch (field.mapKeyDataType) {
+            case GPBDataTypeBool:
+              [(GPBBoolObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  BOOL key, id _Nonnull object, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:object];
+              }];
+              break;
+            case GPBDataTypeFixed32:
+            case GPBDataTypeUInt32:
+              [(GPBUInt32ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  uint32_t key, id _Nonnull object, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:object];
+              }];
+              break;
+            case GPBDataTypeInt32:
+            case GPBDataTypeSFixed32:
+            case GPBDataTypeSInt32:
+              [(GPBInt32ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  int32_t key, id _Nonnull object, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:object];
+              }];
+              break;
+            case GPBDataTypeFixed64:
+            case GPBDataTypeUInt64:
+              [(GPBUInt64ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  uint64_t key, id _Nonnull object, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:object];
+              }];
+              break;
+            case GPBDataTypeInt64:
+            case GPBDataTypeSFixed64:
+            case GPBDataTypeSInt64:
+              [(GPBInt64ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  int64_t key, id _Nonnull object, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:object];
+              }];
+              break;
+            case GPBDataTypeString:
+              [(NSDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  NSString * _Nonnull key, GPBMessage * _Nonnull obj, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:obj];
+              }];
+              break;
+            case GPBDataTypeFloat:
+            case GPBDataTypeDouble:
+            case GPBDataTypeEnum:
+            case GPBDataTypeBytes:
+            case GPBDataTypeGroup:
+            case GPBDataTypeMessage:
+              NSCAssert(NO, @"Aren't valid key types.");
+          }
+          break;
+        }  // switch(field.mapKeyDataType)
+      }  // switch(field.fieldType)
+    }  // for(fields)
+
+    // Handle any extensions holding messages.
+    for (GPBExtensionDescriptor *extension in [msg extensionsCurrentlySet]) {
+      if (!GPBDataTypeIsMessage(extension.dataType)) {
+        continue;
+      }
+      if (extension.isRepeated) {
+        NSArray *extMessages = [msg getExtension:extension];
+        [todo addObjectsFromArray:extMessages];
+      } else {
+        GPBMessage *extMessage = [msg getExtension:extension];
+        [todo addObject:extMessage];
+      }
+    }  // for(extensionsCurrentlySet)
+
+  }  // while(todo.count)
+}
+
+
+// -- About Version Checks --
+// There's actually 3 places these checks all come into play:
+// 1. When the generated source is compile into .o files, the header check
+//    happens. This is checking the protoc used matches the library being used
+//    when making the .o.
+// 2. Every place a generated proto header is included in a developer's code,
+//    the header check comes into play again. But this time it is checking that
+//    the current library headers being used still support/match the ones for
+//    the generated code.
+// 3. At runtime the final check here (GPBCheckRuntimeVersionsInternal), is
+//    called from the generated code passing in values captured when the
+//    generated code's .o was made. This checks that at runtime the generated
+//    code and runtime library match.
+
+void GPBCheckRuntimeVersionSupport(int32_t objcRuntimeVersion) {
+  // NOTE: This is passing the value captured in the compiled code to check
+  // against the values captured when the runtime support was compiled. This
+  // ensures the library code isn't in a different framework/library that
+  // was generated with a non matching version.
+  if (GOOGLE_PROTOBUF_OBJC_VERSION < objcRuntimeVersion) {
+    // Library is too old for headers.
+    [NSException raise:NSInternalInconsistencyException
+                format:@"Linked to ProtocolBuffer runtime version %d,"
+                       @" but code compiled needing atleast %d!",
+                       GOOGLE_PROTOBUF_OBJC_VERSION, objcRuntimeVersion];
+  }
+  if (objcRuntimeVersion < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION) {
+    // Headers are too old for library.
+    [NSException raise:NSInternalInconsistencyException
+                format:@"Proto generation source compiled against runtime"
+                       @" version %d, but this version of the runtime only"
+                       @" supports back to %d!",
+                       objcRuntimeVersion,
+                       GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION];
+  }
+}
+
+// This api is no longer used for version checks. 30001 is the last version
+// using this old versioning model. When that support is removed, this function
+// can be removed (along with the declaration in GPBUtilities_PackagePrivate.h).
+void GPBCheckRuntimeVersionInternal(int32_t version) {
+  GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION == 30001,
+                           time_to_remove_this_old_version_shim);
+  if (version != GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION) {
+    [NSException raise:NSInternalInconsistencyException
+                format:@"Linked to ProtocolBuffer runtime version %d,"
+                       @" but code compiled with version %d!",
+                       GOOGLE_PROTOBUF_OBJC_GEN_VERSION, version];
+  }
+}
+
+BOOL GPBMessageHasFieldNumberSet(GPBMessage *self, uint32_t fieldNumber) {
+  GPBDescriptor *descriptor = [self descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:fieldNumber];
+  return GPBMessageHasFieldSet(self, field);
+}
+
+BOOL GPBMessageHasFieldSet(GPBMessage *self, GPBFieldDescriptor *field) {
+  if (self == nil || field == nil) return NO;
+
+  // Repeated/Map don't use the bit, they check the count.
+  if (GPBFieldIsMapOrArray(field)) {
+    // Array/map type doesn't matter, since GPB*Array/NSArray and
+    // GPB*Dictionary/NSDictionary all support -count;
+    NSArray *arrayOrMap = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+    return (arrayOrMap.count > 0);
+  } else {
+    return GPBGetHasIvarField(self, field);
+  }
+}
+
+void GPBClearMessageField(GPBMessage *self, GPBFieldDescriptor *field) {
+  // If not set, nothing to do.
+  if (!GPBGetHasIvarField(self, field)) {
+    return;
+  }
+
+  if (GPBFieldStoresObject(field)) {
+    // Object types are handled slightly differently, they need to be released.
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    id *typePtr = (id *)&storage[field->description_->offset];
+    [*typePtr release];
+    *typePtr = nil;
+  } else {
+    // POD types just need to clear the has bit as the Get* method will
+    // fetch the default when needed.
+  }
+  GPBSetHasIvarField(self, field, NO);
+}
+
+BOOL GPBGetHasIvar(GPBMessage *self, int32_t idx, uint32_t fieldNumber) {
+  NSCAssert(self->messageStorage_ != NULL,
+            @"%@: All messages should have storage (from init)",
+            [self class]);
+  if (idx < 0) {
+    NSCAssert(fieldNumber != 0, @"Invalid field number.");
+    BOOL hasIvar = (self->messageStorage_->_has_storage_[-idx] == fieldNumber);
+    return hasIvar;
+  } else {
+    NSCAssert(idx != GPBNoHasBit, @"Invalid has bit.");
+    uint32_t byteIndex = idx / 32;
+    uint32_t bitMask = (1U << (idx % 32));
+    BOOL hasIvar =
+        (self->messageStorage_->_has_storage_[byteIndex] & bitMask) ? YES : NO;
+    return hasIvar;
+  }
+}
+
+uint32_t GPBGetHasOneof(GPBMessage *self, int32_t idx) {
+  NSCAssert(idx < 0, @"%@: invalid index (%d) for oneof.",
+            [self class], idx);
+  uint32_t result = self->messageStorage_->_has_storage_[-idx];
+  return result;
+}
+
+void GPBSetHasIvar(GPBMessage *self, int32_t idx, uint32_t fieldNumber,
+                   BOOL value) {
+  if (idx < 0) {
+    NSCAssert(fieldNumber != 0, @"Invalid field number.");
+    uint32_t *has_storage = self->messageStorage_->_has_storage_;
+    has_storage[-idx] = (value ? fieldNumber : 0);
+  } else {
+    NSCAssert(idx != GPBNoHasBit, @"Invalid has bit.");
+    uint32_t *has_storage = self->messageStorage_->_has_storage_;
+    uint32_t byte = idx / 32;
+    uint32_t bitMask = (1U << (idx % 32));
+    if (value) {
+      has_storage[byte] |= bitMask;
+    } else {
+      has_storage[byte] &= ~bitMask;
+    }
+  }
+}
+
+void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof,
+                        int32_t oneofHasIndex, uint32_t fieldNumberNotToClear) {
+  uint32_t fieldNumberSet = GPBGetHasOneof(self, oneofHasIndex);
+  if ((fieldNumberSet == fieldNumberNotToClear) || (fieldNumberSet == 0)) {
+    // Do nothing/nothing set in the oneof.
+    return;
+  }
+
+  // Like GPBClearMessageField(), free the memory if an objecttype is set,
+  // pod types don't need to do anything.
+  GPBFieldDescriptor *fieldSet = [oneof fieldWithNumber:fieldNumberSet];
+  NSCAssert(fieldSet,
+            @"%@: oneof set to something (%u) not in the oneof?",
+            [self class], fieldNumberSet);
+  if (fieldSet && GPBFieldStoresObject(fieldSet)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    id *typePtr = (id *)&storage[fieldSet->description_->offset];
+    [*typePtr release];
+    *typePtr = nil;
+  }
+
+  // Set to nothing stored in the oneof.
+  // (field number doesn't matter since setting to nothing).
+  GPBSetHasIvar(self, oneofHasIndex, 1, NO);
+}
+
+#pragma mark - IVar accessors
+
+//%PDDM-DEFINE IVAR_POD_ACCESSORS_DEFN(NAME, TYPE)
+//%TYPE GPBGetMessage##NAME##Field(GPBMessage *self,
+//% TYPE$S            NAME$S       GPBFieldDescriptor *field) {
+//%#if defined(DEBUG) && DEBUG
+//%  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+//%                                GPBDataType##NAME),
+//%            @"Attempting to get value of TYPE from field %@ "
+//%            @"of %@ which is of type %@.",
+//%            [self class], field.name,
+//%            TypeToString(GPBGetFieldDataType(field)));
+//%#endif
+//%  if (GPBGetHasIvarField(self, field)) {
+//%    uint8_t *storage = (uint8_t *)self->messageStorage_;
+//%    TYPE *typePtr = (TYPE *)&storage[field->description_->offset];
+//%    return *typePtr;
+//%  } else {
+//%    return field.defaultValue.value##NAME;
+//%  }
+//%}
+//%
+//%// Only exists for public api, no core code should use this.
+//%void GPBSetMessage##NAME##Field(GPBMessage *self,
+//%                   NAME$S     GPBFieldDescriptor *field,
+//%                   NAME$S     TYPE value) {
+//%  if (self == nil || field == nil) return;
+//%  GPBFileSyntax syntax = [self descriptor].file.syntax;
+//%  GPBSet##NAME##IvarWithFieldInternal(self, field, value, syntax);
+//%}
+//%
+//%void GPBSet##NAME##IvarWithFieldInternal(GPBMessage *self,
+//%            NAME$S                     GPBFieldDescriptor *field,
+//%            NAME$S                     TYPE value,
+//%            NAME$S                     GPBFileSyntax syntax) {
+//%#if defined(DEBUG) && DEBUG
+//%  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+//%                                GPBDataType##NAME),
+//%            @"Attempting to set field %@ of %@ which is of type %@ with "
+//%            @"value of type TYPE.",
+//%            [self class], field.name,
+//%            TypeToString(GPBGetFieldDataType(field)));
+//%#endif
+//%  GPBOneofDescriptor *oneof = field->containingOneof_;
+//%  if (oneof) {
+//%    GPBMessageFieldDescription *fieldDesc = field->description_;
+//%    GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number);
+//%  }
+//%#if defined(DEBUG) && DEBUG
+//%  NSCAssert(self->messageStorage_ != NULL,
+//%            @"%@: All messages should have storage (from init)",
+//%            [self class]);
+//%#endif
+//%#if defined(__clang_analyzer__)
+//%  if (self->messageStorage_ == NULL) return;
+//%#endif
+//%  uint8_t *storage = (uint8_t *)self->messageStorage_;
+//%  TYPE *typePtr = (TYPE *)&storage[field->description_->offset];
+//%  *typePtr = value;
+//%  // proto2: any value counts as having been set; proto3, it
+//%  // has to be a non zero value or be in a oneof.
+//%  BOOL hasValue = ((syntax == GPBFileSyntaxProto2)
+//%                   || (value != (TYPE)0)
+//%                   || (field->containingOneof_ != NULL));
+//%  GPBSetHasIvarField(self, field, hasValue);
+//%  GPBBecomeVisibleToAutocreator(self);
+//%}
+//%
+//%PDDM-DEFINE IVAR_ALIAS_DEFN_OBJECT(NAME, TYPE)
+//%// Only exists for public api, no core code should use this.
+//%TYPE *GPBGetMessage##NAME##Field(GPBMessage *self,
+//% TYPE$S             NAME$S       GPBFieldDescriptor *field) {
+//%#if defined(DEBUG) && DEBUG
+//%  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+//%                                GPBDataType##NAME),
+//%            @"Attempting to get value of TYPE from field %@ "
+//%            @"of %@ which is of type %@.",
+//%            [self class], field.name,
+//%            TypeToString(GPBGetFieldDataType(field)));
+//%#endif
+//%  return (TYPE *)GPBGetObjectIvarWithField(self, field);
+//%}
+//%
+//%// Only exists for public api, no core code should use this.
+//%void GPBSetMessage##NAME##Field(GPBMessage *self,
+//%                   NAME$S     GPBFieldDescriptor *field,
+//%                   NAME$S     TYPE *value) {
+//%#if defined(DEBUG) && DEBUG
+//%  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+//%                                GPBDataType##NAME),
+//%            @"Attempting to set field %@ of %@ which is of type %@ with "
+//%            @"value of type TYPE.",
+//%            [self class], field.name,
+//%            TypeToString(GPBGetFieldDataType(field)));
+//%#endif
+//%  GPBSetObjectIvarWithField(self, field, (id)value);
+//%}
+//%
+
+// Object types are handled slightly differently, they need to be released
+// and retained.
+
+void GPBSetAutocreatedRetainedObjectIvarWithField(
+    GPBMessage *self, GPBFieldDescriptor *field,
+    id __attribute__((ns_consumed)) value) {
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  id *typePtr = (id *)&storage[field->description_->offset];
+  NSCAssert(*typePtr == NULL, @"Can't set autocreated object more than once.");
+  *typePtr = value;
+}
+
+void GPBClearAutocreatedMessageIvarWithField(GPBMessage *self,
+                                             GPBFieldDescriptor *field) {
+  if (GPBGetHasIvarField(self, field)) {
+    return;
+  }
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  id *typePtr = (id *)&storage[field->description_->offset];
+  GPBMessage *oldValue = *typePtr;
+  *typePtr = NULL;
+  GPBClearMessageAutocreator(oldValue);
+  [oldValue release];
+}
+
+// This exists only for briging some aliased types, nothing else should use it.
+static void GPBSetObjectIvarWithField(GPBMessage *self,
+                                      GPBFieldDescriptor *field, id value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetRetainedObjectIvarWithFieldInternal(self, field, [value retain],
+                                            syntax);
+}
+
+void GPBSetObjectIvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field, id value,
+                                       GPBFileSyntax syntax) {
+  GPBSetRetainedObjectIvarWithFieldInternal(self, field, [value retain],
+                                            syntax);
+}
+
+void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self,
+                                               GPBFieldDescriptor *field,
+                                               id value, GPBFileSyntax syntax) {
+  NSCAssert(self->messageStorage_ != NULL,
+            @"%@: All messages should have storage (from init)",
+            [self class]);
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  GPBDataType fieldType = GPBGetFieldDataType(field);
+  BOOL isMapOrArray = GPBFieldIsMapOrArray(field);
+  BOOL fieldIsMessage = GPBDataTypeIsMessage(fieldType);
+#if defined(DEBUG) && DEBUG
+  if (value == nil && !isMapOrArray && !fieldIsMessage &&
+      field.hasDefaultValue) {
+    // Setting a message to nil is an obvious way to "clear" the value
+    // as there is no way to set a non-empty default value for messages.
+    //
+    // For Strings and Bytes that have default values set it is not clear what
+    // should be done when their value is set to nil. Is the intention just to
+    // clear the set value and reset to default, or is the intention to set the
+    // value to the empty string/data? Arguments can be made for both cases.
+    // 'nil' has been abused as a replacement for an empty string/data in ObjC.
+    // We decided to be consistent with all "object" types and clear the has
+    // field, and fall back on the default value. The warning below will only
+    // appear in debug, but the could should be changed so the intention is
+    // clear.
+    NSString *hasSel = NSStringFromSelector(field->hasOrCountSel_);
+    NSString *propName = field.name;
+    NSString *className = self.descriptor.name;
+    NSLog(@"warning: '%@.%@ = nil;' is not clearly defined for fields with "
+          @"default values. Please use '%@.%@ = %@' if you want to set it to "
+          @"empty, or call '%@.%@ = NO' to reset it to it's default value of "
+          @"'%@'. Defaulting to resetting default value.",
+          className, propName, className, propName,
+          (fieldType == GPBDataTypeString) ? @"@\"\"" : @"GPBEmptyNSData()",
+          className, hasSel, field.defaultValue.valueString);
+    // Note: valueString, depending on the type, it could easily be
+    // valueData/valueMessage.
+  }
+#endif  // DEBUG
+  if (!isMapOrArray) {
+    // Non repeated/map can be in an oneof, clear any existing value from the
+    // oneof.
+    GPBOneofDescriptor *oneof = field->containingOneof_;
+    if (oneof) {
+      GPBMessageFieldDescription *fieldDesc = field->description_;
+      GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number);
+    }
+    // Clear "has" if they are being set to nil.
+    BOOL setHasValue = (value != nil);
+    // Under proto3, Bytes & String fields get cleared by resetting them to
+    // their default (empty) values, so if they are set to something of length
+    // zero, they are being cleared.
+    if ((syntax == GPBFileSyntaxProto3) && !fieldIsMessage &&
+        ([value length] == 0)) {
+      // Except, if the field was in a oneof, then it still gets recorded as
+      // having been set so the state of the oneof can be serialized back out.
+      if (!oneof) {
+        setHasValue = NO;
+      }
+      if (setHasValue) {
+        NSCAssert(value != nil, @"Should never be setting has for nil");
+      } else {
+        // The value passed in was retained, it must be released since we
+        // aren't saving anything in the field.
+        [value release];
+        value = nil;
+      }
+    }
+    GPBSetHasIvarField(self, field, setHasValue);
+  }
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  id *typePtr = (id *)&storage[field->description_->offset];
+
+  id oldValue = *typePtr;
+
+  *typePtr = value;
+
+  if (oldValue) {
+    if (isMapOrArray) {
+      if (field.fieldType == GPBFieldTypeRepeated) {
+        // If the old array was autocreated by us, then clear it.
+        if (GPBDataTypeIsObject(fieldType)) {
+          if ([oldValue isKindOfClass:[GPBAutocreatedArray class]]) {
+            GPBAutocreatedArray *autoArray = oldValue;
+            if (autoArray->_autocreator == self) {
+              autoArray->_autocreator = nil;
+            }
+          }
+        } else {
+          // Type doesn't matter, it is a GPB*Array.
+          GPBInt32Array *gpbArray = oldValue;
+          if (gpbArray->_autocreator == self) {
+            gpbArray->_autocreator = nil;
+          }
+        }
+      } else { // GPBFieldTypeMap
+        // If the old map was autocreated by us, then clear it.
+        if ((field.mapKeyDataType == GPBDataTypeString) &&
+            GPBDataTypeIsObject(fieldType)) {
+          if ([oldValue isKindOfClass:[GPBAutocreatedDictionary class]]) {
+            GPBAutocreatedDictionary *autoDict = oldValue;
+            if (autoDict->_autocreator == self) {
+              autoDict->_autocreator = nil;
+            }
+          }
+        } else {
+          // Type doesn't matter, it is a GPB*Dictionary.
+          GPBInt32Int32Dictionary *gpbDict = oldValue;
+          if (gpbDict->_autocreator == self) {
+            gpbDict->_autocreator = nil;
+          }
+        }
+      }
+    } else if (fieldIsMessage) {
+      // If the old message value was autocreated by us, then clear it.
+      GPBMessage *oldMessageValue = oldValue;
+      if (GPBWasMessageAutocreatedBy(oldMessageValue, self)) {
+        GPBClearMessageAutocreator(oldMessageValue);
+      }
+    }
+    [oldValue release];
+  }
+
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+id GPBGetObjectIvarWithFieldNoAutocreate(GPBMessage *self,
+                                         GPBFieldDescriptor *field) {
+  if (self->messageStorage_ == nil) {
+    return nil;
+  }
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  id *typePtr = (id *)&storage[field->description_->offset];
+  return *typePtr;
+}
+
+// Only exists for public api, no core code should use this.
+int32_t GPBGetMessageEnumField(GPBMessage *self, GPBFieldDescriptor *field) {
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  return GPBGetEnumIvarWithFieldInternal(self, field, syntax);
+}
+
+int32_t GPBGetEnumIvarWithFieldInternal(GPBMessage *self,
+                                        GPBFieldDescriptor *field,
+                                        GPBFileSyntax syntax) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(GPBGetFieldDataType(field) == GPBDataTypeEnum,
+            @"Attempting to get value of type Enum from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  int32_t result = GPBGetMessageInt32Field(self, field);
+  // If this is presevering unknown enums, make sure the value is valid before
+  // returning it.
+  if (GPBHasPreservingUnknownEnumSemantics(syntax) &&
+      ![field isValidEnumValue:result]) {
+    result = kGPBUnrecognizedEnumeratorValue;
+  }
+  return result;
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageEnumField(GPBMessage *self, GPBFieldDescriptor *field,
+                            int32_t value) {
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetInt32IvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetEnumIvarWithFieldInternal(GPBMessage *self,
+                                     GPBFieldDescriptor *field, int32_t value,
+                                     GPBFileSyntax syntax) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(GPBGetFieldDataType(field) == GPBDataTypeEnum,
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type Enum.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  // Don't allow in unknown values.  Proto3 can use the Raw method.
+  if (![field isValidEnumValue:value]) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@.%@: Attempt to set an unknown enum value (%d)",
+                       [self class], field.name, value];
+  }
+  GPBSetInt32IvarWithFieldInternal(self, field, value, syntax);
+}
+
+// Only exists for public api, no core code should use this.
+int32_t GPBGetMessageRawEnumField(GPBMessage *self,
+                                  GPBFieldDescriptor *field) {
+  int32_t result = GPBGetMessageInt32Field(self, field);
+  return result;
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageRawEnumField(GPBMessage *self, GPBFieldDescriptor *field,
+                               int32_t value) {
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetInt32IvarWithFieldInternal(self, field, value, syntax);
+}
+
+BOOL GPBGetMessageBoolField(GPBMessage *self,
+                            GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeBool),
+            @"Attempting to get value of type bool from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  if (GPBGetHasIvarField(self, field)) {
+    // Bools are stored in the has bits to avoid needing explicit space in the
+    // storage structure.
+    // (the field number passed to the HasIvar helper doesn't really matter
+    // since the offset is never negative)
+    GPBMessageFieldDescription *fieldDesc = field->description_;
+    return GPBGetHasIvar(self, (int32_t)(fieldDesc->offset), fieldDesc->number);
+  } else {
+    return field.defaultValue.valueBool;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageBoolField(GPBMessage *self,
+                            GPBFieldDescriptor *field,
+                            BOOL value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetBoolIvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetBoolIvarWithFieldInternal(GPBMessage *self,
+                                     GPBFieldDescriptor *field,
+                                     BOOL value,
+                                     GPBFileSyntax syntax) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeBool),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type bool.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBMessageFieldDescription *fieldDesc = field->description_;
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number);
+  }
+
+  // Bools are stored in the has bits to avoid needing explicit space in the
+  // storage structure.
+  // (the field number passed to the HasIvar helper doesn't really matter since
+  // the offset is never negative)
+  GPBSetHasIvar(self, (int32_t)(fieldDesc->offset), fieldDesc->number, value);
+
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value or be in a oneof.
+  BOOL hasValue = ((syntax == GPBFileSyntaxProto2)
+                   || (value != (BOOL)0)
+                   || (field->containingOneof_ != NULL));
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(Int32, int32_t)
+// This block of code is generated, do not edit it directly.
+
+int32_t GPBGetMessageInt32Field(GPBMessage *self,
+                                GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeInt32),
+            @"Attempting to get value of int32_t from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    int32_t *typePtr = (int32_t *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueInt32;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageInt32Field(GPBMessage *self,
+                             GPBFieldDescriptor *field,
+                             int32_t value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetInt32IvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetInt32IvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      int32_t value,
+                                      GPBFileSyntax syntax) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeInt32),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type int32_t.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMessageFieldDescription *fieldDesc = field->description_;
+    GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number);
+  }
+#if defined(DEBUG) && DEBUG
+  NSCAssert(self->messageStorage_ != NULL,
+            @"%@: All messages should have storage (from init)",
+            [self class]);
+#endif
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  int32_t *typePtr = (int32_t *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value or be in a oneof.
+  BOOL hasValue = ((syntax == GPBFileSyntaxProto2)
+                   || (value != (int32_t)0)
+                   || (field->containingOneof_ != NULL));
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(UInt32, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+uint32_t GPBGetMessageUInt32Field(GPBMessage *self,
+                                  GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeUInt32),
+            @"Attempting to get value of uint32_t from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    uint32_t *typePtr = (uint32_t *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueUInt32;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageUInt32Field(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              uint32_t value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetUInt32IvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       uint32_t value,
+                                       GPBFileSyntax syntax) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeUInt32),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type uint32_t.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMessageFieldDescription *fieldDesc = field->description_;
+    GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number);
+  }
+#if defined(DEBUG) && DEBUG
+  NSCAssert(self->messageStorage_ != NULL,
+            @"%@: All messages should have storage (from init)",
+            [self class]);
+#endif
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  uint32_t *typePtr = (uint32_t *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value or be in a oneof.
+  BOOL hasValue = ((syntax == GPBFileSyntaxProto2)
+                   || (value != (uint32_t)0)
+                   || (field->containingOneof_ != NULL));
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(Int64, int64_t)
+// This block of code is generated, do not edit it directly.
+
+int64_t GPBGetMessageInt64Field(GPBMessage *self,
+                                GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeInt64),
+            @"Attempting to get value of int64_t from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    int64_t *typePtr = (int64_t *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueInt64;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageInt64Field(GPBMessage *self,
+                             GPBFieldDescriptor *field,
+                             int64_t value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetInt64IvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetInt64IvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      int64_t value,
+                                      GPBFileSyntax syntax) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeInt64),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type int64_t.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMessageFieldDescription *fieldDesc = field->description_;
+    GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number);
+  }
+#if defined(DEBUG) && DEBUG
+  NSCAssert(self->messageStorage_ != NULL,
+            @"%@: All messages should have storage (from init)",
+            [self class]);
+#endif
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  int64_t *typePtr = (int64_t *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value or be in a oneof.
+  BOOL hasValue = ((syntax == GPBFileSyntaxProto2)
+                   || (value != (int64_t)0)
+                   || (field->containingOneof_ != NULL));
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(UInt64, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+uint64_t GPBGetMessageUInt64Field(GPBMessage *self,
+                                  GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeUInt64),
+            @"Attempting to get value of uint64_t from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    uint64_t *typePtr = (uint64_t *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueUInt64;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageUInt64Field(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              uint64_t value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetUInt64IvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       uint64_t value,
+                                       GPBFileSyntax syntax) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeUInt64),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type uint64_t.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMessageFieldDescription *fieldDesc = field->description_;
+    GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number);
+  }
+#if defined(DEBUG) && DEBUG
+  NSCAssert(self->messageStorage_ != NULL,
+            @"%@: All messages should have storage (from init)",
+            [self class]);
+#endif
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  uint64_t *typePtr = (uint64_t *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value or be in a oneof.
+  BOOL hasValue = ((syntax == GPBFileSyntaxProto2)
+                   || (value != (uint64_t)0)
+                   || (field->containingOneof_ != NULL));
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(Float, float)
+// This block of code is generated, do not edit it directly.
+
+float GPBGetMessageFloatField(GPBMessage *self,
+                              GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeFloat),
+            @"Attempting to get value of float from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    float *typePtr = (float *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueFloat;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageFloatField(GPBMessage *self,
+                             GPBFieldDescriptor *field,
+                             float value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetFloatIvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetFloatIvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      float value,
+                                      GPBFileSyntax syntax) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeFloat),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type float.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMessageFieldDescription *fieldDesc = field->description_;
+    GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number);
+  }
+#if defined(DEBUG) && DEBUG
+  NSCAssert(self->messageStorage_ != NULL,
+            @"%@: All messages should have storage (from init)",
+            [self class]);
+#endif
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  float *typePtr = (float *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value or be in a oneof.
+  BOOL hasValue = ((syntax == GPBFileSyntaxProto2)
+                   || (value != (float)0)
+                   || (field->containingOneof_ != NULL));
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(Double, double)
+// This block of code is generated, do not edit it directly.
+
+double GPBGetMessageDoubleField(GPBMessage *self,
+                                GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeDouble),
+            @"Attempting to get value of double from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  if (GPBGetHasIvarField(self, field)) {
+    uint8_t *storage = (uint8_t *)self->messageStorage_;
+    double *typePtr = (double *)&storage[field->description_->offset];
+    return *typePtr;
+  } else {
+    return field.defaultValue.valueDouble;
+  }
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageDoubleField(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              double value) {
+  if (self == nil || field == nil) return;
+  GPBFileSyntax syntax = [self descriptor].file.syntax;
+  GPBSetDoubleIvarWithFieldInternal(self, field, value, syntax);
+}
+
+void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       double value,
+                                       GPBFileSyntax syntax) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeDouble),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type double.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBOneofDescriptor *oneof = field->containingOneof_;
+  if (oneof) {
+    GPBMessageFieldDescription *fieldDesc = field->description_;
+    GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number);
+  }
+#if defined(DEBUG) && DEBUG
+  NSCAssert(self->messageStorage_ != NULL,
+            @"%@: All messages should have storage (from init)",
+            [self class]);
+#endif
+#if defined(__clang_analyzer__)
+  if (self->messageStorage_ == NULL) return;
+#endif
+  uint8_t *storage = (uint8_t *)self->messageStorage_;
+  double *typePtr = (double *)&storage[field->description_->offset];
+  *typePtr = value;
+  // proto2: any value counts as having been set; proto3, it
+  // has to be a non zero value or be in a oneof.
+  BOOL hasValue = ((syntax == GPBFileSyntaxProto2)
+                   || (value != (double)0)
+                   || (field->containingOneof_ != NULL));
+  GPBSetHasIvarField(self, field, hasValue);
+  GPBBecomeVisibleToAutocreator(self);
+}
+
+//%PDDM-EXPAND-END (6 expansions)
+
+// Aliases are function calls that are virtually the same.
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN_OBJECT(String, NSString)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+NSString *GPBGetMessageStringField(GPBMessage *self,
+                                   GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeString),
+            @"Attempting to get value of NSString from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  return (NSString *)GPBGetObjectIvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageStringField(GPBMessage *self,
+                              GPBFieldDescriptor *field,
+                              NSString *value) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeString),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type NSString.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBSetObjectIvarWithField(self, field, (id)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN_OBJECT(Bytes, NSData)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+NSData *GPBGetMessageBytesField(GPBMessage *self,
+                                GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeBytes),
+            @"Attempting to get value of NSData from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  return (NSData *)GPBGetObjectIvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageBytesField(GPBMessage *self,
+                             GPBFieldDescriptor *field,
+                             NSData *value) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeBytes),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type NSData.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBSetObjectIvarWithField(self, field, (id)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN_OBJECT(Message, GPBMessage)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+GPBMessage *GPBGetMessageMessageField(GPBMessage *self,
+                                      GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeMessage),
+            @"Attempting to get value of GPBMessage from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  return (GPBMessage *)GPBGetObjectIvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageMessageField(GPBMessage *self,
+                               GPBFieldDescriptor *field,
+                               GPBMessage *value) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeMessage),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type GPBMessage.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBSetObjectIvarWithField(self, field, (id)value);
+}
+
+//%PDDM-EXPAND IVAR_ALIAS_DEFN_OBJECT(Group, GPBMessage)
+// This block of code is generated, do not edit it directly.
+
+// Only exists for public api, no core code should use this.
+GPBMessage *GPBGetMessageGroupField(GPBMessage *self,
+                                    GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeGroup),
+            @"Attempting to get value of GPBMessage from field %@ "
+            @"of %@ which is of type %@.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  return (GPBMessage *)GPBGetObjectIvarWithField(self, field);
+}
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageGroupField(GPBMessage *self,
+                             GPBFieldDescriptor *field,
+                             GPBMessage *value) {
+#if defined(DEBUG) && DEBUG
+  NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field),
+                                GPBDataTypeGroup),
+            @"Attempting to set field %@ of %@ which is of type %@ with "
+            @"value of type GPBMessage.",
+            [self class], field.name,
+            TypeToString(GPBGetFieldDataType(field)));
+#endif
+  GPBSetObjectIvarWithField(self, field, (id)value);
+}
+
+//%PDDM-EXPAND-END (4 expansions)
+
+// GPBGetMessageRepeatedField is defined in GPBMessage.m
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageRepeatedField(GPBMessage *self, GPBFieldDescriptor *field, id array) {
+#if defined(DEBUG) && DEBUG
+  if (field.fieldType != GPBFieldTypeRepeated) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@.%@ is not a repeated field.",
+                       [self class], field.name];
+  }
+  Class expectedClass = Nil;
+  switch (GPBGetFieldDataType(field)) {
+    case GPBDataTypeBool:
+      expectedClass = [GPBBoolArray class];
+      break;
+    case GPBDataTypeSFixed32:
+    case GPBDataTypeInt32:
+    case GPBDataTypeSInt32:
+      expectedClass = [GPBInt32Array class];
+      break;
+    case GPBDataTypeFixed32:
+    case GPBDataTypeUInt32:
+      expectedClass = [GPBUInt32Array class];
+      break;
+    case GPBDataTypeSFixed64:
+    case GPBDataTypeInt64:
+    case GPBDataTypeSInt64:
+      expectedClass = [GPBInt64Array class];
+      break;
+    case GPBDataTypeFixed64:
+    case GPBDataTypeUInt64:
+      expectedClass = [GPBUInt64Array class];
+      break;
+    case GPBDataTypeFloat:
+      expectedClass = [GPBFloatArray class];
+      break;
+    case GPBDataTypeDouble:
+      expectedClass = [GPBDoubleArray class];
+      break;
+    case GPBDataTypeBytes:
+    case GPBDataTypeString:
+    case GPBDataTypeMessage:
+    case GPBDataTypeGroup:
+      expectedClass = [NSMutableArray class];
+      break;
+    case GPBDataTypeEnum:
+      expectedClass = [GPBEnumArray class];
+      break;
+  }
+  if (array && ![array isKindOfClass:expectedClass]) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@.%@: Expected %@ object, got %@.",
+                       [self class], field.name, expectedClass, [array class]];
+  }
+#endif
+  GPBSetObjectIvarWithField(self, field, array);
+}
+
+static GPBDataType BaseDataType(GPBDataType type) {
+  switch (type) {
+    case GPBDataTypeSFixed32:
+    case GPBDataTypeInt32:
+    case GPBDataTypeSInt32:
+    case GPBDataTypeEnum:
+      return GPBDataTypeInt32;
+    case GPBDataTypeFixed32:
+    case GPBDataTypeUInt32:
+      return GPBDataTypeUInt32;
+    case GPBDataTypeSFixed64:
+    case GPBDataTypeInt64:
+    case GPBDataTypeSInt64:
+      return GPBDataTypeInt64;
+    case GPBDataTypeFixed64:
+    case GPBDataTypeUInt64:
+      return GPBDataTypeUInt64;
+    case GPBDataTypeMessage:
+    case GPBDataTypeGroup:
+      return GPBDataTypeMessage;
+    case GPBDataTypeBool:
+    case GPBDataTypeFloat:
+    case GPBDataTypeDouble:
+    case GPBDataTypeBytes:
+    case GPBDataTypeString:
+      return type;
+   }
+}
+
+static BOOL DataTypesEquivalent(GPBDataType type1, GPBDataType type2) {
+  return BaseDataType(type1) == BaseDataType(type2);
+}
+
+static NSString *TypeToString(GPBDataType dataType) {
+  switch (dataType) {
+    case GPBDataTypeBool:
+      return @"Bool";
+    case GPBDataTypeSFixed32:
+    case GPBDataTypeInt32:
+    case GPBDataTypeSInt32:
+      return @"Int32";
+    case GPBDataTypeFixed32:
+    case GPBDataTypeUInt32:
+      return @"UInt32";
+    case GPBDataTypeSFixed64:
+    case GPBDataTypeInt64:
+    case GPBDataTypeSInt64:
+      return @"Int64";
+    case GPBDataTypeFixed64:
+    case GPBDataTypeUInt64:
+      return @"UInt64";
+    case GPBDataTypeFloat:
+      return @"Float";
+    case GPBDataTypeDouble:
+      return @"Double";
+    case GPBDataTypeBytes:
+    case GPBDataTypeString:
+    case GPBDataTypeMessage:
+    case GPBDataTypeGroup:
+      return @"Object";
+    case GPBDataTypeEnum:
+      return @"Enum";
+  }
+}
+
+// GPBGetMessageMapField is defined in GPBMessage.m
+
+// Only exists for public api, no core code should use this.
+void GPBSetMessageMapField(GPBMessage *self, GPBFieldDescriptor *field,
+                           id dictionary) {
+#if defined(DEBUG) && DEBUG
+  if (field.fieldType != GPBFieldTypeMap) {
+    [NSException raise:NSInvalidArgumentException
+                format:@"%@.%@ is not a map<> field.",
+                       [self class], field.name];
+  }
+  if (dictionary) {
+    GPBDataType keyDataType = field.mapKeyDataType;
+    GPBDataType valueDataType = GPBGetFieldDataType(field);
+    NSString *keyStr = TypeToString(keyDataType);
+    NSString *valueStr = TypeToString(valueDataType);
+    if (keyDataType == GPBDataTypeString) {
+      keyStr = @"String";
+    }
+    Class expectedClass = Nil;
+    if ((keyDataType == GPBDataTypeString) &&
+        GPBDataTypeIsObject(valueDataType)) {
+      expectedClass = [NSMutableDictionary class];
+    } else {
+      NSString *className =
+          [NSString stringWithFormat:@"GPB%@%@Dictionary", keyStr, valueStr];
+      expectedClass = NSClassFromString(className);
+      NSCAssert(expectedClass, @"Missing a class (%@)?", expectedClass);
+    }
+    if (![dictionary isKindOfClass:expectedClass]) {
+      [NSException raise:NSInvalidArgumentException
+                  format:@"%@.%@: Expected %@ object, got %@.",
+                         [self class], field.name, expectedClass,
+                         [dictionary class]];
+    }
+  }
+#endif
+  GPBSetObjectIvarWithField(self, field, dictionary);
+}
+
+#pragma mark - Misc Dynamic Runtime Utils
+
+const char *GPBMessageEncodingForSelector(SEL selector, BOOL instanceSel) {
+  Protocol *protocol =
+      objc_getProtocol(GPBStringifySymbol(GPBMessageSignatureProtocol));
+  NSCAssert(protocol, @"Missing GPBMessageSignatureProtocol");
+  struct objc_method_description description =
+      protocol_getMethodDescription(protocol, selector, NO, instanceSel);
+  NSCAssert(description.name != Nil && description.types != nil,
+            @"Missing method for selector %@", NSStringFromSelector(selector));
+  return description.types;
+}
+
+#pragma mark - Text Format Support
+
+static void AppendStringEscaped(NSString *toPrint, NSMutableString *destStr) {
+  [destStr appendString:@"\""];
+  NSUInteger len = [toPrint length];
+  for (NSUInteger i = 0; i < len; ++i) {
+    unichar aChar = [toPrint characterAtIndex:i];
+    switch (aChar) {
+      case '\n': [destStr appendString:@"\\n"];  break;
+      case '\r': [destStr appendString:@"\\r"];  break;
+      case '\t': [destStr appendString:@"\\t"];  break;
+      case '\"': [destStr appendString:@"\\\""]; break;
+      case '\'': [destStr appendString:@"\\\'"]; break;
+      case '\\': [destStr appendString:@"\\\\"]; break;
+      default:
+        // This differs slightly from the C++ code in that the C++ doesn't
+        // generate UTF8; it looks at the string in UTF8, but escapes every
+        // byte > 0x7E.
+        if (aChar < 0x20) {
+          [destStr appendFormat:@"\\%d%d%d",
+                                (aChar / 64), ((aChar % 64) / 8), (aChar % 8)];
+        } else {
+          [destStr appendFormat:@"%C", aChar];
+        }
+        break;
+    }
+  }
+  [destStr appendString:@"\""];
+}
+
+static void AppendBufferAsString(NSData *buffer, NSMutableString *destStr) {
+  const char *src = (const char *)[buffer bytes];
+  size_t srcLen = [buffer length];
+  [destStr appendString:@"\""];
+  for (const char *srcEnd = src + srcLen; src < srcEnd; src++) {
+    switch (*src) {
+      case '\n': [destStr appendString:@"\\n"];  break;
+      case '\r': [destStr appendString:@"\\r"];  break;
+      case '\t': [destStr appendString:@"\\t"];  break;
+      case '\"': [destStr appendString:@"\\\""]; break;
+      case '\'': [destStr appendString:@"\\\'"]; break;
+      case '\\': [destStr appendString:@"\\\\"]; break;
+      default:
+        if (isprint(*src)) {
+          [destStr appendFormat:@"%c", *src];
+        } else {
+          // NOTE: doing hex means you have to worry about the letter after
+          // the hex being another hex char and forcing that to be escaped, so
+          // use octal to keep it simple.
+          [destStr appendFormat:@"\\%03o", (uint8_t)(*src)];
+        }
+        break;
+    }
+  }
+  [destStr appendString:@"\""];
+}
+
+static void AppendTextFormatForMapMessageField(
+    id map, GPBFieldDescriptor *field, NSMutableString *toStr,
+    NSString *lineIndent, NSString *fieldName, NSString *lineEnding) {
+  GPBDataType keyDataType = field.mapKeyDataType;
+  GPBDataType valueDataType = GPBGetFieldDataType(field);
+  BOOL isMessageValue = GPBDataTypeIsMessage(valueDataType);
+
+  NSString *msgStartFirst =
+      [NSString stringWithFormat:@"%@%@ {%@\n", lineIndent, fieldName, lineEnding];
+  NSString *msgStart =
+      [NSString stringWithFormat:@"%@%@ {\n", lineIndent, fieldName];
+  NSString *msgEnd = [NSString stringWithFormat:@"%@}\n", lineIndent];
+
+  NSString *keyLine = [NSString stringWithFormat:@"%@  key: ", lineIndent];
+  NSString *valueLine = [NSString stringWithFormat:@"%@  value%s ", lineIndent,
+                                                   (isMessageValue ? "" : ":")];
+
+  __block BOOL isFirst = YES;
+
+  if ((keyDataType == GPBDataTypeString) &&
+      GPBDataTypeIsObject(valueDataType)) {
+    // map is an NSDictionary.
+    NSDictionary *dict = map;
+    [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
+      #pragma unused(stop)
+      [toStr appendString:(isFirst ? msgStartFirst : msgStart)];
+      isFirst = NO;
+
+      [toStr appendString:keyLine];
+      AppendStringEscaped(key, toStr);
+      [toStr appendString:@"\n"];
+
+      [toStr appendString:valueLine];
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wswitch-enum"
+      switch (valueDataType) {
+        case GPBDataTypeString:
+          AppendStringEscaped(value, toStr);
+          break;
+
+        case GPBDataTypeBytes:
+          AppendBufferAsString(value, toStr);
+          break;
+
+        case GPBDataTypeMessage:
+          [toStr appendString:@"{\n"];
+          NSString *subIndent = [lineIndent stringByAppendingString:@"    "];
+          AppendTextFormatForMessage(value, toStr, subIndent);
+          [toStr appendFormat:@"%@  }", lineIndent];
+          break;
+
+        default:
+          NSCAssert(NO, @"Can't happen");
+          break;
+      }
+#pragma clang diagnostic pop
+      [toStr appendString:@"\n"];
+
+      [toStr appendString:msgEnd];
+    }];
+  } else {
+    // map is one of the GPB*Dictionary classes, type doesn't matter.
+    GPBInt32Int32Dictionary *dict = map;
+    [dict enumerateForTextFormat:^(id keyObj, id valueObj) {
+      [toStr appendString:(isFirst ? msgStartFirst : msgStart)];
+      isFirst = NO;
+
+      // Key always is a NSString.
+      if (keyDataType == GPBDataTypeString) {
+        [toStr appendString:keyLine];
+        AppendStringEscaped(keyObj, toStr);
+        [toStr appendString:@"\n"];
+      } else {
+        [toStr appendFormat:@"%@%@\n", keyLine, keyObj];
+      }
+
+      [toStr appendString:valueLine];
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wswitch-enum"
+      switch (valueDataType) {
+        case GPBDataTypeString:
+          AppendStringEscaped(valueObj, toStr);
+          break;
+
+        case GPBDataTypeBytes:
+          AppendBufferAsString(valueObj, toStr);
+          break;
+
+        case GPBDataTypeMessage:
+          [toStr appendString:@"{\n"];
+          NSString *subIndent = [lineIndent stringByAppendingString:@"    "];
+          AppendTextFormatForMessage(valueObj, toStr, subIndent);
+          [toStr appendFormat:@"%@  }", lineIndent];
+          break;
+
+        case GPBDataTypeEnum: {
+          int32_t enumValue = [valueObj intValue];
+          NSString *valueStr = nil;
+          GPBEnumDescriptor *descriptor = field.enumDescriptor;
+          if (descriptor) {
+            valueStr = [descriptor textFormatNameForValue:enumValue];
+          }
+          if (valueStr) {
+            [toStr appendString:valueStr];
+          } else {
+            [toStr appendFormat:@"%d", enumValue];
+          }
+          break;
+        }
+
+        default:
+          NSCAssert(valueDataType != GPBDataTypeGroup, @"Can't happen");
+          // Everything else is a NSString.
+          [toStr appendString:valueObj];
+          break;
+      }
+#pragma clang diagnostic pop
+      [toStr appendString:@"\n"];
+
+      [toStr appendString:msgEnd];
+    }];
+  }
+}
+
+static void AppendTextFormatForMessageField(GPBMessage *message,
+                                            GPBFieldDescriptor *field,
+                                            NSMutableString *toStr,
+                                            NSString *lineIndent) {
+  id arrayOrMap;
+  NSUInteger count;
+  GPBFieldType fieldType = field.fieldType;
+  switch (fieldType) {
+    case GPBFieldTypeSingle:
+      arrayOrMap = nil;
+      count = (GPBGetHasIvarField(message, field) ? 1 : 0);
+      break;
+
+    case GPBFieldTypeRepeated:
+      // Will be NSArray or GPB*Array, type doesn't matter, they both
+      // implement count.
+      arrayOrMap = GPBGetObjectIvarWithFieldNoAutocreate(message, field);
+      count = [(NSArray *)arrayOrMap count];
+      break;
+
+    case GPBFieldTypeMap: {
+      // Will be GPB*Dictionary or NSMutableDictionary, type doesn't matter,
+      // they both implement count.
+      arrayOrMap = GPBGetObjectIvarWithFieldNoAutocreate(message, field);
+      count = [(NSDictionary *)arrayOrMap count];
+      break;
+    }
+  }
+
+  if (count == 0) {
+    // Nothing to print, out of here.
+    return;
+  }
+
+  NSString *lineEnding = @"";
+
+  // If the name can't be reversed or support for extra info was turned off,
+  // this can return nil.
+  NSString *fieldName = [field textFormatName];
+  if ([fieldName length] == 0) {
+    fieldName = [NSString stringWithFormat:@"%u", GPBFieldNumber(field)];
+    // If there is only one entry, put the objc name as a comment, other wise
+    // add it before the repeated values.
+    if (count > 1) {
+      [toStr appendFormat:@"%@# %@\n", lineIndent, field.name];
+    } else {
+      lineEnding = [NSString stringWithFormat:@"  # %@", field.name];
+    }
+  }
+
+  if (fieldType == GPBFieldTypeMap) {
+    AppendTextFormatForMapMessageField(arrayOrMap, field, toStr, lineIndent,
+                                       fieldName, lineEnding);
+    return;
+  }
+
+  id array = arrayOrMap;
+  const BOOL isRepeated = (array != nil);
+
+  GPBDataType fieldDataType = GPBGetFieldDataType(field);
+  BOOL isMessageField = GPBDataTypeIsMessage(fieldDataType);
+  for (NSUInteger j = 0; j < count; ++j) {
+    // Start the line.
+    [toStr appendFormat:@"%@%@%s ", lineIndent, fieldName,
+                        (isMessageField ? "" : ":")];
+
+    // The value.
+    switch (fieldDataType) {
+#define FIELD_CASE(GPBDATATYPE, CTYPE, REAL_TYPE, ...)                        \
+  case GPBDataType##GPBDATATYPE: {                                            \
+    CTYPE v = (isRepeated ? [(GPB##REAL_TYPE##Array *)array valueAtIndex:j]   \
+                          : GPBGetMessage##REAL_TYPE##Field(message, field)); \
+    [toStr appendFormat:__VA_ARGS__, v];                                      \
+    break;                                                                    \
+  }
+
+      FIELD_CASE(Int32, int32_t, Int32, @"%d")
+      FIELD_CASE(SInt32, int32_t, Int32, @"%d")
+      FIELD_CASE(SFixed32, int32_t, Int32, @"%d")
+      FIELD_CASE(UInt32, uint32_t, UInt32, @"%u")
+      FIELD_CASE(Fixed32, uint32_t, UInt32, @"%u")
+      FIELD_CASE(Int64, int64_t, Int64, @"%lld")
+      FIELD_CASE(SInt64, int64_t, Int64, @"%lld")
+      FIELD_CASE(SFixed64, int64_t, Int64, @"%lld")
+      FIELD_CASE(UInt64, uint64_t, UInt64, @"%llu")
+      FIELD_CASE(Fixed64, uint64_t, UInt64, @"%llu")
+      FIELD_CASE(Float, float, Float, @"%.*g", FLT_DIG)
+      FIELD_CASE(Double, double, Double, @"%.*lg", DBL_DIG)
+
+#undef FIELD_CASE
+
+      case GPBDataTypeEnum: {
+        int32_t v = (isRepeated ? [(GPBEnumArray *)array rawValueAtIndex:j]
+                                : GPBGetMessageInt32Field(message, field));
+        NSString *valueStr = nil;
+        GPBEnumDescriptor *descriptor = field.enumDescriptor;
+        if (descriptor) {
+          valueStr = [descriptor textFormatNameForValue:v];
+        }
+        if (valueStr) {
+          [toStr appendString:valueStr];
+        } else {
+          [toStr appendFormat:@"%d", v];
+        }
+        break;
+      }
+
+      case GPBDataTypeBool: {
+        BOOL v = (isRepeated ? [(GPBBoolArray *)array valueAtIndex:j]
+                             : GPBGetMessageBoolField(message, field));
+        [toStr appendString:(v ? @"true" : @"false")];
+        break;
+      }
+
+      case GPBDataTypeString: {
+        NSString *v = (isRepeated ? [(NSArray *)array objectAtIndex:j]
+                                  : GPBGetMessageStringField(message, field));
+        AppendStringEscaped(v, toStr);
+        break;
+      }
+
+      case GPBDataTypeBytes: {
+        NSData *v = (isRepeated ? [(NSArray *)array objectAtIndex:j]
+                                : GPBGetMessageBytesField(message, field));
+        AppendBufferAsString(v, toStr);
+        break;
+      }
+
+      case GPBDataTypeGroup:
+      case GPBDataTypeMessage: {
+        GPBMessage *v =
+            (isRepeated ? [(NSArray *)array objectAtIndex:j]
+                        : GPBGetObjectIvarWithField(message, field));
+        [toStr appendFormat:@"{%@\n", lineEnding];
+        NSString *subIndent = [lineIndent stringByAppendingString:@"  "];
+        AppendTextFormatForMessage(v, toStr, subIndent);
+        [toStr appendFormat:@"%@}", lineIndent];
+        lineEnding = @"";
+        break;
+      }
+
+    }  // switch(fieldDataType)
+
+    // End the line.
+    [toStr appendFormat:@"%@\n", lineEnding];
+
+  }  // for(count)
+}
+
+static void AppendTextFormatForMessageExtensionRange(GPBMessage *message,
+                                                     NSArray *activeExtensions,
+                                                     GPBExtensionRange range,
+                                                     NSMutableString *toStr,
+                                                     NSString *lineIndent) {
+  uint32_t start = range.start;
+  uint32_t end = range.end;
+  for (GPBExtensionDescriptor *extension in activeExtensions) {
+    uint32_t fieldNumber = extension.fieldNumber;
+    if (fieldNumber < start) {
+      // Not there yet.
+      continue;
+    }
+    if (fieldNumber > end) {
+      // Done.
+      break;
+    }
+
+    id rawExtValue = [message getExtension:extension];
+    BOOL isRepeated = extension.isRepeated;
+
+    NSUInteger numValues = 1;
+    NSString *lineEnding = @"";
+    if (isRepeated) {
+      numValues = [(NSArray *)rawExtValue count];
+    }
+
+    NSString *singletonName = extension.singletonName;
+    if (numValues == 1) {
+      lineEnding = [NSString stringWithFormat:@"  # [%@]", singletonName];
+    } else {
+      [toStr appendFormat:@"%@# [%@]\n", lineIndent, singletonName];
+    }
+
+    GPBDataType extDataType = extension.dataType;
+    for (NSUInteger j = 0; j < numValues; ++j) {
+      id curValue = (isRepeated ? [rawExtValue objectAtIndex:j] : rawExtValue);
+
+      // Start the line.
+      [toStr appendFormat:@"%@%u%s ", lineIndent, fieldNumber,
+                          (GPBDataTypeIsMessage(extDataType) ? "" : ":")];
+
+      // The value.
+      switch (extDataType) {
+#define FIELD_CASE(GPBDATATYPE, CTYPE, NUMSELECTOR, ...) \
+  case GPBDataType##GPBDATATYPE: {                       \
+    CTYPE v = [(NSNumber *)curValue NUMSELECTOR];        \
+    [toStr appendFormat:__VA_ARGS__, v];                 \
+    break;                                               \
+  }
+
+        FIELD_CASE(Int32, int32_t, intValue, @"%d")
+        FIELD_CASE(SInt32, int32_t, intValue, @"%d")
+        FIELD_CASE(SFixed32, int32_t, unsignedIntValue, @"%d")
+        FIELD_CASE(UInt32, uint32_t, unsignedIntValue, @"%u")
+        FIELD_CASE(Fixed32, uint32_t, unsignedIntValue, @"%u")
+        FIELD_CASE(Int64, int64_t, longLongValue, @"%lld")
+        FIELD_CASE(SInt64, int64_t, longLongValue, @"%lld")
+        FIELD_CASE(SFixed64, int64_t, longLongValue, @"%lld")
+        FIELD_CASE(UInt64, uint64_t, unsignedLongLongValue, @"%llu")
+        FIELD_CASE(Fixed64, uint64_t, unsignedLongLongValue, @"%llu")
+        FIELD_CASE(Float, float, floatValue, @"%.*g", FLT_DIG)
+        FIELD_CASE(Double, double, doubleValue, @"%.*lg", DBL_DIG)
+        // TODO: Add a comment with the enum name from enum descriptors
+        // (might not be real value, so leave it as a comment, ObjC compiler
+        // name mangles differently).  Doesn't look like we actually generate
+        // an enum descriptor reference like we do for normal fields, so this
+        // will take a compiler change.
+        FIELD_CASE(Enum, int32_t, intValue, @"%d")
+
+#undef FIELD_CASE
+
+        case GPBDataTypeBool:
+          [toStr appendString:([(NSNumber *)curValue boolValue] ? @"true"
+                                                                : @"false")];
+          break;
+
+        case GPBDataTypeString:
+          AppendStringEscaped(curValue, toStr);
+          break;
+
+        case GPBDataTypeBytes:
+          AppendBufferAsString((NSData *)curValue, toStr);
+          break;
+
+        case GPBDataTypeGroup:
+        case GPBDataTypeMessage: {
+          [toStr appendFormat:@"{%@\n", lineEnding];
+          NSString *subIndent = [lineIndent stringByAppendingString:@"  "];
+          AppendTextFormatForMessage(curValue, toStr, subIndent);
+          [toStr appendFormat:@"%@}", lineIndent];
+          lineEnding = @"";
+          break;
+        }
+
+      }  // switch(extDataType)
+
+    }  //  for(numValues)
+
+    // End the line.
+    [toStr appendFormat:@"%@\n", lineEnding];
+
+  }  // for..in(activeExtensions)
+}
+
+static void AppendTextFormatForMessage(GPBMessage *message,
+                                       NSMutableString *toStr,
+                                       NSString *lineIndent) {
+  GPBDescriptor *descriptor = [message descriptor];
+  NSArray *fieldsArray = descriptor->fields_;
+  NSUInteger fieldCount = fieldsArray.count;
+  const GPBExtensionRange *extensionRanges = descriptor.extensionRanges;
+  NSUInteger extensionRangesCount = descriptor.extensionRangesCount;
+  NSArray *activeExtensions = [[message extensionsCurrentlySet]
+      sortedArrayUsingSelector:@selector(compareByFieldNumber:)];
+  for (NSUInteger i = 0, j = 0; i < fieldCount || j < extensionRangesCount;) {
+    if (i == fieldCount) {
+      AppendTextFormatForMessageExtensionRange(
+          message, activeExtensions, extensionRanges[j++], toStr, lineIndent);
+    } else if (j == extensionRangesCount ||
+               GPBFieldNumber(fieldsArray[i]) < extensionRanges[j].start) {
+      AppendTextFormatForMessageField(message, fieldsArray[i++], toStr,
+                                      lineIndent);
+    } else {
+      AppendTextFormatForMessageExtensionRange(
+          message, activeExtensions, extensionRanges[j++], toStr, lineIndent);
+    }
+  }
+
+  NSString *unknownFieldsStr =
+      GPBTextFormatForUnknownFieldSet(message.unknownFields, lineIndent);
+  if ([unknownFieldsStr length] > 0) {
+    [toStr appendFormat:@"%@# --- Unknown fields ---\n", lineIndent];
+    [toStr appendString:unknownFieldsStr];
+  }
+}
+
+NSString *GPBTextFormatForMessage(GPBMessage *message, NSString *lineIndent) {
+  if (message == nil) return @"";
+  if (lineIndent == nil) lineIndent = @"";
+
+  NSMutableString *buildString = [NSMutableString string];
+  AppendTextFormatForMessage(message, buildString, lineIndent);
+  return buildString;
+}
+
+NSString *GPBTextFormatForUnknownFieldSet(GPBUnknownFieldSet *unknownSet,
+                                          NSString *lineIndent) {
+  if (unknownSet == nil) return @"";
+  if (lineIndent == nil) lineIndent = @"";
+
+  NSMutableString *result = [NSMutableString string];
+  for (GPBUnknownField *field in [unknownSet sortedFields]) {
+    int32_t fieldNumber = [field number];
+
+#define PRINT_LOOP(PROPNAME, CTYPE, FORMAT)                                   \
+  [field.PROPNAME                                                             \
+      enumerateValuesWithBlock:^(CTYPE value, NSUInteger idx, BOOL * stop) {  \
+    _Pragma("unused(idx, stop)");                                             \
+    [result                                                                   \
+        appendFormat:@"%@%d: " #FORMAT "\n", lineIndent, fieldNumber, value]; \
+      }];
+
+    PRINT_LOOP(varintList, uint64_t, %llu);
+    PRINT_LOOP(fixed32List, uint32_t, 0x%X);
+    PRINT_LOOP(fixed64List, uint64_t, 0x%llX);
+
+#undef PRINT_LOOP
+
+    // NOTE: C++ version of TextFormat tries to parse this as a message
+    // and print that if it succeeds.
+    for (NSData *data in field.lengthDelimitedList) {
+      [result appendFormat:@"%@%d: ", lineIndent, fieldNumber];
+      AppendBufferAsString(data, result);
+      [result appendString:@"\n"];
+    }
+
+    for (GPBUnknownFieldSet *subUnknownSet in field.groupList) {
+      [result appendFormat:@"%@%d: {\n", lineIndent, fieldNumber];
+      NSString *subIndent = [lineIndent stringByAppendingString:@"  "];
+      NSString *subUnknwonSetStr =
+          GPBTextFormatForUnknownFieldSet(subUnknownSet, subIndent);
+      [result appendString:subUnknwonSetStr];
+      [result appendFormat:@"%@}\n", lineIndent];
+    }
+  }
+  return result;
+}
+
+// Helpers to decode a varint. Not using GPBCodedInputStream version because
+// that needs a state object, and we don't want to create an input stream out
+// of the data.
+GPB_INLINE int8_t ReadRawByteFromData(const uint8_t **data) {
+  int8_t result = *((int8_t *)(*data));
+  ++(*data);
+  return result;
+}
+
+static int32_t ReadRawVarint32FromData(const uint8_t **data) {
+  int8_t tmp = ReadRawByteFromData(data);
+  if (tmp >= 0) {
+    return tmp;
+  }
+  int32_t result = tmp & 0x7f;
+  if ((tmp = ReadRawByteFromData(data)) >= 0) {
+    result |= tmp << 7;
+  } else {
+    result |= (tmp & 0x7f) << 7;
+    if ((tmp = ReadRawByteFromData(data)) >= 0) {
+      result |= tmp << 14;
+    } else {
+      result |= (tmp & 0x7f) << 14;
+      if ((tmp = ReadRawByteFromData(data)) >= 0) {
+        result |= tmp << 21;
+      } else {
+        result |= (tmp & 0x7f) << 21;
+        result |= (tmp = ReadRawByteFromData(data)) << 28;
+        if (tmp < 0) {
+          // Discard upper 32 bits.
+          for (int i = 0; i < 5; i++) {
+            if (ReadRawByteFromData(data) >= 0) {
+              return result;
+            }
+          }
+          [NSException raise:NSParseErrorException
+                      format:@"Unable to read varint32"];
+        }
+      }
+    }
+  }
+  return result;
+}
+
+NSString *GPBDecodeTextFormatName(const uint8_t *decodeData, int32_t key,
+                                  NSString *inputStr) {
+  // decodData form:
+  //  varint32: num entries
+  //  for each entry:
+  //    varint32: key
+  //    bytes*: decode data
+  //
+  // decode data one of two forms:
+  //  1: a \0 followed by the string followed by an \0
+  //  2: bytecodes to transform an input into the right thing, ending with \0
+  //
+  // the bytes codes are of the form:
+  //  0xabbccccc
+  //  0x0 (all zeros), end.
+  //  a - if set, add an underscore
+  //  bb - 00 ccccc bytes as is
+  //  bb - 10 ccccc upper first, as is on rest, ccccc byte total
+  //  bb - 01 ccccc lower first, as is on rest, ccccc byte total
+  //  bb - 11 ccccc all upper, ccccc byte total
+
+  if (!decodeData || !inputStr) {
+    return nil;
+  }
+
+  // Find key
+  const uint8_t *scan = decodeData;
+  int32_t numEntries = ReadRawVarint32FromData(&scan);
+  BOOL foundKey = NO;
+  while (!foundKey && (numEntries > 0)) {
+    --numEntries;
+    int32_t dataKey = ReadRawVarint32FromData(&scan);
+    if (dataKey == key) {
+      foundKey = YES;
+    } else {
+      // If it is a inlined string, it will start with \0; if it is bytecode it
+      // will start with a code. So advance one (skipping the inline string
+      // marker), and then loop until reaching the end marker (\0).
+      ++scan;
+      while (*scan != 0) ++scan;
+      // Now move past the end marker.
+      ++scan;
+    }
+  }
+
+  if (!foundKey) {
+    return nil;
+  }
+
+  // Decode
+
+  if (*scan == 0) {
+    // Inline string. Move over the marker, and NSString can take it as
+    // UTF8.
+    ++scan;
+    NSString *result = [NSString stringWithUTF8String:(const char *)scan];
+    return result;
+  }
+
+  NSMutableString *result =
+      [NSMutableString stringWithCapacity:[inputStr length]];
+
+  const uint8_t kAddUnderscore  = 0b10000000;
+  const uint8_t kOpMask         = 0b01100000;
+  // const uint8_t kOpAsIs        = 0b00000000;
+  const uint8_t kOpFirstUpper     = 0b01000000;
+  const uint8_t kOpFirstLower     = 0b00100000;
+  const uint8_t kOpAllUpper       = 0b01100000;
+  const uint8_t kSegmentLenMask = 0b00011111;
+
+  NSInteger i = 0;
+  for (; *scan != 0; ++scan) {
+    if (*scan & kAddUnderscore) {
+      [result appendString:@"_"];
+    }
+    int segmentLen = *scan & kSegmentLenMask;
+    uint8_t decodeOp = *scan & kOpMask;
+
+    // Do op specific handling of the first character.
+    if (decodeOp == kOpFirstUpper) {
+      unichar c = [inputStr characterAtIndex:i];
+      [result appendFormat:@"%c", toupper((char)c)];
+      ++i;
+      --segmentLen;
+    } else if (decodeOp == kOpFirstLower) {
+      unichar c = [inputStr characterAtIndex:i];
+      [result appendFormat:@"%c", tolower((char)c)];
+      ++i;
+      --segmentLen;
+    }
+    // else op == kOpAsIs || op == kOpAllUpper
+
+    // Now pull over the rest of the length for this segment.
+    for (int x = 0; x < segmentLen; ++x) {
+      unichar c = [inputStr characterAtIndex:(i + x)];
+      if (decodeOp == kOpAllUpper) {
+        [result appendFormat:@"%c", toupper((char)c)];
+      } else {
+        [result appendFormat:@"%C", c];
+      }
+    }
+    i += segmentLen;
+  }
+
+  return result;
+}
+
+#pragma clang diagnostic pop
+
+BOOL GPBClassHasSel(Class aClass, SEL sel) {
+  // NOTE: We have to use class_copyMethodList, all other runtime method
+  // lookups actually also resolve the method implementation and this
+  // is called from within those methods.
+
+  BOOL result = NO;
+  unsigned int methodCount = 0;
+  Method *methodList = class_copyMethodList(aClass, &methodCount);
+  for (unsigned int i = 0; i < methodCount; ++i) {
+    SEL methodSelector = method_getName(methodList[i]);
+    if (methodSelector == sel) {
+      result = YES;
+      break;
+    }
+  }
+  free(methodList);
+  return result;
+}
diff --git a/iOS/Pods/Protobuf/objectivec/GPBUtilities_PackagePrivate.h b/iOS/Pods/Protobuf/objectivec/GPBUtilities_PackagePrivate.h
new file mode 100644 (file)
index 0000000..ed424ce
--- /dev/null
@@ -0,0 +1,351 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "GPBUtilities.h"
+
+#import "GPBDescriptor_PackagePrivate.h"
+
+// Macros for stringifying library symbols. These are used in the generated
+// PB descriptor classes wherever a library symbol name is represented as a
+// string. See README.google for more information.
+#define GPBStringify(S) #S
+#define GPBStringifySymbol(S) GPBStringify(S)
+
+#define GPBNSStringify(S) @#S
+#define GPBNSStringifySymbol(S) GPBNSStringify(S)
+
+// Constant to internally mark when there is no has bit.
+#define GPBNoHasBit INT32_MAX
+
+CF_EXTERN_C_BEGIN
+
+// These two are used to inject a runtime check for version mismatch into the
+// generated sources to make sure they are linked with a supporting runtime.
+void GPBCheckRuntimeVersionSupport(int32_t objcRuntimeVersion);
+GPB_INLINE void GPB_DEBUG_CHECK_RUNTIME_VERSIONS() {
+  // NOTE: By being inline here, this captures the value from the library's
+  // headers at the time the generated code was compiled.
+#if defined(DEBUG) && DEBUG
+  GPBCheckRuntimeVersionSupport(GOOGLE_PROTOBUF_OBJC_VERSION);
+#endif
+}
+
+// Legacy version of the checks, remove when GOOGLE_PROTOBUF_OBJC_GEN_VERSION
+// goes away (see more info in GPBBootstrap.h).
+void GPBCheckRuntimeVersionInternal(int32_t version);
+GPB_INLINE void GPBDebugCheckRuntimeVersion() {
+#if defined(DEBUG) && DEBUG
+  GPBCheckRuntimeVersionInternal(GOOGLE_PROTOBUF_OBJC_GEN_VERSION);
+#endif
+}
+
+// Conversion functions for de/serializing floating point types.
+
+GPB_INLINE int64_t GPBConvertDoubleToInt64(double v) {
+  union { double f; int64_t i; } u;
+  u.f = v;
+  return u.i;
+}
+
+GPB_INLINE int32_t GPBConvertFloatToInt32(float v) {
+  union { float f; int32_t i; } u;
+  u.f = v;
+  return u.i;
+}
+
+GPB_INLINE double GPBConvertInt64ToDouble(int64_t v) {
+  union { double f; int64_t i; } u;
+  u.i = v;
+  return u.f;
+}
+
+GPB_INLINE float GPBConvertInt32ToFloat(int32_t v) {
+  union { float f; int32_t i; } u;
+  u.i = v;
+  return u.f;
+}
+
+GPB_INLINE int32_t GPBLogicalRightShift32(int32_t value, int32_t spaces) {
+  return (int32_t)((uint32_t)(value) >> spaces);
+}
+
+GPB_INLINE int64_t GPBLogicalRightShift64(int64_t value, int32_t spaces) {
+  return (int64_t)((uint64_t)(value) >> spaces);
+}
+
+// Decode a ZigZag-encoded 32-bit value.  ZigZag encodes signed integers
+// into values that can be efficiently encoded with varint.  (Otherwise,
+// negative values must be sign-extended to 64 bits to be varint encoded,
+// thus always taking 10 bytes on the wire.)
+GPB_INLINE int32_t GPBDecodeZigZag32(uint32_t n) {
+  return (int32_t)(GPBLogicalRightShift32((int32_t)n, 1) ^ -((int32_t)(n) & 1));
+}
+
+// Decode a ZigZag-encoded 64-bit value.  ZigZag encodes signed integers
+// into values that can be efficiently encoded with varint.  (Otherwise,
+// negative values must be sign-extended to 64 bits to be varint encoded,
+// thus always taking 10 bytes on the wire.)
+GPB_INLINE int64_t GPBDecodeZigZag64(uint64_t n) {
+  return (int64_t)(GPBLogicalRightShift64((int64_t)n, 1) ^ -((int64_t)(n) & 1));
+}
+
+// Encode a ZigZag-encoded 32-bit value.  ZigZag encodes signed integers
+// into values that can be efficiently encoded with varint.  (Otherwise,
+// negative values must be sign-extended to 64 bits to be varint encoded,
+// thus always taking 10 bytes on the wire.)
+GPB_INLINE uint32_t GPBEncodeZigZag32(int32_t n) {
+  // Note:  the right-shift must be arithmetic
+  return ((uint32_t)n << 1) ^ (uint32_t)(n >> 31);
+}
+
+// Encode a ZigZag-encoded 64-bit value.  ZigZag encodes signed integers
+// into values that can be efficiently encoded with varint.  (Otherwise,
+// negative values must be sign-extended to 64 bits to be varint encoded,
+// thus always taking 10 bytes on the wire.)
+GPB_INLINE uint64_t GPBEncodeZigZag64(int64_t n) {
+  // Note:  the right-shift must be arithmetic
+  return ((uint64_t)n << 1) ^ (uint64_t)(n >> 63);
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wswitch-enum"
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+GPB_INLINE BOOL GPBDataTypeIsObject(GPBDataType type) {
+  switch (type) {
+    case GPBDataTypeBytes:
+    case GPBDataTypeString:
+    case GPBDataTypeMessage:
+    case GPBDataTypeGroup:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+GPB_INLINE BOOL GPBDataTypeIsMessage(GPBDataType type) {
+  switch (type) {
+    case GPBDataTypeMessage:
+    case GPBDataTypeGroup:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+GPB_INLINE BOOL GPBFieldDataTypeIsMessage(GPBFieldDescriptor *field) {
+  return GPBDataTypeIsMessage(field->description_->dataType);
+}
+
+GPB_INLINE BOOL GPBFieldDataTypeIsObject(GPBFieldDescriptor *field) {
+  return GPBDataTypeIsObject(field->description_->dataType);
+}
+
+GPB_INLINE BOOL GPBExtensionIsMessage(GPBExtensionDescriptor *ext) {
+  return GPBDataTypeIsMessage(ext->description_->dataType);
+}
+
+// The field is an array/map or it has an object value.
+GPB_INLINE BOOL GPBFieldStoresObject(GPBFieldDescriptor *field) {
+  GPBMessageFieldDescription *desc = field->description_;
+  if ((desc->flags & (GPBFieldRepeated | GPBFieldMapKeyMask)) != 0) {
+    return YES;
+  }
+  return GPBDataTypeIsObject(desc->dataType);
+}
+
+BOOL GPBGetHasIvar(GPBMessage *self, int32_t index, uint32_t fieldNumber);
+void GPBSetHasIvar(GPBMessage *self, int32_t idx, uint32_t fieldNumber,
+                   BOOL value);
+uint32_t GPBGetHasOneof(GPBMessage *self, int32_t index);
+
+GPB_INLINE BOOL
+GPBGetHasIvarField(GPBMessage *self, GPBFieldDescriptor *field) {
+  GPBMessageFieldDescription *fieldDesc = field->description_;
+  return GPBGetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number);
+}
+GPB_INLINE void GPBSetHasIvarField(GPBMessage *self, GPBFieldDescriptor *field,
+                                   BOOL value) {
+  GPBMessageFieldDescription *fieldDesc = field->description_;
+  GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, value);
+}
+
+void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof,
+                        int32_t oneofHasIndex, uint32_t fieldNumberNotToClear);
+
+#pragma clang diagnostic pop
+
+//%PDDM-DEFINE GPB_IVAR_SET_DECL(NAME, TYPE)
+//%void GPBSet##NAME##IvarWithFieldInternal(GPBMessage *self,
+//%            NAME$S                     GPBFieldDescriptor *field,
+//%            NAME$S                     TYPE value,
+//%            NAME$S                     GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Bool, BOOL)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetBoolIvarWithFieldInternal(GPBMessage *self,
+                                     GPBFieldDescriptor *field,
+                                     BOOL value,
+                                     GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Int32, int32_t)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetInt32IvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      int32_t value,
+                                      GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(UInt32, uint32_t)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       uint32_t value,
+                                       GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Int64, int64_t)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetInt64IvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      int64_t value,
+                                      GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(UInt64, uint64_t)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       uint64_t value,
+                                       GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Float, float)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetFloatIvarWithFieldInternal(GPBMessage *self,
+                                      GPBFieldDescriptor *field,
+                                      float value,
+                                      GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Double, double)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field,
+                                       double value,
+                                       GPBFileSyntax syntax);
+//%PDDM-EXPAND GPB_IVAR_SET_DECL(Enum, int32_t)
+// This block of code is generated, do not edit it directly.
+
+void GPBSetEnumIvarWithFieldInternal(GPBMessage *self,
+                                     GPBFieldDescriptor *field,
+                                     int32_t value,
+                                     GPBFileSyntax syntax);
+//%PDDM-EXPAND-END (8 expansions)
+
+int32_t GPBGetEnumIvarWithFieldInternal(GPBMessage *self,
+                                        GPBFieldDescriptor *field,
+                                        GPBFileSyntax syntax);
+
+id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field);
+
+void GPBSetObjectIvarWithFieldInternal(GPBMessage *self,
+                                       GPBFieldDescriptor *field, id value,
+                                       GPBFileSyntax syntax);
+void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self,
+                                               GPBFieldDescriptor *field,
+                                               id __attribute__((ns_consumed))
+                                               value,
+                                               GPBFileSyntax syntax);
+
+// GPBGetObjectIvarWithField will automatically create the field (message) if
+// it doesn't exist. GPBGetObjectIvarWithFieldNoAutocreate will return nil.
+id GPBGetObjectIvarWithFieldNoAutocreate(GPBMessage *self,
+                                         GPBFieldDescriptor *field);
+
+void GPBSetAutocreatedRetainedObjectIvarWithField(
+    GPBMessage *self, GPBFieldDescriptor *field,
+    id __attribute__((ns_consumed)) value);
+
+// Clears and releases the autocreated message ivar, if it's autocreated. If
+// it's not set as autocreated, this method does nothing.
+void GPBClearAutocreatedMessageIvarWithField(GPBMessage *self,
+                                             GPBFieldDescriptor *field);
+
+// Returns an Objective C encoding for |selector|. |instanceSel| should be
+// YES if it's an instance selector (as opposed to a class selector).
+// |selector| must be a selector from MessageSignatureProtocol.
+const char *GPBMessageEncodingForSelector(SEL selector, BOOL instanceSel);
+
+// Helper for text format name encoding.
+// decodeData is the data describing the sepecial decodes.
+// key and inputString are the input that needs decoding.
+NSString *GPBDecodeTextFormatName(const uint8_t *decodeData, int32_t key,
+                                  NSString *inputString);
+
+// A series of selectors that are used solely to get @encoding values
+// for them by the dynamic protobuf runtime code. See
+// GPBMessageEncodingForSelector for details. GPBRootObject conforms to
+// the protocol so that it is encoded in the Objective C runtime.
+@protocol GPBMessageSignatureProtocol
+@optional
+
+#define GPB_MESSAGE_SIGNATURE_ENTRY(TYPE, NAME) \
+  -(TYPE)get##NAME;                             \
+  -(void)set##NAME : (TYPE)value;               \
+  -(TYPE)get##NAME##AtIndex : (NSUInteger)index;
+
+GPB_MESSAGE_SIGNATURE_ENTRY(BOOL, Bool)
+GPB_MESSAGE_SIGNATURE_ENTRY(uint32_t, Fixed32)
+GPB_MESSAGE_SIGNATURE_ENTRY(int32_t, SFixed32)
+GPB_MESSAGE_SIGNATURE_ENTRY(float, Float)
+GPB_MESSAGE_SIGNATURE_ENTRY(uint64_t, Fixed64)
+GPB_MESSAGE_SIGNATURE_ENTRY(int64_t, SFixed64)
+GPB_MESSAGE_SIGNATURE_ENTRY(double, Double)
+GPB_MESSAGE_SIGNATURE_ENTRY(int32_t, Int32)
+GPB_MESSAGE_SIGNATURE_ENTRY(int64_t, Int64)
+GPB_MESSAGE_SIGNATURE_ENTRY(int32_t, SInt32)
+GPB_MESSAGE_SIGNATURE_ENTRY(int64_t, SInt64)
+GPB_MESSAGE_SIGNATURE_ENTRY(uint32_t, UInt32)
+GPB_MESSAGE_SIGNATURE_ENTRY(uint64_t, UInt64)
+GPB_MESSAGE_SIGNATURE_ENTRY(NSData *, Bytes)
+GPB_MESSAGE_SIGNATURE_ENTRY(NSString *, String)
+GPB_MESSAGE_SIGNATURE_ENTRY(GPBMessage *, Message)
+GPB_MESSAGE_SIGNATURE_ENTRY(GPBMessage *, Group)
+GPB_MESSAGE_SIGNATURE_ENTRY(int32_t, Enum)
+
+#undef GPB_MESSAGE_SIGNATURE_ENTRY
+
+- (id)getArray;
+- (NSUInteger)getArrayCount;
+- (void)setArray:(NSArray *)array;
++ (id)getClassValue;
+@end
+
+BOOL GPBClassHasSel(Class aClass, SEL sel);
+
+CF_EXTERN_C_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBWellKnownTypes.h b/iOS/Pods/Protobuf/objectivec/GPBWellKnownTypes.h
new file mode 100644 (file)
index 0000000..04df417
--- /dev/null
@@ -0,0 +1,245 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import <Foundation/Foundation.h>
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/Any.pbobjc.h>
+ #import <Protobuf/Duration.pbobjc.h>
+ #import <Protobuf/Timestamp.pbobjc.h>
+#else
+ #import "google/protobuf/Any.pbobjc.h"
+ #import "google/protobuf/Duration.pbobjc.h"
+ #import "google/protobuf/Timestamp.pbobjc.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - Errors
+
+/** NSError domain used for errors. */
+extern NSString *const GPBWellKnownTypesErrorDomain;
+
+/** Error code for NSError with GPBWellKnownTypesErrorDomain. */
+typedef NS_ENUM(NSInteger, GPBWellKnownTypesErrorCode) {
+  /** The type_url could not be computed for the requested GPBMessage class. */
+  GPBWellKnownTypesErrorCodeFailedToComputeTypeURL = -100,
+  /** type_url in a Any doesn’t match that of the requested GPBMessage class. */
+  GPBWellKnownTypesErrorCodeTypeURLMismatch = -101,
+};
+
+#pragma mark - GPBTimestamp
+
+/**
+ * Category for GPBTimestamp to work with standard Foundation time/date types.
+ **/
+@interface GPBTimestamp (GBPWellKnownTypes)
+
+/** The NSDate representation of this GPBTimestamp. */
+@property(nonatomic, readwrite, strong) NSDate *date;
+
+/**
+ * The NSTimeInterval representation of this GPBTimestamp.
+ *
+ * @note: Not all second/nanos combinations can be represented in a
+ * NSTimeInterval, so getting this could be a lossy transform.
+ **/
+@property(nonatomic, readwrite) NSTimeInterval timeIntervalSince1970;
+
+/**
+ * Initializes a GPBTimestamp with the given NSDate.
+ *
+ * @param date The date to configure the GPBTimestamp with.
+ *
+ * @return A newly initialized GPBTimestamp.
+ **/
+- (instancetype)initWithDate:(NSDate *)date;
+
+/**
+ * Initializes a GPBTimestamp with the given NSTimeInterval.
+ *
+ * @param timeIntervalSince1970 Time interval to configure the GPBTimestamp with.
+ *
+ * @return A newly initialized GPBTimestamp.
+ **/
+- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970;
+
+@end
+
+#pragma mark - GPBDuration
+
+/**
+ * Category for GPBDuration to work with standard Foundation time type.
+ **/
+@interface GPBDuration (GBPWellKnownTypes)
+
+/**
+ * The NSTimeInterval representation of this GPBDuration.
+ *
+ * @note: Not all second/nanos combinations can be represented in a
+ * NSTimeInterval, so getting this could be a lossy transform.
+ **/
+@property(nonatomic, readwrite) NSTimeInterval timeInterval;
+
+/**
+ * Initializes a GPBDuration with the given NSTimeInterval.
+ *
+ * @param timeInterval Time interval to configure the GPBDuration with.
+ *
+ * @return A newly initialized GPBDuration.
+ **/
+- (instancetype)initWithTimeInterval:(NSTimeInterval)timeInterval;
+
+// These next two methods are deprecated because GBPDuration has no need of a
+// "base" time. The older methods were about symmetry with GBPTimestamp, but
+// the unix epoch usage is too confusing.
+
+/** Deprecated, use timeInterval instead. */
+@property(nonatomic, readwrite) NSTimeInterval timeIntervalSince1970
+    __attribute__((deprecated("Use timeInterval")));
+/** Deprecated, use initWithTimeInterval: instead. */
+- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970
+    __attribute__((deprecated("Use initWithTimeInterval:")));
+
+@end
+
+#pragma mark - GPBAny
+
+/**
+ * Category for GPBAny to help work with the message within the object.
+ **/
+@interface GPBAny (GBPWellKnownTypes)
+
+/**
+ * Convenience method to create a GPBAny containing the serialized message.
+ * This uses type.googleapis.com/ as the type_url's prefix.
+ *
+ * @param message  The message to be packed into the GPBAny.
+ * @param errorPtr Pointer to an error that will be populated if something goes
+ *                 wrong.
+ *
+ * @return A newly configured GPBAny with the given message, or nil on failure.
+ */
++ (nullable instancetype)anyWithMessage:(nonnull GPBMessage *)message
+                                  error:(NSError **)errorPtr;
+
+/**
+ * Convenience method to create a GPBAny containing the serialized message.
+ *
+ * @param message       The message to be packed into the GPBAny.
+ * @param typeURLPrefix The URL prefix to apply for type_url.
+ * @param errorPtr      Pointer to an error that will be populated if something
+ *                      goes wrong.
+ *
+ * @return A newly configured GPBAny with the given message, or nil on failure.
+ */
++ (nullable instancetype)anyWithMessage:(nonnull GPBMessage *)message
+                          typeURLPrefix:(nonnull NSString *)typeURLPrefix
+                                  error:(NSError **)errorPtr;
+
+/**
+ * Initializes a GPBAny to contain the serialized message. This uses
+ * type.googleapis.com/ as the type_url's prefix.
+ *
+ * @param message  The message to be packed into the GPBAny.
+ * @param errorPtr Pointer to an error that will be populated if something goes
+ *                 wrong.
+ *
+ * @return A newly configured GPBAny with the given message, or nil on failure.
+ */
+- (nullable instancetype)initWithMessage:(nonnull GPBMessage *)message
+                                   error:(NSError **)errorPtr;
+
+/**
+ * Initializes a GPBAny to contain the serialized message.
+ *
+ * @param message       The message to be packed into the GPBAny.
+ * @param typeURLPrefix The URL prefix to apply for type_url.
+ * @param errorPtr      Pointer to an error that will be populated if something
+ *                      goes wrong.
+ *
+ * @return A newly configured GPBAny with the given message, or nil on failure.
+ */
+- (nullable instancetype)initWithMessage:(nonnull GPBMessage *)message
+                           typeURLPrefix:(nonnull NSString *)typeURLPrefix
+                                   error:(NSError **)errorPtr;
+
+/**
+ * Packs the serialized message into this GPBAny. This uses
+ * type.googleapis.com/ as the type_url's prefix.
+ *
+ * @param message  The message to be packed into the GPBAny.
+ * @param errorPtr Pointer to an error that will be populated if something goes
+ *                 wrong.
+ *
+ * @return Whether the packing was successful or not.
+ */
+- (BOOL)packWithMessage:(nonnull GPBMessage *)message
+                  error:(NSError **)errorPtr;
+
+/**
+ * Packs the serialized message into this GPBAny.
+ *
+ * @param message       The message to be packed into the GPBAny.
+ * @param typeURLPrefix The URL prefix to apply for type_url.
+ * @param errorPtr      Pointer to an error that will be populated if something
+ *                      goes wrong.
+ *
+ * @return Whether the packing was successful or not.
+ */
+- (BOOL)packWithMessage:(nonnull GPBMessage *)message
+          typeURLPrefix:(nonnull NSString *)typeURLPrefix
+                  error:(NSError **)errorPtr;
+
+/**
+ * Unpacks the serialized message as if it was an instance of the given class.
+ *
+ * @note When checking type_url, the base URL is not checked, only the fully
+ *       qualified name.
+ *
+ * @param messageClass The class to use to deserialize the contained message.
+ * @param errorPtr     Pointer to an error that will be populated if something
+ *                     goes wrong.
+ *
+ * @return An instance of the given class populated with the contained data, or
+ *         nil on failure.
+ */
+- (nullable GPBMessage *)unpackMessageClass:(Class)messageClass
+                                      error:(NSError **)errorPtr;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBWellKnownTypes.m b/iOS/Pods/Protobuf/objectivec/GPBWellKnownTypes.m
new file mode 100644 (file)
index 0000000..2808afe
--- /dev/null
@@ -0,0 +1,272 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+// Importing sources here to force the linker to include our category methods in
+// the static library. If these were compiled separately, the category methods
+// below would be stripped by the linker.
+
+#import "GPBWellKnownTypes.h"
+
+#import "GPBUtilities_PackagePrivate.h"
+
+NSString *const GPBWellKnownTypesErrorDomain =
+    GPBNSStringifySymbol(GPBWellKnownTypesErrorDomain);
+
+static NSString *kTypePrefixGoogleApisCom = @"type.googleapis.com/";
+
+static NSTimeInterval TimeIntervalFromSecondsAndNanos(int64_t seconds,
+                                                      int32_t nanos) {
+  return seconds + (NSTimeInterval)nanos / 1e9;
+}
+
+static int32_t SecondsAndNanosFromTimeInterval(NSTimeInterval time,
+                                               int64_t *outSeconds,
+                                               BOOL nanosMustBePositive) {
+  NSTimeInterval seconds;
+  NSTimeInterval nanos = modf(time, &seconds);
+
+  if (nanosMustBePositive && (nanos < 0)) {
+    // Per Timestamp.proto, nanos is non-negative and "Negative second values with
+    // fractions must still have non-negative nanos values that count forward in
+    // time. Must be from 0 to 999,999,999 inclusive."
+    --seconds;
+    nanos = 1.0 + nanos;
+  }
+
+  nanos *= 1e9;
+  *outSeconds = (int64_t)seconds;
+  return (int32_t)nanos;
+}
+
+static NSString *BuildTypeURL(NSString *typeURLPrefix, NSString *fullName) {
+  if (typeURLPrefix.length == 0) {
+    return fullName;
+  }
+
+  if ([typeURLPrefix hasSuffix:@"/"]) {
+    return [typeURLPrefix stringByAppendingString:fullName];
+  }
+
+  return [NSString stringWithFormat:@"%@/%@", typeURLPrefix, fullName];
+}
+
+static NSString *ParseTypeFromURL(NSString *typeURLString) {
+  NSRange range = [typeURLString rangeOfString:@"/" options:NSBackwardsSearch];
+  if ((range.location == NSNotFound) ||
+      (NSMaxRange(range) == typeURLString.length)) {
+    return nil;
+  }
+  NSString *result = [typeURLString substringFromIndex:range.location + 1];
+  return result;
+}
+
+#pragma mark - GPBTimestamp
+
+@implementation GPBTimestamp (GBPWellKnownTypes)
+
+- (instancetype)initWithDate:(NSDate *)date {
+  return [self initWithTimeIntervalSince1970:date.timeIntervalSince1970];
+}
+
+- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
+  if ((self = [super init])) {
+    int64_t seconds;
+    int32_t nanos = SecondsAndNanosFromTimeInterval(
+        timeIntervalSince1970, &seconds, YES);
+    self.seconds = seconds;
+    self.nanos = nanos;
+  }
+  return self;
+}
+
+- (NSDate *)date {
+  return [NSDate dateWithTimeIntervalSince1970:self.timeIntervalSince1970];
+}
+
+- (void)setDate:(NSDate *)date {
+  self.timeIntervalSince1970 = date.timeIntervalSince1970;
+}
+
+- (NSTimeInterval)timeIntervalSince1970 {
+  return TimeIntervalFromSecondsAndNanos(self.seconds, self.nanos);
+}
+
+- (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
+  int64_t seconds;
+  int32_t nanos =
+      SecondsAndNanosFromTimeInterval(timeIntervalSince1970, &seconds, YES);
+  self.seconds = seconds;
+  self.nanos = nanos;
+}
+
+@end
+
+#pragma mark - GPBDuration
+
+@implementation GPBDuration (GBPWellKnownTypes)
+
+- (instancetype)initWithTimeInterval:(NSTimeInterval)timeInterval {
+  if ((self = [super init])) {
+    int64_t seconds;
+    int32_t nanos = SecondsAndNanosFromTimeInterval(
+        timeInterval, &seconds, NO);
+    self.seconds = seconds;
+    self.nanos = nanos;
+  }
+  return self;
+}
+
+- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
+  return [self initWithTimeInterval:timeIntervalSince1970];
+}
+
+- (NSTimeInterval)timeInterval {
+  return TimeIntervalFromSecondsAndNanos(self.seconds, self.nanos);
+}
+
+- (void)setTimeInterval:(NSTimeInterval)timeInterval {
+  int64_t seconds;
+  int32_t nanos =
+      SecondsAndNanosFromTimeInterval(timeInterval, &seconds, NO);
+  self.seconds = seconds;
+  self.nanos = nanos;
+}
+
+- (NSTimeInterval)timeIntervalSince1970 {
+  return self.timeInterval;
+}
+
+- (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
+  self.timeInterval = timeIntervalSince1970;
+}
+
+@end
+
+#pragma mark - GPBAny
+
+@implementation GPBAny (GBPWellKnownTypes)
+
++ (instancetype)anyWithMessage:(GPBMessage *)message
+                         error:(NSError **)errorPtr {
+  return [self anyWithMessage:message
+                typeURLPrefix:kTypePrefixGoogleApisCom
+                        error:errorPtr];
+}
+
++ (instancetype)anyWithMessage:(GPBMessage *)message
+                 typeURLPrefix:(NSString *)typeURLPrefix
+                         error:(NSError **)errorPtr {
+  return [[[self alloc] initWithMessage:message
+                          typeURLPrefix:typeURLPrefix
+                                  error:errorPtr] autorelease];
+}
+
+- (instancetype)initWithMessage:(GPBMessage *)message
+                          error:(NSError **)errorPtr {
+  return [self initWithMessage:message
+                 typeURLPrefix:kTypePrefixGoogleApisCom
+                         error:errorPtr];
+}
+
+- (instancetype)initWithMessage:(GPBMessage *)message
+                  typeURLPrefix:(NSString *)typeURLPrefix
+                          error:(NSError **)errorPtr {
+  self = [self init];
+  if (self) {
+    if (![self packWithMessage:message
+                 typeURLPrefix:typeURLPrefix
+                         error:errorPtr]) {
+      [self release];
+      self = nil;
+    }
+  }
+  return self;
+}
+
+- (BOOL)packWithMessage:(GPBMessage *)message
+                  error:(NSError **)errorPtr {
+  return [self packWithMessage:message
+                 typeURLPrefix:kTypePrefixGoogleApisCom
+                         error:errorPtr];
+}
+
+- (BOOL)packWithMessage:(GPBMessage *)message
+          typeURLPrefix:(NSString *)typeURLPrefix
+                  error:(NSError **)errorPtr {
+  NSString *fullName = [message descriptor].fullName;
+  if (fullName.length == 0) {
+    if (errorPtr) {
+      *errorPtr =
+          [NSError errorWithDomain:GPBWellKnownTypesErrorDomain
+                              code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL
+                          userInfo:nil];
+    }
+    return NO;
+  }
+  if (errorPtr) {
+    *errorPtr = nil;
+  }
+  self.typeURL = BuildTypeURL(typeURLPrefix, fullName);
+  self.value = message.data;
+  return YES;
+}
+
+- (GPBMessage *)unpackMessageClass:(Class)messageClass
+                             error:(NSError **)errorPtr {
+  NSString *fullName = [messageClass descriptor].fullName;
+  if (fullName.length == 0) {
+    if (errorPtr) {
+      *errorPtr =
+          [NSError errorWithDomain:GPBWellKnownTypesErrorDomain
+                              code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL
+                      userInfo:nil];
+    }
+    return nil;
+  }
+
+  NSString *expectedFullName = ParseTypeFromURL(self.typeURL);
+  if ((expectedFullName == nil) || ![expectedFullName isEqual:fullName]) {
+    if (errorPtr) {
+      *errorPtr =
+          [NSError errorWithDomain:GPBWellKnownTypesErrorDomain
+                              code:GPBWellKnownTypesErrorCodeTypeURLMismatch
+                          userInfo:nil];
+    }
+    return nil;
+  }
+
+  // Any is proto3, which means no extensions, so this assumes anything put
+  // within an any also won't need extensions. A second helper could be added
+  // if needed.
+  return [messageClass parseFromData:self.value
+                               error:errorPtr];
+}
+
+@end
diff --git a/iOS/Pods/Protobuf/objectivec/GPBWireFormat.h b/iOS/Pods/Protobuf/objectivec/GPBWireFormat.h
new file mode 100644 (file)
index 0000000..c5941a3
--- /dev/null
@@ -0,0 +1,73 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBRuntimeTypes.h"
+
+CF_EXTERN_C_BEGIN
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef enum {
+  GPBWireFormatVarint = 0,
+  GPBWireFormatFixed64 = 1,
+  GPBWireFormatLengthDelimited = 2,
+  GPBWireFormatStartGroup = 3,
+  GPBWireFormatEndGroup = 4,
+  GPBWireFormatFixed32 = 5,
+} GPBWireFormat;
+
+enum {
+  GPBWireFormatMessageSetItem = 1,
+  GPBWireFormatMessageSetTypeId = 2,
+  GPBWireFormatMessageSetMessage = 3
+};
+
+uint32_t GPBWireFormatMakeTag(uint32_t fieldNumber, GPBWireFormat wireType)
+    __attribute__((const));
+GPBWireFormat GPBWireFormatGetTagWireType(uint32_t tag) __attribute__((const));
+uint32_t GPBWireFormatGetTagFieldNumber(uint32_t tag) __attribute__((const));
+BOOL GPBWireFormatIsValidTag(uint32_t tag) __attribute__((const));
+
+GPBWireFormat GPBWireFormatForType(GPBDataType dataType, BOOL isPacked)
+    __attribute__((const));
+
+#define GPBWireFormatMessageSetItemTag \
+  (GPBWireFormatMakeTag(GPBWireFormatMessageSetItem, GPBWireFormatStartGroup))
+#define GPBWireFormatMessageSetItemEndTag \
+  (GPBWireFormatMakeTag(GPBWireFormatMessageSetItem, GPBWireFormatEndGroup))
+#define GPBWireFormatMessageSetTypeIdTag \
+  (GPBWireFormatMakeTag(GPBWireFormatMessageSetTypeId, GPBWireFormatVarint))
+#define GPBWireFormatMessageSetMessageTag               \
+  (GPBWireFormatMakeTag(GPBWireFormatMessageSetMessage, \
+                        GPBWireFormatLengthDelimited))
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
diff --git a/iOS/Pods/Protobuf/objectivec/GPBWireFormat.m b/iOS/Pods/Protobuf/objectivec/GPBWireFormat.m
new file mode 100644 (file)
index 0000000..860a339
--- /dev/null
@@ -0,0 +1,85 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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.
+
+#import "GPBWireFormat.h"
+
+#import "GPBUtilities_PackagePrivate.h"
+
+enum {
+  GPBWireFormatTagTypeBits = 3,
+  GPBWireFormatTagTypeMask = 7 /* = (1 << GPBWireFormatTagTypeBits) - 1 */,
+};
+
+uint32_t GPBWireFormatMakeTag(uint32_t fieldNumber, GPBWireFormat wireType) {
+  return (fieldNumber << GPBWireFormatTagTypeBits) | wireType;
+}
+
+GPBWireFormat GPBWireFormatGetTagWireType(uint32_t tag) {
+  return (GPBWireFormat)(tag & GPBWireFormatTagTypeMask);
+}
+
+uint32_t GPBWireFormatGetTagFieldNumber(uint32_t tag) {
+  return GPBLogicalRightShift32(tag, GPBWireFormatTagTypeBits);
+}
+
+BOOL GPBWireFormatIsValidTag(uint32_t tag) {
+  uint32_t formatBits = (tag & GPBWireFormatTagTypeMask);
+  // The valid GPBWireFormat* values are 0-5, anything else is not a valid tag.
+  BOOL result = (formatBits <= 5);
+  return result;
+}
+
+GPBWireFormat GPBWireFormatForType(GPBDataType type, BOOL isPacked) {
+  if (isPacked) {
+    return GPBWireFormatLengthDelimited;
+  }
+
+  static const GPBWireFormat format[GPBDataType_Count] = {
+      GPBWireFormatVarint,           // GPBDataTypeBool
+      GPBWireFormatFixed32,          // GPBDataTypeFixed32
+      GPBWireFormatFixed32,          // GPBDataTypeSFixed32
+      GPBWireFormatFixed32,          // GPBDataTypeFloat
+      GPBWireFormatFixed64,          // GPBDataTypeFixed64
+      GPBWireFormatFixed64,          // GPBDataTypeSFixed64
+      GPBWireFormatFixed64,          // GPBDataTypeDouble
+      GPBWireFormatVarint,           // GPBDataTypeInt32
+      GPBWireFormatVarint,           // GPBDataTypeInt64
+      GPBWireFormatVarint,           // GPBDataTypeSInt32
+      GPBWireFormatVarint,           // GPBDataTypeSInt64
+      GPBWireFormatVarint,           // GPBDataTypeUInt32
+      GPBWireFormatVarint,           // GPBDataTypeUInt64
+      GPBWireFormatLengthDelimited,  // GPBDataTypeBytes
+      GPBWireFormatLengthDelimited,  // GPBDataTypeString
+      GPBWireFormatLengthDelimited,  // GPBDataTypeMessage
+      GPBWireFormatStartGroup,       // GPBDataTypeGroup
+      GPBWireFormatVarint            // GPBDataTypeEnum
+  };
+  return format[type];
+}
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Any.pbobjc.h b/iOS/Pods/Protobuf/objectivec/google/protobuf/Any.pbobjc.h
new file mode 100644 (file)
index 0000000..ad26189
--- /dev/null
@@ -0,0 +1,182 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/any.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBDescriptor.h>
+ #import <Protobuf/GPBMessage.h>
+ #import <Protobuf/GPBRootObject.h>
+#else
+ #import "GPBDescriptor.h"
+ #import "GPBMessage.h"
+ #import "GPBRootObject.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - GPBAnyRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GPBAnyRoot : GPBRootObject
+@end
+
+#pragma mark - GPBAny
+
+typedef GPB_ENUM(GPBAny_FieldNumber) {
+  GPBAny_FieldNumber_TypeURL = 1,
+  GPBAny_FieldNumber_Value = 2,
+};
+
+/**
+ * `Any` contains an arbitrary serialized protocol buffer message along with a
+ * URL that describes the type of the serialized message.
+ *
+ * Protobuf library provides support to pack/unpack Any values in the form
+ * of utility functions or additional generated methods of the Any type.
+ *
+ * Example 1: Pack and unpack a message in C++.
+ *
+ *     Foo foo = ...;
+ *     Any any;
+ *     any.PackFrom(foo);
+ *     ...
+ *     if (any.UnpackTo(&foo)) {
+ *       ...
+ *     }
+ *
+ * Example 2: Pack and unpack a message in Java.
+ *
+ *     Foo foo = ...;
+ *     Any any = Any.pack(foo);
+ *     ...
+ *     if (any.is(Foo.class)) {
+ *       foo = any.unpack(Foo.class);
+ *     }
+ *
+ *  Example 3: Pack and unpack a message in Python.
+ *
+ *     foo = Foo(...)
+ *     any = Any()
+ *     any.Pack(foo)
+ *     ...
+ *     if any.Is(Foo.DESCRIPTOR):
+ *       any.Unpack(foo)
+ *       ...
+ *
+ *  Example 4: Pack and unpack a message in Go
+ *
+ *      foo := &pb.Foo{...}
+ *      any, err := ptypes.MarshalAny(foo)
+ *      ...
+ *      foo := &pb.Foo{}
+ *      if err := ptypes.UnmarshalAny(any, foo); err != nil {
+ *        ...
+ *      }
+ *
+ * The pack methods provided by protobuf library will by default use
+ * 'type.googleapis.com/full.type.name' as the type URL and the unpack
+ * methods only use the fully qualified type name after the last '/'
+ * in the type URL, for example "foo.bar.com/x/y.z" will yield type
+ * name "y.z".
+ *
+ *
+ * JSON
+ * ====
+ * The JSON representation of an `Any` value uses the regular
+ * representation of the deserialized, embedded message, with an
+ * additional field `\@type` which contains the type URL. Example:
+ *
+ *     package google.profile;
+ *     message Person {
+ *       string first_name = 1;
+ *       string last_name = 2;
+ *     }
+ *
+ *     {
+ *       "\@type": "type.googleapis.com/google.profile.Person",
+ *       "firstName": <string>,
+ *       "lastName": <string>
+ *     }
+ *
+ * If the embedded message type is well-known and has a custom JSON
+ * representation, that representation will be embedded adding a field
+ * `value` which holds the custom JSON in addition to the `\@type`
+ * field. Example (for message [google.protobuf.Duration][]):
+ *
+ *     {
+ *       "\@type": "type.googleapis.com/google.protobuf.Duration",
+ *       "value": "1.212s"
+ *     }
+ **/
+@interface GPBAny : GPBMessage
+
+/**
+ * A URL/resource name that uniquely identifies the type of the serialized
+ * protocol buffer message. The last segment of the URL's path must represent
+ * the fully qualified name of the type (as in
+ * `path/google.protobuf.Duration`). The name should be in a canonical form
+ * (e.g., leading "." is not accepted).
+ *
+ * In practice, teams usually precompile into the binary all types that they
+ * expect it to use in the context of Any. However, for URLs which use the
+ * scheme `http`, `https`, or no scheme, one can optionally set up a type
+ * server that maps type URLs to message definitions as follows:
+ *
+ * * If no scheme is provided, `https` is assumed.
+ * * An HTTP GET on the URL must yield a [google.protobuf.Type][]
+ *   value in binary format, or produce an error.
+ * * Applications are allowed to cache lookup results based on the
+ *   URL, or have them precompiled into a binary to avoid any
+ *   lookup. Therefore, binary compatibility needs to be preserved
+ *   on changes to types. (Use versioned type names to manage
+ *   breaking changes.)
+ *
+ * Note: this functionality is not currently available in the official
+ * protobuf release, and it is not used for type URLs beginning with
+ * type.googleapis.com.
+ *
+ * Schemes other than `http`, `https` (or the empty scheme) might be
+ * used with implementation specific semantics.
+ **/
+@property(nonatomic, readwrite, copy, null_resettable) NSString *typeURL;
+
+/** Must be a valid serialized protocol buffer of the above specified type. */
+@property(nonatomic, readwrite, copy, null_resettable) NSData *value;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Any.pbobjc.m b/iOS/Pods/Protobuf/objectivec/google/protobuf/Any.pbobjc.m
new file mode 100644 (file)
index 0000000..d210643
--- /dev/null
@@ -0,0 +1,112 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/any.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/Any.pbobjc.h>
+#else
+ #import "google/protobuf/Any.pbobjc.h"
+#endif
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GPBAnyRoot
+
+@implementation GPBAnyRoot
+
+// No extensions in the file and no imports, so no need to generate
+// +extensionRegistry.
+
+@end
+
+#pragma mark - GPBAnyRoot_FileDescriptor
+
+static GPBFileDescriptor *GPBAnyRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
+                                                 objcPrefix:@"GPB"
+                                                     syntax:GPBFileSyntaxProto3];
+  }
+  return descriptor;
+}
+
+#pragma mark - GPBAny
+
+@implementation GPBAny
+
+@dynamic typeURL;
+@dynamic value;
+
+typedef struct GPBAny__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *typeURL;
+  NSData *value;
+} GPBAny__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "typeURL",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBAny_FieldNumber_TypeURL,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBAny__storage_, typeURL),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBAny_FieldNumber_Value,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBAny__storage_, value),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBytes,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBAny class]
+                                     rootClass:[GPBAnyRoot class]
+                                          file:GPBAnyRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBAny__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+#if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    static const char *extraTextFormatInfo =
+        "\001\001\004\241!!\000";
+    [localDescriptor setupExtraTextInfo:extraTextFormatInfo];
+#endif  // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Api.pbobjc.h b/iOS/Pods/Protobuf/objectivec/google/protobuf/Api.pbobjc.h
new file mode 100644 (file)
index 0000000..c93f3f1
--- /dev/null
@@ -0,0 +1,311 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/api.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBDescriptor.h>
+ #import <Protobuf/GPBMessage.h>
+ #import <Protobuf/GPBRootObject.h>
+#else
+ #import "GPBDescriptor.h"
+ #import "GPBMessage.h"
+ #import "GPBRootObject.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+@class GPBMethod;
+@class GPBMixin;
+@class GPBOption;
+@class GPBSourceContext;
+GPB_ENUM_FWD_DECLARE(GPBSyntax);
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - GPBApiRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GPBApiRoot : GPBRootObject
+@end
+
+#pragma mark - GPBApi
+
+typedef GPB_ENUM(GPBApi_FieldNumber) {
+  GPBApi_FieldNumber_Name = 1,
+  GPBApi_FieldNumber_MethodsArray = 2,
+  GPBApi_FieldNumber_OptionsArray = 3,
+  GPBApi_FieldNumber_Version = 4,
+  GPBApi_FieldNumber_SourceContext = 5,
+  GPBApi_FieldNumber_MixinsArray = 6,
+  GPBApi_FieldNumber_Syntax = 7,
+};
+
+/**
+ * Api is a light-weight descriptor for an API Interface.
+ *
+ * Interfaces are also described as "protocol buffer services" in some contexts,
+ * such as by the "service" keyword in a .proto file, but they are different
+ * from API Services, which represent a concrete implementation of an interface
+ * as opposed to simply a description of methods and bindings. They are also
+ * sometimes simply referred to as "APIs" in other contexts, such as the name of
+ * this message itself. See https://cloud.google.com/apis/design/glossary for
+ * detailed terminology.
+ **/
+@interface GPBApi : GPBMessage
+
+/**
+ * The fully qualified name of this interface, including package name
+ * followed by the interface's simple name.
+ **/
+@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
+
+/** The methods of this interface, in unspecified order. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMethod*> *methodsArray;
+/** The number of items in @c methodsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger methodsArray_Count;
+
+/** Any metadata attached to the interface. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
+/** The number of items in @c optionsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger optionsArray_Count;
+
+/**
+ * A version string for this interface. If specified, must have the form
+ * `major-version.minor-version`, as in `1.10`. If the minor version is
+ * omitted, it defaults to zero. If the entire version field is empty, the
+ * major version is derived from the package name, as outlined below. If the
+ * field is not empty, the version in the package name will be verified to be
+ * consistent with what is provided here.
+ *
+ * The versioning schema uses [semantic
+ * versioning](http://semver.org) where the major version number
+ * indicates a breaking change and the minor version an additive,
+ * non-breaking change. Both version numbers are signals to users
+ * what to expect from different versions, and should be carefully
+ * chosen based on the product plan.
+ *
+ * The major version is also reflected in the package name of the
+ * interface, which must end in `v<major-version>`, as in
+ * `google.feature.v1`. For major versions 0 and 1, the suffix can
+ * be omitted. Zero major versions must only be used for
+ * experimental, non-GA interfaces.
+ **/
+@property(nonatomic, readwrite, copy, null_resettable) NSString *version;
+
+/**
+ * Source context for the protocol buffer service represented by this
+ * message.
+ **/
+@property(nonatomic, readwrite, strong, null_resettable) GPBSourceContext *sourceContext;
+/** Test to see if @c sourceContext has been set. */
+@property(nonatomic, readwrite) BOOL hasSourceContext;
+
+/** Included interfaces. See [Mixin][]. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMixin*> *mixinsArray;
+/** The number of items in @c mixinsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger mixinsArray_Count;
+
+/** The source syntax of the service. */
+@property(nonatomic, readwrite) enum GPBSyntax syntax;
+
+@end
+
+/**
+ * Fetches the raw value of a @c GPBApi's @c syntax property, even
+ * if the value was not defined by the enum at the time the code was generated.
+ **/
+int32_t GPBApi_Syntax_RawValue(GPBApi *message);
+/**
+ * Sets the raw value of an @c GPBApi's @c syntax property, allowing
+ * it to be set to a value that was not defined by the enum at the time the code
+ * was generated.
+ **/
+void SetGPBApi_Syntax_RawValue(GPBApi *message, int32_t value);
+
+#pragma mark - GPBMethod
+
+typedef GPB_ENUM(GPBMethod_FieldNumber) {
+  GPBMethod_FieldNumber_Name = 1,
+  GPBMethod_FieldNumber_RequestTypeURL = 2,
+  GPBMethod_FieldNumber_RequestStreaming = 3,
+  GPBMethod_FieldNumber_ResponseTypeURL = 4,
+  GPBMethod_FieldNumber_ResponseStreaming = 5,
+  GPBMethod_FieldNumber_OptionsArray = 6,
+  GPBMethod_FieldNumber_Syntax = 7,
+};
+
+/**
+ * Method represents a method of an API interface.
+ **/
+@interface GPBMethod : GPBMessage
+
+/** The simple name of this method. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
+
+/** A URL of the input message type. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *requestTypeURL;
+
+/** If true, the request is streamed. */
+@property(nonatomic, readwrite) BOOL requestStreaming;
+
+/** The URL of the output message type. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *responseTypeURL;
+
+/** If true, the response is streamed. */
+@property(nonatomic, readwrite) BOOL responseStreaming;
+
+/** Any metadata attached to the method. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
+/** The number of items in @c optionsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger optionsArray_Count;
+
+/** The source syntax of this method. */
+@property(nonatomic, readwrite) enum GPBSyntax syntax;
+
+@end
+
+/**
+ * Fetches the raw value of a @c GPBMethod's @c syntax property, even
+ * if the value was not defined by the enum at the time the code was generated.
+ **/
+int32_t GPBMethod_Syntax_RawValue(GPBMethod *message);
+/**
+ * Sets the raw value of an @c GPBMethod's @c syntax property, allowing
+ * it to be set to a value that was not defined by the enum at the time the code
+ * was generated.
+ **/
+void SetGPBMethod_Syntax_RawValue(GPBMethod *message, int32_t value);
+
+#pragma mark - GPBMixin
+
+typedef GPB_ENUM(GPBMixin_FieldNumber) {
+  GPBMixin_FieldNumber_Name = 1,
+  GPBMixin_FieldNumber_Root = 2,
+};
+
+/**
+ * Declares an API Interface to be included in this interface. The including
+ * interface must redeclare all the methods from the included interface, but
+ * documentation and options are inherited as follows:
+ *
+ * - If after comment and whitespace stripping, the documentation
+ *   string of the redeclared method is empty, it will be inherited
+ *   from the original method.
+ *
+ * - Each annotation belonging to the service config (http,
+ *   visibility) which is not set in the redeclared method will be
+ *   inherited.
+ *
+ * - If an http annotation is inherited, the path pattern will be
+ *   modified as follows. Any version prefix will be replaced by the
+ *   version of the including interface plus the [root][] path if
+ *   specified.
+ *
+ * Example of a simple mixin:
+ *
+ *     package google.acl.v1;
+ *     service AccessControl {
+ *       // Get the underlying ACL object.
+ *       rpc GetAcl(GetAclRequest) returns (Acl) {
+ *         option (google.api.http).get = "/v1/{resource=**}:getAcl";
+ *       }
+ *     }
+ *
+ *     package google.storage.v2;
+ *     service Storage {
+ *       rpc GetAcl(GetAclRequest) returns (Acl);
+ *
+ *       // Get a data record.
+ *       rpc GetData(GetDataRequest) returns (Data) {
+ *         option (google.api.http).get = "/v2/{resource=**}";
+ *       }
+ *     }
+ *
+ * Example of a mixin configuration:
+ *
+ *     apis:
+ *     - name: google.storage.v2.Storage
+ *       mixins:
+ *       - name: google.acl.v1.AccessControl
+ *
+ * The mixin construct implies that all methods in `AccessControl` are
+ * also declared with same name and request/response types in
+ * `Storage`. A documentation generator or annotation processor will
+ * see the effective `Storage.GetAcl` method after inherting
+ * documentation and annotations as follows:
+ *
+ *     service Storage {
+ *       // Get the underlying ACL object.
+ *       rpc GetAcl(GetAclRequest) returns (Acl) {
+ *         option (google.api.http).get = "/v2/{resource=**}:getAcl";
+ *       }
+ *       ...
+ *     }
+ *
+ * Note how the version in the path pattern changed from `v1` to `v2`.
+ *
+ * If the `root` field in the mixin is specified, it should be a
+ * relative path under which inherited HTTP paths are placed. Example:
+ *
+ *     apis:
+ *     - name: google.storage.v2.Storage
+ *       mixins:
+ *       - name: google.acl.v1.AccessControl
+ *         root: acls
+ *
+ * This implies the following inherited HTTP annotation:
+ *
+ *     service Storage {
+ *       // Get the underlying ACL object.
+ *       rpc GetAcl(GetAclRequest) returns (Acl) {
+ *         option (google.api.http).get = "/v2/acls/{resource=**}:getAcl";
+ *       }
+ *       ...
+ *     }
+ **/
+@interface GPBMixin : GPBMessage
+
+/** The fully qualified name of the interface which is included. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
+
+/**
+ * If non-empty specifies a path under which inherited HTTP paths
+ * are rooted.
+ **/
+@property(nonatomic, readwrite, copy, null_resettable) NSString *root;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Api.pbobjc.m b/iOS/Pods/Protobuf/objectivec/google/protobuf/Api.pbobjc.m
new file mode 100644 (file)
index 0000000..58b4715
--- /dev/null
@@ -0,0 +1,356 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/api.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/Api.pbobjc.h>
+ #import <Protobuf/SourceContext.pbobjc.h>
+ #import <Protobuf/Type.pbobjc.h>
+#else
+ #import "google/protobuf/Api.pbobjc.h"
+ #import "google/protobuf/SourceContext.pbobjc.h"
+ #import "google/protobuf/Type.pbobjc.h"
+#endif
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GPBApiRoot
+
+@implementation GPBApiRoot
+
+// No extensions in the file and none of the imports (direct or indirect)
+// defined extensions, so no need to generate +extensionRegistry.
+
+@end
+
+#pragma mark - GPBApiRoot_FileDescriptor
+
+static GPBFileDescriptor *GPBApiRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
+                                                 objcPrefix:@"GPB"
+                                                     syntax:GPBFileSyntaxProto3];
+  }
+  return descriptor;
+}
+
+#pragma mark - GPBApi
+
+@implementation GPBApi
+
+@dynamic name;
+@dynamic methodsArray, methodsArray_Count;
+@dynamic optionsArray, optionsArray_Count;
+@dynamic version;
+@dynamic hasSourceContext, sourceContext;
+@dynamic mixinsArray, mixinsArray_Count;
+@dynamic syntax;
+
+typedef struct GPBApi__storage_ {
+  uint32_t _has_storage_[1];
+  GPBSyntax syntax;
+  NSString *name;
+  NSMutableArray *methodsArray;
+  NSMutableArray *optionsArray;
+  NSString *version;
+  GPBSourceContext *sourceContext;
+  NSMutableArray *mixinsArray;
+} GPBApi__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "name",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBApi_FieldNumber_Name,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBApi__storage_, name),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "methodsArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBMethod),
+        .number = GPBApi_FieldNumber_MethodsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBApi__storage_, methodsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "optionsArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBOption),
+        .number = GPBApi_FieldNumber_OptionsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBApi__storage_, optionsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "version",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBApi_FieldNumber_Version,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBApi__storage_, version),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "sourceContext",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceContext),
+        .number = GPBApi_FieldNumber_SourceContext,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GPBApi__storage_, sourceContext),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "mixinsArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBMixin),
+        .number = GPBApi_FieldNumber_MixinsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBApi__storage_, mixinsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "syntax",
+        .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor,
+        .number = GPBApi_FieldNumber_Syntax,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GPBApi__storage_, syntax),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBApi class]
+                                     rootClass:[GPBApiRoot class]
+                                          file:GPBApiRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBApi__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+int32_t GPBApi_Syntax_RawValue(GPBApi *message) {
+  GPBDescriptor *descriptor = [GPBApi descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBApi_FieldNumber_Syntax];
+  return GPBGetMessageInt32Field(message, field);
+}
+
+void SetGPBApi_Syntax_RawValue(GPBApi *message, int32_t value) {
+  GPBDescriptor *descriptor = [GPBApi descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBApi_FieldNumber_Syntax];
+  GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax);
+}
+
+#pragma mark - GPBMethod
+
+@implementation GPBMethod
+
+@dynamic name;
+@dynamic requestTypeURL;
+@dynamic requestStreaming;
+@dynamic responseTypeURL;
+@dynamic responseStreaming;
+@dynamic optionsArray, optionsArray_Count;
+@dynamic syntax;
+
+typedef struct GPBMethod__storage_ {
+  uint32_t _has_storage_[1];
+  GPBSyntax syntax;
+  NSString *name;
+  NSString *requestTypeURL;
+  NSString *responseTypeURL;
+  NSMutableArray *optionsArray;
+} GPBMethod__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "name",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBMethod_FieldNumber_Name,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBMethod__storage_, name),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "requestTypeURL",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBMethod_FieldNumber_RequestTypeURL,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBMethod__storage_, requestTypeURL),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "requestStreaming",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBMethod_FieldNumber_RequestStreaming,
+        .hasIndex = 2,
+        .offset = 3,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "responseTypeURL",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBMethod_FieldNumber_ResponseTypeURL,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GPBMethod__storage_, responseTypeURL),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "responseStreaming",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBMethod_FieldNumber_ResponseStreaming,
+        .hasIndex = 5,
+        .offset = 6,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "optionsArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBOption),
+        .number = GPBMethod_FieldNumber_OptionsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBMethod__storage_, optionsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "syntax",
+        .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor,
+        .number = GPBMethod_FieldNumber_Syntax,
+        .hasIndex = 7,
+        .offset = (uint32_t)offsetof(GPBMethod__storage_, syntax),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBMethod class]
+                                     rootClass:[GPBApiRoot class]
+                                          file:GPBApiRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBMethod__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+#if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    static const char *extraTextFormatInfo =
+        "\002\002\007\244\241!!\000\004\010\244\241!!\000";
+    [localDescriptor setupExtraTextInfo:extraTextFormatInfo];
+#endif  // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+int32_t GPBMethod_Syntax_RawValue(GPBMethod *message) {
+  GPBDescriptor *descriptor = [GPBMethod descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBMethod_FieldNumber_Syntax];
+  return GPBGetMessageInt32Field(message, field);
+}
+
+void SetGPBMethod_Syntax_RawValue(GPBMethod *message, int32_t value) {
+  GPBDescriptor *descriptor = [GPBMethod descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBMethod_FieldNumber_Syntax];
+  GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax);
+}
+
+#pragma mark - GPBMixin
+
+@implementation GPBMixin
+
+@dynamic name;
+@dynamic root;
+
+typedef struct GPBMixin__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *name;
+  NSString *root;
+} GPBMixin__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "name",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBMixin_FieldNumber_Name,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBMixin__storage_, name),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "root",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBMixin_FieldNumber_Root,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBMixin__storage_, root),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBMixin class]
+                                     rootClass:[GPBApiRoot class]
+                                          file:GPBApiRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBMixin__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Duration.pbobjc.h b/iOS/Pods/Protobuf/objectivec/google/protobuf/Duration.pbobjc.h
new file mode 100644 (file)
index 0000000..3e36759
--- /dev/null
@@ -0,0 +1,145 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/duration.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBDescriptor.h>
+ #import <Protobuf/GPBMessage.h>
+ #import <Protobuf/GPBRootObject.h>
+#else
+ #import "GPBDescriptor.h"
+ #import "GPBMessage.h"
+ #import "GPBRootObject.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - GPBDurationRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GPBDurationRoot : GPBRootObject
+@end
+
+#pragma mark - GPBDuration
+
+typedef GPB_ENUM(GPBDuration_FieldNumber) {
+  GPBDuration_FieldNumber_Seconds = 1,
+  GPBDuration_FieldNumber_Nanos = 2,
+};
+
+/**
+ * A Duration represents a signed, fixed-length span of time represented
+ * as a count of seconds and fractions of seconds at nanosecond
+ * resolution. It is independent of any calendar and concepts like "day"
+ * or "month". It is related to Timestamp in that the difference between
+ * two Timestamp values is a Duration and it can be added or subtracted
+ * from a Timestamp. Range is approximately +-10,000 years.
+ *
+ * # Examples
+ *
+ * Example 1: Compute Duration from two Timestamps in pseudo code.
+ *
+ *     Timestamp start = ...;
+ *     Timestamp end = ...;
+ *     Duration duration = ...;
+ *
+ *     duration.seconds = end.seconds - start.seconds;
+ *     duration.nanos = end.nanos - start.nanos;
+ *
+ *     if (duration.seconds < 0 && duration.nanos > 0) {
+ *       duration.seconds += 1;
+ *       duration.nanos -= 1000000000;
+ *     } else if (durations.seconds > 0 && duration.nanos < 0) {
+ *       duration.seconds -= 1;
+ *       duration.nanos += 1000000000;
+ *     }
+ *
+ * Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
+ *
+ *     Timestamp start = ...;
+ *     Duration duration = ...;
+ *     Timestamp end = ...;
+ *
+ *     end.seconds = start.seconds + duration.seconds;
+ *     end.nanos = start.nanos + duration.nanos;
+ *
+ *     if (end.nanos < 0) {
+ *       end.seconds -= 1;
+ *       end.nanos += 1000000000;
+ *     } else if (end.nanos >= 1000000000) {
+ *       end.seconds += 1;
+ *       end.nanos -= 1000000000;
+ *     }
+ *
+ * Example 3: Compute Duration from datetime.timedelta in Python.
+ *
+ *     td = datetime.timedelta(days=3, minutes=10)
+ *     duration = Duration()
+ *     duration.FromTimedelta(td)
+ *
+ * # JSON Mapping
+ *
+ * In JSON format, the Duration type is encoded as a string rather than an
+ * object, where the string ends in the suffix "s" (indicating seconds) and
+ * is preceded by the number of seconds, with nanoseconds expressed as
+ * fractional seconds. For example, 3 seconds with 0 nanoseconds should be
+ * encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should
+ * be expressed in JSON format as "3.000000001s", and 3 seconds and 1
+ * microsecond should be expressed in JSON format as "3.000001s".
+ **/
+@interface GPBDuration : GPBMessage
+
+/**
+ * Signed seconds of the span of time. Must be from -315,576,000,000
+ * to +315,576,000,000 inclusive. Note: these bounds are computed from:
+ * 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
+ **/
+@property(nonatomic, readwrite) int64_t seconds;
+
+/**
+ * Signed fractions of a second at nanosecond resolution of the span
+ * of time. Durations less than one second are represented with a 0
+ * `seconds` field and a positive or negative `nanos` field. For durations
+ * of one second or more, a non-zero value for the `nanos` field must be
+ * of the same sign as the `seconds` field. Must be from -999,999,999
+ * to +999,999,999 inclusive.
+ **/
+@property(nonatomic, readwrite) int32_t nanos;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Duration.pbobjc.m b/iOS/Pods/Protobuf/objectivec/google/protobuf/Duration.pbobjc.m
new file mode 100644 (file)
index 0000000..bafb64a
--- /dev/null
@@ -0,0 +1,107 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/duration.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/Duration.pbobjc.h>
+#else
+ #import "google/protobuf/Duration.pbobjc.h"
+#endif
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GPBDurationRoot
+
+@implementation GPBDurationRoot
+
+// No extensions in the file and no imports, so no need to generate
+// +extensionRegistry.
+
+@end
+
+#pragma mark - GPBDurationRoot_FileDescriptor
+
+static GPBFileDescriptor *GPBDurationRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
+                                                 objcPrefix:@"GPB"
+                                                     syntax:GPBFileSyntaxProto3];
+  }
+  return descriptor;
+}
+
+#pragma mark - GPBDuration
+
+@implementation GPBDuration
+
+@dynamic seconds;
+@dynamic nanos;
+
+typedef struct GPBDuration__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t nanos;
+  int64_t seconds;
+} GPBDuration__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "seconds",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBDuration_FieldNumber_Seconds,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBDuration__storage_, seconds),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "nanos",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBDuration_FieldNumber_Nanos,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBDuration__storage_, nanos),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBDuration class]
+                                     rootClass:[GPBDurationRoot class]
+                                          file:GPBDurationRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBDuration__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Empty.pbobjc.h b/iOS/Pods/Protobuf/objectivec/google/protobuf/Empty.pbobjc.h
new file mode 100644 (file)
index 0000000..fdc247a
--- /dev/null
@@ -0,0 +1,74 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/empty.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBDescriptor.h>
+ #import <Protobuf/GPBMessage.h>
+ #import <Protobuf/GPBRootObject.h>
+#else
+ #import "GPBDescriptor.h"
+ #import "GPBMessage.h"
+ #import "GPBRootObject.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - GPBEmptyRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GPBEmptyRoot : GPBRootObject
+@end
+
+#pragma mark - GPBEmpty
+
+/**
+ * A generic empty message that you can re-use to avoid defining duplicated
+ * empty messages in your APIs. A typical example is to use it as the request
+ * or the response type of an API method. For instance:
+ *
+ *     service Foo {
+ *       rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
+ *     }
+ *
+ * The JSON representation for `Empty` is empty JSON object `{}`.
+ **/
+@interface GPBEmpty : GPBMessage
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Empty.pbobjc.m b/iOS/Pods/Protobuf/objectivec/google/protobuf/Empty.pbobjc.m
new file mode 100644 (file)
index 0000000..506b500
--- /dev/null
@@ -0,0 +1,83 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/empty.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/Empty.pbobjc.h>
+#else
+ #import "google/protobuf/Empty.pbobjc.h"
+#endif
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GPBEmptyRoot
+
+@implementation GPBEmptyRoot
+
+// No extensions in the file and no imports, so no need to generate
+// +extensionRegistry.
+
+@end
+
+#pragma mark - GPBEmptyRoot_FileDescriptor
+
+static GPBFileDescriptor *GPBEmptyRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
+                                                 objcPrefix:@"GPB"
+                                                     syntax:GPBFileSyntaxProto3];
+  }
+  return descriptor;
+}
+
+#pragma mark - GPBEmpty
+
+@implementation GPBEmpty
+
+
+typedef struct GPBEmpty__storage_ {
+  uint32_t _has_storage_[1];
+} GPBEmpty__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBEmpty class]
+                                     rootClass:[GPBEmptyRoot class]
+                                          file:GPBEmptyRoot_FileDescriptor()
+                                        fields:NULL
+                                    fieldCount:0
+                                   storageSize:sizeof(GPBEmpty__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/FieldMask.pbobjc.h b/iOS/Pods/Protobuf/objectivec/google/protobuf/FieldMask.pbobjc.h
new file mode 100644 (file)
index 0000000..73296d5
--- /dev/null
@@ -0,0 +1,281 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/field_mask.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBDescriptor.h>
+ #import <Protobuf/GPBMessage.h>
+ #import <Protobuf/GPBRootObject.h>
+#else
+ #import "GPBDescriptor.h"
+ #import "GPBMessage.h"
+ #import "GPBRootObject.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - GPBFieldMaskRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GPBFieldMaskRoot : GPBRootObject
+@end
+
+#pragma mark - GPBFieldMask
+
+typedef GPB_ENUM(GPBFieldMask_FieldNumber) {
+  GPBFieldMask_FieldNumber_PathsArray = 1,
+};
+
+/**
+ * `FieldMask` represents a set of symbolic field paths, for example:
+ *
+ *     paths: "f.a"
+ *     paths: "f.b.d"
+ *
+ * Here `f` represents a field in some root message, `a` and `b`
+ * fields in the message found in `f`, and `d` a field found in the
+ * message in `f.b`.
+ *
+ * Field masks are used to specify a subset of fields that should be
+ * returned by a get operation or modified by an update operation.
+ * Field masks also have a custom JSON encoding (see below).
+ *
+ * # Field Masks in Projections
+ *
+ * When used in the context of a projection, a response message or
+ * sub-message is filtered by the API to only contain those fields as
+ * specified in the mask. For example, if the mask in the previous
+ * example is applied to a response message as follows:
+ *
+ *     f {
+ *       a : 22
+ *       b {
+ *         d : 1
+ *         x : 2
+ *       }
+ *       y : 13
+ *     }
+ *     z: 8
+ *
+ * The result will not contain specific values for fields x,y and z
+ * (their value will be set to the default, and omitted in proto text
+ * output):
+ *
+ *
+ *     f {
+ *       a : 22
+ *       b {
+ *         d : 1
+ *       }
+ *     }
+ *
+ * A repeated field is not allowed except at the last position of a
+ * paths string.
+ *
+ * If a FieldMask object is not present in a get operation, the
+ * operation applies to all fields (as if a FieldMask of all fields
+ * had been specified).
+ *
+ * Note that a field mask does not necessarily apply to the
+ * top-level response message. In case of a REST get operation, the
+ * field mask applies directly to the response, but in case of a REST
+ * list operation, the mask instead applies to each individual message
+ * in the returned resource list. In case of a REST custom method,
+ * other definitions may be used. Where the mask applies will be
+ * clearly documented together with its declaration in the API.  In
+ * any case, the effect on the returned resource/resources is required
+ * behavior for APIs.
+ *
+ * # Field Masks in Update Operations
+ *
+ * A field mask in update operations specifies which fields of the
+ * targeted resource are going to be updated. The API is required
+ * to only change the values of the fields as specified in the mask
+ * and leave the others untouched. If a resource is passed in to
+ * describe the updated values, the API ignores the values of all
+ * fields not covered by the mask.
+ *
+ * If a repeated field is specified for an update operation, the existing
+ * repeated values in the target resource will be overwritten by the new values.
+ * Note that a repeated field is only allowed in the last position of a `paths`
+ * string.
+ *
+ * If a sub-message is specified in the last position of the field mask for an
+ * update operation, then the existing sub-message in the target resource is
+ * overwritten. Given the target message:
+ *
+ *     f {
+ *       b {
+ *         d : 1
+ *         x : 2
+ *       }
+ *       c : 1
+ *     }
+ *
+ * And an update message:
+ *
+ *     f {
+ *       b {
+ *         d : 10
+ *       }
+ *     }
+ *
+ * then if the field mask is:
+ *
+ *  paths: "f.b"
+ *
+ * then the result will be:
+ *
+ *     f {
+ *       b {
+ *         d : 10
+ *       }
+ *       c : 1
+ *     }
+ *
+ * However, if the update mask was:
+ *
+ *  paths: "f.b.d"
+ *
+ * then the result would be:
+ *
+ *     f {
+ *       b {
+ *         d : 10
+ *         x : 2
+ *       }
+ *       c : 1
+ *     }
+ *
+ * In order to reset a field's value to the default, the field must
+ * be in the mask and set to the default value in the provided resource.
+ * Hence, in order to reset all fields of a resource, provide a default
+ * instance of the resource and set all fields in the mask, or do
+ * not provide a mask as described below.
+ *
+ * If a field mask is not present on update, the operation applies to
+ * all fields (as if a field mask of all fields has been specified).
+ * Note that in the presence of schema evolution, this may mean that
+ * fields the client does not know and has therefore not filled into
+ * the request will be reset to their default. If this is unwanted
+ * behavior, a specific service may require a client to always specify
+ * a field mask, producing an error if not.
+ *
+ * As with get operations, the location of the resource which
+ * describes the updated values in the request message depends on the
+ * operation kind. In any case, the effect of the field mask is
+ * required to be honored by the API.
+ *
+ * ## Considerations for HTTP REST
+ *
+ * The HTTP kind of an update operation which uses a field mask must
+ * be set to PATCH instead of PUT in order to satisfy HTTP semantics
+ * (PUT must only be used for full updates).
+ *
+ * # JSON Encoding of Field Masks
+ *
+ * In JSON, a field mask is encoded as a single string where paths are
+ * separated by a comma. Fields name in each path are converted
+ * to/from lower-camel naming conventions.
+ *
+ * As an example, consider the following message declarations:
+ *
+ *     message Profile {
+ *       User user = 1;
+ *       Photo photo = 2;
+ *     }
+ *     message User {
+ *       string display_name = 1;
+ *       string address = 2;
+ *     }
+ *
+ * In proto a field mask for `Profile` may look as such:
+ *
+ *     mask {
+ *       paths: "user.display_name"
+ *       paths: "photo"
+ *     }
+ *
+ * In JSON, the same mask is represented as below:
+ *
+ *     {
+ *       mask: "user.displayName,photo"
+ *     }
+ *
+ * # Field Masks and Oneof Fields
+ *
+ * Field masks treat fields in oneofs just as regular fields. Consider the
+ * following message:
+ *
+ *     message SampleMessage {
+ *       oneof test_oneof {
+ *         string name = 4;
+ *         SubMessage sub_message = 9;
+ *       }
+ *     }
+ *
+ * The field mask can be:
+ *
+ *     mask {
+ *       paths: "name"
+ *     }
+ *
+ * Or:
+ *
+ *     mask {
+ *       paths: "sub_message"
+ *     }
+ *
+ * Note that oneof type names ("test_oneof" in this case) cannot be used in
+ * paths.
+ *
+ * ## Field Mask Verification
+ *
+ * The implementation of any API method which has a FieldMask type field in the
+ * request should verify the included field paths, and return an
+ * `INVALID_ARGUMENT` error if any path is duplicated or unmappable.
+ **/
+@interface GPBFieldMask : GPBMessage
+
+/** The set of field mask paths. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *pathsArray;
+/** The number of items in @c pathsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger pathsArray_Count;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/FieldMask.pbobjc.m b/iOS/Pods/Protobuf/objectivec/google/protobuf/FieldMask.pbobjc.m
new file mode 100644 (file)
index 0000000..b0915af
--- /dev/null
@@ -0,0 +1,96 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/field_mask.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/FieldMask.pbobjc.h>
+#else
+ #import "google/protobuf/FieldMask.pbobjc.h"
+#endif
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GPBFieldMaskRoot
+
+@implementation GPBFieldMaskRoot
+
+// No extensions in the file and no imports, so no need to generate
+// +extensionRegistry.
+
+@end
+
+#pragma mark - GPBFieldMaskRoot_FileDescriptor
+
+static GPBFileDescriptor *GPBFieldMaskRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
+                                                 objcPrefix:@"GPB"
+                                                     syntax:GPBFileSyntaxProto3];
+  }
+  return descriptor;
+}
+
+#pragma mark - GPBFieldMask
+
+@implementation GPBFieldMask
+
+@dynamic pathsArray, pathsArray_Count;
+
+typedef struct GPBFieldMask__storage_ {
+  uint32_t _has_storage_[1];
+  NSMutableArray *pathsArray;
+} GPBFieldMask__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "pathsArray",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBFieldMask_FieldNumber_PathsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBFieldMask__storage_, pathsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBFieldMask class]
+                                     rootClass:[GPBFieldMaskRoot class]
+                                          file:GPBFieldMaskRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBFieldMask__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/SourceContext.pbobjc.h b/iOS/Pods/Protobuf/objectivec/google/protobuf/SourceContext.pbobjc.h
new file mode 100644 (file)
index 0000000..e492395
--- /dev/null
@@ -0,0 +1,77 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/source_context.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBDescriptor.h>
+ #import <Protobuf/GPBMessage.h>
+ #import <Protobuf/GPBRootObject.h>
+#else
+ #import "GPBDescriptor.h"
+ #import "GPBMessage.h"
+ #import "GPBRootObject.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - GPBSourceContextRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GPBSourceContextRoot : GPBRootObject
+@end
+
+#pragma mark - GPBSourceContext
+
+typedef GPB_ENUM(GPBSourceContext_FieldNumber) {
+  GPBSourceContext_FieldNumber_FileName = 1,
+};
+
+/**
+ * `SourceContext` represents information about the source of a
+ * protobuf element, like the file in which it is defined.
+ **/
+@interface GPBSourceContext : GPBMessage
+
+/**
+ * The path-qualified name of the .proto file that contained the associated
+ * protobuf element.  For example: `"google/protobuf/source_context.proto"`.
+ **/
+@property(nonatomic, readwrite, copy, null_resettable) NSString *fileName;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/SourceContext.pbobjc.m b/iOS/Pods/Protobuf/objectivec/google/protobuf/SourceContext.pbobjc.m
new file mode 100644 (file)
index 0000000..83bfa34
--- /dev/null
@@ -0,0 +1,96 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/source_context.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/SourceContext.pbobjc.h>
+#else
+ #import "google/protobuf/SourceContext.pbobjc.h"
+#endif
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GPBSourceContextRoot
+
+@implementation GPBSourceContextRoot
+
+// No extensions in the file and no imports, so no need to generate
+// +extensionRegistry.
+
+@end
+
+#pragma mark - GPBSourceContextRoot_FileDescriptor
+
+static GPBFileDescriptor *GPBSourceContextRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
+                                                 objcPrefix:@"GPB"
+                                                     syntax:GPBFileSyntaxProto3];
+  }
+  return descriptor;
+}
+
+#pragma mark - GPBSourceContext
+
+@implementation GPBSourceContext
+
+@dynamic fileName;
+
+typedef struct GPBSourceContext__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *fileName;
+} GPBSourceContext__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "fileName",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBSourceContext_FieldNumber_FileName,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBSourceContext__storage_, fileName),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBSourceContext class]
+                                     rootClass:[GPBSourceContextRoot class]
+                                          file:GPBSourceContextRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBSourceContext__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Struct.pbobjc.h b/iOS/Pods/Protobuf/objectivec/google/protobuf/Struct.pbobjc.h
new file mode 100644 (file)
index 0000000..fb20425
--- /dev/null
@@ -0,0 +1,204 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/struct.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBDescriptor.h>
+ #import <Protobuf/GPBMessage.h>
+ #import <Protobuf/GPBRootObject.h>
+#else
+ #import "GPBDescriptor.h"
+ #import "GPBMessage.h"
+ #import "GPBRootObject.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+@class GPBListValue;
+@class GPBStruct;
+@class GPBValue;
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - Enum GPBNullValue
+
+/**
+ * `NullValue` is a singleton enumeration to represent the null value for the
+ * `Value` type union.
+ *
+ *  The JSON representation for `NullValue` is JSON `null`.
+ **/
+typedef GPB_ENUM(GPBNullValue) {
+  /**
+   * Value used if any message's field encounters a value that is not defined
+   * by this enum. The message will also have C functions to get/set the rawValue
+   * of the field.
+   **/
+  GPBNullValue_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
+  /** Null value. */
+  GPBNullValue_NullValue = 0,
+};
+
+GPBEnumDescriptor *GPBNullValue_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GPBNullValue_IsValidValue(int32_t value);
+
+#pragma mark - GPBStructRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GPBStructRoot : GPBRootObject
+@end
+
+#pragma mark - GPBStruct
+
+typedef GPB_ENUM(GPBStruct_FieldNumber) {
+  GPBStruct_FieldNumber_Fields = 1,
+};
+
+/**
+ * `Struct` represents a structured data value, consisting of fields
+ * which map to dynamically typed values. In some languages, `Struct`
+ * might be supported by a native representation. For example, in
+ * scripting languages like JS a struct is represented as an
+ * object. The details of that representation are described together
+ * with the proto support for the language.
+ *
+ * The JSON representation for `Struct` is JSON object.
+ **/
+@interface GPBStruct : GPBMessage
+
+/** Unordered map of dynamically typed values. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableDictionary<NSString*, GPBValue*> *fields;
+/** The number of items in @c fields without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger fields_Count;
+
+@end
+
+#pragma mark - GPBValue
+
+typedef GPB_ENUM(GPBValue_FieldNumber) {
+  GPBValue_FieldNumber_NullValue = 1,
+  GPBValue_FieldNumber_NumberValue = 2,
+  GPBValue_FieldNumber_StringValue = 3,
+  GPBValue_FieldNumber_BoolValue = 4,
+  GPBValue_FieldNumber_StructValue = 5,
+  GPBValue_FieldNumber_ListValue = 6,
+};
+
+typedef GPB_ENUM(GPBValue_Kind_OneOfCase) {
+  GPBValue_Kind_OneOfCase_GPBUnsetOneOfCase = 0,
+  GPBValue_Kind_OneOfCase_NullValue = 1,
+  GPBValue_Kind_OneOfCase_NumberValue = 2,
+  GPBValue_Kind_OneOfCase_StringValue = 3,
+  GPBValue_Kind_OneOfCase_BoolValue = 4,
+  GPBValue_Kind_OneOfCase_StructValue = 5,
+  GPBValue_Kind_OneOfCase_ListValue = 6,
+};
+
+/**
+ * `Value` represents a dynamically typed value which can be either
+ * null, a number, a string, a boolean, a recursive struct value, or a
+ * list of values. A producer of value is expected to set one of that
+ * variants, absence of any variant indicates an error.
+ *
+ * The JSON representation for `Value` is JSON value.
+ **/
+@interface GPBValue : GPBMessage
+
+/** The kind of value. */
+@property(nonatomic, readonly) GPBValue_Kind_OneOfCase kindOneOfCase;
+
+/** Represents a null value. */
+@property(nonatomic, readwrite) GPBNullValue nullValue;
+
+/** Represents a double value. */
+@property(nonatomic, readwrite) double numberValue;
+
+/** Represents a string value. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *stringValue;
+
+/** Represents a boolean value. */
+@property(nonatomic, readwrite) BOOL boolValue;
+
+/** Represents a structured value. */
+@property(nonatomic, readwrite, strong, null_resettable) GPBStruct *structValue;
+
+/** Represents a repeated `Value`. */
+@property(nonatomic, readwrite, strong, null_resettable) GPBListValue *listValue;
+
+@end
+
+/**
+ * Fetches the raw value of a @c GPBValue's @c nullValue property, even
+ * if the value was not defined by the enum at the time the code was generated.
+ **/
+int32_t GPBValue_NullValue_RawValue(GPBValue *message);
+/**
+ * Sets the raw value of an @c GPBValue's @c nullValue property, allowing
+ * it to be set to a value that was not defined by the enum at the time the code
+ * was generated.
+ **/
+void SetGPBValue_NullValue_RawValue(GPBValue *message, int32_t value);
+
+/**
+ * Clears whatever value was set for the oneof 'kind'.
+ **/
+void GPBValue_ClearKindOneOfCase(GPBValue *message);
+
+#pragma mark - GPBListValue
+
+typedef GPB_ENUM(GPBListValue_FieldNumber) {
+  GPBListValue_FieldNumber_ValuesArray = 1,
+};
+
+/**
+ * `ListValue` is a wrapper around a repeated field of values.
+ *
+ * The JSON representation for `ListValue` is JSON array.
+ **/
+@interface GPBListValue : GPBMessage
+
+/** Repeated field of dynamically typed values. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBValue*> *valuesArray;
+/** The number of items in @c valuesArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger valuesArray_Count;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Struct.pbobjc.m b/iOS/Pods/Protobuf/objectivec/google/protobuf/Struct.pbobjc.m
new file mode 100644 (file)
index 0000000..816fd6d
--- /dev/null
@@ -0,0 +1,296 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/struct.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+#import <stdatomic.h>
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/Struct.pbobjc.h>
+#else
+ #import "google/protobuf/Struct.pbobjc.h"
+#endif
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+#pragma mark - GPBStructRoot
+
+@implementation GPBStructRoot
+
+// No extensions in the file and no imports, so no need to generate
+// +extensionRegistry.
+
+@end
+
+#pragma mark - GPBStructRoot_FileDescriptor
+
+static GPBFileDescriptor *GPBStructRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
+                                                 objcPrefix:@"GPB"
+                                                     syntax:GPBFileSyntaxProto3];
+  }
+  return descriptor;
+}
+
+#pragma mark - Enum GPBNullValue
+
+GPBEnumDescriptor *GPBNullValue_EnumDescriptor(void) {
+  static _Atomic(GPBEnumDescriptor*) descriptor = nil;
+  if (!descriptor) {
+    static const char *valueNames =
+        "NullValue\000";
+    static const int32_t values[] = {
+        GPBNullValue_NullValue,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBNullValue)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GPBNullValue_IsValidValue];
+    GPBEnumDescriptor *expected = nil;
+    if (!atomic_compare_exchange_strong(&descriptor, &expected, worker)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GPBNullValue_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GPBNullValue_NullValue:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GPBStruct
+
+@implementation GPBStruct
+
+@dynamic fields, fields_Count;
+
+typedef struct GPBStruct__storage_ {
+  uint32_t _has_storage_[1];
+  NSMutableDictionary *fields;
+} GPBStruct__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "fields",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBValue),
+        .number = GPBStruct_FieldNumber_Fields,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBStruct__storage_, fields),
+        .flags = GPBFieldMapKeyString,
+        .dataType = GPBDataTypeMessage,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBStruct class]
+                                     rootClass:[GPBStructRoot class]
+                                          file:GPBStructRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBStruct__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GPBValue
+
+@implementation GPBValue
+
+@dynamic kindOneOfCase;
+@dynamic nullValue;
+@dynamic numberValue;
+@dynamic stringValue;
+@dynamic boolValue;
+@dynamic structValue;
+@dynamic listValue;
+
+typedef struct GPBValue__storage_ {
+  uint32_t _has_storage_[2];
+  GPBNullValue nullValue;
+  NSString *stringValue;
+  GPBStruct *structValue;
+  GPBListValue *listValue;
+  double numberValue;
+} GPBValue__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "nullValue",
+        .dataTypeSpecific.enumDescFunc = GPBNullValue_EnumDescriptor,
+        .number = GPBValue_FieldNumber_NullValue,
+        .hasIndex = -1,
+        .offset = (uint32_t)offsetof(GPBValue__storage_, nullValue),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "numberValue",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBValue_FieldNumber_NumberValue,
+        .hasIndex = -1,
+        .offset = (uint32_t)offsetof(GPBValue__storage_, numberValue),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeDouble,
+      },
+      {
+        .name = "stringValue",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBValue_FieldNumber_StringValue,
+        .hasIndex = -1,
+        .offset = (uint32_t)offsetof(GPBValue__storage_, stringValue),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "boolValue",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBValue_FieldNumber_BoolValue,
+        .hasIndex = -1,
+        .offset = 0,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "structValue",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBStruct),
+        .number = GPBValue_FieldNumber_StructValue,
+        .hasIndex = -1,
+        .offset = (uint32_t)offsetof(GPBValue__storage_, structValue),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "listValue",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBListValue),
+        .number = GPBValue_FieldNumber_ListValue,
+        .hasIndex = -1,
+        .offset = (uint32_t)offsetof(GPBValue__storage_, listValue),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBValue class]
+                                     rootClass:[GPBStructRoot class]
+                                          file:GPBStructRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBValue__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    static const char *oneofs[] = {
+      "kind",
+    };
+    [localDescriptor setupOneofs:oneofs
+                           count:(uint32_t)(sizeof(oneofs) / sizeof(char*))
+                   firstHasIndex:-1];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+int32_t GPBValue_NullValue_RawValue(GPBValue *message) {
+  GPBDescriptor *descriptor = [GPBValue descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBValue_FieldNumber_NullValue];
+  return GPBGetMessageInt32Field(message, field);
+}
+
+void SetGPBValue_NullValue_RawValue(GPBValue *message, int32_t value) {
+  GPBDescriptor *descriptor = [GPBValue descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBValue_FieldNumber_NullValue];
+  GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax);
+}
+
+void GPBValue_ClearKindOneOfCase(GPBValue *message) {
+  GPBDescriptor *descriptor = [message descriptor];
+  GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:0];
+  GPBMaybeClearOneof(message, oneof, -1, 0);
+}
+#pragma mark - GPBListValue
+
+@implementation GPBListValue
+
+@dynamic valuesArray, valuesArray_Count;
+
+typedef struct GPBListValue__storage_ {
+  uint32_t _has_storage_[1];
+  NSMutableArray *valuesArray;
+} GPBListValue__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "valuesArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBValue),
+        .number = GPBListValue_FieldNumber_ValuesArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBListValue__storage_, valuesArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBListValue class]
+                                     rootClass:[GPBStructRoot class]
+                                          file:GPBStructRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBListValue__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Timestamp.pbobjc.h b/iOS/Pods/Protobuf/objectivec/google/protobuf/Timestamp.pbobjc.h
new file mode 100644 (file)
index 0000000..2c4b8b2
--- /dev/null
@@ -0,0 +1,163 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/timestamp.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBDescriptor.h>
+ #import <Protobuf/GPBMessage.h>
+ #import <Protobuf/GPBRootObject.h>
+#else
+ #import "GPBDescriptor.h"
+ #import "GPBMessage.h"
+ #import "GPBRootObject.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - GPBTimestampRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GPBTimestampRoot : GPBRootObject
+@end
+
+#pragma mark - GPBTimestamp
+
+typedef GPB_ENUM(GPBTimestamp_FieldNumber) {
+  GPBTimestamp_FieldNumber_Seconds = 1,
+  GPBTimestamp_FieldNumber_Nanos = 2,
+};
+
+/**
+ * A Timestamp represents a point in time independent of any time zone
+ * or calendar, represented as seconds and fractions of seconds at
+ * nanosecond resolution in UTC Epoch time. It is encoded using the
+ * Proleptic Gregorian Calendar which extends the Gregorian calendar
+ * backwards to year one. It is encoded assuming all minutes are 60
+ * seconds long, i.e. leap seconds are "smeared" so that no leap second
+ * table is needed for interpretation. Range is from
+ * 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
+ * By restricting to that range, we ensure that we can convert to
+ * and from  RFC 3339 date strings.
+ * See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
+ *
+ * # Examples
+ *
+ * Example 1: Compute Timestamp from POSIX `time()`.
+ *
+ *     Timestamp timestamp;
+ *     timestamp.set_seconds(time(NULL));
+ *     timestamp.set_nanos(0);
+ *
+ * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+ *
+ *     struct timeval tv;
+ *     gettimeofday(&tv, NULL);
+ *
+ *     Timestamp timestamp;
+ *     timestamp.set_seconds(tv.tv_sec);
+ *     timestamp.set_nanos(tv.tv_usec * 1000);
+ *
+ * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+ *
+ *     FILETIME ft;
+ *     GetSystemTimeAsFileTime(&ft);
+ *     UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+ *
+ *     // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+ *     // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+ *     Timestamp timestamp;
+ *     timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+ *     timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+ *
+ * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+ *
+ *     long millis = System.currentTimeMillis();
+ *
+ *     Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+ *         .setNanos((int) ((millis % 1000) * 1000000)).build();
+ *
+ *
+ * Example 5: Compute Timestamp from current time in Python.
+ *
+ *     timestamp = Timestamp()
+ *     timestamp.GetCurrentTime()
+ *
+ * # JSON Mapping
+ *
+ * In JSON format, the Timestamp type is encoded as a string in the
+ * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
+ * format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
+ * where {year} is always expressed using four digits while {month}, {day},
+ * {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
+ * seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
+ * are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
+ * is required. A proto3 JSON serializer should always use UTC (as indicated by
+ * "Z") when printing the Timestamp type and a proto3 JSON parser should be
+ * able to accept both UTC and other timezones (as indicated by an offset).
+ *
+ * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
+ * 01:30 UTC on January 15, 2017.
+ *
+ * In JavaScript, one can convert a Date object to this format using the
+ * standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
+ * method. In Python, a standard `datetime.datetime` object can be converted
+ * to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
+ * with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
+ * can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
+ * http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
+ * ) to obtain a formatter capable of generating timestamps in this format.
+ **/
+@interface GPBTimestamp : GPBMessage
+
+/**
+ * Represents seconds of UTC time since Unix epoch
+ * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
+ * 9999-12-31T23:59:59Z inclusive.
+ **/
+@property(nonatomic, readwrite) int64_t seconds;
+
+/**
+ * Non-negative fractions of a second at nanosecond resolution. Negative
+ * second values with fractions must still have non-negative nanos values
+ * that count forward in time. Must be from 0 to 999,999,999
+ * inclusive.
+ **/
+@property(nonatomic, readwrite) int32_t nanos;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Timestamp.pbobjc.m b/iOS/Pods/Protobuf/objectivec/google/protobuf/Timestamp.pbobjc.m
new file mode 100644 (file)
index 0000000..4ab159f
--- /dev/null
@@ -0,0 +1,107 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/timestamp.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/Timestamp.pbobjc.h>
+#else
+ #import "google/protobuf/Timestamp.pbobjc.h"
+#endif
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GPBTimestampRoot
+
+@implementation GPBTimestampRoot
+
+// No extensions in the file and no imports, so no need to generate
+// +extensionRegistry.
+
+@end
+
+#pragma mark - GPBTimestampRoot_FileDescriptor
+
+static GPBFileDescriptor *GPBTimestampRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
+                                                 objcPrefix:@"GPB"
+                                                     syntax:GPBFileSyntaxProto3];
+  }
+  return descriptor;
+}
+
+#pragma mark - GPBTimestamp
+
+@implementation GPBTimestamp
+
+@dynamic seconds;
+@dynamic nanos;
+
+typedef struct GPBTimestamp__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t nanos;
+  int64_t seconds;
+} GPBTimestamp__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "seconds",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBTimestamp_FieldNumber_Seconds,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBTimestamp__storage_, seconds),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+      {
+        .name = "nanos",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBTimestamp_FieldNumber_Nanos,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBTimestamp__storage_, nanos),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBTimestamp class]
+                                     rootClass:[GPBTimestampRoot class]
+                                          file:GPBTimestampRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBTimestamp__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Type.pbobjc.h b/iOS/Pods/Protobuf/objectivec/google/protobuf/Type.pbobjc.h
new file mode 100644 (file)
index 0000000..e14d15d
--- /dev/null
@@ -0,0 +1,444 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/type.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBDescriptor.h>
+ #import <Protobuf/GPBMessage.h>
+ #import <Protobuf/GPBRootObject.h>
+#else
+ #import "GPBDescriptor.h"
+ #import "GPBMessage.h"
+ #import "GPBRootObject.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+@class GPBAny;
+@class GPBEnumValue;
+@class GPBField;
+@class GPBOption;
+@class GPBSourceContext;
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - Enum GPBSyntax
+
+/** The syntax in which a protocol buffer element is defined. */
+typedef GPB_ENUM(GPBSyntax) {
+  /**
+   * Value used if any message's field encounters a value that is not defined
+   * by this enum. The message will also have C functions to get/set the rawValue
+   * of the field.
+   **/
+  GPBSyntax_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
+  /** Syntax `proto2`. */
+  GPBSyntax_SyntaxProto2 = 0,
+
+  /** Syntax `proto3`. */
+  GPBSyntax_SyntaxProto3 = 1,
+};
+
+GPBEnumDescriptor *GPBSyntax_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GPBSyntax_IsValidValue(int32_t value);
+
+#pragma mark - Enum GPBField_Kind
+
+/** Basic field types. */
+typedef GPB_ENUM(GPBField_Kind) {
+  /**
+   * Value used if any message's field encounters a value that is not defined
+   * by this enum. The message will also have C functions to get/set the rawValue
+   * of the field.
+   **/
+  GPBField_Kind_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
+  /** Field type unknown. */
+  GPBField_Kind_TypeUnknown = 0,
+
+  /** Field type double. */
+  GPBField_Kind_TypeDouble = 1,
+
+  /** Field type float. */
+  GPBField_Kind_TypeFloat = 2,
+
+  /** Field type int64. */
+  GPBField_Kind_TypeInt64 = 3,
+
+  /** Field type uint64. */
+  GPBField_Kind_TypeUint64 = 4,
+
+  /** Field type int32. */
+  GPBField_Kind_TypeInt32 = 5,
+
+  /** Field type fixed64. */
+  GPBField_Kind_TypeFixed64 = 6,
+
+  /** Field type fixed32. */
+  GPBField_Kind_TypeFixed32 = 7,
+
+  /** Field type bool. */
+  GPBField_Kind_TypeBool = 8,
+
+  /** Field type string. */
+  GPBField_Kind_TypeString = 9,
+
+  /** Field type group. Proto2 syntax only, and deprecated. */
+  GPBField_Kind_TypeGroup = 10,
+
+  /** Field type message. */
+  GPBField_Kind_TypeMessage = 11,
+
+  /** Field type bytes. */
+  GPBField_Kind_TypeBytes = 12,
+
+  /** Field type uint32. */
+  GPBField_Kind_TypeUint32 = 13,
+
+  /** Field type enum. */
+  GPBField_Kind_TypeEnum = 14,
+
+  /** Field type sfixed32. */
+  GPBField_Kind_TypeSfixed32 = 15,
+
+  /** Field type sfixed64. */
+  GPBField_Kind_TypeSfixed64 = 16,
+
+  /** Field type sint32. */
+  GPBField_Kind_TypeSint32 = 17,
+
+  /** Field type sint64. */
+  GPBField_Kind_TypeSint64 = 18,
+};
+
+GPBEnumDescriptor *GPBField_Kind_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GPBField_Kind_IsValidValue(int32_t value);
+
+#pragma mark - Enum GPBField_Cardinality
+
+/** Whether a field is optional, required, or repeated. */
+typedef GPB_ENUM(GPBField_Cardinality) {
+  /**
+   * Value used if any message's field encounters a value that is not defined
+   * by this enum. The message will also have C functions to get/set the rawValue
+   * of the field.
+   **/
+  GPBField_Cardinality_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
+  /** For fields with unknown cardinality. */
+  GPBField_Cardinality_CardinalityUnknown = 0,
+
+  /** For optional fields. */
+  GPBField_Cardinality_CardinalityOptional = 1,
+
+  /** For required fields. Proto2 syntax only. */
+  GPBField_Cardinality_CardinalityRequired = 2,
+
+  /** For repeated fields. */
+  GPBField_Cardinality_CardinalityRepeated = 3,
+};
+
+GPBEnumDescriptor *GPBField_Cardinality_EnumDescriptor(void);
+
+/**
+ * Checks to see if the given value is defined by the enum or was not known at
+ * the time this source was generated.
+ **/
+BOOL GPBField_Cardinality_IsValidValue(int32_t value);
+
+#pragma mark - GPBTypeRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GPBTypeRoot : GPBRootObject
+@end
+
+#pragma mark - GPBType
+
+typedef GPB_ENUM(GPBType_FieldNumber) {
+  GPBType_FieldNumber_Name = 1,
+  GPBType_FieldNumber_FieldsArray = 2,
+  GPBType_FieldNumber_OneofsArray = 3,
+  GPBType_FieldNumber_OptionsArray = 4,
+  GPBType_FieldNumber_SourceContext = 5,
+  GPBType_FieldNumber_Syntax = 6,
+};
+
+/**
+ * A protocol buffer message type.
+ **/
+@interface GPBType : GPBMessage
+
+/** The fully qualified message name. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
+
+/** The list of fields. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBField*> *fieldsArray;
+/** The number of items in @c fieldsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger fieldsArray_Count;
+
+/** The list of types appearing in `oneof` definitions in this type. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *oneofsArray;
+/** The number of items in @c oneofsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger oneofsArray_Count;
+
+/** The protocol buffer options. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
+/** The number of items in @c optionsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger optionsArray_Count;
+
+/** The source context. */
+@property(nonatomic, readwrite, strong, null_resettable) GPBSourceContext *sourceContext;
+/** Test to see if @c sourceContext has been set. */
+@property(nonatomic, readwrite) BOOL hasSourceContext;
+
+/** The source syntax. */
+@property(nonatomic, readwrite) GPBSyntax syntax;
+
+@end
+
+/**
+ * Fetches the raw value of a @c GPBType's @c syntax property, even
+ * if the value was not defined by the enum at the time the code was generated.
+ **/
+int32_t GPBType_Syntax_RawValue(GPBType *message);
+/**
+ * Sets the raw value of an @c GPBType's @c syntax property, allowing
+ * it to be set to a value that was not defined by the enum at the time the code
+ * was generated.
+ **/
+void SetGPBType_Syntax_RawValue(GPBType *message, int32_t value);
+
+#pragma mark - GPBField
+
+typedef GPB_ENUM(GPBField_FieldNumber) {
+  GPBField_FieldNumber_Kind = 1,
+  GPBField_FieldNumber_Cardinality = 2,
+  GPBField_FieldNumber_Number = 3,
+  GPBField_FieldNumber_Name = 4,
+  GPBField_FieldNumber_TypeURL = 6,
+  GPBField_FieldNumber_OneofIndex = 7,
+  GPBField_FieldNumber_Packed = 8,
+  GPBField_FieldNumber_OptionsArray = 9,
+  GPBField_FieldNumber_JsonName = 10,
+  GPBField_FieldNumber_DefaultValue = 11,
+};
+
+/**
+ * A single field of a message type.
+ **/
+@interface GPBField : GPBMessage
+
+/** The field type. */
+@property(nonatomic, readwrite) GPBField_Kind kind;
+
+/** The field cardinality. */
+@property(nonatomic, readwrite) GPBField_Cardinality cardinality;
+
+/** The field number. */
+@property(nonatomic, readwrite) int32_t number;
+
+/** The field name. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
+
+/**
+ * The field type URL, without the scheme, for message or enumeration
+ * types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`.
+ **/
+@property(nonatomic, readwrite, copy, null_resettable) NSString *typeURL;
+
+/**
+ * The index of the field type in `Type.oneofs`, for message or enumeration
+ * types. The first type has index 1; zero means the type is not in the list.
+ **/
+@property(nonatomic, readwrite) int32_t oneofIndex;
+
+/** Whether to use alternative packed wire representation. */
+@property(nonatomic, readwrite) BOOL packed;
+
+/** The protocol buffer options. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
+/** The number of items in @c optionsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger optionsArray_Count;
+
+/** The field JSON name. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *jsonName;
+
+/** The string value of the default value of this field. Proto2 syntax only. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *defaultValue;
+
+@end
+
+/**
+ * Fetches the raw value of a @c GPBField's @c kind property, even
+ * if the value was not defined by the enum at the time the code was generated.
+ **/
+int32_t GPBField_Kind_RawValue(GPBField *message);
+/**
+ * Sets the raw value of an @c GPBField's @c kind property, allowing
+ * it to be set to a value that was not defined by the enum at the time the code
+ * was generated.
+ **/
+void SetGPBField_Kind_RawValue(GPBField *message, int32_t value);
+
+/**
+ * Fetches the raw value of a @c GPBField's @c cardinality property, even
+ * if the value was not defined by the enum at the time the code was generated.
+ **/
+int32_t GPBField_Cardinality_RawValue(GPBField *message);
+/**
+ * Sets the raw value of an @c GPBField's @c cardinality property, allowing
+ * it to be set to a value that was not defined by the enum at the time the code
+ * was generated.
+ **/
+void SetGPBField_Cardinality_RawValue(GPBField *message, int32_t value);
+
+#pragma mark - GPBEnum
+
+typedef GPB_ENUM(GPBEnum_FieldNumber) {
+  GPBEnum_FieldNumber_Name = 1,
+  GPBEnum_FieldNumber_EnumvalueArray = 2,
+  GPBEnum_FieldNumber_OptionsArray = 3,
+  GPBEnum_FieldNumber_SourceContext = 4,
+  GPBEnum_FieldNumber_Syntax = 5,
+};
+
+/**
+ * Enum type definition.
+ **/
+@interface GPBEnum : GPBMessage
+
+/** Enum type name. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
+
+/** Enum value definitions. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBEnumValue*> *enumvalueArray;
+/** The number of items in @c enumvalueArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger enumvalueArray_Count;
+
+/** Protocol buffer options. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
+/** The number of items in @c optionsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger optionsArray_Count;
+
+/** The source context. */
+@property(nonatomic, readwrite, strong, null_resettable) GPBSourceContext *sourceContext;
+/** Test to see if @c sourceContext has been set. */
+@property(nonatomic, readwrite) BOOL hasSourceContext;
+
+/** The source syntax. */
+@property(nonatomic, readwrite) GPBSyntax syntax;
+
+@end
+
+/**
+ * Fetches the raw value of a @c GPBEnum's @c syntax property, even
+ * if the value was not defined by the enum at the time the code was generated.
+ **/
+int32_t GPBEnum_Syntax_RawValue(GPBEnum *message);
+/**
+ * Sets the raw value of an @c GPBEnum's @c syntax property, allowing
+ * it to be set to a value that was not defined by the enum at the time the code
+ * was generated.
+ **/
+void SetGPBEnum_Syntax_RawValue(GPBEnum *message, int32_t value);
+
+#pragma mark - GPBEnumValue
+
+typedef GPB_ENUM(GPBEnumValue_FieldNumber) {
+  GPBEnumValue_FieldNumber_Name = 1,
+  GPBEnumValue_FieldNumber_Number = 2,
+  GPBEnumValue_FieldNumber_OptionsArray = 3,
+};
+
+/**
+ * Enum value definition.
+ **/
+@interface GPBEnumValue : GPBMessage
+
+/** Enum value name. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
+
+/** Enum value number. */
+@property(nonatomic, readwrite) int32_t number;
+
+/** Protocol buffer options. */
+@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
+/** The number of items in @c optionsArray without causing the array to be created. */
+@property(nonatomic, readonly) NSUInteger optionsArray_Count;
+
+@end
+
+#pragma mark - GPBOption
+
+typedef GPB_ENUM(GPBOption_FieldNumber) {
+  GPBOption_FieldNumber_Name = 1,
+  GPBOption_FieldNumber_Value = 2,
+};
+
+/**
+ * A protocol buffer option, which can be attached to a message, field,
+ * enumeration, etc.
+ **/
+@interface GPBOption : GPBMessage
+
+/**
+ * The option's name. For protobuf built-in options (options defined in
+ * descriptor.proto), this is the short name. For example, `"map_entry"`.
+ * For custom options, it should be the fully-qualified name. For example,
+ * `"google.api.http"`.
+ **/
+@property(nonatomic, readwrite, copy, null_resettable) NSString *name;
+
+/**
+ * The option's value packed in an Any message. If the value is a primitive,
+ * the corresponding wrapper type defined in google/protobuf/wrappers.proto
+ * should be used. If the value is an enum, it should be stored as an int32
+ * value using the google.protobuf.Int32Value type.
+ **/
+@property(nonatomic, readwrite, strong, null_resettable) GPBAny *value;
+/** Test to see if @c value has been set. */
+@property(nonatomic, readwrite) BOOL hasValue;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Type.pbobjc.m b/iOS/Pods/Protobuf/objectivec/google/protobuf/Type.pbobjc.m
new file mode 100644 (file)
index 0000000..bb64d87
--- /dev/null
@@ -0,0 +1,706 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/type.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+#import <stdatomic.h>
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/Type.pbobjc.h>
+ #import <Protobuf/Any.pbobjc.h>
+ #import <Protobuf/SourceContext.pbobjc.h>
+#else
+ #import "google/protobuf/Type.pbobjc.h"
+ #import "google/protobuf/Any.pbobjc.h"
+ #import "google/protobuf/SourceContext.pbobjc.h"
+#endif
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GPBTypeRoot
+
+@implementation GPBTypeRoot
+
+// No extensions in the file and none of the imports (direct or indirect)
+// defined extensions, so no need to generate +extensionRegistry.
+
+@end
+
+#pragma mark - GPBTypeRoot_FileDescriptor
+
+static GPBFileDescriptor *GPBTypeRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
+                                                 objcPrefix:@"GPB"
+                                                     syntax:GPBFileSyntaxProto3];
+  }
+  return descriptor;
+}
+
+#pragma mark - Enum GPBSyntax
+
+GPBEnumDescriptor *GPBSyntax_EnumDescriptor(void) {
+  static _Atomic(GPBEnumDescriptor*) descriptor = nil;
+  if (!descriptor) {
+    static const char *valueNames =
+        "SyntaxProto2\000SyntaxProto3\000";
+    static const int32_t values[] = {
+        GPBSyntax_SyntaxProto2,
+        GPBSyntax_SyntaxProto3,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBSyntax)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GPBSyntax_IsValidValue];
+    GPBEnumDescriptor *expected = nil;
+    if (!atomic_compare_exchange_strong(&descriptor, &expected, worker)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GPBSyntax_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GPBSyntax_SyntaxProto2:
+    case GPBSyntax_SyntaxProto3:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GPBType
+
+@implementation GPBType
+
+@dynamic name;
+@dynamic fieldsArray, fieldsArray_Count;
+@dynamic oneofsArray, oneofsArray_Count;
+@dynamic optionsArray, optionsArray_Count;
+@dynamic hasSourceContext, sourceContext;
+@dynamic syntax;
+
+typedef struct GPBType__storage_ {
+  uint32_t _has_storage_[1];
+  GPBSyntax syntax;
+  NSString *name;
+  NSMutableArray *fieldsArray;
+  NSMutableArray *oneofsArray;
+  NSMutableArray *optionsArray;
+  GPBSourceContext *sourceContext;
+} GPBType__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "name",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBType_FieldNumber_Name,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBType__storage_, name),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "fieldsArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBField),
+        .number = GPBType_FieldNumber_FieldsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBType__storage_, fieldsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "oneofsArray",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBType_FieldNumber_OneofsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBType__storage_, oneofsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "optionsArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBOption),
+        .number = GPBType_FieldNumber_OptionsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBType__storage_, optionsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "sourceContext",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceContext),
+        .number = GPBType_FieldNumber_SourceContext,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBType__storage_, sourceContext),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "syntax",
+        .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor,
+        .number = GPBType_FieldNumber_Syntax,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GPBType__storage_, syntax),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBType class]
+                                     rootClass:[GPBTypeRoot class]
+                                          file:GPBTypeRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBType__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+int32_t GPBType_Syntax_RawValue(GPBType *message) {
+  GPBDescriptor *descriptor = [GPBType descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBType_FieldNumber_Syntax];
+  return GPBGetMessageInt32Field(message, field);
+}
+
+void SetGPBType_Syntax_RawValue(GPBType *message, int32_t value) {
+  GPBDescriptor *descriptor = [GPBType descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBType_FieldNumber_Syntax];
+  GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax);
+}
+
+#pragma mark - GPBField
+
+@implementation GPBField
+
+@dynamic kind;
+@dynamic cardinality;
+@dynamic number;
+@dynamic name;
+@dynamic typeURL;
+@dynamic oneofIndex;
+@dynamic packed;
+@dynamic optionsArray, optionsArray_Count;
+@dynamic jsonName;
+@dynamic defaultValue;
+
+typedef struct GPBField__storage_ {
+  uint32_t _has_storage_[1];
+  GPBField_Kind kind;
+  GPBField_Cardinality cardinality;
+  int32_t number;
+  int32_t oneofIndex;
+  NSString *name;
+  NSString *typeURL;
+  NSMutableArray *optionsArray;
+  NSString *jsonName;
+  NSString *defaultValue;
+} GPBField__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "kind",
+        .dataTypeSpecific.enumDescFunc = GPBField_Kind_EnumDescriptor,
+        .number = GPBField_FieldNumber_Kind,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBField__storage_, kind),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "cardinality",
+        .dataTypeSpecific.enumDescFunc = GPBField_Cardinality_EnumDescriptor,
+        .number = GPBField_FieldNumber_Cardinality,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBField__storage_, cardinality),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+      {
+        .name = "number",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBField_FieldNumber_Number,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GPBField__storage_, number),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "name",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBField_FieldNumber_Name,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GPBField__storage_, name),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "typeURL",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBField_FieldNumber_TypeURL,
+        .hasIndex = 4,
+        .offset = (uint32_t)offsetof(GPBField__storage_, typeURL),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom),
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "oneofIndex",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBField_FieldNumber_OneofIndex,
+        .hasIndex = 5,
+        .offset = (uint32_t)offsetof(GPBField__storage_, oneofIndex),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "packed",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBField_FieldNumber_Packed,
+        .hasIndex = 6,
+        .offset = 7,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+      {
+        .name = "optionsArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBOption),
+        .number = GPBField_FieldNumber_OptionsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBField__storage_, optionsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "jsonName",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBField_FieldNumber_JsonName,
+        .hasIndex = 8,
+        .offset = (uint32_t)offsetof(GPBField__storage_, jsonName),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "defaultValue",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBField_FieldNumber_DefaultValue,
+        .hasIndex = 9,
+        .offset = (uint32_t)offsetof(GPBField__storage_, defaultValue),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBField class]
+                                     rootClass:[GPBTypeRoot class]
+                                          file:GPBTypeRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBField__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+#if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    static const char *extraTextFormatInfo =
+        "\001\006\004\241!!\000";
+    [localDescriptor setupExtraTextInfo:extraTextFormatInfo];
+#endif  // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+int32_t GPBField_Kind_RawValue(GPBField *message) {
+  GPBDescriptor *descriptor = [GPBField descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBField_FieldNumber_Kind];
+  return GPBGetMessageInt32Field(message, field);
+}
+
+void SetGPBField_Kind_RawValue(GPBField *message, int32_t value) {
+  GPBDescriptor *descriptor = [GPBField descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBField_FieldNumber_Kind];
+  GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax);
+}
+
+int32_t GPBField_Cardinality_RawValue(GPBField *message) {
+  GPBDescriptor *descriptor = [GPBField descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBField_FieldNumber_Cardinality];
+  return GPBGetMessageInt32Field(message, field);
+}
+
+void SetGPBField_Cardinality_RawValue(GPBField *message, int32_t value) {
+  GPBDescriptor *descriptor = [GPBField descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBField_FieldNumber_Cardinality];
+  GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax);
+}
+
+#pragma mark - Enum GPBField_Kind
+
+GPBEnumDescriptor *GPBField_Kind_EnumDescriptor(void) {
+  static _Atomic(GPBEnumDescriptor*) descriptor = nil;
+  if (!descriptor) {
+    static const char *valueNames =
+        "TypeUnknown\000TypeDouble\000TypeFloat\000TypeInt"
+        "64\000TypeUint64\000TypeInt32\000TypeFixed64\000Type"
+        "Fixed32\000TypeBool\000TypeString\000TypeGroup\000Ty"
+        "peMessage\000TypeBytes\000TypeUint32\000TypeEnum\000"
+        "TypeSfixed32\000TypeSfixed64\000TypeSint32\000Typ"
+        "eSint64\000";
+    static const int32_t values[] = {
+        GPBField_Kind_TypeUnknown,
+        GPBField_Kind_TypeDouble,
+        GPBField_Kind_TypeFloat,
+        GPBField_Kind_TypeInt64,
+        GPBField_Kind_TypeUint64,
+        GPBField_Kind_TypeInt32,
+        GPBField_Kind_TypeFixed64,
+        GPBField_Kind_TypeFixed32,
+        GPBField_Kind_TypeBool,
+        GPBField_Kind_TypeString,
+        GPBField_Kind_TypeGroup,
+        GPBField_Kind_TypeMessage,
+        GPBField_Kind_TypeBytes,
+        GPBField_Kind_TypeUint32,
+        GPBField_Kind_TypeEnum,
+        GPBField_Kind_TypeSfixed32,
+        GPBField_Kind_TypeSfixed64,
+        GPBField_Kind_TypeSint32,
+        GPBField_Kind_TypeSint64,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBField_Kind)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GPBField_Kind_IsValidValue];
+    GPBEnumDescriptor *expected = nil;
+    if (!atomic_compare_exchange_strong(&descriptor, &expected, worker)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GPBField_Kind_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GPBField_Kind_TypeUnknown:
+    case GPBField_Kind_TypeDouble:
+    case GPBField_Kind_TypeFloat:
+    case GPBField_Kind_TypeInt64:
+    case GPBField_Kind_TypeUint64:
+    case GPBField_Kind_TypeInt32:
+    case GPBField_Kind_TypeFixed64:
+    case GPBField_Kind_TypeFixed32:
+    case GPBField_Kind_TypeBool:
+    case GPBField_Kind_TypeString:
+    case GPBField_Kind_TypeGroup:
+    case GPBField_Kind_TypeMessage:
+    case GPBField_Kind_TypeBytes:
+    case GPBField_Kind_TypeUint32:
+    case GPBField_Kind_TypeEnum:
+    case GPBField_Kind_TypeSfixed32:
+    case GPBField_Kind_TypeSfixed64:
+    case GPBField_Kind_TypeSint32:
+    case GPBField_Kind_TypeSint64:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - Enum GPBField_Cardinality
+
+GPBEnumDescriptor *GPBField_Cardinality_EnumDescriptor(void) {
+  static _Atomic(GPBEnumDescriptor*) descriptor = nil;
+  if (!descriptor) {
+    static const char *valueNames =
+        "CardinalityUnknown\000CardinalityOptional\000C"
+        "ardinalityRequired\000CardinalityRepeated\000";
+    static const int32_t values[] = {
+        GPBField_Cardinality_CardinalityUnknown,
+        GPBField_Cardinality_CardinalityOptional,
+        GPBField_Cardinality_CardinalityRequired,
+        GPBField_Cardinality_CardinalityRepeated,
+    };
+    GPBEnumDescriptor *worker =
+        [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBField_Cardinality)
+                                       valueNames:valueNames
+                                           values:values
+                                            count:(uint32_t)(sizeof(values) / sizeof(int32_t))
+                                     enumVerifier:GPBField_Cardinality_IsValidValue];
+    GPBEnumDescriptor *expected = nil;
+    if (!atomic_compare_exchange_strong(&descriptor, &expected, worker)) {
+      [worker release];
+    }
+  }
+  return descriptor;
+}
+
+BOOL GPBField_Cardinality_IsValidValue(int32_t value__) {
+  switch (value__) {
+    case GPBField_Cardinality_CardinalityUnknown:
+    case GPBField_Cardinality_CardinalityOptional:
+    case GPBField_Cardinality_CardinalityRequired:
+    case GPBField_Cardinality_CardinalityRepeated:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+#pragma mark - GPBEnum
+
+@implementation GPBEnum
+
+@dynamic name;
+@dynamic enumvalueArray, enumvalueArray_Count;
+@dynamic optionsArray, optionsArray_Count;
+@dynamic hasSourceContext, sourceContext;
+@dynamic syntax;
+
+typedef struct GPBEnum__storage_ {
+  uint32_t _has_storage_[1];
+  GPBSyntax syntax;
+  NSString *name;
+  NSMutableArray *enumvalueArray;
+  NSMutableArray *optionsArray;
+  GPBSourceContext *sourceContext;
+} GPBEnum__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "name",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBEnum_FieldNumber_Name,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBEnum__storage_, name),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "enumvalueArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBEnumValue),
+        .number = GPBEnum_FieldNumber_EnumvalueArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBEnum__storage_, enumvalueArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "optionsArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBOption),
+        .number = GPBEnum_FieldNumber_OptionsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBEnum__storage_, optionsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "sourceContext",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceContext),
+        .number = GPBEnum_FieldNumber_SourceContext,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBEnum__storage_, sourceContext),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+      {
+        .name = "syntax",
+        .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor,
+        .number = GPBEnum_FieldNumber_Syntax,
+        .hasIndex = 2,
+        .offset = (uint32_t)offsetof(GPBEnum__storage_, syntax),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor),
+        .dataType = GPBDataTypeEnum,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBEnum class]
+                                     rootClass:[GPBTypeRoot class]
+                                          file:GPBTypeRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBEnum__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+int32_t GPBEnum_Syntax_RawValue(GPBEnum *message) {
+  GPBDescriptor *descriptor = [GPBEnum descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBEnum_FieldNumber_Syntax];
+  return GPBGetMessageInt32Field(message, field);
+}
+
+void SetGPBEnum_Syntax_RawValue(GPBEnum *message, int32_t value) {
+  GPBDescriptor *descriptor = [GPBEnum descriptor];
+  GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBEnum_FieldNumber_Syntax];
+  GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax);
+}
+
+#pragma mark - GPBEnumValue
+
+@implementation GPBEnumValue
+
+@dynamic name;
+@dynamic number;
+@dynamic optionsArray, optionsArray_Count;
+
+typedef struct GPBEnumValue__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t number;
+  NSString *name;
+  NSMutableArray *optionsArray;
+} GPBEnumValue__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "name",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBEnumValue_FieldNumber_Name,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBEnumValue__storage_, name),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "number",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBEnumValue_FieldNumber_Number,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBEnumValue__storage_, number),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+      {
+        .name = "optionsArray",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBOption),
+        .number = GPBEnumValue_FieldNumber_OptionsArray,
+        .hasIndex = GPBNoHasBit,
+        .offset = (uint32_t)offsetof(GPBEnumValue__storage_, optionsArray),
+        .flags = GPBFieldRepeated,
+        .dataType = GPBDataTypeMessage,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBEnumValue class]
+                                     rootClass:[GPBTypeRoot class]
+                                          file:GPBTypeRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBEnumValue__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GPBOption
+
+@implementation GPBOption
+
+@dynamic name;
+@dynamic hasValue, value;
+
+typedef struct GPBOption__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *name;
+  GPBAny *value;
+} GPBOption__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "name",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBOption_FieldNumber_Name,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBOption__storage_, name),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+      {
+        .name = "value",
+        .dataTypeSpecific.className = GPBStringifySymbol(GPBAny),
+        .number = GPBOption_FieldNumber_Value,
+        .hasIndex = 1,
+        .offset = (uint32_t)offsetof(GPBOption__storage_, value),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeMessage,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBOption class]
+                                     rootClass:[GPBTypeRoot class]
+                                          file:GPBTypeRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBOption__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Wrappers.pbobjc.h b/iOS/Pods/Protobuf/objectivec/google/protobuf/Wrappers.pbobjc.h
new file mode 100644 (file)
index 0000000..0411e1e
--- /dev/null
@@ -0,0 +1,219 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/wrappers.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBDescriptor.h>
+ #import <Protobuf/GPBMessage.h>
+ #import <Protobuf/GPBRootObject.h>
+#else
+ #import "GPBDescriptor.h"
+ #import "GPBMessage.h"
+ #import "GPBRootObject.h"
+#endif
+
+#if GOOGLE_PROTOBUF_OBJC_VERSION < 30002
+#error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+#if 30002 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION
+#error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources.
+#endif
+
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+CF_EXTERN_C_BEGIN
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - GPBWrappersRoot
+
+/**
+ * Exposes the extension registry for this file.
+ *
+ * The base class provides:
+ * @code
+ *   + (GPBExtensionRegistry *)extensionRegistry;
+ * @endcode
+ * which is a @c GPBExtensionRegistry that includes all the extensions defined by
+ * this file and all files that it depends on.
+ **/
+@interface GPBWrappersRoot : GPBRootObject
+@end
+
+#pragma mark - GPBDoubleValue
+
+typedef GPB_ENUM(GPBDoubleValue_FieldNumber) {
+  GPBDoubleValue_FieldNumber_Value = 1,
+};
+
+/**
+ * Wrapper message for `double`.
+ *
+ * The JSON representation for `DoubleValue` is JSON number.
+ **/
+@interface GPBDoubleValue : GPBMessage
+
+/** The double value. */
+@property(nonatomic, readwrite) double value;
+
+@end
+
+#pragma mark - GPBFloatValue
+
+typedef GPB_ENUM(GPBFloatValue_FieldNumber) {
+  GPBFloatValue_FieldNumber_Value = 1,
+};
+
+/**
+ * Wrapper message for `float`.
+ *
+ * The JSON representation for `FloatValue` is JSON number.
+ **/
+@interface GPBFloatValue : GPBMessage
+
+/** The float value. */
+@property(nonatomic, readwrite) float value;
+
+@end
+
+#pragma mark - GPBInt64Value
+
+typedef GPB_ENUM(GPBInt64Value_FieldNumber) {
+  GPBInt64Value_FieldNumber_Value = 1,
+};
+
+/**
+ * Wrapper message for `int64`.
+ *
+ * The JSON representation for `Int64Value` is JSON string.
+ **/
+@interface GPBInt64Value : GPBMessage
+
+/** The int64 value. */
+@property(nonatomic, readwrite) int64_t value;
+
+@end
+
+#pragma mark - GPBUInt64Value
+
+typedef GPB_ENUM(GPBUInt64Value_FieldNumber) {
+  GPBUInt64Value_FieldNumber_Value = 1,
+};
+
+/**
+ * Wrapper message for `uint64`.
+ *
+ * The JSON representation for `UInt64Value` is JSON string.
+ **/
+@interface GPBUInt64Value : GPBMessage
+
+/** The uint64 value. */
+@property(nonatomic, readwrite) uint64_t value;
+
+@end
+
+#pragma mark - GPBInt32Value
+
+typedef GPB_ENUM(GPBInt32Value_FieldNumber) {
+  GPBInt32Value_FieldNumber_Value = 1,
+};
+
+/**
+ * Wrapper message for `int32`.
+ *
+ * The JSON representation for `Int32Value` is JSON number.
+ **/
+@interface GPBInt32Value : GPBMessage
+
+/** The int32 value. */
+@property(nonatomic, readwrite) int32_t value;
+
+@end
+
+#pragma mark - GPBUInt32Value
+
+typedef GPB_ENUM(GPBUInt32Value_FieldNumber) {
+  GPBUInt32Value_FieldNumber_Value = 1,
+};
+
+/**
+ * Wrapper message for `uint32`.
+ *
+ * The JSON representation for `UInt32Value` is JSON number.
+ **/
+@interface GPBUInt32Value : GPBMessage
+
+/** The uint32 value. */
+@property(nonatomic, readwrite) uint32_t value;
+
+@end
+
+#pragma mark - GPBBoolValue
+
+typedef GPB_ENUM(GPBBoolValue_FieldNumber) {
+  GPBBoolValue_FieldNumber_Value = 1,
+};
+
+/**
+ * Wrapper message for `bool`.
+ *
+ * The JSON representation for `BoolValue` is JSON `true` and `false`.
+ **/
+@interface GPBBoolValue : GPBMessage
+
+/** The bool value. */
+@property(nonatomic, readwrite) BOOL value;
+
+@end
+
+#pragma mark - GPBStringValue
+
+typedef GPB_ENUM(GPBStringValue_FieldNumber) {
+  GPBStringValue_FieldNumber_Value = 1,
+};
+
+/**
+ * Wrapper message for `string`.
+ *
+ * The JSON representation for `StringValue` is JSON string.
+ **/
+@interface GPBStringValue : GPBMessage
+
+/** The string value. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *value;
+
+@end
+
+#pragma mark - GPBBytesValue
+
+typedef GPB_ENUM(GPBBytesValue_FieldNumber) {
+  GPBBytesValue_FieldNumber_Value = 1,
+};
+
+/**
+ * Wrapper message for `bytes`.
+ *
+ * The JSON representation for `BytesValue` is JSON string.
+ **/
+@interface GPBBytesValue : GPBMessage
+
+/** The bytes value. */
+@property(nonatomic, readwrite, copy, null_resettable) NSData *value;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+CF_EXTERN_C_END
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Protobuf/objectivec/google/protobuf/Wrappers.pbobjc.m b/iOS/Pods/Protobuf/objectivec/google/protobuf/Wrappers.pbobjc.m
new file mode 100644 (file)
index 0000000..5479eb1
--- /dev/null
@@ -0,0 +1,439 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: google/protobuf/wrappers.proto
+
+// This CPP symbol can be defined to use imports that match up to the framework
+// imports needed when using CocoaPods.
+#if !defined(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS)
+ #define GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS 0
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/GPBProtocolBuffers_RuntimeSupport.h>
+#else
+ #import "GPBProtocolBuffers_RuntimeSupport.h"
+#endif
+
+#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS
+ #import <Protobuf/Wrappers.pbobjc.h>
+#else
+ #import "google/protobuf/Wrappers.pbobjc.h"
+#endif
+// @@protoc_insertion_point(imports)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#pragma mark - GPBWrappersRoot
+
+@implementation GPBWrappersRoot
+
+// No extensions in the file and no imports, so no need to generate
+// +extensionRegistry.
+
+@end
+
+#pragma mark - GPBWrappersRoot_FileDescriptor
+
+static GPBFileDescriptor *GPBWrappersRoot_FileDescriptor(void) {
+  // This is called by +initialize so there is no need to worry
+  // about thread safety of the singleton.
+  static GPBFileDescriptor *descriptor = NULL;
+  if (!descriptor) {
+    GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
+    descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf"
+                                                 objcPrefix:@"GPB"
+                                                     syntax:GPBFileSyntaxProto3];
+  }
+  return descriptor;
+}
+
+#pragma mark - GPBDoubleValue
+
+@implementation GPBDoubleValue
+
+@dynamic value;
+
+typedef struct GPBDoubleValue__storage_ {
+  uint32_t _has_storage_[1];
+  double value;
+} GPBDoubleValue__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBDoubleValue_FieldNumber_Value,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBDoubleValue__storage_, value),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeDouble,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBDoubleValue class]
+                                     rootClass:[GPBWrappersRoot class]
+                                          file:GPBWrappersRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBDoubleValue__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GPBFloatValue
+
+@implementation GPBFloatValue
+
+@dynamic value;
+
+typedef struct GPBFloatValue__storage_ {
+  uint32_t _has_storage_[1];
+  float value;
+} GPBFloatValue__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBFloatValue_FieldNumber_Value,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBFloatValue__storage_, value),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeFloat,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBFloatValue class]
+                                     rootClass:[GPBWrappersRoot class]
+                                          file:GPBWrappersRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBFloatValue__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GPBInt64Value
+
+@implementation GPBInt64Value
+
+@dynamic value;
+
+typedef struct GPBInt64Value__storage_ {
+  uint32_t _has_storage_[1];
+  int64_t value;
+} GPBInt64Value__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBInt64Value_FieldNumber_Value,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBInt64Value__storage_, value),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt64,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBInt64Value class]
+                                     rootClass:[GPBWrappersRoot class]
+                                          file:GPBWrappersRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBInt64Value__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GPBUInt64Value
+
+@implementation GPBUInt64Value
+
+@dynamic value;
+
+typedef struct GPBUInt64Value__storage_ {
+  uint32_t _has_storage_[1];
+  uint64_t value;
+} GPBUInt64Value__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBUInt64Value_FieldNumber_Value,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBUInt64Value__storage_, value),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeUInt64,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBUInt64Value class]
+                                     rootClass:[GPBWrappersRoot class]
+                                          file:GPBWrappersRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBUInt64Value__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GPBInt32Value
+
+@implementation GPBInt32Value
+
+@dynamic value;
+
+typedef struct GPBInt32Value__storage_ {
+  uint32_t _has_storage_[1];
+  int32_t value;
+} GPBInt32Value__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBInt32Value_FieldNumber_Value,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBInt32Value__storage_, value),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBInt32Value class]
+                                     rootClass:[GPBWrappersRoot class]
+                                          file:GPBWrappersRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBInt32Value__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GPBUInt32Value
+
+@implementation GPBUInt32Value
+
+@dynamic value;
+
+typedef struct GPBUInt32Value__storage_ {
+  uint32_t _has_storage_[1];
+  uint32_t value;
+} GPBUInt32Value__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBUInt32Value_FieldNumber_Value,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBUInt32Value__storage_, value),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeUInt32,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBUInt32Value class]
+                                     rootClass:[GPBWrappersRoot class]
+                                          file:GPBWrappersRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBUInt32Value__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GPBBoolValue
+
+@implementation GPBBoolValue
+
+@dynamic value;
+
+typedef struct GPBBoolValue__storage_ {
+  uint32_t _has_storage_[1];
+} GPBBoolValue__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBBoolValue_FieldNumber_Value,
+        .hasIndex = 0,
+        .offset = 1,  // Stored in _has_storage_ to save space.
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBool,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBBoolValue class]
+                                     rootClass:[GPBWrappersRoot class]
+                                          file:GPBWrappersRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBBoolValue__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GPBStringValue
+
+@implementation GPBStringValue
+
+@dynamic value;
+
+typedef struct GPBStringValue__storage_ {
+  uint32_t _has_storage_[1];
+  NSString *value;
+} GPBStringValue__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBStringValue_FieldNumber_Value,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBStringValue__storage_, value),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeString,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBStringValue class]
+                                     rootClass:[GPBWrappersRoot class]
+                                          file:GPBWrappersRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBStringValue__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+#pragma mark - GPBBytesValue
+
+@implementation GPBBytesValue
+
+@dynamic value;
+
+typedef struct GPBBytesValue__storage_ {
+  uint32_t _has_storage_[1];
+  NSData *value;
+} GPBBytesValue__storage_;
+
+// This method is threadsafe because it is initially called
+// in +initialize for each subclass.
++ (GPBDescriptor *)descriptor {
+  static GPBDescriptor *descriptor = nil;
+  if (!descriptor) {
+    static GPBMessageFieldDescription fields[] = {
+      {
+        .name = "value",
+        .dataTypeSpecific.className = NULL,
+        .number = GPBBytesValue_FieldNumber_Value,
+        .hasIndex = 0,
+        .offset = (uint32_t)offsetof(GPBBytesValue__storage_, value),
+        .flags = GPBFieldOptional,
+        .dataType = GPBDataTypeBytes,
+      },
+    };
+    GPBDescriptor *localDescriptor =
+        [GPBDescriptor allocDescriptorForClass:[GPBBytesValue class]
+                                     rootClass:[GPBWrappersRoot class]
+                                          file:GPBWrappersRoot_FileDescriptor()
+                                        fields:fields
+                                    fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription))
+                                   storageSize:sizeof(GPBBytesValue__storage_)
+                                         flags:GPBDescriptorInitializationFlag_None];
+    NSAssert(descriptor == nil, @"Startup recursed!");
+    descriptor = localDescriptor;
+  }
+  return descriptor;
+}
+
+@end
+
+
+#pragma clang diagnostic pop
+
+// @@protoc_insertion_point(global_scope)
diff --git a/iOS/Pods/Realm/LICENSE b/iOS/Pods/Realm/LICENSE
new file mode 100644 (file)
index 0000000..57a0e0b
--- /dev/null
@@ -0,0 +1,248 @@
+TABLE OF CONTENTS
+
+1. Apache License version 2.0
+2. Realm Components
+3. Export Compliance
+
+1. -------------------------------------------------------------------------------
+
+                                 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.
+
+2. -------------------------------------------------------------------------------
+
+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 Platform Extensions component
+
+  Realm Platform Extensions License
+
+  Copyright (c) 2011-2017 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.
+
+3. -------------------------------------------------------------------------------
+
+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/iOS/Pods/Realm/README.md b/iOS/Pods/Realm/README.md
new file mode 100644 (file)
index 0000000..ae255b2
--- /dev/null
@@ -0,0 +1,74 @@
+![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 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**](https://realm.io/realm-news-subscribe) 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 8.x.
+* If cloning from git, submodules are required: `git submodule update --init --recursive`.
+* 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) 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/iOS/Pods/Realm/Realm/NSError+RLMSync.m b/iOS/Pods/Realm/Realm/NSError+RLMSync.m
new file mode 100644 (file)
index 0000000..e8c2590
--- /dev/null
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 "NSError+RLMSync.h"
+
+#import "RLMSyncUtil.h"
+
+@implementation NSError (RLMSync)
+
+- (RLMSyncErrorActionToken *)rlmSync_errorActionToken {
+    if (self.domain != RLMSyncErrorDomain) {
+        return nil;
+    }
+    if (self.code == RLMSyncErrorClientResetError
+        || self.code == RLMSyncErrorPermissionDeniedError) {
+        return (RLMSyncErrorActionToken *)self.userInfo[kRLMSyncErrorActionTokenKey];
+    }
+    return nil;
+}
+
+- (NSString *)rlmSync_clientResetBackedUpRealmPath {
+    if (self.domain == RLMSyncErrorDomain && self.code == RLMSyncErrorClientResetError) {
+        return self.userInfo[kRLMSyncPathOfRealmBackupCopyKey];
+    }
+    return nil;
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/binding_callback_thread_observer.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/binding_callback_thread_observer.cpp
new file mode 100644 (file)
index 0000000..0c388d0
--- /dev/null
@@ -0,0 +1,23 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 "binding_callback_thread_observer.hpp"
+
+namespace realm {
+BindingCallbackThreadObserver* g_binding_callback_thread_observer = nullptr;
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp
new file mode 100644 (file)
index 0000000..03fad73
--- /dev/null
@@ -0,0 +1,61 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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, uint64_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;
+}
+
+void NotificationToken::suppress_next()
+{
+    m_notifier.load()->suppress_next_notification(m_token);
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp
new file mode 100644 (file)
index 0000000..60f7a66
--- /dev/null
@@ -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 "impl/external_commit_helper.hpp"
+
+#include "impl/realm_coordinator.hpp"
+
+#include <asl.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <sstream>
+#include <sys/event.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <system_error>
+#include <unistd.h>
+
+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<std::string>()(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();
+        }
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        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;
+        }
+#pragma clang diagnostic pop
+    });
+}
+
+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/iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp
new file mode 100644 (file)
index 0000000..8c9000c
--- /dev/null
@@ -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.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include "impl/apple/keychain_helper.hpp"
+
+
+#include <realm/util/cf_ptr.hpp>
+#include <realm/util/optional.hpp>
+
+#include <Security/Security.h>
+
+#include <string>
+
+using realm::util::CFPtr;
+using realm::util::adoptCF;
+using realm::util::retainCF;
+
+namespace realm {
+namespace keychain {
+
+KeychainAccessException::KeychainAccessException(int32_t error_code)
+: std::runtime_error(util::format("Keychain returned unexpected status code: %1", error_code)) { }
+
+namespace {
+
+constexpr size_t key_size = 64;
+
+#if !TARGET_IPHONE_SIMULATOR
+CFPtr<CFStringRef> convert_string(const std::string& string)
+{
+    auto result = adoptCF(CFStringCreateWithBytes(nullptr, reinterpret_cast<const UInt8*>(string.data()),
+                                                  string.size(), kCFStringEncodingASCII, false));
+    if (!result) {
+        throw std::bad_alloc();
+    }
+    return result;
+}
+#endif
+
+CFPtr<CFMutableDictionaryRef> build_search_dictionary(CFStringRef account, CFStringRef service,
+                                                      __unused util::Optional<std::string> 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, account);
+    CFDictionaryAddValue(d.get(), kSecAttrService, service);
+#if !TARGET_IPHONE_SIMULATOR
+    if (group)
+        CFDictionaryAddValue(d.get(), kSecAttrAccessGroup, convert_string(*group).get());
+#endif
+    return d;
+}
+
+/// Get the encryption key for a given service, returning it only if it exists.
+util::Optional<std::vector<char>> get_key(CFStringRef account, CFStringRef service)
+{
+    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.
+        return none;
+    }
+
+    // Key was previously stored. Extract it.
+    CFPtr<CFDataRef> key_data = adoptCF(retained_key_data);
+    if (key_size != CFDataGetLength(key_data.get()))
+        throw std::runtime_error("Password stored in keychain was not expected size.");
+
+    auto key_bytes = reinterpret_cast<const char *>(CFDataGetBytePtr(key_data.get()));
+    return std::vector<char>(key_bytes, key_bytes + key_size);
+}
+
+void set_key(const std::vector<char>& key, CFStringRef account, CFStringRef service)
+{
+    auto search_dictionary = build_search_dictionary(account, service, none);
+    auto key_data = adoptCF(CFDataCreate(nullptr, reinterpret_cast<const UInt8 *>(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);
+}
+
+}   // anonymous namespace
+
+std::vector<char> metadata_realm_encryption_key(bool check_legacy_service)
+{
+    CFStringRef account = CFSTR("metadata");
+    CFStringRef legacy_service = CFSTR("io.realm.sync.keychain");
+
+    CFPtr<CFStringRef> service;
+    if (CFStringRef bundle_id = CFBundleGetIdentifier(CFBundleGetMainBundle()))
+        service = adoptCF(CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ - Realm Sync Metadata Key"), bundle_id));
+    else {
+        service = retainCF(legacy_service);
+        check_legacy_service = false;
+    }
+
+    // Try retrieving the key.
+    if (auto existing_key = get_key(account, service.get())) {
+        return *existing_key;
+    } else if (check_legacy_service) {
+        // See if there's a key stored using the legacy shared keychain item.
+        if (auto existing_legacy_key = get_key(account, legacy_service)) {
+            // If so, copy it to the per-app keychain item before returning it.
+            set_key(*existing_legacy_key, account, service.get());
+            return *existing_legacy_key;
+        }
+    }
+    // Make a completely new key.
+    std::vector<char> key(key_size);
+    arc4random_buf(key.data(), key_size);
+    set_key(key, account, service.get());
+    return key;
+}
+
+}   // keychain
+}   // realm
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp
new file mode 100644 (file)
index 0000000..40b5754
--- /dev/null
@@ -0,0 +1,887 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <realm/util/assert.hpp>
+#include <algorithm>
+
+#include <algorithm>
+
+using namespace realm;
+using namespace realm::_impl;
+
+CollectionChangeBuilder::CollectionChangeBuilder(IndexSet deletions,
+                                                 IndexSet insertions,
+                                                 IndexSet modifications,
+                                                 std::vector<Move> 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();
+
+    auto for_each_col = [&](auto&& f) {
+        f(modifications, c.modifications);
+        if (m_track_columns) {
+            if (columns.size() < c.columns.size())
+                columns.resize(c.columns.size());
+            else if (columns.size() > c.columns.size())
+                c.columns.resize(columns.size());
+            for (size_t i = 0; i < columns.size(); ++i)
+                f(columns[i], c.columns[i]);
+        }
+    };
+
+    // 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()) {
+                for_each_col([&](auto& col, auto& other) {
+                    if (col.contains(it->from))
+                        other.add(it->to);
+                });
+                old.to = it->to;
+                *it = c.moves.back();
+                c.moves.pop_back();
+                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) {
+            for_each_col([&](auto& col, auto& other) {
+                if (col.contains(move.from))
+                    other.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();
+
+    for_each_col([&](auto& col, auto& other) {
+        col.erase_at(c.deletions);
+        col.shift_for_insert_at(c.insertions);
+        col.add(other);
+    });
+
+    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, size_t col)
+{
+    modifications.add(ndx);
+    if (!m_track_columns || col == IndexSet::npos)
+        return;
+
+    if (col >= columns.size())
+        columns.resize(col + 1);
+    columns[col].add(ndx);
+}
+
+template<typename Func>
+void CollectionChangeBuilder::for_each_col(Func&& f)
+{
+    f(modifications);
+    if (m_track_columns) {
+        for (auto& col : columns)
+            f(col);
+    }
+}
+
+void CollectionChangeBuilder::insert(size_t index, size_t count, bool track_moves)
+{
+    REALM_ASSERT(count != 0);
+
+    for_each_col([=](auto& col) { col.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 += count;
+    }
+
+    if (m_move_mapping.empty())
+        return;
+
+    // m_move_mapping is new_ndx -> old_ndx, so updating the keys requires
+    // deleting and re-inserting at the new index
+    std::vector<std::pair<size_t, size_t>> shifted;
+    for (auto it = m_move_mapping.begin(); it != m_move_mapping.end(); ) {
+        if (it->first >= index) {
+            shifted.emplace_back(it->first + count, it->second);
+            it = m_move_mapping.erase(it);
+        }
+        else {
+            ++it;
+        }
+    }
+    for (auto& pair : shifted)
+        m_move_mapping.insert(pair);
+}
+
+void CollectionChangeBuilder::erase(size_t index)
+{
+    for_each_col([=](auto& col) { col.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<size_t>::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();
+    columns.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});
+        }
+    }
+
+    for_each_col([=](auto& col) {
+        bool modified = col.contains(from);
+        col.erase_at(from);
+
+        if (modified)
+            col.insert_at(to);
+        else
+            col.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);
+        }
+        for_each_col([=](auto& col) { col.remove(row_ndx); });
+        return;
+    }
+
+    for_each_col([=](auto& col) {
+        bool modified = col.contains(last_row);
+        if (modified) {
+            col.remove(last_row);
+            col.add(row_ndx);
+        }
+        else
+            col.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);
+
+    for_each_col([=](auto& col) {
+        bool row_1_modified = col.contains(ndx_1);
+        bool row_2_modified = col.contains(ndx_2);
+        if (row_1_modified != row_2_modified) {
+            if (row_1_modified) {
+                col.remove(ndx_1);
+                col.add(ndx_2);
+            }
+            else {
+                col.remove(ndx_2);
+                col.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);
+
+    for_each_col([=](auto& col) {
+        if (col.contains(old_ndx)) {
+            col.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
+}
+
+void CollectionChangeBuilder::insert_column(size_t ndx)
+{
+    if (ndx < columns.size())
+        columns.insert(columns.begin() + ndx, IndexSet{});
+}
+
+void CollectionChangeBuilder::move_column(size_t from, size_t to)
+{
+    if (from >= columns.size() && to >= columns.size())
+        return;
+    if (from >= columns.size() || to >= columns.size())
+        columns.resize(std::max(from, to) + 1);
+    if (from < to)
+        std::rotate(begin(columns) + from, begin(columns) + from + 1, begin(columns) + to + 1);
+    else
+        std::rotate(begin(columns) + to, begin(columns) + from, begin(columns) + from + 1);
+}
+
+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<RowInfo>& 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<Match> m_longest_matches;
+
+    LongestCommonSubsequenceCalculator(std::vector<Row>& a, std::vector<Row>& 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<Row> &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<Length> prev;
+        // The length of the matching block for each `j` for the row currently being checked
+        std::vector<Length> 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<RowInfo>& 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<LongestCommonSubsequenceCalculator::Row> 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<size_t> const& prev_rows,
+                                                           std::vector<size_t> const& next_rows,
+                                                           std::function<bool (size_t)> row_did_change,
+                                                           util::Optional<IndexSet> 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<RowInfo> 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<RowInfo> 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),
+        std::move(columns)
+    };
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp
new file mode 100644 (file)
index 0000000..8fa94d3
--- /dev/null
@@ -0,0 +1,497 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <realm/group_shared.hpp>
+#include <realm/link_view.hpp>
+
+using namespace realm;
+using namespace realm::_impl;
+
+std::function<bool (size_t)>
+CollectionNotifier::get_modification_checker(TransactionChangeInfo const& info,
+                                             Table const& root_table)
+{
+    if (info.schema_changed)
+        set_table(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; };
+    }
+    if (m_related_tables.size() == 1) {
+        auto& modifications = info.tables[m_related_tables[0].table_ndx].modifications;
+        return [&](size_t row) { return modifications.contains(row); };
+    }
+
+    return DeepChangeChecker(info, root_table, m_related_tables);
+}
+
+void DeepChangeChecker::find_related_tables(std::vector<RelatedTable>& out, Table const& table)
+{
+    auto table_ndx = table.get_index_in_group();
+    if (table_ndx == npos)
+        return;
+    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<RelatedTable> 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) {
+        auto end = m_current_path.begin() + depth;
+        auto match = std::find_if(m_current_path.begin(), end, [&](auto& p) {
+            return p.table == table_ndx && p.row == row_ndx && p.col == col;
+        });
+        if (match != end) {
+            for (; match < end; ++match) match->depth_exceeded = true;
+            return true;
+        }
+        m_current_path[depth] = {table_ndx, row_ndx, col, false};
+        return false;
+    };
+
+    auto linked_object_changed = [&](OutgoingLink const& link) {
+        if (already_checking(link.col_ndx))
+            return false;
+        if (!link.is_list) {
+            if (table.is_null_link(link.col_ndx, row_ndx))
+                return false;
+            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;
+    };
+
+    return std::any_of(begin(it->links), end(it->links), linked_object_changed);
+}
+
+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 = 0; 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 && (depth == 0 || !m_current_path[depth - 1].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> 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();
+}
+
+uint64_t CollectionNotifier::add_callback(CollectionChangeCallback callback)
+{
+    m_realm->verify_thread();
+
+    std::lock_guard<std::mutex> lock(m_callback_mutex);
+    auto token = m_next_token++;
+    m_callbacks.push_back({std::move(callback), {}, {}, token, false, false});
+    if (m_callback_index == npos) { // Don't need to wake up if we're already sending notifications
+        Realm::Internal::get_coordinator(*m_realm).wake_up_notifier_worker();
+        m_have_callbacks = true;
+    }
+    return token;
+}
+
+void CollectionNotifier::remove_callback(uint64_t token)
+{
+    // the callback needs to be destroyed after releasing the lock as destroying
+    // it could cause user code to be called
+    Callback old;
+    {
+        std::lock_guard<std::mutex> lock(m_callback_mutex);
+        auto it = find_callback(token);
+        if (it == end(m_callbacks)) {
+            return;
+        }
+
+        size_t idx = distance(begin(m_callbacks), it);
+        if (m_callback_index != npos) {
+            if (m_callback_index >= idx)
+                --m_callback_index;
+        }
+        --m_callback_count;
+
+        old = std::move(*it);
+        m_callbacks.erase(it);
+
+        m_have_callbacks = !m_callbacks.empty();
+    }
+}
+
+void CollectionNotifier::suppress_next_notification(uint64_t token)
+{
+    {
+        std::lock_guard<std::mutex> lock(m_realm_mutex);
+        REALM_ASSERT(m_realm);
+        m_realm->verify_thread();
+        m_realm->verify_in_write();
+    }
+
+    std::lock_guard<std::mutex> lock(m_callback_mutex);
+    auto it = find_callback(token);
+    if (it != end(m_callbacks)) {
+        it->skip_next = true;
+    }
+}
+
+std::vector<CollectionNotifier::Callback>::iterator CollectionNotifier::find_callback(uint64_t token)
+{
+    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));
+    return it;
+}
+
+void CollectionNotifier::unregister() noexcept
+{
+    std::lock_guard<std::mutex> lock(m_realm_mutex);
+    m_realm = nullptr;
+}
+
+bool CollectionNotifier::is_alive() const noexcept
+{
+    std::lock_guard<std::mutex> lock(m_realm_mutex);
+    return m_realm != nullptr;
+}
+
+std::unique_lock<std::mutex> CollectionNotifier::lock_target()
+{
+    return std::unique_lock<std::mutex>{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) || m_related_tables.empty()) {
+        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);
+    m_has_run = true;
+
+#ifdef REALM_DEBUG
+    std::lock_guard<std::mutex> lock(m_callback_mutex);
+    for (auto& callback : m_callbacks)
+        REALM_ASSERT(!callback.skip_next);
+#endif
+}
+
+void CollectionNotifier::before_advance()
+{
+    for_each_callback([&](auto& lock, auto& callback) {
+        if (callback.changes_to_deliver.empty()) {
+            return;
+        }
+
+        auto changes = callback.changes_to_deliver;
+        // acquire a local reference to the callback so that removing the
+        // callback from within it can't result in a dangling pointer
+        auto cb = callback.fn;
+        lock.unlock();
+        cb.before(changes);
+    });
+}
+
+void CollectionNotifier::after_advance()
+{
+    for_each_callback([&](auto& lock, auto& callback) {
+        if (callback.initial_delivered && callback.changes_to_deliver.empty()) {
+            return;
+        }
+        callback.initial_delivered = true;
+
+        auto changes = std::move(callback.changes_to_deliver);
+        // acquire a local reference to the callback so that removing the
+        // callback from within it can't result in a dangling pointer
+        auto cb = callback.fn;
+        lock.unlock();
+        cb.after(changes);
+    });
+}
+
+void CollectionNotifier::deliver_error(std::exception_ptr error)
+{
+    // Don't complain about double-unregistering callbacks
+    m_error = true;
+
+    m_callback_count = m_callbacks.size();
+    for_each_callback([this, &error](auto& lock, auto& callback) {
+        // acquire a local reference to the callback so that removing the
+        // callback from within it can't result in a dangling pointer
+        auto cb = std::move(callback.fn);
+        auto token = callback.token;
+        lock.unlock();
+        cb.error(error);
+
+        // We never want to call the callback again after this, so just remove it
+        this->remove_callback(token);
+    });
+}
+
+bool CollectionNotifier::is_for_realm(Realm& realm) const noexcept
+{
+    std::lock_guard<std::mutex> lock(m_realm_mutex);
+    return m_realm.get() == &realm;
+}
+
+bool CollectionNotifier::package_for_delivery()
+{
+    if (!prepare_to_deliver())
+        return false;
+    std::lock_guard<std::mutex> l(m_callback_mutex);
+    for (auto& callback : m_callbacks)
+        callback.changes_to_deliver = std::move(callback.accumulated_changes).finalize();
+    m_callback_count = m_callbacks.size();
+    return true;
+}
+
+template<typename Fn>
+void CollectionNotifier::for_each_callback(Fn&& fn)
+{
+    std::unique_lock<std::mutex> callback_lock(m_callback_mutex);
+    REALM_ASSERT_DEBUG(m_callback_count <= m_callbacks.size());
+    for (++m_callback_index; m_callback_index < m_callback_count; ++m_callback_index) {
+        fn(callback_lock, m_callbacks[m_callback_index]);
+        if (!callback_lock.owns_lock())
+            callback_lock.lock();
+    }
+
+    m_callback_index = npos;
+}
+
+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;
+}
+
+SharedGroup& CollectionNotifier::source_shared_group()
+{
+    return *Realm::Internal::get_shared_group(*m_realm);
+}
+
+void CollectionNotifier::add_changes(CollectionChangeBuilder change)
+{
+    std::lock_guard<std::mutex> lock(m_callback_mutex);
+    for (auto& callback : m_callbacks) {
+        if (callback.skip_next) {
+            REALM_ASSERT_DEBUG(callback.accumulated_changes.empty());
+            callback.skip_next = false;
+        }
+        else {
+            if (&callback == &m_callbacks.back())
+                callback.accumulated_changes.merge(std::move(change));
+            else
+                callback.accumulated_changes.merge(CollectionChangeBuilder(change));
+        }
+    }
+}
+
+NotifierPackage::NotifierPackage(std::exception_ptr error,
+                                 std::vector<std::shared_ptr<CollectionNotifier>> notifiers,
+                                 RealmCoordinator* coordinator)
+: m_notifiers(std::move(notifiers))
+, m_coordinator(coordinator)
+, m_error(std::move(error))
+{
+}
+
+void NotifierPackage::package_and_wait(util::Optional<VersionID::version_type> target_version)
+{
+    if (!m_coordinator || m_error || !*this)
+        return;
+
+    auto lock = m_coordinator->wait_for_notifiers([&] {
+        if (!target_version)
+            return true;
+        return std::all_of(begin(m_notifiers), end(m_notifiers), [&](auto const& n) {
+            return !n->have_callbacks() || (n->has_run() && n->version().version >= *target_version);
+        });
+    });
+
+    // Package the notifiers for delivery and remove any which don't have anything to deliver
+    auto package = [&](auto& notifier) {
+        if (notifier->has_run() && notifier->package_for_delivery()) {
+            m_version = notifier->version();
+            return false;
+        }
+        return true;
+    };
+    m_notifiers.erase(std::remove_if(begin(m_notifiers), end(m_notifiers), package), end(m_notifiers));
+    if (m_version && target_version && m_version->version < *target_version) {
+        m_notifiers.clear();
+        m_version = util::none;
+    }
+    REALM_ASSERT(m_version || m_notifiers.empty());
+
+    m_coordinator = nullptr;
+}
+
+void NotifierPackage::before_advance()
+{
+    if (m_error)
+        return;
+    for (auto& notifier : m_notifiers)
+        notifier->before_advance();
+}
+
+void NotifierPackage::deliver(SharedGroup& sg)
+{
+    if (m_error) {
+        for (auto& notifier : m_notifiers)
+            notifier->deliver_error(m_error);
+        return;
+    }
+    // Can't deliver while in a write transaction
+    if (sg.get_transact_stage() != SharedGroup::transact_Reading)
+        return;
+    for (auto& notifier : m_notifiers)
+        notifier->deliver(sg);
+}
+
+void NotifierPackage::after_advance()
+{
+    if (m_error)
+        return;
+    for (auto& notifier : m_notifiers)
+        notifier->after_advance();
+}
+
+void NotifierPackage::add_notifier(std::shared_ptr<CollectionNotifier> notifier)
+{
+    m_notifiers.push_back(notifier);
+    m_coordinator->register_notifier(notifier);
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp
new file mode 100644 (file)
index 0000000..a2b459c
--- /dev/null
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <realm/link_view.hpp>
+
+using namespace realm;
+using namespace realm::_impl;
+
+ListNotifier::ListNotifier(LinkViewRef lv, std::shared_ptr<Realm> realm)
+: CollectionNotifier(std::move(realm))
+, m_prev_size(lv->size())
+{
+    set_table(lv->get_target_table());
+    m_lv_handover = source_shared_group().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
+    }
+
+    auto& table = m_lv->get_origin_table();
+    size_t row_ndx = m_lv->get_origin_row_index();
+    size_t col_ndx = find_container_column(table, row_ndx, m_lv, type_LinkList, &Table::get_linklist);
+    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/iOS/Pods/Realm/Realm/ObjectStore/src/impl/object_notifier.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/object_notifier.cpp
new file mode 100644 (file)
index 0000000..cb31065
--- /dev/null
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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/object_notifier.hpp"
+
+#include "shared_realm.hpp"
+
+using namespace realm;
+using namespace realm::_impl;
+
+ObjectNotifier::ObjectNotifier(Row const& row, std::shared_ptr<Realm> realm)
+: CollectionNotifier(std::move(realm))
+{
+    REALM_ASSERT(row.get_table());
+    set_table(*row.get_table());
+
+    m_handover = source_shared_group().export_for_handover(row);
+}
+
+void ObjectNotifier::release_data() noexcept
+{
+    m_row = nullptr;
+}
+
+void ObjectNotifier::do_attach_to(SharedGroup& sg)
+{
+    REALM_ASSERT(m_handover);
+    REALM_ASSERT(!m_row);
+    m_row = sg.import_from_handover(std::move(m_handover));
+}
+
+void ObjectNotifier::do_detach_from(SharedGroup& sg)
+{
+    REALM_ASSERT(!m_handover);
+    if (m_row) {
+        m_handover = sg.export_for_handover(*m_row);
+        m_row = nullptr;
+    }
+}
+
+bool ObjectNotifier::do_add_required_change_info(TransactionChangeInfo& info)
+{
+    REALM_ASSERT(!m_handover);
+    m_info = &info;
+    if (m_row && m_row->is_attached()) {
+        size_t table_ndx = m_row->get_table()->get_index_in_group();
+        if (table_ndx >= info.table_modifications_needed.size())
+            info.table_modifications_needed.resize(table_ndx + 1);
+        info.table_modifications_needed[table_ndx] = true;
+    }
+    return false;
+}
+
+void ObjectNotifier::run()
+{
+    if (!m_row)
+        return;
+    if (!m_row->is_attached()) {
+        m_change.deletions.add(0);
+        m_row = nullptr;
+        return;
+    }
+
+    size_t table_ndx = m_row->get_table()->get_index_in_group();
+    if (table_ndx >= m_info->tables.size())
+        return;
+    auto& change = m_info->tables[table_ndx];
+    if (!change.modifications.contains(m_row->get_index()))
+        return;
+    m_change.modifications.add(0);
+    m_change.columns.reserve(change.columns.size());
+    for (auto& col : change.columns) {
+        m_change.columns.emplace_back();
+        if (col.contains(m_row->get_index()))
+            m_change.columns.back().add(0);
+    }
+}
+
+void ObjectNotifier::do_prepare_handover(SharedGroup&)
+{
+    add_changes(std::move(m_change));
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/impl/primitive_list_notifier.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/primitive_list_notifier.cpp
new file mode 100644 (file)
index 0000000..9be9acb
--- /dev/null
@@ -0,0 +1,99 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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/primitive_list_notifier.hpp"
+
+#include "shared_realm.hpp"
+
+#include <realm/link_view.hpp>
+
+using namespace realm;
+using namespace realm::_impl;
+
+PrimitiveListNotifier::PrimitiveListNotifier(TableRef table, std::shared_ptr<Realm> realm)
+: CollectionNotifier(std::move(realm))
+, m_prev_size(table->size())
+{
+    set_table(*table->get_parent_table());
+    m_table_handover = source_shared_group().export_table_for_handover(table);
+}
+
+void PrimitiveListNotifier::release_data() noexcept
+{
+    m_table.reset();
+}
+
+void PrimitiveListNotifier::do_attach_to(SharedGroup& sg)
+{
+    REALM_ASSERT(m_table_handover);
+    REALM_ASSERT(!m_table);
+    m_table = sg.import_table_from_handover(std::move(m_table_handover));
+}
+
+void PrimitiveListNotifier::do_detach_from(SharedGroup& sg)
+{
+    REALM_ASSERT(!m_table_handover);
+    if (m_table) {
+        m_table_handover = sg.export_table_for_handover(m_table);
+        m_table = {};
+    }
+}
+
+bool PrimitiveListNotifier::do_add_required_change_info(TransactionChangeInfo& info)
+{
+    REALM_ASSERT(!m_table_handover);
+    if (!m_table || !m_table->is_attached()) {
+        return false; // origin row was deleted after the notification was added
+    }
+
+    auto& table = *m_table->get_parent_table();
+    size_t row_ndx = m_table->get_parent_row_index();
+    size_t col_ndx = find_container_column(table, row_ndx, m_table, type_Table, &Table::get_subtable);
+    info.lists.push_back({table.get_index_in_group(), row_ndx, col_ndx, &m_change});
+
+    m_info = &info;
+    return true;
+}
+
+void PrimitiveListNotifier::run()
+{
+    if (!m_table || !m_table->is_attached()) {
+        // Table was deleted entirely, 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;
+    }
+
+    if (!m_change.deletions.empty() && m_change.deletions.begin()->second == std::numeric_limits<size_t>::max()) {
+        // Table was cleared, so set the deletions to the actual previous size
+        m_change.deletions.set(m_prev_size);
+    }
+
+    m_prev_size = m_table->size();
+}
+
+void PrimitiveListNotifier::do_prepare_handover(SharedGroup&)
+{
+    add_changes(std::move(m_change));
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp
new file mode 100644 (file)
index 0000000..7135a67
--- /dev/null
@@ -0,0 +1,892 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "binding_context.hpp"
+#include "object_schema.hpp"
+#include "object_store.hpp"
+#include "schema.hpp"
+
+#if REALM_ENABLE_SYNC
+#include "sync/sync_config.hpp"
+#include "sync/sync_manager.hpp"
+#include "sync/sync_session.hpp"
+#endif
+
+#include <realm/group_shared.hpp>
+#include <realm/lang_bind_helper.hpp>
+#include <realm/string_data.hpp>
+
+#include <algorithm>
+#include <unordered_map>
+
+using namespace realm;
+using namespace realm::_impl;
+
+static auto& s_coordinator_mutex = *new std::mutex;
+static auto& s_coordinators_per_path = *new std::unordered_map<std::string, std::weak_ptr<RealmCoordinator>>;
+
+std::shared_ptr<RealmCoordinator> RealmCoordinator::get_coordinator(StringData path)
+{
+    std::lock_guard<std::mutex> 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<RealmCoordinator>();
+    weak_coordinator = coordinator;
+    return coordinator;
+}
+
+std::shared_ptr<RealmCoordinator> RealmCoordinator::get_coordinator(const Realm::Config& config)
+{
+    auto coordinator = get_coordinator(config.path);
+    std::lock_guard<std::mutex> lock(coordinator->m_realm_mutex);
+    coordinator->set_config(config);
+    return coordinator;
+}
+
+std::shared_ptr<RealmCoordinator> RealmCoordinator::get_existing_coordinator(StringData path)
+{
+    std::lock_guard<std::mutex> 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;
+
+    if (!m_config.encryption_key.empty() && !m_config.sync_config->realm_encryption_key) {
+        throw std::logic_error("A realm encryption key was specified in Realm::Config but not in SyncConfig");
+    } else if (m_config.sync_config->realm_encryption_key && m_config.encryption_key.empty()) {
+        throw std::logic_error("A realm encryption key was specified in SyncConfig but not in Realm::Config");
+    } else if (m_config.sync_config->realm_encryption_key &&
+               !std::equal(m_config.sync_config->realm_encryption_key->begin(), m_config.sync_config->realm_encryption_key->end(),
+                           m_config.encryption_key.begin(), m_config.encryption_key.end())) {
+        throw std::logic_error("The realm encryption key specified in SyncConfig does not match the one in Realm::Config");
+    }
+
+    auto sync_config = *m_config.sync_config;
+    sync_config.validate_sync_history = false;
+    m_sync_session = SyncManager::shared().get_session(m_config.path, sync_config);
+
+    std::weak_ptr<RealmCoordinator> 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);
+            if (self->m_notifier)
+                self->m_notifier->notify_others();
+        }
+    });
+#endif
+}
+
+void RealmCoordinator::set_config(const Realm::Config& config)
+{
+    if (config.encryption_key.data() && config.encryption_key.size() != 64)
+        throw InvalidEncryptionKeyException();
+    if (config.schema_mode == SchemaMode::Immutable && config.sync_config)
+        throw std::logic_error("Synchronized Realms cannot be opened in immutable mode");
+    if (config.schema_mode == SchemaMode::Additive && config.migration_function)
+        throw std::logic_error("Realms opened in Additive-only schema mode do not use a migration function");
+    if (config.schema_mode == SchemaMode::Immutable && config.migration_function)
+        throw std::logic_error("Realms opened in immutable mode do not use a migration function");
+    if (config.schema_mode == SchemaMode::ReadOnlyAlternative && config.migration_function)
+        throw std::logic_error("Realms opened in read-only mode do not use a migration function");
+    if (config.schema_mode == SchemaMode::Immutable && config.initialization_function)
+        throw std::logic_error("Realms opened in immutable mode do not use an initialization function");
+    if (config.schema_mode == SchemaMode::ReadOnlyAlternative && config.initialization_function)
+        throw std::logic_error("Realms opened in read-only mode do not use an initialization function");
+    if (config.schema && config.schema_version == ObjectStore::NotVersioned)
+        throw std::logic_error("A schema version must be specified when the schema is specified");
+    if (!config.realm_data.is_null() && (!config.immutable() || !config.in_memory))
+        throw std::logic_error("In-memory realms initialized from memory buffers can only be opened in read-only mode");
+    if (!config.realm_data.is_null() && !config.path.empty())
+        throw std::logic_error("Specifying both memory buffer and path is invalid");
+    if (!config.realm_data.is_null() && !config.encryption_key.empty())
+        throw std::logic_error("Memory buffers do not support encryption");
+    // ResetFile also won't use the migration function, but specifying one is
+    // allowed to simplify temporarily switching modes during development
+
+    bool no_existing_realm = std::all_of(begin(m_weak_realm_notifiers), end(m_weak_realm_notifiers),
+                                         [](auto& notifier) { return notifier.expired(); });
+    if (no_existing_realm) {
+        m_config = config;
+    }
+    else {
+        if (m_config.immutable() != config.immutable()) {
+            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 (config.schema && m_schema_version != ObjectStore::NotVersioned && m_schema_version != config.schema_version) {
+            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 != config.sync_config->user) {
+                throw MismatchedConfigException("Realm at path '%1' already opened with different sync user.", 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);
+            }
+            if (m_config.sync_config->transformer != config.sync_config->transformer) {
+                throw MismatchedConfigException("Realm at path '%1' already opened with different transformer.", config.path);
+            }
+            if (m_config.sync_config->realm_encryption_key != config.sync_config->realm_encryption_key) {
+                throw MismatchedConfigException("Realm at path '%1' already opened with sync session encryption key.", config.path);
+            }
+        }
+#endif
+
+        // Realm::update_schema() handles complaining about schema mismatches
+    }
+}
+
+std::shared_ptr<Realm> RealmCoordinator::get_realm(Realm::Config config)
+{
+    // realm must be declared before lock so that the mutex is released before
+    // we release the strong reference to realm, as Realm's destructor may want
+    // to acquire the same lock
+    std::shared_ptr<Realm> realm;
+    std::unique_lock<std::mutex> lock(m_realm_mutex);
+
+    set_config(config);
+
+    auto schema = std::move(config.schema);
+    auto migration_function = std::move(config.migration_function);
+    auto initialization_function = std::move(config.initialization_function);
+    config.schema = {};
+
+    if (config.cache) {
+        AnyExecutionContextID execution_context(config.execution_context);
+        for (auto& cached_realm : m_weak_realm_notifiers) {
+            if (!cached_realm.is_cached_for_execution_context(execution_context))
+                continue;
+            // can be null if we jumped in between ref count hitting zero and
+            // unregister_realm() getting the lock
+            if ((realm = cached_realm.realm())) {
+                // If the file is uninitialized and was opened without a schema,
+                // do the normal schema init
+                if (realm->schema_version() == ObjectStore::NotVersioned)
+                    break;
+
+                // Otherwise if we have a realm schema it needs to be an exact
+                // match (even having the same properties but in different
+                // orders isn't good enough)
+                if (schema && realm->schema() != *schema)
+                    throw MismatchedConfigException("Realm at path '%1' already opened on current thread with different schema.", config.path);
+
+                return realm;
+            }
+        }
+    }
+
+    if (!realm) {
+        bool should_initialize_notifier = !config.immutable() && config.automatic_change_notifications;
+        realm = Realm::make_shared_realm(std::move(config), shared_from_this());
+        if (!m_notifier && should_initialize_notifier) {
+            try {
+                m_notifier = std::make_unique<ExternalCommitHelper>(*this);
+            }
+            catch (std::system_error const& ex) {
+                throw RealmFileException(RealmFileException::Kind::AccessError, get_path(), ex.code().message(), "");
+            }
+        }
+        m_weak_realm_notifiers.emplace_back(realm, m_config.cache);
+    }
+
+    if (realm->config().sync_config)
+        create_sync_session();
+
+    if (schema) {
+        lock.unlock();
+        realm->update_schema(std::move(*schema), config.schema_version, std::move(migration_function),
+                             std::move(initialization_function));
+    }
+
+    return realm;
+}
+
+std::shared_ptr<Realm> RealmCoordinator::get_realm()
+{
+    return get_realm(m_config);
+}
+
+bool RealmCoordinator::get_cached_schema(Schema& schema, uint64_t& schema_version,
+                                         uint64_t& transaction) const noexcept
+{
+    std::lock_guard<std::mutex> lock(m_schema_cache_mutex);
+    if (!m_cached_schema)
+        return false;
+    schema = *m_cached_schema;
+    schema_version = m_schema_version;
+    transaction = m_schema_transaction_version_max;
+    return true;
+}
+
+void RealmCoordinator::cache_schema(Schema const& new_schema, uint64_t new_schema_version,
+                                    uint64_t transaction_version)
+{
+    std::lock_guard<std::mutex> lock(m_schema_cache_mutex);
+    if (transaction_version < m_schema_transaction_version_max)
+        return;
+    if (new_schema.empty() || new_schema_version == ObjectStore::NotVersioned)
+        return;
+
+    m_cached_schema = new_schema;
+    m_schema_version = new_schema_version;
+    m_schema_transaction_version_min = transaction_version;
+    m_schema_transaction_version_max = transaction_version;
+}
+
+void RealmCoordinator::clear_schema_cache_and_set_schema_version(uint64_t new_schema_version)
+{
+    std::lock_guard<std::mutex> lock(m_schema_cache_mutex);
+    m_cached_schema = util::none;
+    m_schema_version = new_schema_version;
+}
+
+void RealmCoordinator::advance_schema_cache(uint64_t previous, uint64_t next)
+{
+    std::lock_guard<std::mutex> lock(m_schema_cache_mutex);
+    if (!m_cached_schema)
+        return;
+    REALM_ASSERT(previous <= m_schema_transaction_version_max);
+    if (next < m_schema_transaction_version_min)
+        return;
+    m_schema_transaction_version_min = std::min(previous, m_schema_transaction_version_min);
+    m_schema_transaction_version_max = std::max(next, m_schema_transaction_version_max);
+}
+
+RealmCoordinator::RealmCoordinator() = default;
+
+RealmCoordinator::~RealmCoordinator()
+{
+    std::lock_guard<std::mutex> 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<std::mutex> 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<WeakRealm> realms_to_close;
+    {
+        std::lock_guard<std::mutex> 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<std::weak_ptr<RealmCoordinator>> to_clear;
+    {
+        std::lock_guard<std::mutex> 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::assert_no_open_realms() noexcept
+{
+#ifdef REALM_DEBUG
+    std::lock_guard<std::mutex> lock(s_coordinator_mutex);
+    REALM_ASSERT(s_coordinators_per_path.empty());
+#endif
+}
+
+void RealmCoordinator::wake_up_notifier_worker()
+{
+    if (m_notifier) {
+        // FIXME: this wakes up the notification workers for all processes and
+        // not just us. This might be worth optimizing in the future.
+        m_notifier->notify_others();
+    }
+}
+
+void RealmCoordinator::commit_write(Realm& realm)
+{
+    REALM_ASSERT(!m_config.immutable());
+    REALM_ASSERT(realm.is_in_transaction());
+
+    {
+        // Need to acquire this lock before committing or another process could
+        // perform a write and notify us before we get the chance to set the
+        // skip version
+        std::lock_guard<std::mutex> l(m_notifier_mutex);
+
+        transaction::commit(*Realm::Internal::get_shared_group(realm));
+
+        // Don't need to check m_new_notifiers because those don't skip versions
+        bool have_notifiers = std::any_of(m_notifiers.begin(), m_notifiers.end(),
+                                          [&](auto&& notifier) { return notifier->is_for_realm(realm); });
+        if (have_notifiers) {
+            m_notifier_skip_version = Realm::Internal::get_shared_group(realm)->get_version_of_current_transaction();
+        }
+    }
+
+#if REALM_ENABLE_SYNC
+    // Realm could be closed in did_change. So send sync notification first before did_change.
+    if (m_sync_session) {
+        auto& sg = Realm::Internal::get_shared_group(realm);
+        auto version = LangBindHelper::get_version_of_latest_snapshot(*sg);
+        SyncSession::Internal::nonsync_transact_notify(*m_sync_session, version);
+    }
+#endif
+    if (realm.m_binding_context) {
+        realm.m_binding_context->did_change({}, {});
+    }
+
+    if (m_notifier) {
+        m_notifier->notify_others();
+    }
+}
+
+void RealmCoordinator::pin_version(VersionID versionid)
+{
+    REALM_ASSERT_DEBUG(!m_notifier_mutex.try_lock());
+    if (m_async_error) {
+        return;
+    }
+
+    if (!m_advancer_sg) {
+        try {
+            std::unique_ptr<Group> 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<CollectionNotifier> notifier)
+{
+    auto version = notifier->version();
+    auto& self = Realm::Internal::get_coordinator(*notifier->get_realm());
+    {
+        std::lock_guard<std::mutex> lock(self.m_notifier_mutex);
+        self.pin_version(version);
+        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();
+            m_notifier_skip_version = {0, 0};
+        }
+    }
+    if (swap_remove(m_new_notifiers) && m_advancer_sg) {
+        REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Reading);
+        if (m_new_notifiers.empty()) {
+            m_advancer_sg->end_read();
+        }
+    }
+}
+
+void RealmCoordinator::on_change()
+{
+    run_async_notifiers();
+
+    std::lock_guard<std::mutex> lock(m_realm_mutex);
+    for (auto& realm : m_weak_realm_notifiers) {
+        realm.notify();
+    }
+}
+
+namespace {
+class IncrementalChangeInfo {
+public:
+    IncrementalChangeInfo(SharedGroup& sg,
+                          std::vector<std::shared_ptr<_impl::CollectionNotifier>>& notifiers)
+    : m_sg(sg)
+    {
+        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(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(VersionID version)
+    {
+        if (!m_current) {
+            transaction::advance(m_sg, nullptr, 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<TransactionChangeInfo> m_info;
+    TransactionChangeInfo* m_current = nullptr;
+    SharedGroup& m_sg;
+};
+} // anonymous namespace
+
+void RealmCoordinator::run_async_notifiers()
+{
+    std::unique_lock<std::mutex> 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;
+    }
+
+    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, 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, 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(VersionID{});
+
+        for (auto& notifier : new_notifiers) {
+            notifier->detach();
+        }
+
+        // We want to advance the non-new notifiers to the same version as the
+        // new notifiers to avoid having to merge changes from any new
+        // transaction that happen immediately after this into the new notifier
+        // changes
+        version = m_advancer_sg->get_version_of_current_transaction();
+        m_advancer_sg->end_read();
+    }
+    else {
+        // If we have no new notifiers we want to just advance to the latest
+        // version, but we have to pick a "latest" version while holding the
+        // notifier lock to avoid advancing over a transaction which should be
+        // skipped
+        m_advancer_sg->begin_read();
+        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);
+
+    auto skip_version = m_notifier_skip_version;
+    m_notifier_skip_version = {0, 0};
+
+    // 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;
+    m_notifiers.insert(m_notifiers.end(), new_notifiers.begin(), new_notifiers.end());
+    lock.unlock();
+
+    if (skip_version.version) {
+        REALM_ASSERT(!notifiers.empty());
+        REALM_ASSERT(version >= skip_version);
+        IncrementalChangeInfo change_info(*m_notifier_sg, notifiers);
+        for (auto& notifier : notifiers)
+            notifier->add_required_change_info(change_info.current());
+        change_info.advance_to_final(skip_version);
+
+        for (auto& notifier : notifiers)
+            notifier->run();
+
+        lock.lock();
+        for (auto& notifier : notifiers)
+            notifier->prepare_handover();
+        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, 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);
+        notifier->run();
+    }
+
+    // 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 : new_notifiers) {
+        notifier->prepare_handover();
+    }
+    for (auto& notifier : notifiers) {
+        notifier->prepare_handover();
+    }
+    clean_up_dead_notifiers();
+    m_notifier_cv.notify_all();
+}
+
+void RealmCoordinator::open_helper_shared_group()
+{
+    if (!m_notifier_sg) {
+        try {
+            std::unique_ptr<Group> 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();
+    }
+}
+
+void RealmCoordinator::advance_to_ready(Realm& realm)
+{
+    std::unique_lock<std::mutex> lock(m_notifier_mutex);
+    _impl::NotifierPackage notifiers(m_async_error, notifiers_for_realm(realm), this);
+    lock.unlock();
+    notifiers.package_and_wait(util::none);
+
+    auto& sg = Realm::Internal::get_shared_group(realm);
+    if (notifiers) {
+        auto version = notifiers.version();
+        if (version) {
+            auto current_version = sg->get_version_of_current_transaction();
+            // Notifications are out of date, so just discard
+            // This should only happen if begin_read() was used to change the
+            // read version outside of our control
+            if (*version < current_version)
+                return;
+            // While there is a newer version, notifications are for the current
+            // version so just deliver them without advancing
+            if (*version == current_version) {
+                notifiers.deliver(*sg);
+                notifiers.after_advance();
+                return;
+            }
+        }
+    }
+
+    transaction::advance(sg, realm.m_binding_context.get(), notifiers);
+}
+
+std::vector<std::shared_ptr<_impl::CollectionNotifier>> RealmCoordinator::notifiers_for_realm(Realm& realm)
+{
+    std::vector<std::shared_ptr<_impl::CollectionNotifier>> ret;
+    for (auto& notifier : m_new_notifiers) {
+        if (notifier->is_for_realm(realm))
+            ret.push_back(notifier);
+    }
+    for (auto& notifier : m_notifiers) {
+        if (notifier->is_for_realm(realm))
+            ret.push_back(notifier);
+    }
+    return ret;
+}
+
+bool RealmCoordinator::advance_to_latest(Realm& realm)
+{
+    using sgf = SharedGroupFriend;
+
+    auto& sg = Realm::Internal::get_shared_group(realm);
+    std::unique_lock<std::mutex> lock(m_notifier_mutex);
+    _impl::NotifierPackage notifiers(m_async_error, notifiers_for_realm(realm), this);
+    lock.unlock();
+    notifiers.package_and_wait(sgf::get_version_of_latest_snapshot(*sg));
+
+    auto version = sg->get_version_of_current_transaction();
+    transaction::advance(sg, realm.m_binding_context.get(), notifiers);
+
+    // Realm could be closed in the callbacks.
+    if (realm.is_closed())
+        return false;
+
+    return version != sg->get_version_of_current_transaction();
+}
+
+void RealmCoordinator::promote_to_write(Realm& realm)
+{
+    REALM_ASSERT(!realm.is_in_transaction());
+
+    std::unique_lock<std::mutex> lock(m_notifier_mutex);
+    _impl::NotifierPackage notifiers(m_async_error, notifiers_for_realm(realm), this);
+    lock.unlock();
+
+    auto& sg = Realm::Internal::get_shared_group(realm);
+    transaction::begin(sg, realm.m_binding_context.get(), notifiers);
+}
+
+void RealmCoordinator::process_available_async(Realm& realm)
+{
+    REALM_ASSERT(!realm.is_in_transaction());
+
+    std::unique_lock<std::mutex> lock(m_notifier_mutex);
+    auto notifiers = notifiers_for_realm(realm);
+    if (notifiers.empty())
+        return;
+
+    if (auto error = m_async_error) {
+        lock.unlock();
+        for (auto& notifier : notifiers)
+            notifier->deliver_error(m_async_error);
+        return;
+    }
+
+    bool in_read = realm.is_in_read_transaction();
+    auto& sg = Realm::Internal::get_shared_group(realm);
+    auto version = sg->get_version_of_current_transaction();
+    auto package = [&](auto& notifier) {
+        return !(notifier->has_run() && (!in_read || notifier->version() == version) && notifier->package_for_delivery());
+    };
+    notifiers.erase(std::remove_if(begin(notifiers), end(notifiers), package), end(notifiers));
+    lock.unlock();
+
+    // no before advance because the Realm is already at the given version,
+    // because we're either sending initial notifications or the write was
+    // done on this Realm instance
+
+    // Skip delivering if the Realm isn't in a read transaction
+    if (in_read) {
+        for (auto& notifier : notifiers)
+            notifier->deliver(*sg);
+    }
+
+    // but still call the change callbacks
+    for (auto& notifier : notifiers)
+        notifier->after_advance();
+}
+
+void RealmCoordinator::set_transaction_callback(std::function<void(VersionID, VersionID)> fn)
+{
+    create_sync_session();
+    m_transaction_callback = std::move(fn);
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp
new file mode 100644 (file)
index 0000000..082f520
--- /dev/null
@@ -0,0 +1,252 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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"
+
+#include "shared_realm.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 = source_shared_group().export_for_handover(q, MutableSourcePayload::Move);
+    DescriptorOrdering::generate_patch(target.get_descriptor_ordering(), m_ordering_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 = *m_query->get_table();
+    if (!table.is_attached())
+        return false;
+
+    auto table_ndx = table.get_index_in_group();
+    if (table_ndx == npos) { // is a subtable
+        auto& parent = *table.get_parent_table();
+        size_t row_ndx = table.get_parent_row_index();
+        size_t col_ndx = find_container_column(parent, row_ndx, &table, type_Table, &Table::get_subtable);
+        info.lists.push_back({parent.get_index_in_group(), row_ndx, col_ndx, &m_changes});
+    }
+    else { // is a top-level table
+        if (info.table_moves_needed.size() <= table_ndx)
+            info.table_moves_needed.resize(table_ndx + 1);
+        info.table_moves_needed[table_ndx] = true;
+    }
+
+    return has_run() && 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 (has_run() && 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 (has_run()) {
+        CollectionChangeBuilder* changes = nullptr;
+        if (table_ndx == npos)
+            changes = &m_changes;
+        else if (table_ndx < m_info->tables.size())
+            changes = &m_info->tables[table_ndx];
+
+        std::vector<size_t> 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<IndexSet> move_candidates;
+        if (changes) {
+            auto const& moves = changes->moves;
+            for (auto& idx : m_previous_rows) {
+                if (changes->deletions.contains(idx)) {
+                    // check if this deletion was actually a move
+                    auto it = lower_bound(begin(moves), end(moves), idx,
+                                          [](auto const& a, auto b) { return a.from < b; });
+                    idx = it != moves.end() && it->from == idx ? it->to : npos;
+                }
+                else
+                    idx = changes->insertions.shift(changes->deletions.unshift(idx));
+            }
+            if (m_target_is_in_table_order && !m_descriptor_ordering.will_apply_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()
+{
+    // Table's been deleted, so report all rows as deleted
+    if (!m_query->get_table()->is_attached()) {
+        m_changes = {};
+        m_changes.deletions.set(m_previous_rows.size());
+        m_previous_rows.clear();
+        return;
+    }
+
+    if (!need_to_run())
+        return;
+
+    m_query->sync_view_if_needed();
+    m_tv = m_query->find_all();
+    m_tv.apply_descriptor_ordering(m_descriptor_ordering);
+    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();
+
+        // add_changes() needs to be called even if there are no changes to
+        // clear the skip flag on the callbacks
+        add_changes(std::move(m_changes));
+        return;
+    }
+
+    REALM_ASSERT(m_tv.is_in_sync());
+
+    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();
+    if (!get_realm())
+        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_descriptor_ordering = DescriptorOrdering::create_from_and_consume_patch(m_ordering_handover, *m_query->get_table());
+}
+
+void ResultsNotifier::do_detach_from(SharedGroup& sg)
+{
+    REALM_ASSERT(m_query);
+    REALM_ASSERT(!m_tv.is_attached());
+
+    DescriptorOrdering::generate_patch(m_descriptor_ordering, m_ordering_handover);
+    m_query_handover = sg.export_for_handover(*m_query, MutableSourcePayload::Move);
+    m_query = nullptr;
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp
new file mode 100644 (file)
index 0000000..ae4a668
--- /dev/null
@@ -0,0 +1,862 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <realm/group_shared.hpp>
+#include <realm/lang_bind_helper.hpp>
+
+#include <algorithm>
+#include <numeric>
+
+using namespace realm;
+
+namespace {
+
+class KVOAdapter : public _impl::TransactionChangeInfo {
+public:
+    KVOAdapter(std::vector<BindingContext::ObserverState>& observers, BindingContext* context);
+
+    void before(SharedGroup& sg);
+    void after(SharedGroup& sg);
+
+private:
+    BindingContext* m_context;
+    std::vector<BindingContext::ObserverState>& m_observers;
+    std::vector<void *> m_invalidated;
+
+    struct ListInfo {
+        BindingContext::ObserverState* observer;
+        _impl::CollectionChangeBuilder builder;
+        size_t col;
+        size_t initial_size;
+    };
+    std::vector<ListInfo> m_lists;
+    VersionID m_version;
+
+    size_t new_table_ndx(size_t ndx) const { return ndx < table_indices.size() ? table_indices[ndx] : ndx; }
+};
+
+KVOAdapter::KVOAdapter(std::vector<BindingContext::ObserverState>& observers, BindingContext* context)
+: _impl::TransactionChangeInfo{}
+, m_context(context)
+, m_observers(observers)
+{
+    if (m_observers.empty())
+        return;
+
+    std::vector<size_t> tables_needed;
+    for (auto& observer : observers) {
+        tables_needed.push_back(observer.table_ndx);
+    }
+    std::sort(begin(tables_needed), end(tables_needed));
+    tables_needed.erase(std::unique(begin(tables_needed), end(tables_needed)), end(tables_needed));
+
+    auto realm = context->realm.lock();
+    auto& group = realm->read_group();
+    for (auto& observer : observers) {
+        auto table = group.get_table(observer.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_LinkList)
+                m_lists.push_back({&observer, {}, i, size_t(-1)});
+            else if (type == type_Table)
+                m_lists.push_back({&observer, {}, i, table->get_subtable_size(i, observer.row_ndx)});
+        }
+    }
+
+    auto max = std::max_element(begin(tables_needed), end(tables_needed));
+    if (*max >= table_modifications_needed.size())
+        table_modifications_needed.resize(*max + 1, false);
+    if (*max >= table_moves_needed.size())
+        table_moves_needed.resize(*max + 1, false);
+    for (auto& tbl : tables_needed) {
+        table_modifications_needed[tbl] = true;
+        table_moves_needed[tbl] = true;
+    }
+    for (auto& list : m_lists)
+        lists.push_back({list.observer->table_ndx, list.observer->row_ndx, list.col, &list.builder});
+}
+
+void KVOAdapter::before(SharedGroup& sg)
+{
+    if (!m_context)
+        return;
+
+    m_version = sg.get_version_of_current_transaction();
+    if (tables.empty())
+        return;
+
+    for (auto& observer : m_observers) {
+        size_t table_ndx = new_table_ndx(observer.table_ndx);
+        if (table_ndx >= tables.size())
+            continue;
+
+        auto const& table = tables[table_ndx];
+        auto const& moves = table.moves;
+        auto idx = observer.row_ndx;
+        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 (table.deletions.contains(idx)) {
+            m_invalidated.push_back(observer.info);
+            continue;
+        }
+        else
+            idx = table.insertions.shift(table.deletions.unshift(idx));
+        if (table.modifications.contains(idx)) {
+            observer.changes.resize(table.columns.size());
+            size_t i = 0;
+            for (auto& c : table.columns) {
+                auto& change = observer.changes[i];
+                if (table_ndx >= column_indices.size() || column_indices[table_ndx].empty())
+                    change.initial_column_index = i;
+                else if (i >= column_indices[table_ndx].size())
+                    change.initial_column_index = i - column_indices[table_ndx].size() + column_indices[table_ndx].back() + 1;
+                else
+                    change.initial_column_index = column_indices[table_ndx][i];
+                if (change.initial_column_index != npos && c.contains(idx))
+                    change.kind = BindingContext::ColumnInfo::Kind::Set;
+                ++i;
+            }
+        }
+    }
+
+    for (auto& list : m_lists) {
+        if (list.builder.empty()) {
+            // We may have pre-emptively marked the column as modified if the
+            // LinkList was selected but the actual changes made ended up being
+            // a no-op
+            if (list.col < list.observer->changes.size())
+                list.observer->changes[list.col].kind = BindingContext::ColumnInfo::Kind::None;
+            continue;
+        }
+        // If the containing row was deleted then changes will be empty
+        if (list.observer->changes.empty()) {
+            REALM_ASSERT_DEBUG(tables[new_table_ndx(list.observer->table_ndx)].deletions.contains(list.observer->row_ndx));
+            continue;
+        }
+        // otherwise the column should have been marked as modified
+        REALM_ASSERT(list.col < list.observer->changes.size());
+        auto& builder = list.builder;
+        auto& changes = list.observer->changes[list.col];
+
+        builder.modifications.remove(builder.insertions);
+
+        // KVO can't express moves (becaue NSArray doesn't have them), so
+        // transform them into a series of sets on each affected index when possible
+        if (!builder.moves.empty() && builder.insertions.count() == builder.moves.size() && builder.deletions.count() == builder.moves.size()) {
+            changes.kind = BindingContext::ColumnInfo::Kind::Set;
+            changes.indices = builder.modifications;
+            changes.indices.add(builder.deletions);
+
+            // Iterate over each of the rows which may have been shifted by
+            // the moves and check if it actually has been, or if it's ended
+            // up in the same place as it started (either because the moves were
+            // actually a swap that doesn't effect the rows in between, or the
+            // combination of moves happen to leave some intermediate rows in
+            // the same place)
+            auto in_range = [](auto& it, auto end, size_t i) {
+                if (it != end && i >= it->second)
+                    ++it;
+                return it != end && i >= it->first && i < it->second;
+            };
+
+            auto del_it = builder.deletions.begin(), del_end = builder.deletions.end();
+            auto ins_it = builder.insertions.begin(), ins_end = builder.insertions.end();
+            size_t start = std::min(ins_it->first, del_it->first);
+            size_t end = std::max(std::prev(ins_end)->second, std::prev(del_end)->second);
+            ptrdiff_t shift = 0;
+            for (size_t i = start; i < end; ++i) {
+                if (in_range(del_it, del_end, i))
+                    --shift;
+                else if (in_range(ins_it, ins_end, i + shift))
+                    ++shift;
+                if (shift != 0)
+                    changes.indices.add(i);
+            }
+        }
+        // KVO can't express multiple types of changes at once
+        else if (builder.insertions.empty() + builder.modifications.empty() + builder.deletions.empty() < 2) {
+            changes.kind = BindingContext::ColumnInfo::Kind::SetAll;
+        }
+        else if (!builder.insertions.empty()) {
+            changes.kind = BindingContext::ColumnInfo::Kind::Insert;
+            changes.indices = builder.insertions;
+        }
+        else if (!builder.modifications.empty()) {
+            changes.kind = BindingContext::ColumnInfo::Kind::Set;
+            changes.indices = builder.modifications;
+        }
+        else {
+            REALM_ASSERT(!builder.deletions.empty());
+            changes.kind = BindingContext::ColumnInfo::Kind::Remove;
+            // Table clears don't come with the size, so we need to fix up the
+            // notification to make it just delete all rows that actually existed
+            if (std::prev(builder.deletions.end())->second > list.initial_size)
+                changes.indices.set(list.initial_size);
+            else
+                changes.indices = builder.deletions;
+        }
+    }
+    m_context->will_change(m_observers, m_invalidated);
+}
+
+void KVOAdapter::after(SharedGroup& sg)
+{
+    if (!m_context)
+        return;
+    m_context->did_change(m_observers, m_invalidated,
+                          m_version != VersionID{} &&
+                          m_version != sg.get_version_of_current_transaction());
+}
+
+template<typename Derived>
+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<Derived *>(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, int_fast64_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; }
+
+    bool add_row_with_key(size_t, size_t, size_t, int64_t) { return true; }
+};
+
+class TransactLogValidationMixin {
+    // Index of currently selected table
+    size_t m_current_table = 0;
+
+    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");
+    }
+
+protected:
+    size_t current_table() const noexcept { return m_current_table; }
+
+public:
+
+    bool select_table(size_t group_level_ndx, int, const size_t*) noexcept
+    {
+        m_current_table = group_level_ndx;
+        return true;
+    }
+
+    // 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(); }
+
+    // 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; }
+
+    // Additive changes and reorderings are supported
+    bool insert_group_level_table(size_t, size_t, StringData) { return true; }
+    bool insert_column(size_t, DataType, StringData, bool) { return true; }
+    bool insert_link_column(size_t, DataType, StringData, size_t, size_t) { return true; }
+    bool set_link_type(size_t, LinkType) { return true; }
+    bool move_column(size_t, size_t) { return true; }
+    bool move_group_level_table(size_t, size_t) { return true; }
+
+    // Non-schema changes are all allowed
+    void parse_complete() { }
+    bool select_descriptor(int, const size_t*) { return true; }
+    bool select_link_list(size_t, size_t, size_t) { return true; }
+    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 move_row(size_t, size_t) { return true; }
+    bool clear_table(size_t=0) 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> {
+    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<typename Container>
+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) + from + 1, 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<typename Container>
+void insert_empty_at(Container& container, size_t pos)
+{
+    if (pos < container.size())
+        container.emplace(container.begin() + pos);
+}
+
+// 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;
+}
+
+void adjust_for_move(std::vector<size_t>& values, size_t from, size_t to)
+{
+    for (auto& value : values)
+        adjust_for_move(value, from, to);
+}
+
+void expand_to(std::vector<size_t>& cols, size_t i)
+{
+    auto old_size = cols.size();
+    if (old_size > i)
+        return;
+
+    cols.resize(std::max(old_size * 2, i + 1));
+    std::iota(begin(cols) + old_size, end(cols), old_size == 0 ? 0 : cols[old_size - 1] + 1);
+}
+
+void adjust_ge(std::vector<size_t>& values, size_t i)
+{
+    for (auto& value : values) {
+        if (value >= i)
+            ++value;
+    }
+}
+
+// Extends TransactLogValidator to track changes made to LinkViews
+class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyMixin<TransactLogObserver> {
+    _impl::TransactionChangeInfo& m_info;
+    _impl::CollectionChangeBuilder* m_active_list = nullptr;
+    _impl::CollectionChangeBuilder* m_active_table = nullptr;
+    _impl::CollectionChangeBuilder* m_active_descriptor = nullptr;
+
+    bool m_need_move_info = false;
+    bool m_is_top_level_table = true;
+
+    _impl::CollectionChangeBuilder* find_list(size_t tbl, size_t col, size_t row)
+    {
+        // 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 == tbl && it->row_ndx == row && it->col_ndx == col)
+                return it->changes;
+        }
+        return nullptr;
+    }
+
+public:
+    TransactLogObserver(_impl::TransactionChangeInfo& info)
+    : m_info(info) { }
+
+    void mark_dirty(size_t row, size_t col)
+    {
+        if (m_active_table)
+            m_active_table->modify(row, col);
+    }
+
+    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_descriptor(int levels, const size_t*) noexcept
+    {
+        if (levels == 0) // schema of selected table is being modified
+            m_active_descriptor = m_active_table;
+        else // schema of subtable is being modified; currently don't need to track this
+            m_active_descriptor = nullptr;
+        return true;
+    }
+
+    bool select_table(size_t group_level_ndx, int len, size_t const* path) noexcept
+    {
+        TransactLogValidationMixin::select_table(group_level_ndx, len, path);
+        m_active_table = nullptr;
+        m_is_top_level_table = true;
+
+        // Nested subtables currently not supported
+        if (len > 1) {
+            m_is_top_level_table = false;
+            return true;
+        }
+
+        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 true;
+
+        m_need_move_info = m_info.track_all || (tbl_ndx < m_info.table_moves_needed.size() &&
+                                                m_info.table_moves_needed[tbl_ndx]);
+        if (m_info.tables.size() <= tbl_ndx)
+            m_info.tables.resize(std::max(m_info.tables.size() * 2, tbl_ndx + 1));
+        m_active_table = &m_info.tables[tbl_ndx];
+
+        if (len == 1) {
+            // Mark the cell containing the subtable as modified since selecting
+            // a table is always followed by a modification of some sort
+            size_t col = path[0];
+            size_t row = path[1];
+            mark_dirty(row, col);
+
+            m_active_table = nullptr;
+            m_is_top_level_table = false;
+            if (auto table = find_list(current_table(), col, row)) {
+                m_active_table = table;
+                m_need_move_info = true;
+            }
+        }
+        return true;
+    }
+
+    bool select_link_list(size_t col, size_t row, size_t)
+    {
+        mark_dirty(row, col);
+        m_active_list = find_list(current_table(), col, row);
+        return true;
+    }
+
+    bool link_list_set(size_t index, size_t, size_t)
+    {
+        if (m_active_list)
+            m_active_list->modify(index);
+        return true;
+    }
+
+    bool link_list_insert(size_t index, size_t, size_t)
+    {
+        if (m_active_list)
+            m_active_list->insert(index);
+        return true;
+    }
+
+    bool link_list_erase(size_t index, size_t)
+    {
+        if (m_active_list)
+            m_active_list->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_list)
+            m_active_list->clear(old_size);
+        return true;
+    }
+
+    bool link_list_move(size_t from, size_t to)
+    {
+        if (m_active_list)
+            m_active_list->move(from, to);
+        return true;
+    }
+
+    bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t, bool)
+    {
+        if (m_active_table)
+            m_active_table->insert(row_ndx, num_rows_to_insert, m_need_move_info);
+        if (!m_is_top_level_table)
+            return true;
+        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 add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t, int64_t)
+    {
+        insert_empty_rows(row_ndx, 1, prior_num_rows, false);
+        return true;
+    }
+
+    bool erase_rows(size_t row_ndx, size_t, size_t prior_num_rows, bool unordered)
+    {
+        if (!unordered) {
+            if (m_active_table)
+                m_active_table->erase(row_ndx);
+            return true;
+        }
+        size_t last_row = prior_num_rows - 1;
+        if (m_active_table)
+            m_active_table->move_over(row_ndx, last_row, m_need_move_info);
+
+        if (!m_is_top_level_table)
+            return true;
+        for (size_t i = 0; i < m_info.lists.size(); ++i) {
+            auto& list = m_info.lists[i];
+            if (list.table_ndx != current_table())
+                continue;
+            if (list.row_ndx == row_ndx) {
+                if (i + 1 < m_info.lists.size())
+                    m_info.lists[i] = std::move(m_info.lists.back());
+                m_info.lists.pop_back();
+                continue;
+            }
+            if (list.row_ndx == last_row)
+                list.row_ndx = row_ndx;
+        }
+
+        return true;
+    }
+
+    bool swap_rows(size_t row_ndx_1, size_t row_ndx_2) {
+        REALM_ASSERT(row_ndx_1 < row_ndx_2);
+        if (!m_is_top_level_table) {
+            if (m_active_table) {
+                m_active_table->move(row_ndx_1, row_ndx_2);
+                if (row_ndx_1 + 1 != row_ndx_2)
+                    m_active_table->move(row_ndx_2 - 1, row_ndx_1);
+            }
+            return true;
+        }
+
+        if (m_active_table)
+            m_active_table->swap(row_ndx_1, row_ndx_2, m_need_move_info);
+        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;
+            }
+        }
+        return true;
+    }
+
+    bool move_row(size_t from_ndx, size_t to_ndx) {
+        // Move row is not supported for top level tables:
+        REALM_ASSERT(!m_active_table || !m_is_top_level_table);
+
+        if (m_active_table)
+            m_active_table->move(from_ndx, to_ndx);
+        return true;
+    }
+
+    bool merge_rows(size_t from, size_t to)
+    {
+        if (m_active_table)
+            m_active_table->subsume(from, to, m_need_move_info);
+        if (!m_is_top_level_table)
+            return true;
+        for (auto& list : m_info.lists) {
+            if (list.table_ndx == current_table() && list.row_ndx == from)
+                list.row_ndx = to;
+        }
+        return true;
+    }
+
+    bool clear_table(size_t=0)
+    {
+        auto tbl_ndx = current_table();
+        if (m_active_table)
+            m_active_table->clear(std::numeric_limits<size_t>::max());
+        if (!m_is_top_level_table)
+            return true;
+        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));
+        return true;
+    }
+
+    bool insert_column(size_t ndx, DataType, StringData, bool)
+    {
+        m_info.schema_changed = true;
+
+        if (m_active_descriptor)
+            m_active_descriptor->insert_column(ndx);
+        if (m_active_descriptor != m_active_table || !m_is_top_level_table)
+            return true;
+        for (auto& list : m_info.lists) {
+            if (list.table_ndx == current_table() && list.col_ndx >= ndx)
+                ++list.col_ndx;
+        }
+        if (m_info.column_indices.size() <= current_table())
+            m_info.column_indices.resize(current_table() + 1);
+        auto& indices = m_info.column_indices[current_table()];
+        expand_to(indices, ndx);
+        insert_empty_at(indices, ndx);
+        indices[ndx] = npos;
+        return true;
+    }
+
+    void prepare_table_indices()
+    {
+        if (m_info.table_indices.empty() && !m_info.table_modifications_needed.empty()) {
+            m_info.table_indices.resize(m_info.table_modifications_needed.size());
+            std::iota(begin(m_info.table_indices), end(m_info.table_indices), 0);
+        }
+    }
+
+    bool insert_group_level_table(size_t ndx, size_t, StringData)
+    {
+        m_info.schema_changed = true;
+
+        for (auto& list : m_info.lists) {
+            if (list.table_ndx >= ndx)
+                ++list.table_ndx;
+        }
+        prepare_table_indices();
+        adjust_ge(m_info.table_indices, 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)
+    {
+        m_info.schema_changed = true;
+
+        if (m_active_descriptor)
+            m_active_descriptor->move_column(from, to);
+        if (m_active_descriptor != m_active_table || !m_is_top_level_table)
+            return true;
+        for (auto& list : m_info.lists) {
+            if (list.table_ndx == current_table())
+                adjust_for_move(list.col_ndx, from, to);
+        }
+        if (m_info.column_indices.size() <= current_table())
+            m_info.column_indices.resize(current_table() + 1);
+        expand_to(m_info.column_indices[current_table()], std::max(from, to) + 1);
+        rotate(m_info.column_indices[current_table()], from, to);
+        return true;
+    }
+
+    bool move_group_level_table(size_t from, size_t to)
+    {
+        m_info.schema_changed = true;
+
+        for (auto& list : m_info.lists)
+            adjust_for_move(list.table_ndx, from, to);
+
+        prepare_table_indices();
+        adjust_for_move(m_info.table_indices, 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); }
+};
+
+class KVOTransactLogObserver : public TransactLogObserver {
+    KVOAdapter m_adapter;
+    _impl::NotifierPackage& m_notifiers;
+    SharedGroup& m_sg;
+
+public:
+    KVOTransactLogObserver(std::vector<BindingContext::ObserverState>& observers,
+                           BindingContext* context,
+                           _impl::NotifierPackage& notifiers,
+                           SharedGroup& sg)
+    : TransactLogObserver(m_adapter)
+    , m_adapter(observers, context)
+    , m_notifiers(notifiers)
+    , m_sg(sg)
+    {
+    }
+
+    ~KVOTransactLogObserver()
+    {
+        m_adapter.after(m_sg);
+    }
+
+    void parse_complete()
+    {
+        TransactLogObserver::parse_complete();
+        m_adapter.before(m_sg);
+
+        using sgf = _impl::SharedGroupFriend;
+        m_notifiers.package_and_wait(sgf::get_version_of_latest_snapshot(m_sg));
+        m_notifiers.before_advance();
+    }
+};
+
+template<typename Func>
+void advance_with_notifications(BindingContext* context, const std::unique_ptr<SharedGroup>& sg,
+                                Func&& func, _impl::NotifierPackage& notifiers)
+{
+    auto old_version = sg->get_version_of_current_transaction();
+    std::vector<BindingContext::ObserverState> observers;
+    if (context) {
+        observers = context->get_observed_rows();
+    }
+
+    // Advancing to the latest version with notifiers requires using the full
+    // transaction log observer so that we have a point where we know what
+    // version we're going to before we actually advance to that version
+    if (observers.empty() && (!notifiers || notifiers.version())) {
+        notifiers.before_advance();
+        func(TransactLogValidator());
+        auto new_version = sg->get_version_of_current_transaction();
+        if (context && old_version != new_version)
+            context->did_change({}, {});
+        // did_change() could close the Realm. Just return if it does.
+        if (!sg)
+            return;
+        // did_change() can change the read version, and if it does we can't
+        // deliver notifiers
+        if (new_version == sg->get_version_of_current_transaction())
+            notifiers.deliver(*sg);
+        notifiers.after_advance();
+        return;
+    }
+
+    func(KVOTransactLogObserver(observers, context, notifiers, *sg));
+    notifiers.package_and_wait(sg->get_version_of_current_transaction().version); // is a no-op if parse_complete() was called
+    notifiers.deliver(*sg);
+    notifiers.after_advance();
+}
+
+} // anonymous namespace
+
+namespace realm {
+namespace _impl {
+
+namespace transaction {
+void advance(SharedGroup& sg, BindingContext*, VersionID version)
+{
+    LangBindHelper::advance_read(sg, TransactLogValidator(), version);
+}
+
+void advance(const std::unique_ptr<SharedGroup>& sg, BindingContext* context, NotifierPackage& notifiers)
+{
+    advance_with_notifications(context, sg, [&](auto&&... args) {
+        LangBindHelper::advance_read(*sg, std::move(args)..., notifiers.version().value_or(VersionID{}));
+    }, notifiers);
+}
+
+void begin_without_validation(SharedGroup& sg)
+{
+    LangBindHelper::promote_to_write(sg);
+}
+
+void begin(const std::unique_ptr<SharedGroup>& sg, BindingContext* context, NotifierPackage& notifiers)
+{
+    advance_with_notifications(context, sg, [&](auto&&... args) {
+        LangBindHelper::promote_to_write(*sg, std::move(args)...);
+    }, notifiers);
+}
+
+void commit(SharedGroup& sg)
+{
+    LangBindHelper::commit_and_continue_as_read(sg);
+}
+
+void cancel(SharedGroup& sg, BindingContext* context)
+{
+    std::vector<BindingContext::ObserverState> observers;
+    if (context) {
+        observers = context->get_observed_rows();
+    }
+    if (observers.empty()) {
+        LangBindHelper::rollback_and_continue_as_read(sg);
+        return;
+    }
+
+    _impl::NotifierPackage notifiers;
+    LangBindHelper::rollback_and_continue_as_read(sg, KVOTransactLogObserver(observers, context, notifiers, sg));
+}
+
+void advance(SharedGroup& sg, TransactionChangeInfo& info, VersionID version)
+{
+    if (!info.track_all && info.table_modifications_needed.empty() && info.lists.empty()) {
+        LangBindHelper::advance_read(sg, version);
+    }
+    else {
+        LangBindHelper::advance_read(sg, TransactLogObserver(info), version);
+    }
+
+}
+
+} // namespace transaction
+} // namespace _impl
+} // namespace realm
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp
new file mode 100644 (file)
index 0000000..1620eea
--- /dev/null
@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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>& realm, bool cache)
+: m_realm(realm)
+, m_execution_context(realm->config().execution_context)
+, m_realm_key(realm.get())
+, m_cache(cache)
+, m_signal(std::make_shared<util::EventLoopSignal<Callback>>(Callback{realm}))
+{
+}
+
+WeakRealmNotifier::~WeakRealmNotifier() = default;
+
+void WeakRealmNotifier::Callback::operator()() const
+{
+    if (auto realm = weak_realm.lock()) {
+        realm->notify();
+    }
+}
+
+void WeakRealmNotifier::notify()
+{
+    m_signal->notify();
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/index_set.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/index_set.cpp
new file mode 100644 (file)
index 0000000..e1a3b10
--- /dev/null
@@ -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 <realm/util/assert.hpp>
+
+#include <algorithm>
+
+using namespace realm;
+using namespace realm::_impl;
+
+const size_t IndexSet::npos;
+
+template<typename T>
+void MutableChunkedRangeVectorIterator<T>::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<typename T>
+void MutableChunkedRangeVectorIterator<T>::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<typename T>
+void MutableChunkedRangeVectorIterator<T>::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({{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) noexcept
+{
+    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<size_t, size_t>;
+
+    ChunkedRangeVectorBuilder(ChunkedRangeVector const& expected);
+    void push_back(size_t index);
+    void push_back(std::pair<size_t, size_t> range);
+    std::vector<ChunkedRangeVector::Chunk> finalize();
+private:
+    std::vector<ChunkedRangeVector::Chunk> 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<size_t, size_t> 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<ChunkedRangeVector::Chunk> 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<size_t> values)
+{
+    for (size_t v : values)
+        add(v);
+}
+
+bool IndexSet::contains(size_t index) const noexcept
+{
+    auto it = const_cast<IndexSet*>(this)->find(index);
+    return it != end() && it->first <= index;
+}
+
+size_t IndexSet::count(size_t start_index, size_t end_index) const noexcept
+{
+    auto it = const_cast<IndexSet*>(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) noexcept
+{
+    return find(index, begin());
+}
+
+IndexSet::iterator IndexSet::find(size_t index, iterator begin) noexcept
+{
+    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 noexcept
+{
+    // 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 noexcept
+{
+    REALM_ASSERT_DEBUG(!contains(index));
+    return index - count(0, index);
+}
+
+void IndexSet::clear() noexcept
+{
+    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/iOS/Pods/Realm/Realm/ObjectStore/src/list.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/list.cpp
new file mode 100644 (file)
index 0000000..12b4082
--- /dev/null
@@ -0,0 +1,468 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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/primitive_list_notifier.hpp"
+#include "impl/realm_coordinator.hpp"
+#include "object_schema.hpp"
+#include "object_store.hpp"
+#include "results.hpp"
+#include "schema.hpp"
+#include "shared_realm.hpp"
+
+#include <realm/link_view.hpp>
+
+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<Realm> r, Table& parent_table, size_t col, size_t row)
+: m_realm(std::move(r))
+{
+    auto type = parent_table.get_column_type(col);
+    REALM_ASSERT(type == type_LinkList || type == type_Table);
+    if (type == type_LinkList) {
+        m_link_view = parent_table.get_linklist(col, row);
+        m_table.reset(&m_link_view->get_target_table());
+    }
+    else {
+        m_table = parent_table.get_subtable(col, row);
+    }
+}
+
+List::List(std::shared_ptr<Realm> r, LinkViewRef l) noexcept
+: m_realm(std::move(r))
+, m_link_view(std::move(l))
+{
+    m_table.reset(&m_link_view->get_target_table());
+}
+
+List::List(std::shared_ptr<Realm> r, TableRef t) noexcept
+: m_realm(std::move(r))
+, m_table(std::move(t))
+{
+}
+
+static StringData object_name(Table const& table)
+{
+    return ObjectStore::object_type_for_table_name(table.get_name());
+}
+
+ObjectSchema const& List::get_object_schema() const
+{
+    verify_attached();
+    REALM_ASSERT(m_link_view);
+
+    if (!m_object_schema) {
+        REALM_ASSERT(get_type() == PropertyType::Object);
+        auto object_type = object_name(m_link_view->get_target_table());
+        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 ? m_table->where(m_link_view) : m_table->where();
+}
+
+size_t List::get_origin_row_index() const
+{
+    verify_attached();
+    return m_link_view ? m_link_view->get_origin_row_index() : m_table->get_parent_row_index();
+}
+
+void List::verify_valid_row(size_t row_ndx, bool insertion) const
+{
+    size_t s = size();
+    if (row_ndx > s || (!insertion && row_ndx == s)) {
+        throw OutOfBoundsIndexException{row_ndx, s + insertion};
+    }
+}
+
+void List::validate(RowExpr row) const
+{
+    if (!row.is_attached())
+        throw std::invalid_argument("Object has been deleted or invalidated");
+    if (row.get_table() != &m_link_view->get_target_table())
+        throw std::invalid_argument(util::format("Object of type (%1) does not match List type (%2)",
+                                                 object_name(*row.get_table()),
+                                                 object_name(m_link_view->get_target_table())));
+}
+
+bool List::is_valid() const
+{
+    if (!m_realm)
+        return false;
+    m_realm->verify_thread();
+    if (m_link_view)
+        return m_link_view->is_attached();
+    return m_table && m_table->is_attached();
+}
+
+void List::verify_attached() const
+{
+    if (!is_valid()) {
+        throw InvalidatedException();
+    }
+}
+
+void List::verify_in_transaction() const
+{
+    verify_attached();
+    m_realm->verify_in_write();
+}
+
+size_t List::size() const
+{
+    verify_attached();
+    return m_link_view ? m_link_view->size() : m_table->size();
+}
+
+size_t List::to_table_ndx(size_t row) const noexcept
+{
+    return m_link_view ? m_link_view->get(row).get_index() : row;
+}
+
+PropertyType List::get_type() const
+{
+    verify_attached();
+    return m_link_view ? PropertyType::Object
+                       : ObjectSchema::from_core_type(*m_table->get_descriptor(), 0);
+}
+
+namespace {
+template<typename T>
+auto get(Table& table, size_t row)
+{
+    return table.get<T>(0, row);
+}
+
+template<>
+auto get<RowExpr>(Table& table, size_t row)
+{
+    return table.get(row);
+}
+}
+
+template<typename T>
+T List::get(size_t row_ndx) const
+{
+    verify_valid_row(row_ndx);
+    return realm::get<T>(*m_table, to_table_ndx(row_ndx));
+}
+
+template RowExpr List::get(size_t) const;
+
+template<typename T>
+size_t List::find(T const& value) const
+{
+    verify_attached();
+    return m_table->find_first(0, value);
+}
+
+template<>
+size_t List::find(RowExpr const& row) const
+{
+    verify_attached();
+    if (!row.is_attached())
+        return not_found;
+    validate(row);
+
+    return m_link_view ? m_link_view->find(row.get_index()) : row.get_index();
+}
+
+size_t List::find(Query&& q) const
+{
+    verify_attached();
+    if (m_link_view) {
+        size_t index = get_query().and_query(std::move(q)).find();
+        return index == not_found ? index : m_link_view->find(index);
+    }
+    return q.find();
+}
+
+template<typename T>
+void List::add(T value)
+{
+    verify_in_transaction();
+    m_table->set(0, m_table->add_empty_row(), value);
+}
+
+template<>
+void List::add(size_t target_row_ndx)
+{
+    verify_in_transaction();
+    m_link_view->add(target_row_ndx);
+}
+
+template<>
+void List::add(RowExpr row)
+{
+    validate(row);
+    add(row.get_index());
+}
+
+template<>
+void List::add(int value)
+{
+    verify_in_transaction();
+    if (m_link_view)
+        add(static_cast<size_t>(value));
+    else
+        add(static_cast<int64_t>(value));
+}
+
+template<typename T>
+void List::insert(size_t row_ndx, T value)
+{
+    verify_in_transaction();
+    verify_valid_row(row_ndx, true);
+    m_table->insert_empty_row(row_ndx);
+    m_table->set(0, row_ndx, value);
+}
+
+template<>
+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);
+}
+
+template<>
+void List::insert(size_t row_ndx, RowExpr row)
+{
+    validate(row);
+    insert(row_ndx, row.get_index());
+}
+
+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
+    if (source_ndx == dest_ndx)
+        return;
+
+    if (m_link_view)
+        m_link_view->move(source_ndx, dest_ndx);
+    else
+        m_table->move_row(source_ndx, dest_ndx);
+}
+
+void List::remove(size_t row_ndx)
+{
+    verify_in_transaction();
+    verify_valid_row(row_ndx);
+    if (m_link_view)
+        m_link_view->remove(row_ndx);
+    else
+        m_table->remove(row_ndx);
+}
+
+void List::remove_all()
+{
+    verify_in_transaction();
+    if (m_link_view)
+        m_link_view->clear();
+    else
+        m_table->clear();
+}
+
+template<typename T>
+void List::set(size_t row_ndx, T value)
+{
+    verify_in_transaction();
+    verify_valid_row(row_ndx);
+    m_table->set(0, row_ndx, value);
+}
+
+template<>
+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);
+}
+
+template<>
+void List::set(size_t row_ndx, RowExpr row)
+{
+    validate(row);
+    set(row_ndx, row.get_index());
+}
+
+void List::swap(size_t ndx1, size_t ndx2)
+{
+    verify_in_transaction();
+    verify_valid_row(ndx1);
+    verify_valid_row(ndx2);
+    if (m_link_view)
+        m_link_view->swap(ndx1, ndx2);
+    else
+        m_table->swap_rows(ndx1, ndx2);
+}
+
+void List::delete_at(size_t row_ndx)
+{
+    verify_in_transaction();
+    verify_valid_row(row_ndx);
+    if (m_link_view)
+        m_link_view->remove_target_row(row_ndx);
+    else
+        m_table->remove(row_ndx);
+}
+
+void List::delete_all()
+{
+    verify_in_transaction();
+    if (m_link_view)
+        m_link_view->remove_all_target_rows();
+    else
+        m_table->clear();
+}
+
+Results List::sort(SortDescriptor order) const
+{
+    verify_attached();
+    if (m_link_view)
+        return Results(m_realm, m_link_view, util::none, std::move(order));
+
+    DescriptorOrdering new_order;
+    new_order.append_sort(std::move(order));
+    return Results(m_realm, get_query(), std::move(new_order));
+}
+
+Results List::sort(std::vector<std::pair<std::string, bool>> const& keypaths) const
+{
+    return as_results().sort(keypaths);
+}
+
+Results List::filter(Query q) const
+{
+    verify_attached();
+    if (m_link_view)
+        return Results(m_realm, m_link_view, get_query().and_query(std::move(q)));
+    return Results(m_realm, get_query().and_query(std::move(q)));
+}
+
+Results List::as_results() const
+{
+    verify_attached();
+    return m_link_view ? Results(m_realm, m_link_view) : Results(m_realm, *m_table);
+}
+
+Results List::snapshot() const
+{
+    return as_results().snapshot();
+}
+
+util::Optional<Mixed> List::max(size_t column)
+{
+    return as_results().max(column);
+}
+
+util::Optional<Mixed> List::min(size_t column)
+{
+    return as_results().min(column);
+}
+
+Mixed List::sum(size_t column)
+{
+    // Results::sum() returns none only for Mode::Empty Results, so we can
+    // safely ignore that possibility here
+    return *as_results().sum(column);
+}
+
+util::Optional<double> List::average(size_t column)
+{
+    return as_results().average(column);
+}
+
+// These definitions rely on that LinkViews and Tables are interned by core
+bool List::operator==(List const& rgt) const noexcept
+{
+    return m_link_view == rgt.m_link_view && m_table.get() == rgt.m_table.get();
+}
+
+NotificationToken List::add_notification_callback(CollectionChangeCallback cb) &
+{
+    verify_attached();
+    // Adding a new callback to a notifier which had all of its callbacks
+    // removed does not properly reinitialize the notifier. Work around this by
+    // recreating it instead.
+    // FIXME: The notifier lifecycle here is dumb (when all callbacks are removed
+    // from a notifier a zombie is left sitting around uselessly) and should be
+    // cleaned up.
+    if (m_notifier && !m_notifier->have_callbacks())
+        m_notifier.reset();
+    if (!m_notifier) {
+        if (get_type() == PropertyType::Object)
+            m_notifier = std::static_pointer_cast<_impl::CollectionNotifier>(std::make_shared<ListNotifier>(m_link_view, m_realm));
+        else
+            m_notifier = std::static_pointer_cast<_impl::CollectionNotifier>(std::make_shared<PrimitiveListNotifier>(m_table, 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 - 1))
+, requested(r), valid_count(c) {}
+
+#define REALM_PRIMITIVE_LIST_TYPE(T) \
+    template T List::get<T>(size_t) const; \
+    template size_t List::find<T>(T const&) const; \
+    template void List::add<T>(T); \
+    template void List::insert<T>(size_t, T); \
+    template void List::set<T>(size_t, T);
+
+REALM_PRIMITIVE_LIST_TYPE(bool)
+REALM_PRIMITIVE_LIST_TYPE(int64_t)
+REALM_PRIMITIVE_LIST_TYPE(float)
+REALM_PRIMITIVE_LIST_TYPE(double)
+REALM_PRIMITIVE_LIST_TYPE(StringData)
+REALM_PRIMITIVE_LIST_TYPE(BinaryData)
+REALM_PRIMITIVE_LIST_TYPE(Timestamp)
+REALM_PRIMITIVE_LIST_TYPE(util::Optional<bool>)
+REALM_PRIMITIVE_LIST_TYPE(util::Optional<int64_t>)
+REALM_PRIMITIVE_LIST_TYPE(util::Optional<float>)
+REALM_PRIMITIVE_LIST_TYPE(util::Optional<double>)
+
+#undef REALM_PRIMITIVE_LIST_TYPE
+} // namespace realm
+
+namespace std {
+size_t hash<realm::List>::operator()(realm::List const& list) const
+{
+    return std::hash<void*>()(list.m_link_view ? list.m_link_view.get() : (void*)list.m_table.get());
+}
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/object.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/object.cpp
new file mode 100644 (file)
index 0000000..dea2708
--- /dev/null
@@ -0,0 +1,93 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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.hpp"
+
+#include "impl/object_notifier.hpp"
+#include "impl/realm_coordinator.hpp"
+#include "object_schema.hpp"
+#include "object_store.hpp"
+
+using namespace realm;
+
+InvalidatedObjectException::InvalidatedObjectException(const std::string& object_type)
+: std::logic_error("Accessing object of type " + object_type + " which has been invalidated or deleted")
+, object_type(object_type)
+{}
+
+InvalidPropertyException::InvalidPropertyException(const std::string& object_type, const std::string& property_name)
+: std::logic_error(util::format("Property '%1.%2' does not exist", object_type, property_name))
+, object_type(object_type), property_name(property_name)
+{}
+
+MissingPropertyValueException::MissingPropertyValueException(const std::string& object_type, const std::string& property_name)
+: std::logic_error(util::format("Missing value for property '%1.%2'", object_type, property_name))
+, object_type(object_type), property_name(property_name)
+{}
+
+MissingPrimaryKeyException::MissingPrimaryKeyException(const std::string& object_type)
+: std::logic_error(util::format("'%1' does not have a primary key defined", object_type))
+, object_type(object_type)
+{}
+
+ReadOnlyPropertyException::ReadOnlyPropertyException(const std::string& object_type, const std::string& property_name)
+: std::logic_error(util::format("Cannot modify read-only property '%1.%2'", object_type, property_name))
+, object_type(object_type), property_name(property_name) {}
+
+Object::Object(SharedRealm r, ObjectSchema const& s, RowExpr const& o)
+: m_realm(std::move(r)), m_object_schema(&s), m_row(o) { }
+
+Object::Object(SharedRealm r, StringData object_type, size_t ndx)
+: m_realm(std::move(r))
+, m_object_schema(&*m_realm->schema().find(object_type))
+, m_row(ObjectStore::table_for_object_type(m_realm->read_group(), object_type)->get(ndx))
+{ }
+
+Object::Object() = default;
+Object::~Object() = default;
+Object::Object(Object const&) = default;
+Object::Object(Object&&) = default;
+Object& Object::operator=(Object const&) = default;
+Object& Object::operator=(Object&&) = default;
+
+NotificationToken Object::add_notification_callback(CollectionChangeCallback callback) &
+{
+    verify_attached();
+    if (!m_notifier) {
+        m_notifier = std::make_shared<_impl::ObjectNotifier>(m_row, m_realm);
+        _impl::RealmCoordinator::register_notifier(m_notifier);
+    }
+    return {m_notifier, m_notifier->add_callback(std::move(callback))};
+}
+
+void Object::verify_attached() const
+{
+    m_realm->verify_thread();
+    if (!m_row.is_attached()) {
+        throw InvalidatedObjectException(m_object_schema->name);
+    }
+}
+
+Property const& Object::property_for_name(StringData prop_name) const
+{
+    auto prop = m_object_schema->property_for_name(prop_name);
+    if (!prop) {
+        throw InvalidPropertyException(m_object_schema->name, prop_name);
+    }
+    return *prop;
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp
new file mode 100644 (file)
index 0000000..e5082fb
--- /dev/null
@@ -0,0 +1,237 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "feature_checks.hpp"
+#include "object_store.hpp"
+#include "property.hpp"
+#include "schema.hpp"
+
+
+#include <realm/data_type.hpp>
+#include <realm/descriptor.hpp>
+#include <realm/group.hpp>
+#include <realm/table.hpp>
+
+using namespace realm;
+
+ObjectSchema::ObjectSchema() = default;
+ObjectSchema::~ObjectSchema() = default;
+
+ObjectSchema::ObjectSchema(std::string name, std::initializer_list<Property> persisted_properties)
+: ObjectSchema(std::move(name), persisted_properties, {})
+{
+}
+
+ObjectSchema::ObjectSchema(std::string name, std::initializer_list<Property> persisted_properties,
+                           std::initializer_list<Property> computed_properties)
+: name(std::move(name))
+, persisted_properties(persisted_properties)
+, computed_properties(computed_properties)
+{
+    for (auto const& prop : persisted_properties) {
+        if (prop.is_primary) {
+            primary_key = prop.name;
+            break;
+        }
+    }
+}
+
+PropertyType ObjectSchema::from_core_type(Descriptor const& table, size_t col)
+{
+    auto optional = table.is_nullable(col) ? PropertyType::Nullable : PropertyType::Required;
+    switch (table.get_column_type(col)) {
+        case type_Int:       return PropertyType::Int | optional;
+        case type_Float:     return PropertyType::Float | optional;
+        case type_Double:    return PropertyType::Double | optional;
+        case type_Bool:      return PropertyType::Bool | optional;
+        case type_String:    return PropertyType::String | optional;
+        case type_Binary:    return PropertyType::Data | optional;
+        case type_Timestamp: return PropertyType::Date | optional;
+        case type_Mixed:     return PropertyType::Any | optional;
+        case type_Link:      return PropertyType::Object | PropertyType::Nullable;
+        case type_LinkList:  return PropertyType::Object | PropertyType::Array;
+        case type_Table:     return from_core_type(*table.get_subdescriptor(col), 0) | PropertyType::Array;
+        default: REALM_UNREACHABLE();
+    }
+}
+
+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++) {
+        if (auto property = ObjectStore::property_for_column_index(table, col)) {
+            persisted_properties.push_back(std::move(property.value()));
+        }
+    }
+
+    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 &prop;
+        }
+    }
+    for (auto& prop : computed_properties) {
+        if (StringData(prop.name) == name) {
+            return &prop;
+        }
+    }
+    return nullptr;
+}
+
+const Property *ObjectSchema::property_for_name(StringData name) const {
+    return const_cast<ObjectSchema *>(this)->property_for_name(name);
+}
+
+bool ObjectSchema::property_is_computed(Property const& property) const {
+    auto end = computed_properties.end();
+    return std::find(computed_properties.begin(), end, property) != end;
+}
+
+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<ObjectSchemaValidationException>& exceptions)
+{
+    if (prop.type == PropertyType::LinkingObjects && !is_array(prop.type)) {
+        exceptions.emplace_back("Linking Objects property '%1.%2' must be an array.",
+                                object_name, prop.name);
+    }
+
+    // check nullablity
+    if (is_nullable(prop.type) && !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 && !is_nullable(prop.type) && !is_array(prop.type)) {
+        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.type != PropertyType::Int && prop.type != PropertyType::String) {
+            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 = &prop;
+    }
+
+    // check indexable
+    if (prop.is_indexed && !prop.type_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::LinkingObjects) {
+        if (!prop.object_type.empty()) {
+            exceptions.emplace_back("Property '%1.%2' of type '%3' cannot have an object type.",
+                                    object_name, prop.name, prop.type_string());
+        }
+        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) {
+        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<ObjectSchemaValidationException>& 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/iOS/Pods/Realm/Realm/ObjectStore/src/object_store.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/object_store.cpp
new file mode 100644 (file)
index 0000000..a98e774
--- /dev/null
@@ -0,0 +1,940 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "feature_checks.hpp"
+#include "object_schema.hpp"
+#include "schema.hpp"
+#include "shared_realm.hpp"
+
+#include <realm/descriptor.hpp>
+#include <realm/group.hpp>
+#include <realm/table.hpp>
+#include <realm/table_view.hpp>
+#include <realm/util/assert.hpp>
+
+#if REALM_ENABLE_SYNC
+#include <realm/sync/object.hpp>
+#endif // REALM_ENABLE_SYNC
+
+#include <string.h>
+
+using namespace realm;
+
+constexpr uint64_t ObjectStore::NotVersioned;
+
+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) {
+    // The tables 'pk' and 'metadata' are treated specially by Sync. The 'pk' table
+    // is populated by `sync::create_table` and friends, while the 'metadata' table
+    // is simply ignored.
+    TableRef pk_table = group.get_or_add_table(c_primaryKeyTableName);
+    TableRef metadata_table = group.get_or_add_table(c_metadataTableName);
+    const size_t empty_table_size = 0;
+
+    if (metadata_table->get_column_count() == empty_table_size) {
+        metadata_table->insert_column(c_versionColumnIndex, type_Int, c_versionColumnName);
+        metadata_table->add_empty_row();
+        // set initial version
+        metadata_table->set_int(c_versionColumnIndex, c_zeroRowIndex, ObjectStore::NotVersioned);
+    }
+
+    if (pk_table->get_column_count() == 0) {
+        pk_table->insert_column(c_primaryKeyObjectClassColumnIndex, type_String, c_primaryKeyObjectClassColumnName);
+        pk_table->insert_column(c_primaryKeyPropertyNameColumnIndex, type_String, c_primaryKeyPropertyNameColumnName);
+    }
+    pk_table->add_search_index(c_primaryKeyObjectClassColumnIndex);
+}
+
+void set_schema_version(Group& group, uint64_t version) {
+    TableRef table = group.get_table(c_metadataTableName);
+    table->set_int(c_versionColumnIndex, c_zeroRowIndex, version);
+}
+
+template<typename Group>
+auto table_for_object_schema(Group& group, ObjectSchema const& object_schema)
+{
+    return ObjectStore::table_for_object_type(group, object_schema.name);
+}
+
+DataType to_core_type(PropertyType type)
+{
+    REALM_ASSERT(type != PropertyType::Object); // Link columns have to be handled differently
+    REALM_ASSERT(type != PropertyType::Any); // Mixed columns can't be created
+    switch (type & ~PropertyType::Flags) {
+        case PropertyType::Int:    return type_Int;
+        case PropertyType::Bool:   return type_Bool;
+        case PropertyType::Float:  return type_Float;
+        case PropertyType::Double: return type_Double;
+        case PropertyType::String: return type_String;
+        case PropertyType::Date:   return type_Timestamp;
+        case PropertyType::Data:   return type_Binary;
+        default: REALM_COMPILER_HINT_UNREACHABLE();
+    }
+}
+
+void insert_column(Group& group, Table& table, Property const& property, size_t col_ndx)
+{
+    // Cannot directly insert a LinkingObjects column (a computed property).
+    // LinkingObjects must be an artifact of an existing link column.
+    REALM_ASSERT(property.type != PropertyType::LinkingObjects);
+
+    if (property.type == PropertyType::Object) {
+        auto target_name = ObjectStore::table_name_for_object_type(property.object_type);
+        TableRef link_table = group.get_table(target_name);
+        REALM_ASSERT(link_table);
+        table.insert_column_link(col_ndx, is_array(property.type) ? type_LinkList : type_Link,
+                                 property.name, *link_table);
+    }
+    else if (is_array(property.type)) {
+        DescriptorRef desc;
+        table.insert_column(col_ndx, type_Table, property.name, &desc);
+        desc->add_column(to_core_type(property.type & ~PropertyType::Flags), ObjectStore::ArrayColumnName,
+                         nullptr, is_nullable(property.type));
+    }
+    else {
+        table.insert_column(col_ndx, to_core_type(property.type), property.name, is_nullable(property.type));
+        if (property.requires_index())
+            table.add_search_index(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);
+
+    TableRef table;
+#if REALM_ENABLE_SYNC
+    if (auto* pk_property = object_schema.primary_key_property()) {
+        table = sync::create_table_with_primary_key(group, name, to_core_type(pk_property->type),
+                                                    pk_property->name, is_nullable(pk_property->type));
+    }
+    else {
+        table = sync::create_table(group, name);
+    }
+#else
+    table = group.get_or_add_table(name);
+#endif // REALM_ENABLE_SYNC
+
+    ObjectStore::set_primary_key_for_object(group, object_schema.name, object_schema.primary_key);
+
+    return table;
+}
+
+void add_initial_columns(Group& group, ObjectSchema const& object_schema)
+{
+    auto name = ObjectStore::table_name_for_object_type(object_schema.name);
+    TableRef table = group.get_table(name);
+
+    for (auto const& prop : object_schema.persisted_properties) {
+#if REALM_ENABLE_SYNC
+        // The sync::create_table* functions create the PK column for us.
+        if (prop.is_primary)
+            continue;
+#endif // REALM_ENABLE_SYNC
+        add_column(group, *table, prop);
+    }
+}
+
+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 & ~PropertyType::Flags) {
+        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.type |= PropertyType::Nullable;
+    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.type &= ~PropertyType::Nullable;
+    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
+
+void ObjectStore::set_schema_version(Group& group, uint64_t version) {
+    ::create_metadata_tables(group);
+    ::set_schema_version(group, version);
+}
+
+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);
+
+    size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type);
+
+#if REALM_ENABLE_SYNC
+    // sync::create_table* functions should have already updated the pk table.
+    if (sync::has_object_ids(group)) {
+        if (primary_key.size() == 0)
+            REALM_ASSERT(row == not_found);
+        else {
+             REALM_ASSERT(row != not_found);
+             REALM_ASSERT(table->get_string(c_primaryKeyPropertyNameColumnIndex, row) == primary_key);
+        }
+        return;
+    }
+#endif // REALM_ENABLE_SYNC
+
+    if (row == not_found && primary_key.size()) {
+        row = table->add_empty_row();
+        table->set_string_unique(c_primaryKeyObjectClassColumnIndex, row, object_type);
+        table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key);
+        return;
+    }
+    // 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<ObjectSchemaValidationException> errors;
+
+    void operator()(schema_change::AddTable op)
+    {
+        errors.emplace_back("Class '%1' has been added.", op.object->name);
+    }
+
+    void operator()(schema_change::AddInitialProperties)
+    {
+        // Nothing. Always preceded by AddTable.
+    }
+
+    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,
+                            op.old_property->type_string(),
+                            op.new_property->type_string());
+    }
+
+    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<typename ErrorType, typename Verifier>
+void verify_no_errors(Verifier&& verifier, std::vector<SchemaChange> 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<SchemaChange> const& changes)
+{
+    using namespace schema_change;
+    struct Visitor {
+        bool operator()(AddIndex) { return false; }
+        bool operator()(AddInitialProperties) { 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<SchemaChange> const& changes)
+{
+    verify_no_errors<SchemaMismatchException>(SchemaDifferenceExplainer(), changes);
+}
+
+void ObjectStore::verify_no_migration_required(std::vector<SchemaChange> 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()(AddInitialProperties) { }
+        void operator()(AddIndex) { }
+        void operator()(RemoveIndex) { }
+    } verifier;
+    verify_no_errors<SchemaMismatchException>(verifier, changes);
+}
+
+bool ObjectStore::verify_valid_additive_changes(std::vector<SchemaChange> const& changes, bool update_indexes)
+{
+    using namespace schema_change;
+    struct Verifier : SchemaDifferenceExplainer {
+        using SchemaDifferenceExplainer::operator();
+
+        bool index_changes = false;
+        bool other_changes = false;
+
+        // Additive mode allows adding things, extra columns, and adding/removing indexes
+        void operator()(AddTable) { other_changes = true; }
+        void operator()(AddInitialProperties) { other_changes = true; }
+        void operator()(AddProperty) { other_changes = true; }
+        void operator()(RemoveProperty) { }
+        void operator()(AddIndex) { index_changes = true; }
+        void operator()(RemoveIndex) { index_changes = true; }
+    } verifier;
+    verify_no_errors<InvalidSchemaChangeException>(verifier, changes);
+    return verifier.other_changes || (verifier.index_changes && update_indexes);
+}
+
+void ObjectStore::verify_valid_external_changes(std::vector<SchemaChange> const& changes)
+{
+    using namespace schema_change;
+    struct Verifier : SchemaDifferenceExplainer {
+        using SchemaDifferenceExplainer::operator();
+
+        // Adding new things is fine
+        void operator()(AddTable) { }
+        void operator()(AddInitialProperties) { }
+        void operator()(AddProperty) { }
+        void operator()(AddIndex) { }
+        void operator()(RemoveIndex) { }
+    } verifier;
+    verify_no_errors<InvalidSchemaChangeException>(verifier, changes);
+}
+
+void ObjectStore::verify_compatible_for_immutable_and_readonly(std::vector<SchemaChange> const& changes)
+{
+    using namespace schema_change;
+    struct Verifier : SchemaDifferenceExplainer {
+        using SchemaDifferenceExplainer::operator();
+
+        void operator()(AddTable) { }
+        void operator()(AddInitialProperties) { }
+        void operator()(RemoveProperty) { }
+        void operator()(AddIndex) { }
+        void operator()(RemoveIndex) { }
+    } verifier;
+    verify_no_errors<InvalidSchemaChangeException>(verifier, changes);
+}
+
+static void apply_non_migration_changes(Group& group, std::vector<SchemaChange> 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()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
+        void operator()(AddIndex op) { table(op.object).add_search_index(op.property->table_column); }
+        void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); }
+    } applier{group};
+    verify_no_errors<SchemaMismatchException>(applier, changes);
+}
+
+static void create_initial_tables(Group& group, std::vector<SchemaChange> 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()(AddInitialProperties op) { add_initial_columns(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) { table(op.object).add_search_index(op.property->table_column); }
+        void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); }
+
+        void operator()(ChangePropertyType op)
+        {
+            replace_column(group, table(op.object), *op.old_property, *op.new_property);
+        }
+    } applier{group};
+
+    for (auto& change : changes) {
+        change.visit(applier);
+    }
+}
+
+void ObjectStore::apply_additive_changes(Group& group, std::vector<SchemaChange> 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()(AddInitialProperties op) { add_initial_columns(group, *op.object); }
+        void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); }
+        void operator()(AddIndex op) { if (update_indexes) table(op.object).add_search_index(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<SchemaChange> 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()(AddInitialProperties op) { add_initial_columns(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.c_str(), op.property ? op.property->name.c_str() : ""); }
+        void operator()(AddIndex op) { table(op.object).add_search_index(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);
+    }
+}
+
+enum class DidRereadSchema { Yes, No };
+
+static void apply_post_migration_changes(Group& group, std::vector<SchemaChange> const& changes, Schema const& initial_schema,
+                                         DidRereadSchema did_reread_schema)
+{
+    using namespace schema_change;
+    struct Applier {
+        Applier(Group& group, Schema const& initial_schema, DidRereadSchema did_reread_schema)
+        : group{group}, initial_schema(initial_schema), table(group)
+        , did_reread_schema(did_reread_schema == DidRereadSchema::Yes)
+        { }
+        Group& group;
+        Schema const& initial_schema;
+        TableHelper table;
+        bool did_reread_schema;
+
+        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()(AddInitialProperties op) {
+            if (did_reread_schema)
+                add_initial_columns(group, *op.object);
+            else {
+                // If we didn't re-read the schema then AddInitialProperties was already taken care of
+                // during apply_pre_migration_changes.
+            }
+        }
+
+        void operator()(AddIndex op) { table(op.object).add_search_index(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, did_reread_schema};
+
+    for (auto& change : changes) {
+        change.visit(applier);
+    }
+}
+
+void ObjectStore::apply_schema_changes(Group& group, uint64_t schema_version,
+                                       Schema& target_schema, uint64_t target_schema_version,
+                                       SchemaMode mode, std::vector<SchemaChange> const& changes,
+                                       std::function<void()> migration_function)
+{
+    create_metadata_tables(group);
+
+    if (mode == SchemaMode::Additive) {
+        bool target_schema_is_newer = (schema_version < target_schema_version
+            || schema_version == ObjectStore::NotVersioned);
+
+        // With sync v2.x, indexes are no longer synced, so there's no reason to avoid creating them.
+        bool update_indexes = true;
+        apply_additive_changes(group, changes, update_indexes);
+
+        if (target_schema_is_newer)
+            set_schema_version(group, target_schema_version);
+
+        set_schema_columns(group, target_schema);
+        return;
+    }
+
+    if (schema_version == ObjectStore::NotVersioned) {
+        create_initial_tables(group, changes);
+        set_schema_version(group, target_schema_version);
+        set_schema_columns(group, target_schema);
+        return;
+    }
+
+    if (mode == SchemaMode::Manual) {
+        set_schema_columns(group, target_schema);
+        if (migration_function) {
+            migration_function();
+        }
+
+        verify_no_changes_required(schema_from_group(group).compare(target_schema));
+        validate_primary_column_uniqueness(group);
+        set_schema_columns(group, target_schema);
+        set_schema_version(group, target_schema_version);
+        return;
+    }
+
+    if (schema_version == target_schema_version) {
+        apply_non_migration_changes(group, changes);
+        set_schema_columns(group, target_schema);
+        return;
+    }
+
+    auto old_schema = schema_from_group(group);
+    apply_pre_migration_changes(group, changes);
+    if (migration_function) {
+        set_schema_columns(group, target_schema);
+        migration_function();
+
+        // Migration function may have changed the schema, so we need to re-read it
+        auto schema = schema_from_group(group);
+        apply_post_migration_changes(group, schema.compare(target_schema), old_schema, DidRereadSchema::Yes);
+        validate_primary_column_uniqueness(group);
+    }
+    else {
+        apply_post_migration_changes(group, changes, {}, DidRereadSchema::No);
+    }
+
+    set_schema_version(group, target_schema_version);
+    set_schema_columns(group, target_schema);
+}
+
+Schema ObjectStore::schema_from_group(Group const& group) {
+    std::vector<ObjectSchema> 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;
+}
+
+util::Optional<Property> ObjectStore::property_for_column_index(ConstTableRef& table, size_t column_index)
+{
+    StringData column_name = table->get_column_name(column_index);
+
+#if REALM_ENABLE_SYNC
+    // The object ID column is an implementation detail, and is omitted from the schema.
+    // FIXME: Consider filtering out all column names starting with `!`.
+    if (column_name == sync::object_id_column_name)
+        return util::none;
+#endif
+
+    if (table->get_column_type(column_index) == type_Table) {
+        auto subdesc = table->get_subdescriptor(column_index);
+        if (subdesc->get_column_count() != 1 || subdesc->get_column_name(0) != ObjectStore::ArrayColumnName)
+            return util::none;
+    }
+
+    Property property;
+    property.name = column_name;
+    property.type = ObjectSchema::from_core_type(*table->get_descriptor(), column_index);
+    property.is_indexed = table->has_search_index(column_index);
+    property.table_column = column_index;
+
+    if (property.type == PropertyType::Object) {
+        // set link type for objects and arrays
+        ConstTableRef linkTable = table->get_link_target(column_index);
+        property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data());
+    }
+    return property;
+}
+
+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 (is_nullable(old_property->type) && !is_nullable(new_property->type)) {
+        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 (is_nullable(new_property->type) && !is_nullable(old_property->type)) {
+        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<ObjectSchemaValidationException> 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<ObjectSchemaValidationException> 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<ObjectSchemaValidationException> 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/iOS/Pods/Realm/Realm/ObjectStore/src/placeholder.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/placeholder.cpp
new file mode 100644 (file)
index 0000000..8936534
--- /dev/null
@@ -0,0 +1 @@
+// This file is intentionally left blank.
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/results.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/results.cpp
new file mode 100644 (file)
index 0000000..aac1159
--- /dev/null
@@ -0,0 +1,777 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <stdexcept>
+
+namespace realm {
+
+Results::Results() = default;
+Results::~Results() = default;
+
+Results::Results(SharedRealm r, Query q, DescriptorOrdering o)
+: m_realm(std::move(r))
+, m_query(std::move(q))
+, m_table(m_query.get_table())
+, m_descriptor_ordering(std::move(o))
+, m_mode(Mode::Query)
+{
+}
+
+Results::Results(SharedRealm r, Table& table)
+: m_realm(std::move(r))
+, m_mode(Mode::Table)
+{
+    m_table.reset(&table);
+}
+
+Results::Results(SharedRealm r, LinkViewRef lv, util::Optional<Query> q, SortDescriptor s)
+: m_realm(std::move(r))
+, m_link_view(lv)
+, m_mode(Mode::LinkView)
+{
+    m_table.reset(&lv->get_target_table());
+    if (q) {
+        m_query = std::move(*q);
+        m_mode = Mode::Query;
+    }
+    m_descriptor_ordering.append_sort(std::move(s));
+}
+
+Results::Results(SharedRealm r, TableView tv, DescriptorOrdering o)
+: m_realm(std::move(r))
+, m_table_view(std::move(tv))
+, m_descriptor_ordering(std::move(o))
+, m_mode(Mode::TableView)
+{
+    m_table.reset(&m_table_view.get_parent());
+}
+
+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(std::move(other.m_table))
+, m_descriptor_ordering(std::move(other.m_descriptor_ordering))
+, 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();
+            if (!m_descriptor_ordering.will_apply_distinct())
+                return m_query.count();
+            REALM_FALLTHROUGH;
+        case Mode::TableView:
+            evaluate_query_if_needed();
+            return m_table_view.size();
+    }
+    REALM_COMPILER_HINT_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());
+}
+
+namespace {
+template<typename T>
+auto get(Table& table, size_t row)
+{
+    return table.get<T>(0, row);
+}
+
+template<>
+auto get<RowExpr>(Table& table, size_t row)
+{
+    return table.get(row);
+}
+}
+
+template<typename T>
+util::Optional<T> Results::try_get(size_t row_ndx)
+{
+    validate_read();
+    switch (m_mode) {
+        case Mode::Empty: break;
+        case Mode::Table:
+            if (row_ndx < m_table->size())
+                return realm::get<T>(*m_table, row_ndx);
+            break;
+        case Mode::LinkView:
+            if (update_linkview()) {
+                if (row_ndx < m_link_view->size())
+                    return realm::get<T>(*m_table, m_link_view->get(row_ndx).get_index());
+                break;
+            }
+            REALM_FALLTHROUGH;
+        case Mode::Query:
+        case Mode::TableView:
+            evaluate_query_if_needed();
+            if (row_ndx >= m_table_view.size())
+                break;
+            if (m_update_policy == UpdatePolicy::Never && !m_table_view.is_row_attached(row_ndx))
+                return T{};
+            return realm::get<T>(*m_table, m_table_view.get(row_ndx).get_index());
+    }
+    return util::none;
+}
+
+template<typename T>
+T Results::get(size_t row_ndx)
+{
+    if (auto row = try_get<T>(row_ndx))
+        return *row;
+    throw OutOfBoundsIndexException{row_ndx, size()};
+}
+
+template<typename T>
+util::Optional<T> Results::first()
+{
+    return try_get<T>(0);
+}
+
+template<typename T>
+util::Optional<T> Results::last()
+{
+    validate_read();
+    if (m_mode == Mode::Query)
+        evaluate_query_if_needed(); // avoid running the query twice (for size() and for get())
+    return try_get<T>(size() - 1);
+}
+
+bool Results::update_linkview()
+{
+    REALM_ASSERT(m_update_policy == UpdatePolicy::Auto);
+
+    if (!m_descriptor_ordering.is_empty()) {
+        m_query = get_query();
+        m_mode = Mode::Query;
+        evaluate_query_if_needed();
+        return false;
+    }
+    return true;
+}
+
+void Results::evaluate_query_if_needed(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_descriptor_ordering.is_empty()) {
+                m_table_view.apply_descriptor_ordering(m_descriptor_ordering);
+            }
+            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;
+    }
+}
+
+template<>
+size_t Results::index_of(RowExpr 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"
+        );
+    }
+
+    switch (m_mode) {
+        case Mode::Empty:
+            return not_found;
+        case Mode::Table:
+            return row.get_index();
+        case Mode::LinkView:
+            if (update_linkview())
+                return m_link_view->find(row.get_index());
+            REALM_FALLTHROUGH;
+        case Mode::Query:
+        case Mode::TableView:
+            evaluate_query_if_needed();
+            return m_table_view.find_by_source_ndx(row.get_index());
+    }
+    REALM_COMPILER_HINT_UNREACHABLE();
+}
+
+template<typename T>
+size_t Results::index_of(T const& value)
+{
+    validate_read();
+    switch (m_mode) {
+        case Mode::Empty:
+            return not_found;
+        case Mode::Table:
+            return m_table->find_first(0, value);
+        case Mode::LinkView:
+            REALM_UNREACHABLE();
+        case Mode::Query:
+        case Mode::TableView:
+            evaluate_query_if_needed();
+            return m_table_view.find_first(0, value);
+    }
+    REALM_COMPILER_HINT_UNREACHABLE();
+}
+
+size_t Results::index_of(Query&& q)
+{
+    if (m_descriptor_ordering.will_apply_sort()) {
+        auto first = filter(std::move(q)).first();
+        return first ? index_of(*first) : not_found;
+    }
+
+    auto query = get_query().and_query(std::move(q));
+    query.sync_view_if_needed();
+    size_t row = query.find();
+    return row != not_found ? index_of(m_table->get(row)) : row;
+}
+
+void Results::prepare_for_aggregate(size_t column, const char* name)
+{
+    if (column > m_table->get_column_count())
+        throw OutOfBoundsIndexException{column, m_table->get_column_count()};
+    switch (m_mode) {
+        case Mode::Empty: break;
+        case Mode::Table: break;
+        case Mode::LinkView:
+            m_query = this->get_query();
+            m_mode = Mode::Query;
+            REALM_FALLTHROUGH;
+        case Mode::Query:
+        case Mode::TableView:
+            evaluate_query_if_needed();
+            break;
+        default:
+            REALM_COMPILER_HINT_UNREACHABLE();
+    }
+    switch (m_table->get_column_type(column)) {
+        case type_Timestamp: case type_Double: case type_Float: case type_Int: break;
+        default: throw UnsupportedColumnTypeException{column, m_table.get(), name};
+    }
+}
+
+template<typename Int, typename Float, typename Double, typename Timestamp>
+util::Optional<Mixed> Results::aggregate(size_t column,
+                                         const char* name,
+                                         Int agg_int, Float agg_float,
+                                         Double agg_double, Timestamp agg_timestamp)
+{
+    validate_read();
+    if (!m_table)
+        return none;
+    prepare_for_aggregate(column, name);
+
+    auto do_agg = [&](auto const& getter) {
+        return Mixed(m_mode == Mode::Table ? getter(*m_table) : getter(m_table_view));
+    };
+    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: REALM_COMPILER_HINT_UNREACHABLE();
+    }
+}
+
+util::Optional<Mixed> Results::max(size_t column)
+{
+    size_t return_ndx = npos;
+    auto results = aggregate(column, "max",
+                             [&](auto const& table) { return table.maximum_int(column, &return_ndx); },
+                             [&](auto const& table) { return table.maximum_float(column, &return_ndx); },
+                             [&](auto const& table) { return table.maximum_double(column, &return_ndx); },
+                             [&](auto const& table) { return table.maximum_timestamp(column, &return_ndx); });
+    return return_ndx == npos ? none : results;
+}
+
+util::Optional<Mixed> Results::min(size_t column)
+{
+    size_t return_ndx = npos;
+    auto results = aggregate(column, "min",
+                             [&](auto const& table) { return table.minimum_int(column, &return_ndx); },
+                             [&](auto const& table) { return table.minimum_float(column, &return_ndx); },
+                             [&](auto const& table) { return table.minimum_double(column, &return_ndx); },
+                             [&](auto const& table) { return table.minimum_timestamp(column, &return_ndx); });
+    return return_ndx == npos ? none : results;
+}
+
+util::Optional<Mixed> Results::sum(size_t column)
+{
+    return aggregate(column, "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&) -> Timestamp { throw UnsupportedColumnTypeException{column, m_table.get(), "sum"}; });
+}
+
+util::Optional<double> Results::average(size_t column)
+{
+    size_t value_count = 0;
+    auto results = aggregate(column, "average",
+                             [&](auto const& table) { return table.average_int(column, &value_count); },
+                             [&](auto const& table) { return table.average_float(column, &value_count); },
+                             [&](auto const& table) { return table.average_double(column, &value_count); },
+                             [&](auto const&) -> Timestamp { throw UnsupportedColumnTypeException{column, m_table.get(), "average"}; });
+    return value_count == 0 ? none : util::make_optional(results->get_double());
+}
+
+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();
+            evaluate_query_if_needed();
+
+            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;
+    }
+}
+
+PropertyType Results::get_type() const
+{
+    validate_read();
+    switch (m_mode) {
+        case Mode::Empty:
+        case Mode::LinkView:
+            return PropertyType::Object;
+        case Mode::Query:
+        case Mode::TableView:
+        case Mode::Table:
+            if (m_table->get_index_in_group() != npos)
+                return PropertyType::Object;
+            return ObjectSchema::from_core_type(*m_table->get_descriptor(), 0);
+    }
+    REALM_COMPILER_HINT_UNREACHABLE();
+}
+
+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<TableViewBase>(new TableView(m_table_view)));
+        }
+        case Mode::LinkView:
+            return m_table->where(m_link_view);
+        case Mode::Table:
+            return m_table->where();
+    }
+    REALM_COMPILER_HINT_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:
+            evaluate_query_if_needed();
+            return m_table_view;
+        case Mode::Table:
+            return m_table->where().find_all();
+    }
+    REALM_COMPILER_HINT_UNREACHABLE();
+}
+
+static std::vector<size_t> parse_keypath(StringData keypath, Schema const& schema, const ObjectSchema *object_schema)
+{
+    auto check = [&](bool condition, const char* fmt, auto... args) {
+        if (!condition) {
+            throw std::invalid_argument(util::format("Cannot sort on key path '%1': %2.",
+                                                     keypath, util::format(fmt, args...)));
+        }
+    };
+    auto is_sortable_type = [](PropertyType type) {
+        return !is_array(type) && type != PropertyType::LinkingObjects && type != PropertyType::Data;
+    };
+
+    const char* begin = keypath.data();
+    const char* end = keypath.data() + keypath.size();
+    check(begin != end, "missing property name");
+
+    std::vector<size_t> indices;
+    while (begin != end) {
+        auto sep = std::find(begin, end, '.');
+        check(sep != begin && sep + 1 != end, "missing property name");
+        StringData key(begin, sep - begin);
+        begin = sep + (sep != end);
+
+        auto prop = object_schema->property_for_name(key);
+        check(prop, "property '%1.%2' does not exist", object_schema->name, key);
+        check(is_sortable_type(prop->type), "property '%1.%2' is of unsupported type '%3'",
+              object_schema->name, key, string_for_property_type(prop->type));
+        if (prop->type == PropertyType::Object)
+            check(begin != end, "property '%1.%2' of type 'object' cannot be the final property in the key path",
+                  object_schema->name, key);
+        else
+            check(begin == end, "property '%1.%2' of type '%3' may only be the final property in the key path",
+                  object_schema->name, key, prop->type_string());
+
+        indices.push_back(prop->table_column);
+        if (prop->type == PropertyType::Object)
+            object_schema = &*schema.find(prop->object_type);
+    }
+    return indices;
+}
+
+Results Results::sort(std::vector<std::pair<std::string, bool>> const& keypaths) const
+{
+    if (keypaths.empty())
+        return *this;
+    if (get_type() != PropertyType::Object) {
+        if (keypaths.size() != 1)
+            throw std::invalid_argument(util::format("Cannot sort array of '%1' on more than one key path",
+                                                     string_for_property_type(get_type())));
+        if (keypaths[0].first != "self")
+            throw std::invalid_argument(util::format("Cannot sort on key path '%1': arrays of '%2' can only be sorted on 'self'",
+                                                     keypaths[0].first, string_for_property_type(get_type())));
+        return sort({*m_table, {{0}}, {keypaths[0].second}});
+    }
+
+    std::vector<std::vector<size_t>> column_indices;
+    std::vector<bool> ascending;
+    column_indices.reserve(keypaths.size());
+    ascending.reserve(keypaths.size());
+
+    for (auto& keypath : keypaths) {
+        column_indices.push_back(parse_keypath(keypath.first, m_realm->schema(), &get_object_schema()));
+        ascending.push_back(keypath.second);
+    }
+    return sort({*m_table, std::move(column_indices), std::move(ascending)});
+}
+
+Results Results::sort(SortDescriptor&& sort) const
+{
+    if (m_mode == Mode::LinkView)
+        return Results(m_realm, m_link_view, util::none, std::move(sort));
+    DescriptorOrdering new_order = m_descriptor_ordering;
+    new_order.append_sort(std::move(sort));
+    return Results(m_realm, get_query(), std::move(new_order));
+}
+
+Results Results::filter(Query&& q) const
+{
+    return Results(m_realm, get_query().and_query(std::move(q)), m_descriptor_ordering);
+}
+
+Results Results::distinct(DistinctDescriptor&& uniqueness) const
+{
+    DescriptorOrdering new_order = m_descriptor_ordering;
+    new_order.append_distinct(std::move(uniqueness));
+    return Results(m_realm, get_query(), std::move(new_order));
+}
+
+Results Results::distinct(std::vector<std::string> const& keypaths) const
+{
+    if (keypaths.empty())
+        return *this;
+    if (get_type() != PropertyType::Object) {
+        if (keypaths.size() != 1)
+            throw std::invalid_argument(util::format("Cannot sort array of '%1' on more than one key path",
+                                                     string_for_property_type(get_type())));
+        if (keypaths[0] != "self")
+            throw std::invalid_argument(util::format("Cannot sort on key path '%1': arrays of '%2' can only be sorted on 'self'",
+                                                     keypaths[0], string_for_property_type(get_type())));
+        return distinct({*m_table, {{0}}});
+    }
+
+    std::vector<std::vector<size_t>> column_indices;
+    column_indices.reserve(keypaths.size());
+    for (auto& keypath : keypaths)
+        column_indices.push_back(parse_keypath(keypath, m_realm->schema(), &get_object_schema()));
+    return distinct({*m_table, std::move(column_indices)});
+}
+
+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:
+            evaluate_query_if_needed(false);
+            m_notifier.reset();
+            m_update_policy = UpdatePolicy::Never;
+            return std::move(*this);
+    }
+    REALM_COMPILER_HINT_UNREACHABLE();
+}
+
+void Results::prepare_async()
+{
+    if (m_notifier) {
+        return;
+    }
+    if (m_realm->config().immutable()) {
+        throw InvalidTransactionException("Cannot create asynchronous query for immutable 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.");
+    }
+
+    m_wants_background_updates = true;
+    m_notifier = std::make_shared<_impl::ResultsNotifier>(*this);
+    _impl::RealmCoordinator::register_notifier(m_notifier);
+}
+
+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_descriptor_ordering.will_apply_sort();
+        case Mode::TableView:
+            return m_table_view.is_in_table_order();
+    }
+    REALM_COMPILER_HINT_UNREACHABLE();
+}
+
+void Results::Internal::set_table_view(Results& results, 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());
+}
+
+#define REALM_RESULTS_TYPE(T) \
+    template T Results::get<T>(size_t); \
+    template util::Optional<T> Results::first<T>(); \
+    template util::Optional<T> Results::last<T>(); \
+    template size_t Results::index_of<T>(T const&);
+
+template RowExpr Results::get<RowExpr>(size_t);
+template util::Optional<RowExpr> Results::first<RowExpr>();
+template util::Optional<RowExpr> Results::last<RowExpr>();
+
+REALM_RESULTS_TYPE(bool)
+REALM_RESULTS_TYPE(int64_t)
+REALM_RESULTS_TYPE(float)
+REALM_RESULTS_TYPE(double)
+REALM_RESULTS_TYPE(StringData)
+REALM_RESULTS_TYPE(BinaryData)
+REALM_RESULTS_TYPE(Timestamp)
+REALM_RESULTS_TYPE(util::Optional<bool>)
+REALM_RESULTS_TYPE(util::Optional<int64_t>)
+REALM_RESULTS_TYPE(util::Optional<float>)
+REALM_RESULTS_TYPE(util::Optional<double>)
+
+#undef REALM_RESULTS_TYPE
+
+Results::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c)
+: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c - 1))
+, requested(r), valid_count(c) {}
+
+static std::string unsupported_operation_msg(size_t column, const Table* table, const char* operation)
+{
+    const char* column_type = string_for_property_type(ObjectSchema::from_core_type(*table->get_descriptor(), column));
+    if (table->is_group_level())
+        return util::format("Cannot %1 property '%2': operation not supported for '%3' properties",
+                            operation, table->get_column_name(column), column_type);
+    return util::format("Cannot %1 '%2' array: operation not supported",
+                        operation, column_type);
+}
+
+Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table, const char* operation)
+: std::logic_error(unsupported_operation_msg(column, table, operation))
+, column_index(column)
+, column_name(table->get_column_name(column))
+, property_type(ObjectSchema::from_core_type(*table->get_descriptor(), column))
+{
+}
+
+} // namespace realm
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/schema.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/schema.cpp
new file mode 100644 (file)
index 0000000..efdc257
--- /dev/null
@@ -0,0 +1,258 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <algorithm>
+
+using namespace realm;
+
+namespace realm {
+bool operator==(Schema const& a, Schema const& b)
+{
+    return static_cast<Schema::base const&>(a) == static_cast<Schema::base const&>(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<ObjectSchema> 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<Schema *>(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<Schema *>(this)->find(object);
+}
+
+void Schema::validate() const
+{
+    std::vector<ObjectSchemaValidationException> 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<typename T> 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<typename T> size_t operator()(T) const { REALM_COMPILER_HINT_UNREACHABLE(); }
+};
+}
+
+static void compare(ObjectSchema const& existing_schema,
+                    ObjectSchema const& target_schema,
+                    std::vector<SchemaChange>& 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, &current_prop});
+            continue;
+        }
+        if (target_schema.property_is_computed(*target_prop)) {
+            changes.emplace_back(schema_change::RemoveProperty{&existing_schema, &current_prop});
+            continue;
+        }
+        if (current_prop.type != target_prop->type ||
+            current_prop.object_type != target_prop->object_type ||
+            is_array(current_prop.type) != is_array(target_prop->type)) {
+
+            changes.emplace_back(schema_change::ChangePropertyType{&existing_schema, &current_prop, target_prop});
+            continue;
+        }
+        if (is_nullable(current_prop.type) != is_nullable(target_prop->type)) {
+            if (is_nullable(current_prop.type))
+                changes.emplace_back(schema_change::MakePropertyRequired{&existing_schema, &current_prop});
+            else
+                changes.emplace_back(schema_change::MakePropertyNullable{&existing_schema, &current_prop});
+        }
+        if (target_prop->requires_index()) {
+            if (!current_prop.is_indexed)
+                changes.emplace_back(schema_change::AddIndex{&existing_schema, &current_prop});
+        }
+        else if (current_prop.requires_index()) {
+            changes.emplace_back(schema_change::RemoveIndex{&existing_schema, &current_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); });
+}
+
+template<typename T, typename U, typename Func>
+void Schema::zip_matching(T&& a, U&& b, Func&& func)
+{
+    size_t i = 0, j = 0;
+    while (i < a.size() && j < b.size()) {
+        auto& object_schema = a[i];
+        auto& matching_schema = b[j];
+        int cmp = object_schema.name.compare(matching_schema.name);
+        if (cmp == 0) {
+            func(&object_schema, &matching_schema);
+            ++i;
+            ++j;
+        }
+        else if (cmp < 0) {
+            func(&object_schema, nullptr);
+            ++i;
+        }
+        else {
+            func(nullptr, &matching_schema);
+            ++j;
+        }
+    }
+    for (; i < a.size(); ++i)
+        func(&a[i], nullptr);
+    for (; j < b.size(); ++j)
+        func(nullptr, &b[j]);
+
+}
+
+std::vector<SchemaChange> Schema::compare(Schema const& target_schema) const
+{
+    std::vector<SchemaChange> changes;
+
+    // Add missing tables
+    zip_matching(target_schema, *this, [&](const ObjectSchema* target, const ObjectSchema* existing) {
+        if (target && !existing) {
+            changes.emplace_back(schema_change::AddTable{target});
+        }
+    });
+
+    // Modify columns
+    zip_matching(target_schema, *this, [&](const ObjectSchema* target, const ObjectSchema* existing) {
+        if (target && existing)
+            ::compare(*existing, *target, changes);
+        else if (target) {
+            // Target is a new table -- add all properties
+            changes.emplace_back(schema_change::AddInitialProperties{target});
+        }
+        // nothing for tables in existing but not target
+    });
+    return changes;
+}
+
+void Schema::copy_table_columns_from(realm::Schema const& other)
+{
+    zip_matching(*this, other, [&](ObjectSchema* existing, const ObjectSchema* other) {
+        if (!existing || !other)
+            return;
+
+        for (auto& current_prop : other->persisted_properties) {
+            auto target_prop = existing->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(AddInitialProperties, v.object)
+        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/iOS/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp
new file mode 100644 (file)
index 0000000..ce39268
--- /dev/null
@@ -0,0 +1,945 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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/collection_notifier.hpp"
+#include "impl/realm_coordinator.hpp"
+#include "impl/transact_log_handler.hpp"
+
+#include "binding_context.hpp"
+#include "list.hpp"
+#include "object.hpp"
+#include "object_schema.hpp"
+#include "object_store.hpp"
+#include "results.hpp"
+#include "schema.hpp"
+#include "thread_safe_reference.hpp"
+
+
+#include <realm/history.hpp>
+#include <realm/util/scope_exit.hpp>
+
+#if REALM_ENABLE_SYNC
+#include "sync/impl/sync_file.hpp"
+#include "sync/sync_manager.hpp"
+#include <realm/sync/history.hpp>
+#endif
+
+using namespace realm;
+using namespace realm::_impl;
+
+Realm::Realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator)
+: m_config(std::move(config))
+, m_execution_context(m_config.execution_context)
+{
+    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();
+        m_schema_version = ObjectStore::get_schema_version(*m_group);
+        m_schema = ObjectStore::schema_from_group(*m_group);
+    }
+    else if (!coordinator || !coordinator->get_cached_schema(m_schema, m_schema_version, m_schema_transaction_version)) {
+        if (m_config.should_compact_on_launch_function) {
+            size_t free_space = -1;
+            size_t used_space = -1;
+            // getting stats requires committing a write transaction beforehand.
+            Group* group = nullptr;
+            if (m_shared_group->try_begin_write(group)) {
+                m_shared_group->commit();
+                m_shared_group->get_stats(free_space, used_space);
+                if (m_config.should_compact_on_launch_function(free_space + used_space, used_space))
+                    compact();
+            }
+        }
+        read_group();
+        if (coordinator)
+            coordinator->cache_schema(m_schema, m_schema_version, m_schema_transaction_version);
+        m_shared_group->end_read();
+        m_group = nullptr;
+    }
+
+    m_coordinator = std::move(coordinator);
+}
+
+REALM_NOINLINE static void translate_file_exception(StringData path, bool immutable=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(), immutable ? "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();
+        RealmFileException::Kind error_kind = RealmFileException::Kind::AccessError;
+        // FIXME: Replace this with a proper specific exception type once Core adds support for it.
+        if (underlying == "Bad or incompatible history type")
+            error_kind = RealmFileException::Kind::BadHistoryError;
+        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(error_kind, 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());
+    }
+}
+
+#if REALM_ENABLE_SYNC
+static bool is_nonupgradable_history(IncompatibleHistories const& ex)
+{
+    // FIXME: Replace this with a proper specific exception type once Core adds support for it.
+    return ex.what() == std::string("Incompatible histories. Nonupgradable history schema");
+}
+#endif
+
+void Realm::open_with_config(const Config& config,
+                             std::unique_ptr<Replication>& history,
+                             std::unique_ptr<SharedGroup>& shared_group,
+                             std::unique_ptr<Group>& read_only_group,
+                             Realm* realm)
+{
+    bool server_synchronization_mode = bool(config.sync_config) || config.force_sync_history;
+    try {
+        if (config.immutable()) {
+            if (config.realm_data.is_null()) {
+                read_only_group = std::make_unique<Group>(config.path, config.encryption_key.data(), Group::mode_ReadOnly);
+            }
+            else {
+                // Create in-memory read-only realm from existing buffer (without taking ownership of the buffer)
+                read_only_group = std::make_unique<Group>(config.realm_data, false);
+            }
+        }
+        else {
+            if (server_synchronization_mode) {
+#if REALM_ENABLE_SYNC
+                history = realm::sync::make_client_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 &&
+                                                config.schema_mode != SchemaMode::ResetFile;
+            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<SharedGroup>(*history, options);
+        }
+    }
+    catch (realm::FileFormatUpgradeRequired const&) {
+        if (config.schema_mode != SchemaMode::ResetFile) {
+            translate_file_exception(config.path, config.immutable());
+        }
+        util::File::remove(config.path);
+        open_with_config(config, history, shared_group, read_only_group, realm);
+    }
+#if REALM_ENABLE_SYNC
+    catch (IncompatibleHistories const& ex) {
+        if (!server_synchronization_mode || !is_nonupgradable_history(ex))
+            translate_file_exception(config.path, config.immutable()); // Throws
+
+        // Move the Realm file into the recovery directory.
+        std::string recovery_directory = SyncManager::shared().recovery_directory_path();
+        std::string new_realm_path = util::reserve_unique_file_name(recovery_directory, "synced-realm-XXXXXXX");
+        util::File::move(config.path, new_realm_path);
+
+        const char* message = "The local copy of this synced Realm was created with an incompatible version of "
+                              "Realm. It has been moved aside, and the Realm will be re-downloaded the next time it "
+                              "is opened. You should write a handler for this error that uses the provided "
+                              "configuration to open the old Realm in read-only mode to recover any pending changes "
+                              "and then remove the Realm file.";
+        throw RealmFileException(RealmFileException::Kind::IncompatibleSyncedRealm, std::move(new_realm_path),
+                                 message, ex.what());
+    }
+#endif // REALM_ENABLE_SYNC
+    catch (...) {
+        translate_file_exception(config.path, config.immutable());
+    }
+}
+
+Realm::~Realm()
+{
+    if (m_coordinator) {
+        m_coordinator->unregister_realm(this);
+    }
+}
+
+Group& Realm::read_group()
+{
+    verify_open();
+
+    if (!m_group)
+        begin_read(VersionID{});
+    return *m_group;
+}
+
+void Realm::Internal::begin_read(Realm& realm, VersionID version_id)
+{
+    realm.begin_read(version_id);
+}
+
+void Realm::begin_read(VersionID version_id)
+{
+    REALM_ASSERT(!m_group);
+    m_group = &const_cast<Group&>(m_shared_group->begin_read(version_id));
+    add_schema_change_handler();
+    read_schema_from_group_if_needed();
+}
+
+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 const& reference, Schema schema)
+{
+    m_dynamic_schema = false;
+    schema.copy_table_columns_from(reference);
+    m_schema = std::move(schema);
+    notify_schema_changed();
+}
+
+void Realm::read_schema_from_group_if_needed()
+{
+    REALM_ASSERT(!m_read_only_group);
+
+    Group& group = read_group();
+    auto current_version = m_shared_group->get_version_of_current_transaction().version;
+    if (m_schema_transaction_version == current_version)
+        return;
+
+    m_schema_transaction_version = current_version;
+    m_schema_version = ObjectStore::get_schema_version(group);
+    auto schema = ObjectStore::schema_from_group(group);
+    if (m_coordinator)
+        m_coordinator->cache_schema(schema, m_schema_version,
+                                    m_schema_transaction_version);
+
+    if (m_dynamic_schema) {
+        if (m_schema == schema) {
+            // The structure of the schema hasn't changed. Bring the table column indices up to date.
+            m_schema.copy_table_columns_from(schema);
+        }
+        else {
+            // The structure of the schema has changed, so replace our copy of the schema.
+            // FIXME: This invalidates any pointers to the object schemas within the schema vector,
+            // which will cause problems for anyone that caches such a pointer.
+            m_schema = std::move(schema);
+        }
+    }
+    else {
+        ObjectStore::verify_valid_external_changes(m_schema.compare(schema));
+        m_schema.copy_table_columns_from(schema);
+    }
+    notify_schema_changed();
+}
+
+bool Realm::reset_file(Schema& schema, std::vector<SchemaChange>& required_changes)
+{
+    // 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);
+    m_coordinator->clear_schema_cache_and_set_schema_version(m_schema_version);
+    return false;
+}
+
+bool Realm::schema_change_needs_write_transaction(Schema& schema,
+                                                  std::vector<SchemaChange>& changes,
+                                                  uint64_t version)
+{
+    if (version == m_schema_version && changes.empty())
+        return false;
+
+    switch (m_config.schema_mode) {
+        case SchemaMode::Automatic:
+            if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned)
+                throw InvalidSchemaVersionException(m_schema_version, version);
+            return true;
+
+        case SchemaMode::Immutable:
+            if (version != m_schema_version)
+                throw InvalidSchemaVersionException(m_schema_version, version);
+            REALM_FALLTHROUGH;
+        case SchemaMode::ReadOnlyAlternative:
+            ObjectStore::verify_compatible_for_immutable_and_readonly(changes);
+            return false;
+
+        case SchemaMode::ResetFile:
+            if (m_schema_version == ObjectStore::NotVersioned)
+                return true;
+            if (m_schema_version == version && !ObjectStore::needs_migration(changes))
+                return true;
+            reset_file(schema, changes);
+            return true;
+
+        case SchemaMode::Additive: {
+            bool will_apply_index_changes = version > m_schema_version;
+            if (ObjectStore::verify_valid_additive_changes(changes, will_apply_index_changes))
+                return true;
+            return version != m_schema_version;
+        }
+
+        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(changes);
+                REALM_UNREACHABLE(); // changes is non-empty so above line always throws
+            }
+            return true;
+    }
+    REALM_COMPILER_HINT_UNREACHABLE();
+}
+
+Schema Realm::get_full_schema()
+{
+    if (!m_read_only_group)
+        refresh();
+
+    // If the user hasn't specified a schema previously then m_schema is always
+    // the full schema
+    if (m_dynamic_schema)
+        return m_schema;
+
+    // Otherwise we may have a subset of the file's schema, so we need to get
+    // the complete thing to calculate what changes to make
+    if (m_read_only_group)
+        return ObjectStore::schema_from_group(read_group());
+
+    Schema actual_schema;
+    uint64_t actual_version;
+    uint64_t transaction = -1;
+    bool got_cached = m_coordinator->get_cached_schema(actual_schema, actual_version, transaction);
+    if (!got_cached || transaction != m_shared_group->get_version_of_current_transaction().version)
+        return ObjectStore::schema_from_group(read_group());
+    return actual_schema;
+}
+
+void Realm::set_schema_subset(Schema schema)
+{
+    REALM_ASSERT(m_dynamic_schema);
+    REALM_ASSERT(m_schema_version != ObjectStore::NotVersioned);
+
+    std::vector<SchemaChange> changes = m_schema.compare(schema);
+    switch (m_config.schema_mode) {
+        case SchemaMode::Automatic:
+        case SchemaMode::ResetFile:
+            ObjectStore::verify_no_migration_required(changes);
+            break;
+
+        case SchemaMode::Immutable:
+        case SchemaMode::ReadOnlyAlternative:
+            ObjectStore::verify_compatible_for_immutable_and_readonly(changes);
+            break;
+
+        case SchemaMode::Additive:
+            ObjectStore::verify_valid_additive_changes(changes);
+            break;
+
+        case SchemaMode::Manual:
+            ObjectStore::verify_no_changes_required(changes);
+            break;
+    }
+
+    set_schema(m_schema, std::move(schema));
+}
+
+void Realm::update_schema(Schema schema, uint64_t version, MigrationFunction migration_function,
+                          DataInitializationFunction initialization_function, bool in_transaction)
+{
+    schema.validate();
+
+    Schema actual_schema = get_full_schema();
+    std::vector<SchemaChange> required_changes = actual_schema.compare(schema);
+
+    if (!schema_change_needs_write_transaction(schema, required_changes, version)) {
+        set_schema(actual_schema, std::move(schema));
+        return;
+    }
+    // Either the schema version has changed or we need to do non-migration changes
+
+    if (!in_transaction) {
+        transaction::begin_without_validation(*m_shared_group);
+
+        // Beginning the write transaction may have advanced the version and left
+        // us with nothing to do if someone else initialized the schema on disk
+        if (m_new_schema) {
+            actual_schema = *m_new_schema;
+            required_changes = actual_schema.compare(schema);
+            if (!schema_change_needs_write_transaction(schema, required_changes, version)) {
+                cancel_transaction();
+                cache_new_schema();
+                set_schema(actual_schema, std::move(schema));
+                return;
+            }
+        }
+        cache_new_schema();
+    }
+
+    // Cancel the write transaction if we exit this function before committing it
+    auto cleanup = util::make_scope_exit([&]() noexcept {
+        // When in_transaction is true, caller is responsible to cancel the transaction.
+        if (!in_transaction && is_in_transaction())
+            cancel_transaction();
+    });
+
+    uint64_t old_schema_version = m_schema_version;
+    bool additive = m_config.schema_mode == SchemaMode::Additive;
+    if (migration_function && !additive) {
+        auto wrapper = [&] {
+            SharedRealm old_realm(new Realm(m_config, 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::Immutable;
+            migration_function(old_realm, shared_from_this(), m_schema);
+        };
+
+        // migration function needs to see the target schema on the "new" Realm
+        std::swap(m_schema, schema);
+        std::swap(m_schema_version, version);
+        m_in_migration = true;
+        auto restore = util::make_scope_exit([&]() noexcept {
+            std::swap(m_schema, schema);
+            std::swap(m_schema_version, version);
+            m_in_migration = false;
+        });
+
+        ObjectStore::apply_schema_changes(read_group(), version, m_schema, m_schema_version,
+                                          m_config.schema_mode, required_changes, wrapper);
+    }
+    else {
+        ObjectStore::apply_schema_changes(read_group(), 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());
+    }
+
+    if (initialization_function && old_schema_version == ObjectStore::NotVersioned) {
+        // Initialization function needs to see the latest schema
+        uint64_t temp_version = ObjectStore::get_schema_version(read_group());
+        std::swap(m_schema, schema);
+        std::swap(m_schema_version, temp_version);
+        auto restore = util::make_scope_exit([&]() noexcept {
+            std::swap(m_schema, schema);
+            std::swap(m_schema_version, temp_version);
+        });
+        initialization_function(shared_from_this());
+    }
+
+    if (!in_transaction) {
+        commit_transaction();
+    }
+
+    m_schema = std::move(schema);
+    m_schema_version = ObjectStore::get_schema_version(read_group());
+    m_dynamic_schema = false;
+    m_coordinator->clear_schema_cache_and_set_schema_version(version);
+    notify_schema_changed();
+}
+
+void Realm::add_schema_change_handler()
+{
+    if (m_config.immutable())
+        return;
+    m_group->set_schema_change_notification_handler([&] {
+        m_new_schema = ObjectStore::schema_from_group(read_group());
+        m_schema_version = ObjectStore::get_schema_version(read_group());
+        if (m_dynamic_schema) {
+            // FIXME: This invalidates any pointers to the object schemas within the schema vector,
+            // which will cause problems for anyone that caches such a pointer.
+            m_schema = *m_new_schema;
+        }
+        else
+            m_schema.copy_table_columns_from(*m_new_schema);
+
+        notify_schema_changed();
+    });
+}
+
+void Realm::cache_new_schema()
+{
+    if (!m_shared_group)
+        return;
+
+    auto new_version = m_shared_group->get_version_of_current_transaction().version;
+    if (m_coordinator) {
+        if (m_new_schema)
+            m_coordinator->cache_schema(std::move(*m_new_schema), m_schema_version, new_version);
+        else
+            m_coordinator->advance_schema_cache(m_schema_transaction_version, new_version);
+    }
+    m_schema_transaction_version = new_version;
+    m_new_schema = util::none;
+}
+
+void Realm::notify_schema_changed() {
+    if (m_binding_context) {
+        m_binding_context->schema_did_change(m_schema);
+    }
+}
+
+static void check_read_write(Realm *realm)
+{
+    if (realm->config().immutable()) {
+        throw InvalidTransactionException("Can't perform transactions on read-only Realms.");
+    }
+}
+
+static void check_write(Realm* realm)
+{
+    if (realm->config().immutable() || realm->config().read_only_alternative()) {
+        throw InvalidTransactionException("Can't perform transactions on read-only Realms.");
+    }
+}
+
+void Realm::verify_thread() const
+{
+    if (!m_execution_context.contains<std::thread::id>())
+        return;
+
+    auto thread_id = m_execution_context.get<std::thread::id>();
+    if (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.");
+    }
+}
+
+void Realm::verify_open() const
+{
+    if (is_closed()) {
+        throw ClosedRealmException();
+    }
+}
+
+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_write(this);
+    verify_thread();
+
+    if (is_in_transaction()) {
+        throw InvalidTransactionException("The Realm is already in a write transaction");
+    }
+
+    // Any of the callbacks to user code below could drop the last remaining
+    // strong reference to `this`
+    auto retain_self = shared_from_this();
+
+    // If we're already in the middle of sending notifications, just begin the
+    // write transaction without sending more notifications. If this actually
+    // advances the read version this could leave the user in an inconsistent
+    // state, but that's unavoidable.
+    if (m_is_sending_notifications) {
+        _impl::NotifierPackage notifiers;
+        transaction::begin(m_shared_group, m_binding_context.get(), notifiers);
+        return;
+    }
+
+    // make sure we have a read transaction
+    read_group();
+
+    m_is_sending_notifications = true;
+    auto cleanup = util::make_scope_exit([this]() noexcept { m_is_sending_notifications = false; });
+
+    m_coordinator->promote_to_write(*this);
+    cache_new_schema();
+}
+
+void Realm::commit_transaction()
+{
+    check_write(this);
+    verify_thread();
+
+    if (!is_in_transaction()) {
+        throw InvalidTransactionException("Can't commit a non-existing write transaction");
+    }
+
+    m_coordinator->commit_write(*this);
+    cache_new_schema();
+}
+
+void Realm::cancel_transaction()
+{
+    check_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_open();
+    verify_thread();
+    check_read_write(this);
+
+    if (m_is_sending_notifications) {
+        return;
+    }
+
+    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.immutable() || m_config.read_only_alternative()) {
+        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);
+    }
+}
+
+OwnedBinaryData Realm::write_copy()
+{
+    verify_thread();
+    BinaryData buffer = read_group().write_to_mem();
+
+    // Since OwnedBinaryData does not have a constructor directly taking
+    // ownership of BinaryData, we have to do this to avoid copying the buffer
+    return OwnedBinaryData(std::unique_ptr<char[]>((char*)buffer.data()), buffer.size());
+}
+
+void Realm::notify()
+{
+    if (is_closed() || is_in_transaction()) {
+        return;
+    }
+
+    verify_thread();
+
+    // Any of the callbacks to user code below could drop the last remaining
+    // strong reference to `this`
+    auto retain_self = shared_from_this();
+
+    if (m_binding_context) {
+        m_binding_context->before_notify();
+    }
+
+    auto cleanup = util::make_scope_exit([this]() noexcept { m_is_sending_notifications = false; });
+    if (!m_shared_group->has_changed()) {
+        m_is_sending_notifications = true;
+        m_coordinator->process_available_async(*this);
+        return;
+    }
+
+    if (m_binding_context) {
+        m_binding_context->changes_available();
+
+        // changes_available() may have advanced the read version, and if
+        // so we don't need to do anything further
+        if (!m_shared_group->has_changed())
+            return;
+    }
+
+    m_is_sending_notifications = true;
+    if (m_auto_refresh) {
+        if (m_group) {
+            m_coordinator->advance_to_ready(*this);
+            cache_new_schema();
+        }
+        else  {
+            if (m_binding_context) {
+                m_binding_context->did_change({}, {});
+            }
+            if (!is_closed()) {
+                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;
+    }
+    // don't advance if we're already in the process of advancing as that just
+    // makes things needlessly complicated
+    if (m_is_sending_notifications) {
+        return false;
+    }
+
+    // Any of the callbacks to user code below could drop the last remaining
+    // strong reference to `this`
+    auto retain_self = shared_from_this();
+
+    m_is_sending_notifications = true;
+    auto cleanup = util::make_scope_exit([this]() noexcept { m_is_sending_notifications = false; });
+
+    if (m_binding_context) {
+        m_binding_context->before_notify();
+    }
+    if (m_group) {
+        bool version_changed = m_coordinator->advance_to_latest(*this);
+        cache_new_schema();
+        return version_changed;
+    }
+
+    // No current read transaction, so just create a new one
+    read_group();
+    m_coordinator->process_available_async(*this);
+    return true;
+}
+
+bool Realm::can_deliver_notifications() const noexcept
+{
+    if (m_config.immutable()) {
+        return false;
+    }
+
+    if (m_binding_context && !m_binding_context->can_deliver_notifications()) {
+        return false;
+    }
+
+    return true;
+}
+
+uint64_t Realm::get_schema_version(const 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, nullptr).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<int> Realm::file_format_upgraded_from_version() const
+{
+    if (upgrade_initial_version != upgrade_final_version) {
+        return upgrade_initial_version;
+    }
+    return util::none;
+}
+
+template <typename T>
+realm::ThreadSafeReference<T> Realm::obtain_thread_safe_reference(T const& value)
+{
+    verify_thread();
+    if (is_in_transaction()) {
+        throw InvalidTransactionException("Cannot obtain thread safe reference during a write transaction.");
+    }
+    return ThreadSafeReference<T>(value);
+}
+
+template ThreadSafeReference<Object> Realm::obtain_thread_safe_reference(Object const& value);
+template ThreadSafeReference<List> Realm::obtain_thread_safe_reference(List const& value);
+template ThreadSafeReference<Results> Realm::obtain_thread_safe_reference(Results const& value);
+
+template <typename T>
+T Realm::resolve_thread_safe_reference(ThreadSafeReference<T> reference)
+{
+    verify_thread();
+    if (is_in_transaction()) {
+        throw InvalidTransactionException("Cannot resolve thread safe reference during a write transaction.");
+    }
+    if (reference.is_invalidated()) {
+        throw std::logic_error("Cannot resolve thread safe reference more than once.");
+    }
+    if (!reference.has_same_config(*this)) {
+        throw MismatchedRealmException("Cannot resolve thread safe reference in Realm with different configuration "
+                                       "than the source Realm.");
+    }
+
+    // Any of the callbacks to user code below could drop the last remaining
+    // strong reference to `this`
+    auto retain_self = shared_from_this();
+
+    // Ensure we're on the same version as the reference
+    if (!m_group) {
+        // A read transaction doesn't yet exist, so create at the reference's version
+        begin_read(reference.m_version_id);
+    }
+    else {
+        // A read transaction does exist, but let's make sure that its version matches the reference's
+        auto current_version = m_shared_group->get_version_of_current_transaction();
+        VersionID reference_version(reference.m_version_id);
+
+        if (reference_version == current_version) {
+            return std::move(reference).import_into_realm(shared_from_this());
+        }
+
+        refresh();
+
+        current_version = m_shared_group->get_version_of_current_transaction();
+
+        // If the reference's version is behind, advance it to our version
+        if (reference_version < current_version) {
+            // Duplicate config for uncached Realm so we don't advance the user's Realm
+            Realm::Config config = m_coordinator->get_config();
+            config.cache = false;
+            config.schema = util::none;
+            SharedRealm temporary_realm = m_coordinator->get_realm(config);
+            temporary_realm->begin_read(reference_version);
+
+            // With reference imported, advance temporary Realm to our version
+            T imported_value = std::move(reference).import_into_realm(temporary_realm);
+            transaction::advance(*temporary_realm->m_shared_group, nullptr, current_version);
+            if (!imported_value.is_valid())
+                return T{};
+            reference = ThreadSafeReference<T>(imported_value);
+        }
+    }
+
+    return std::move(reference).import_into_realm(shared_from_this());
+}
+
+template Object Realm::resolve_thread_safe_reference(ThreadSafeReference<Object> reference);
+template List Realm::resolve_thread_safe_reference(ThreadSafeReference<List> reference);
+template Results Realm::resolve_thread_safe_reference(ThreadSafeReference<Results> reference);
+
+MismatchedConfigException::MismatchedConfigException(StringData message, StringData path)
+: std::logic_error(util::format(message.data(), path)) { }
+
+MismatchedRealmException::MismatchedRealmException(StringData message)
+: std::logic_error(message.data()) { }
+
+// FIXME Those are exposed for Java async queries, mainly because of handover related methods.
+SharedGroup& RealmFriend::get_shared_group(Realm& realm)
+{
+    return *realm.m_shared_group;
+}
+
+Group& RealmFriend::read_group_to(Realm& realm, VersionID version)
+{
+    if (realm.m_group && realm.m_shared_group->get_version_of_current_transaction() == version)
+        return *realm.m_group;
+
+    if (realm.m_group)
+        realm.m_shared_group->end_read();
+    realm.begin_read(version);
+    return *realm.m_group;
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/network_reachability_observer.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/network_reachability_observer.cpp
new file mode 100644 (file)
index 0000000..ad040a4
--- /dev/null
@@ -0,0 +1,127 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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/impl/apple/network_reachability_observer.hpp"
+
+#if NETWORK_REACHABILITY_AVAILABLE
+
+using namespace realm;
+using namespace realm::_impl;
+
+namespace {
+
+NetworkReachabilityStatus reachability_status_for_flags(SCNetworkReachabilityFlags flags)
+{
+    if (!(flags & kSCNetworkReachabilityFlagsReachable))
+        return NotReachable;
+
+    if (flags & kSCNetworkReachabilityFlagsConnectionRequired) {
+        if (!(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ||
+            (flags & kSCNetworkReachabilityFlagsInterventionRequired))
+            return NotReachable;
+    }
+
+    NetworkReachabilityStatus status = ReachableViaWiFi;
+
+#if TARGET_OS_IPHONE
+    if (flags & kSCNetworkReachabilityFlagsIsWWAN)
+        status = ReachableViaWWAN;
+#endif
+
+    return status;
+}
+
+} // (anonymous namespace)
+
+NetworkReachabilityObserver::NetworkReachabilityObserver(util::Optional<std::string> hostname,
+                                                         std::function<void (const NetworkReachabilityStatus)> handler)
+: m_callback_queue(dispatch_queue_create("io.realm.sync.reachability", DISPATCH_QUEUE_SERIAL))
+, m_change_handler(std::move(handler))
+{
+    if (hostname) {
+        m_reachability_ref = util::adoptCF(SystemConfiguration::shared().network_reachability_create_with_name(nullptr,
+                                                                                                               hostname->c_str()));
+    } else {
+        struct sockaddr zeroAddress = {};
+        zeroAddress.sa_len = sizeof(zeroAddress);
+        zeroAddress.sa_family = AF_INET;
+
+        m_reachability_ref = util::adoptCF(SystemConfiguration::shared().network_reachability_create_with_address(nullptr,
+                                                                                                                  &zeroAddress));
+    }
+}
+
+NetworkReachabilityObserver::~NetworkReachabilityObserver()
+{
+    stop_observing();
+    dispatch_release(m_callback_queue);
+}
+
+NetworkReachabilityStatus NetworkReachabilityObserver::reachability_status() const
+{
+    SCNetworkReachabilityFlags flags;
+
+    if (SystemConfiguration::shared().network_reachability_get_flags(m_reachability_ref.get(), &flags))
+        return reachability_status_for_flags(flags);
+
+    return NotReachable;
+}
+
+bool NetworkReachabilityObserver::start_observing()
+{
+    m_previous_status = reachability_status();
+
+    auto callback = [](SCNetworkReachabilityRef, SCNetworkReachabilityFlags, void* self) {
+        static_cast<NetworkReachabilityObserver*>(self)->reachability_changed();
+    };
+
+    SCNetworkReachabilityContext context = {0, this, nullptr, nullptr, nullptr};
+
+    if (!SystemConfiguration::shared().network_reachability_set_callback(m_reachability_ref.get(), callback, &context))
+        return false;
+
+    if (!SystemConfiguration::shared().network_reachability_set_dispatch_queue(m_reachability_ref.get(), m_callback_queue))
+        return false;
+
+    return true;
+}
+
+void NetworkReachabilityObserver::stop_observing()
+{
+    SystemConfiguration::shared().network_reachability_set_dispatch_queue(m_reachability_ref.get(), nullptr);
+    SystemConfiguration::shared().network_reachability_set_callback(m_reachability_ref.get(), nullptr, nullptr);
+
+    // Wait for all previously-enqueued blocks to execute to guarantee that
+    // no callback will be called after returning from this method
+    dispatch_sync(m_callback_queue, ^{});
+}
+
+void NetworkReachabilityObserver::reachability_changed()
+{
+    auto current_status = reachability_status();
+
+    // When observing reachability of the specific host the callback might be called
+    // several times (because of DNS queries) with the same reachability flags while
+    // the caller should be notified only when the reachability status is really changed.
+    if (current_status != m_previous_status) {
+        m_change_handler(current_status);
+        m_previous_status = current_status;
+    }
+}
+
+#endif
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/system_configuration.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/apple/system_configuration.cpp
new file mode 100644 (file)
index 0000000..6c4c127
--- /dev/null
@@ -0,0 +1,98 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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/impl/apple/system_configuration.hpp"
+
+#if NETWORK_REACHABILITY_AVAILABLE
+
+#include <asl.h>
+#include "dlfcn.h"
+
+using namespace realm;
+using namespace realm::_impl;
+
+SystemConfiguration::SystemConfiguration()
+{
+    m_framework_handle = dlopen("/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration", RTLD_LAZY);
+
+    if (m_framework_handle) {
+        m_create_with_name = (create_with_name_t)dlsym(m_framework_handle, "SCNetworkReachabilityCreateWithName");
+        m_create_with_address = (create_with_address_t)dlsym(m_framework_handle, "SCNetworkReachabilityCreateWithAddress");
+        m_set_dispatch_queue = (set_dispatch_queue_t)dlsym(m_framework_handle, "SCNetworkReachabilitySetDispatchQueue");
+        m_set_callback = (set_callback_t)dlsym(m_framework_handle, "SCNetworkReachabilitySetCallback");
+        m_get_flags = (get_flags_t)dlsym(m_framework_handle, "SCNetworkReachabilityGetFlags");
+    } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        asl_log(nullptr, nullptr, ASL_LEVEL_WARNING, "network reachability is not available");
+#pragma clang diagnostic pop
+    }
+}
+
+SystemConfiguration& SystemConfiguration::shared()
+{
+    static SystemConfiguration system_configuration;
+
+    return system_configuration;
+}
+
+SCNetworkReachabilityRef SystemConfiguration::network_reachability_create_with_name(CFAllocatorRef allocator,
+                                                                                    const char *hostname)
+{
+    if (m_create_with_name)
+        return m_create_with_name(allocator, hostname);
+
+    return nullptr;
+}
+
+SCNetworkReachabilityRef SystemConfiguration::network_reachability_create_with_address(CFAllocatorRef allocator,
+                                                                                       const sockaddr *address)
+{
+    if (m_create_with_address)
+        return m_create_with_address(allocator, address);
+
+    return nullptr;
+}
+
+bool SystemConfiguration::network_reachability_set_dispatch_queue(SCNetworkReachabilityRef target, dispatch_queue_t queue)
+{
+    if (m_set_dispatch_queue)
+        return m_set_dispatch_queue(target, queue);
+
+    return false;
+}
+
+bool SystemConfiguration::network_reachability_set_callback(SCNetworkReachabilityRef target,
+                                                            SCNetworkReachabilityCallBack callback,
+                                                            SCNetworkReachabilityContext *context)
+{
+    if (m_set_callback)
+        return m_set_callback(target, callback, context);
+
+    return false;
+}
+
+bool SystemConfiguration::network_reachability_get_flags(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags *flags)
+{
+    if (m_get_flags)
+        return m_get_flags(target, flags);
+
+    return false;
+}
+
+#endif // NETWORK_REACHABILITY_AVAILABLE
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp
new file mode 100644 (file)
index 0000000..c58847e
--- /dev/null
@@ -0,0 +1,374 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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/impl/sync_file.hpp"
+
+#include "util/time.hpp"
+
+#include <realm/util/file.hpp>
+#include <realm/util/scope_exit.hpp>
+
+#include <iomanip>
+#include <sstream>
+#include <system_error>
+#include <fstream>
+
+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+
+inline static int mkstemp(char* _template) { return _open(_mktemp(_template), _O_CREAT | _O_TEMPORARY, _S_IREAD | _S_IWRITE); }
+#else
+#include <unistd.h>
+#endif
+
+
+using File = realm::util::File;
+
+namespace realm {
+
+namespace {
+
+uint8_t value_of_hex_digit(char hex_digit)
+{
+    if (hex_digit >= '0' && hex_digit <= '9') {
+        return hex_digit - '0';
+    } else if (hex_digit >= 'A' && hex_digit <= 'F') {
+        return 10 + hex_digit - 'A';
+    } else if (hex_digit >= 'a' && hex_digit <= 'f') {
+        return 10 + hex_digit - 'a';
+    } else {
+        throw std::invalid_argument("Cannot get the value of a character that isn't a hex digit.");
+    }
+}
+
+bool filename_is_reserved(const std::string& filename) {
+    return (filename == "." || filename == "..");
+}
+
+bool character_is_unreserved(char character)
+{
+    bool is_capital_letter = (character >= 'A' && character <= 'Z');
+    bool is_lowercase_letter = (character >= 'a' && character <= 'z');
+    bool is_number = (character >= '0' && character <= '9');
+    bool is_allowed_symbol = (character == '-' || character == '_' || character == '.');
+    return is_capital_letter || is_lowercase_letter || is_number || is_allowed_symbol;
+}
+
+char decoded_char_for(const std::string& percent_encoding, size_t index)
+{
+    if (index+2 >= percent_encoding.length()) {
+        throw std::invalid_argument("Malformed string: not enough characters after '%' before end of string.");
+    }
+    REALM_ASSERT(percent_encoding[index] == '%');
+    return (16*value_of_hex_digit(percent_encoding[index + 1])) + value_of_hex_digit(percent_encoding[index + 2]);
+}
+
+} // (anonymous namespace)
+
+namespace util {
+
+std::string make_percent_encoded_string(const std::string& raw_string)
+{
+    std::string buffer;
+    buffer.reserve(raw_string.size());
+    for (size_t i=0; i<raw_string.size(); i++) {
+        unsigned char character = raw_string[i];
+        if (character_is_unreserved(character)) {
+            buffer.push_back(character);
+        } else {
+            buffer.resize(buffer.size() + 3);
+            // Format string must resolve to exactly 3 characters.
+            sprintf(&buffer.back() - 2, "%%%2X", character);
+        }
+    }
+    return buffer;
+}
+
+std::string make_raw_string(const std::string& percent_encoded_string)
+{
+    std::string buffer;
+    size_t input_len = percent_encoded_string.length();
+    buffer.reserve(input_len);
+    size_t idx = 0;
+    while (idx < input_len) {
+        char current = percent_encoded_string[idx];
+        if (current == '%') {
+            // Decode. +3.
+            buffer.push_back(decoded_char_for(percent_encoded_string, idx));
+            idx += 3;
+        } else {
+            // No need to decode. +1.
+            if (!character_is_unreserved(current)) {
+                throw std::invalid_argument("Input string is invalid: contains reserved characters.");
+            }
+            buffer.push_back(current);
+            idx++;
+        }
+    }
+    return buffer;
+}
+
+std::string file_path_by_appending_component(const std::string& path, const std::string& component, FilePathType path_type)
+{
+    // FIXME: Does this have to be changed to accomodate Windows platforms?
+    std::string buffer;
+    buffer.reserve(2 + path.length() + component.length());
+    buffer.append(path);
+    std::string terminal = "";
+    if (path_type == FilePathType::Directory && component[component.length() - 1] != '/') {
+        terminal = "/";
+    }
+    char path_last = path[path.length() - 1];
+    char component_first = component[0];
+    if (path_last == '/' && component_first == '/') {
+        buffer.append(component.substr(1));
+        buffer.append(terminal);
+    } else if (path_last == '/' || component_first == '/') {
+        buffer.append(component);
+        buffer.append(terminal);
+    } else {
+        buffer.append("/");
+        buffer.append(component);
+        buffer.append(terminal);
+    }
+    return buffer;
+}
+
+std::string file_path_by_appending_extension(const std::string& path, const std::string& extension)
+{
+    std::string buffer;
+    buffer.reserve(1 + path.length() + extension.length());
+    buffer.append(path);
+    char path_last = path[path.length() - 1];
+    char extension_first = extension[0];
+    if (path_last == '.' && extension_first == '.') {
+        buffer.append(extension.substr(1));
+    } else if (path_last == '.' || extension_first == '.') {
+        buffer.append(extension);
+    } else {
+        buffer.append(".");
+        buffer.append(extension);
+    }
+    return buffer;
+}
+
+std::string create_timestamped_template(const std::string& prefix, int wildcard_count)
+{
+    constexpr int WILDCARD_MAX = 20;
+    constexpr int WILDCARD_MIN = 6;
+    wildcard_count = std::min(WILDCARD_MAX, std::max(WILDCARD_MIN, wildcard_count));
+    std::time_t time = std::time(nullptr);
+    std::stringstream stream;
+    stream << prefix << "-" << util::put_time(time, "%Y%m%d-%H%M%S") << "-" << std::string(wildcard_count, 'X');
+    return stream.str();
+}
+
+std::string reserve_unique_file_name(const std::string& path, const std::string& template_string)
+{
+    REALM_ASSERT_DEBUG(template_string.find("XXXXXX") != std::string::npos);
+    std::string path_buffer = file_path_by_appending_component(path, template_string, FilePathType::File);
+    int fd = mkstemp(&path_buffer[0]);
+    if (fd < 0) {
+        int err = errno;
+        throw std::system_error(err, std::system_category());
+    }
+    // Remove the file so we can use the name for our own file.
+#ifdef _WIN32
+    _close(fd);
+    _unlink(path_buffer.c_str());
+#else
+    close(fd);
+    unlink(path_buffer.c_str());
+#endif
+    return path_buffer;
+}
+
+} // util
+
+constexpr const char SyncFileManager::c_sync_directory[];
+constexpr const char SyncFileManager::c_utility_directory[];
+constexpr const char SyncFileManager::c_recovery_directory[];
+constexpr const char SyncFileManager::c_metadata_directory[];
+constexpr const char SyncFileManager::c_metadata_realm[];
+constexpr const char SyncFileManager::c_user_info_file[];
+
+std::string SyncFileManager::get_special_directory(std::string directory_name) const
+{
+    auto dir_path = file_path_by_appending_component(get_base_sync_directory(),
+                                                     directory_name,
+                                                     util::FilePathType::Directory);
+    util::try_make_dir(dir_path);
+    return dir_path;
+}
+
+std::string SyncFileManager::get_base_sync_directory() const
+{
+    auto sync_path = file_path_by_appending_component(m_base_path,
+                                                      c_sync_directory,
+                                                      util::FilePathType::Directory);
+    util::try_make_dir(sync_path);
+    return sync_path;
+}
+
+std::string SyncFileManager::user_directory(const std::string& local_identity,
+                                            util::Optional<SyncUserIdentifier> user_info) const
+{
+    REALM_ASSERT(local_identity.length() > 0);
+    std::string escaped = util::make_percent_encoded_string(local_identity);
+    if (filename_is_reserved(escaped))
+        throw std::invalid_argument("A user can't have an identifier reserved by the filesystem.");
+
+    auto user_path = file_path_by_appending_component(get_base_sync_directory(),
+                                                      escaped,
+                                                      util::FilePathType::Directory);
+    bool dir_created = util::try_make_dir(user_path);
+    if (dir_created && user_info) {
+        // Add a text file in the user directory containing the user identity, for backup purposes.
+        // Only do this the first time the directory is created.
+        auto info_path = util::file_path_by_appending_component(user_path, c_user_info_file);
+        std::ofstream info_file;
+        info_file.open(info_path.c_str());
+        if (info_file.is_open()) {
+            info_file << user_info->user_id << "\n" << user_info->auth_server_url << "\n";
+            info_file.close();
+        }
+    }
+    return user_path;
+}
+
+void SyncFileManager::remove_user_directory(const std::string& local_identity) const
+{
+    REALM_ASSERT(local_identity.length() > 0);
+    const auto& escaped = util::make_percent_encoded_string(local_identity);
+    if (filename_is_reserved(escaped))
+        throw std::invalid_argument("A user can't have an identifier reserved by the filesystem.");
+
+    auto user_path = file_path_by_appending_component(get_base_sync_directory(),
+                                                      escaped,
+                                                      util::FilePathType::Directory);
+    util::try_remove_dir_recursive(user_path);
+}
+
+bool SyncFileManager::try_rename_user_directory(const std::string& old_name, const std::string& new_name) const
+{
+    REALM_ASSERT_DEBUG(old_name.length() > 0 && new_name.length() > 0);
+    const auto& old_name_escaped = util::make_percent_encoded_string(old_name);
+    const auto& new_name_escaped = util::make_percent_encoded_string(new_name);
+    const std::string& base = get_base_sync_directory();
+    if (filename_is_reserved(old_name_escaped) || filename_is_reserved(new_name_escaped))
+        throw std::invalid_argument("A user directory can't be renamed using a reserved identifier.");
+
+    const auto& old_path = file_path_by_appending_component(base, old_name_escaped, util::FilePathType::Directory);
+    const auto& new_path = file_path_by_appending_component(base, new_name_escaped, util::FilePathType::Directory);
+
+    try {
+        File::move(old_path, new_path);
+    } catch (File::NotFound const&) {
+        return false;
+    }
+    return true;
+}
+
+bool SyncFileManager::remove_realm(const std::string& absolute_path) const
+{
+    REALM_ASSERT(absolute_path.length() > 0);
+    bool success = true;
+    // Remove the Realm file (e.g. "example.realm").
+    success = File::try_remove(absolute_path);
+    // Remove the lock file (e.g. "example.realm.lock").
+    auto lock_path = util::file_path_by_appending_extension(absolute_path, "lock");
+    success = File::try_remove(lock_path);
+    // Remove the management directory (e.g. "example.realm.management").
+    auto management_path = util::file_path_by_appending_extension(absolute_path, "management");
+    try {
+        util::try_remove_dir_recursive(management_path);
+    }
+    catch (File::AccessError const&) {
+        success = false;
+    }
+    return success;
+}
+
+bool SyncFileManager::copy_realm_file(const std::string& old_path, const std::string& new_path) const
+{
+    REALM_ASSERT(old_path.length() > 0);
+    try {
+        if (File::exists(new_path)) {
+            return false;
+        }
+        File::copy(old_path, new_path);
+    }
+    catch (File::NotFound const&) {
+        return false;
+    }
+    catch (File::AccessError const&) {
+        return false;
+    }
+    return true;
+}
+
+bool SyncFileManager::remove_realm(const std::string& local_identity, const std::string& raw_realm_path) const
+{
+    REALM_ASSERT(local_identity.length() > 0);
+    REALM_ASSERT(raw_realm_path.length() > 0);
+    if (filename_is_reserved(local_identity) || filename_is_reserved(raw_realm_path))
+        throw std::invalid_argument("A user or Realm can't have an identifier reserved by the filesystem.");
+
+    auto escaped = util::make_percent_encoded_string(raw_realm_path);
+    auto realm_path = util::file_path_by_appending_component(user_directory(local_identity), escaped);
+    return remove_realm(realm_path);
+}
+
+std::string SyncFileManager::path(const std::string& local_identity, const std::string& raw_realm_path,
+                                  util::Optional<SyncUserIdentifier> user_info) const
+{
+    REALM_ASSERT(local_identity.length() > 0);
+    REALM_ASSERT(raw_realm_path.length() > 0);
+    if (filename_is_reserved(local_identity) || filename_is_reserved(raw_realm_path))
+        throw std::invalid_argument("A user or Realm can't have an identifier reserved by the filesystem.");
+
+    auto escaped = util::make_percent_encoded_string(raw_realm_path);
+    auto realm_path = util::file_path_by_appending_component(user_directory(local_identity, user_info), escaped);
+    return realm_path;
+}
+
+std::string SyncFileManager::metadata_path() const
+{
+    auto dir_path = file_path_by_appending_component(get_utility_directory(),
+                                                     c_metadata_directory,
+                                                     util::FilePathType::Directory);
+    util::try_make_dir(dir_path);
+    return util::file_path_by_appending_component(dir_path, c_metadata_realm);
+}
+
+bool SyncFileManager::remove_metadata_realm() const
+{
+    auto dir_path = file_path_by_appending_component(get_utility_directory(),
+                                                     c_metadata_directory,
+                                                     util::FilePathType::Directory);
+    try {
+        util::try_remove_dir_recursive(dir_path);
+        return true;
+    }
+    catch (File::AccessError const&) {
+        return false;
+    }
+}
+
+} // realm
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_metadata.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_metadata.cpp
new file mode 100644 (file)
index 0000000..c001131
--- /dev/null
@@ -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.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include "sync/impl/sync_metadata.hpp"
+
+#include "object_schema.hpp"
+#include "object_store.hpp"
+#include "property.hpp"
+#include "results.hpp"
+#include "schema.hpp"
+#include "util/uuid.hpp"
+#if REALM_PLATFORM_APPLE
+#include "impl/apple/keychain_helper.hpp"
+#endif
+
+#include <realm/descriptor.hpp>
+#include <realm/table.hpp>
+
+namespace {
+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_local_uuid = "local_uuid";
+static const char * const c_sync_auth_server_url = "auth_server_url";
+static const char * const c_sync_user_token = "user_token";
+static const char * const c_sync_user_is_admin = "user_is_admin";
+
+static const char * const c_sync_fileActionMetadata = "FileActionMetadata";
+static const char * const c_sync_original_name = "original_name";
+static const char * const c_sync_new_name = "new_name";
+static const char * const c_sync_action = "action";
+static const char * const c_sync_url = "url";
+
+static const char * const c_sync_clientMetadata = "ClientMetadata";
+static const char * const c_sync_uuid = "uuid";
+
+realm::Schema make_schema()
+{
+    using namespace realm;
+    return Schema{
+        {c_sync_userMetadata, {
+            {c_sync_identity, PropertyType::String},
+            {c_sync_local_uuid, PropertyType::String},
+            {c_sync_marked_for_removal, PropertyType::Bool},
+            {c_sync_user_token, PropertyType::String|PropertyType::Nullable},
+            {c_sync_auth_server_url, PropertyType::String},
+            {c_sync_user_is_admin, PropertyType::Bool},
+        }},
+        {c_sync_fileActionMetadata, {
+            {c_sync_original_name, PropertyType::String, Property::IsPrimary{true}},
+            {c_sync_new_name, PropertyType::String|PropertyType::Nullable},
+            {c_sync_action, PropertyType::Int},
+            {c_sync_url, PropertyType::String},
+            {c_sync_identity, PropertyType::String},
+        }},
+        {c_sync_clientMetadata, {
+            {c_sync_uuid, PropertyType::String},
+        }}
+    };
+}
+
+} // anonymous namespace
+
+namespace realm {
+
+// MARK: - Sync metadata manager
+
+SyncMetadataManager::SyncMetadataManager(std::string path,
+                                         bool should_encrypt,
+                                         util::Optional<std::vector<char>> encryption_key)
+{
+    constexpr uint64_t SCHEMA_VERSION = 2;
+
+    Realm::Config config;
+    config.path = path;
+    config.schema = make_schema();
+    config.schema_version = SCHEMA_VERSION;
+    config.schema_mode = SchemaMode::Automatic;
+#if REALM_PLATFORM_APPLE
+    if (should_encrypt && !encryption_key) {
+        encryption_key = keychain::metadata_realm_encryption_key(File::exists(path));
+    }
+#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);
+    }
+
+    config.migration_function = [](SharedRealm old_realm, SharedRealm realm, Schema&) {
+        if (old_realm->schema_version() < 2) {
+            TableRef old_table = ObjectStore::table_for_object_type(old_realm->read_group(), c_sync_userMetadata);
+            TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata);
+
+            // Get all the SyncUserMetadata objects.
+            Results results(old_realm, *old_table);
+
+            // Column indices.
+            size_t old_idx_identity = old_table->get_column_index(c_sync_identity);
+            size_t old_idx_url = old_table->get_column_index(c_sync_auth_server_url);
+            size_t idx_local_uuid = table->get_column_index(c_sync_local_uuid);
+            size_t idx_url = table->get_column_index(c_sync_auth_server_url);
+
+            for (size_t i = 0; i < results.size(); i++) {
+                RowExpr entry = results.get(i);
+                // Set the UUID equal to the user identity for existing users.
+                auto identity = entry.get_string(old_idx_identity);
+                table->set_string(idx_local_uuid, entry.get_index(), identity);
+                // Migrate the auth server URLs to a non-nullable property.
+                auto url = entry.get_string(old_idx_url);
+                table->set_string(idx_url, entry.get_index(), url.is_null() ? "" : url);
+            }
+        }
+    };
+
+    SharedRealm realm = Realm::get_shared_realm(config);
+
+    // Get data about the (hardcoded) schemas
+    auto object_schema = realm->schema().find(c_sync_userMetadata);
+    m_user_schema = {
+        object_schema->persisted_properties[0].table_column,
+        object_schema->persisted_properties[1].table_column,
+        object_schema->persisted_properties[2].table_column,
+        object_schema->persisted_properties[3].table_column,
+        object_schema->persisted_properties[4].table_column,
+        object_schema->persisted_properties[5].table_column,
+    };
+
+    object_schema = realm->schema().find(c_sync_fileActionMetadata);
+    m_file_action_schema = {
+        object_schema->persisted_properties[0].table_column,
+        object_schema->persisted_properties[1].table_column,
+        object_schema->persisted_properties[2].table_column,
+        object_schema->persisted_properties[3].table_column,
+        object_schema->persisted_properties[4].table_column,
+    };
+
+    object_schema = realm->schema().find(c_sync_clientMetadata);
+    m_client_schema = {
+        object_schema->persisted_properties[0].table_column,
+    };
+
+    m_metadata_config = std::move(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
+{
+    SharedRealm realm = Realm::get_shared_realm(m_metadata_config);
+
+    TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata);
+    Query query = table->where().equal(m_user_schema.idx_marked_for_removal, marked);
+
+    Results results(realm, std::move(query));
+    return SyncUserMetadataResults(std::move(results), std::move(realm), m_user_schema);
+}
+
+SyncFileActionMetadataResults SyncMetadataManager::all_pending_actions() const
+{
+    SharedRealm realm = Realm::get_shared_realm(m_metadata_config);
+    TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata);
+    Results results(realm, table->where());
+    return SyncFileActionMetadataResults(std::move(results), std::move(realm), m_file_action_schema);
+}
+
+bool SyncMetadataManager::delete_metadata_action(const std::string& original_name) const
+{
+    auto shared_realm = Realm::get_shared_realm(m_metadata_config);
+
+    // Retrieve the row for this object.
+    TableRef table = ObjectStore::table_for_object_type(shared_realm->read_group(), c_sync_fileActionMetadata);
+    shared_realm->begin_transaction();
+    size_t row_idx = table->find_first_string(m_file_action_schema.idx_original_name, original_name);
+    if (row_idx == not_found) {
+        shared_realm->cancel_transaction();
+        return false;
+    }
+    table->move_last_over(row_idx);
+    shared_realm->commit_transaction();
+    return true;
+}
+
+util::Optional<SyncUserMetadata> SyncMetadataManager::get_or_make_user_metadata(const std::string& identity,
+                                                                                const std::string& url,
+                                                                                bool make_if_absent) const
+{
+    auto realm = Realm::get_shared_realm(m_metadata_config);
+    auto& schema = m_user_schema;
+
+    // Retrieve or create the row for this object.
+    TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata);
+    Query query = table->where().equal(schema.idx_identity, identity).equal(schema.idx_auth_server_url, url);
+    Results results(realm, std::move(query));
+    REALM_ASSERT_DEBUG(results.size() < 2);
+    auto row = results.first();
+
+    if (!row) {
+        if (!make_if_absent)
+            return none;
+
+        realm->begin_transaction();
+        // Check the results again.
+        row = results.first();
+        if (!row) {
+            auto row = table->get(table->add_empty_row());
+            std::string uuid = util::uuid_string();
+            row.set_string(schema.idx_identity, identity);
+            row.set_string(schema.idx_auth_server_url, url);
+            row.set_string(schema.idx_local_uuid, uuid);
+            row.set_bool(schema.idx_user_is_admin, false);
+            row.set_bool(schema.idx_marked_for_removal, false);
+            realm->commit_transaction();
+            return SyncUserMetadata(schema, std::move(realm), std::move(row));
+        } else {
+            // Someone beat us to adding this user.
+            if (row->get_bool(schema.idx_marked_for_removal)) {
+                // User is dead. Revive or return none.
+                if (make_if_absent) {
+                    row->set_bool(schema.idx_marked_for_removal, false);
+                    realm->commit_transaction();
+                } else {
+                    realm->cancel_transaction();
+                    return none;
+                }
+            } else {
+                // User is alive, nothing else to do.
+                realm->cancel_transaction();
+            }
+            return SyncUserMetadata(schema, std::move(realm), std::move(*row));
+        }
+    }
+
+    // Got an existing user.
+    if (row->get_bool(schema.idx_marked_for_removal)) {
+        // User is dead. Revive or return none.
+        if (make_if_absent) {
+            realm->begin_transaction();
+            row->set_bool(schema.idx_marked_for_removal, false);
+            realm->commit_transaction();
+        } else {
+            return none;
+        }
+    }
+    return SyncUserMetadata(schema, std::move(realm), std::move(*row));
+}
+
+SyncFileActionMetadata SyncMetadataManager::make_file_action_metadata(const std::string &original_name,
+                                                                      const std::string &url,
+                                                                      const std::string &local_uuid,
+                                                                      SyncFileActionMetadata::Action action,
+                                                                      util::Optional<std::string> new_name) const
+{
+    size_t raw_action = static_cast<size_t>(action);
+
+    // Open the Realm.
+    auto realm = Realm::get_shared_realm(m_metadata_config);
+    auto& schema = m_file_action_schema;
+
+    // Retrieve or create the row for this object.
+    TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata);
+    realm->begin_transaction();
+    size_t row_idx = table->find_first_string(schema.idx_original_name, original_name);
+    if (row_idx == not_found) {
+        row_idx = table->add_empty_row();
+        table->set_string(schema.idx_original_name, row_idx, original_name);
+    }
+    table->set_string(schema.idx_new_name, row_idx, new_name);
+    table->set_int(schema.idx_action, row_idx, raw_action);
+    table->set_string(schema.idx_url, row_idx, url);
+    table->set_string(schema.idx_user_identity, row_idx, local_uuid);
+    realm->commit_transaction();
+    return SyncFileActionMetadata(schema, std::move(realm), table->get(row_idx));
+}
+
+util::Optional<SyncFileActionMetadata> SyncMetadataManager::get_file_action_metadata(const std::string& original_name) const
+{
+    auto realm = Realm::get_shared_realm(m_metadata_config);
+    auto schema = m_file_action_schema;
+    TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata);
+    size_t row_idx = table->find_first_string(schema.idx_original_name, original_name);
+    if (row_idx == not_found)
+        return none;
+
+    return SyncFileActionMetadata(std::move(schema), std::move(realm), table->get(row_idx));
+}
+
+std::string SyncMetadataManager::client_uuid() const
+{
+    auto realm = Realm::get_shared_realm(m_metadata_config);
+    TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_clientMetadata);
+    if (table->is_empty()) {
+        realm->begin_transaction();
+        if (table->is_empty()) {
+            size_t idx = table->add_empty_row();
+            REALM_ASSERT_DEBUG(idx == 0);
+            auto uuid = uuid_string();
+            table->set_string(m_client_schema.idx_uuid, idx, uuid);
+            realm->commit_transaction();
+            return uuid;
+        }
+        realm->cancel_transaction();
+    }
+
+    return table->get_string(m_client_schema.idx_uuid, 0);
+}
+
+// MARK: - Sync user metadata
+
+SyncUserMetadata::SyncUserMetadata(Schema schema, SharedRealm realm, RowExpr row)
+: m_realm(std::move(realm))
+, m_schema(std::move(schema))
+, m_row(row)
+{ }
+
+std::string SyncUserMetadata::identity() const
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    return m_row.get_string(m_schema.idx_identity);
+}
+
+std::string SyncUserMetadata::local_uuid() const
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    return m_row.get_string(m_schema.idx_local_uuid);
+}
+
+util::Optional<std::string> SyncUserMetadata::user_token() const
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    StringData result = m_row.get_string(m_schema.idx_user_token);
+    return result.is_null() ? util::none : util::make_optional(std::string(result));
+}
+
+std::string SyncUserMetadata::auth_server_url() const
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    return m_row.get_string(m_schema.idx_auth_server_url);
+}
+
+bool SyncUserMetadata::is_admin() const
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    return m_row.get_bool(m_schema.idx_user_is_admin);
+}
+
+void SyncUserMetadata::set_user_token(util::Optional<std::string> user_token)
+{
+    if (m_invalid)
+        return;
+
+    REALM_ASSERT_DEBUG(m_realm);
+    m_realm->verify_thread();
+    m_realm->begin_transaction();
+    m_row.set_string(m_schema.idx_user_token, *user_token);
+    m_realm->commit_transaction();
+}
+
+void SyncUserMetadata::set_is_admin(bool is_admin)
+{
+    if (m_invalid)
+        return;
+
+    REALM_ASSERT_DEBUG(m_realm);
+    m_realm->verify_thread();
+    m_realm->begin_transaction();
+    m_row.set_bool(m_schema.idx_user_is_admin, is_admin);
+    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;
+}
+
+// MARK: - File action metadata
+
+SyncFileActionMetadata::SyncFileActionMetadata(Schema schema, SharedRealm realm, RowExpr row)
+: m_realm(std::move(realm))
+, m_schema(std::move(schema))
+, m_row(row)
+{ }
+
+std::string SyncFileActionMetadata::original_name() const
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    return m_row.get_string(m_schema.idx_original_name);
+}
+
+util::Optional<std::string> SyncFileActionMetadata::new_name() const
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    StringData result = m_row.get_string(m_schema.idx_new_name);
+    return result.is_null() ? util::none : util::make_optional(std::string(result));
+}
+
+std::string SyncFileActionMetadata::user_local_uuid() const
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    return m_row.get_string(m_schema.idx_user_identity);
+}
+
+SyncFileActionMetadata::Action SyncFileActionMetadata::action() const
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    return static_cast<SyncFileActionMetadata::Action>(m_row.get_int(m_schema.idx_action));
+}
+
+std::string SyncFileActionMetadata::url() const
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    return m_row.get_string(m_schema.idx_url);
+}
+
+void SyncFileActionMetadata::remove()
+{
+    REALM_ASSERT(m_realm);
+    m_realm->verify_thread();
+    m_realm->begin_transaction();
+    TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_fileActionMetadata);
+    table->move_last_over(m_row.get_index());
+    m_realm->commit_transaction();
+    m_realm = nullptr;
+}
+
+} // namespace realm
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/partial_sync.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/partial_sync.cpp
new file mode 100644 (file)
index 0000000..ed5a3c5
--- /dev/null
@@ -0,0 +1,133 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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/partial_sync.hpp"
+
+#include "impl/notification_wrapper.hpp"
+#include "impl/object_accessor_impl.hpp"
+#include "object_schema.hpp"
+#include "results.hpp"
+#include "shared_realm.hpp"
+#include "sync/sync_config.hpp"
+
+#include <realm/util/scope_exit.hpp>
+
+namespace realm {
+namespace partial_sync {
+
+namespace {
+
+constexpr const char* result_sets_type_name = "__ResultSets";
+
+void update_schema(Group& group, Property matches_property)
+{
+    Schema current_schema;
+    std::string table_name = ObjectStore::table_name_for_object_type(result_sets_type_name);
+    if (group.has_table(table_name))
+        current_schema = {ObjectSchema{group, result_sets_type_name}};
+
+    Schema desired_schema({
+        ObjectSchema(result_sets_type_name, {
+            {"matches_property", PropertyType::String},
+            {"query", PropertyType::String},
+            {"status", PropertyType::Int},
+            {"error_message", PropertyType::String},
+            {"query_parse_counter", PropertyType::Int},
+            std::move(matches_property)
+        })
+    });
+    auto required_changes = current_schema.compare(desired_schema);
+    if (!required_changes.empty())
+        ObjectStore::apply_additive_changes(group, required_changes, true);
+}
+
+} // unnamed namespace
+
+void register_query(std::shared_ptr<Realm> realm, const std::string &object_class, const std::string &query,
+                    std::function<void (Results, std::exception_ptr)> callback)
+{
+    auto sync_config = realm->config().sync_config;
+    if (!sync_config || !sync_config->is_partial)
+        throw std::logic_error("A partial sync query can only be registered in a partially synced Realm");
+
+    if (realm->schema().find(object_class) == realm->schema().end())
+        throw std::logic_error("A partial sync query can only be registered for a type that exists in the Realm's schema");
+
+    auto matches_property = object_class + "_matches";
+
+    // The object schema must outlive `object` below.
+    std::unique_ptr<ObjectSchema> result_sets_schema;
+    Object raw_object;
+    {
+        realm->begin_transaction();
+        auto cleanup = util::make_scope_exit([&]() noexcept {
+            if (realm->is_in_transaction())
+                realm->cancel_transaction();
+        });
+
+        update_schema(realm->read_group(),
+                      Property(matches_property, PropertyType::Object|PropertyType::Array, object_class));
+
+        result_sets_schema = std::make_unique<ObjectSchema>(realm->read_group(), result_sets_type_name);
+
+        CppContext context;
+        raw_object = Object::create<util::Any>(context, realm, *result_sets_schema,
+                                               AnyDict{
+                                                   {"matches_property", matches_property},
+                                                   {"query", query},
+                                                   {"status", int64_t(0)},
+                                                   {"error_message", std::string()},
+                                                   {"query_parse_counter", int64_t(0)},
+                                               }, false);
+
+        realm->commit_transaction();
+    }
+
+    auto object = std::make_shared<_impl::NotificationWrapper<Object>>(std::move(raw_object));
+
+    // Observe the new object and notify listener when the results are complete (status != 0).
+    auto notification_callback = [object, matches_property,
+                                  result_sets_schema=std::move(result_sets_schema),
+                                  callback=std::move(callback)](CollectionChangeSet, std::exception_ptr error) mutable {
+        if (error) {
+            callback(Results(), error);
+            object.reset();
+            return;
+        }
+
+        CppContext context;
+        auto status = any_cast<int64_t>(object->get_property_value<util::Any>(context, "status"));
+        if (status == 0) {
+            // Still computing...
+            return;
+        } else if (status == 1) {
+            // Finished successfully.
+            auto list = any_cast<List>(object->get_property_value<util::Any>(context, matches_property));
+            callback(list.as_results(), nullptr);
+        } else {
+            // Finished with error.
+            auto message = any_cast<std::string>(object->get_property_value<util::Any>(context, "error_message"));
+            callback(Results(), std::make_exception_ptr(std::runtime_error(std::move(message))));
+        }
+        object.reset();
+    };
+    object->add_notification_callback(std::move(notification_callback));
+}
+
+} // namespace partial_sync
+} // namespace realm
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_config.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_config.cpp
new file mode 100644 (file)
index 0000000..597d36d
--- /dev/null
@@ -0,0 +1,63 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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/sync_config.hpp"
+
+#include "sync/sync_manager.hpp"
+
+#include <realm/sync/crypto.hpp>
+
+namespace realm {
+
+namespace {
+
+// Construct an identifier for this partially synced Realm by combining client and user identifiers.
+std::string partial_sync_identifier(const SyncUser& user)
+{
+    std::string raw_identifier = SyncManager::shared().client_uuid() + "/" + user.local_identity();
+    uint8_t identifier[20];
+    sync::crypto::sha1(raw_identifier.data(), raw_identifier.size(), (char *)&identifier[0]);
+
+    std::stringstream ss;
+    ss << std::hex << std::setfill('0');
+    for (uint8_t c : identifier)
+        ss << std::setw(2) << (unsigned)c;
+    return ss.str();
+}
+
+} // unnamed namespace
+
+std::string SyncConfig::realm_url() const
+{
+    REALM_ASSERT(reference_realm_url.length() > 0);
+    REALM_ASSERT(user);
+
+    if (!is_partial)
+        return reference_realm_url;
+
+    std::string base_url = reference_realm_url;
+    if (base_url.back() == '/')
+        base_url.pop_back();
+
+    if (custom_partial_sync_identifier)
+        return base_url + "/__partial/" + *custom_partial_sync_identifier;
+
+    return base_url + "/__partial/" + partial_sync_identifier(*user);
+}
+
+} // namespace realm
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_manager.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_manager.cpp
new file mode 100644 (file)
index 0000000..819dfb3
--- /dev/null
@@ -0,0 +1,528 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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/sync_manager.hpp"
+
+#include "sync/impl/sync_client.hpp"
+#include "sync/impl/sync_file.hpp"
+#include "sync/impl/sync_metadata.hpp"
+#include "sync/sync_session.hpp"
+#include "sync/sync_user.hpp"
+
+using namespace realm;
+using namespace realm::_impl;
+
+constexpr const char SyncManager::c_admin_identity[];
+
+SyncManager& SyncManager::shared()
+{
+    // The singleton is heap-allocated in order to fix an issue when running unit tests where tests would crash after
+    // they were done running because the manager was destroyed too early.
+    static SyncManager& manager = *new SyncManager;
+    return manager;
+}
+
+void SyncManager::configure_file_system(const std::string& base_file_path,
+                                        MetadataMode metadata_mode,
+                                        util::Optional<std::vector<char>> custom_encryption_key,
+                                        bool reset_metadata_on_error)
+{
+    struct UserCreationData {
+        std::string identity;
+        std::string user_token;
+        std::string server_url;
+        bool is_admin;
+    };
+
+    std::vector<UserCreationData> users_to_add;
+    {
+        std::lock_guard<std::mutex> lock(m_file_system_mutex);
+
+        // Set up the file manager.
+        if (m_file_manager) {
+            REALM_ASSERT(m_file_manager->base_path() == base_file_path);
+        } else {
+            m_file_manager = std::make_unique<SyncFileManager>(base_file_path);
+        }
+
+        // Set up the metadata manager, and perform initial loading/purging work.
+        if (m_metadata_manager) {
+            return;
+        }
+        switch (metadata_mode) {
+            case MetadataMode::NoEncryption:
+                m_metadata_manager = std::make_unique<SyncMetadataManager>(m_file_manager->metadata_path(),
+                                                                           false);
+                break;
+            case MetadataMode::Encryption:
+                try {
+                    m_metadata_manager = std::make_unique<SyncMetadataManager>(m_file_manager->metadata_path(),
+                                                                               true,
+                                                                               custom_encryption_key);
+                } catch (RealmFileException const& ex) {
+                    if (reset_metadata_on_error && m_file_manager->remove_metadata_realm()) {
+                        m_metadata_manager = std::make_unique<SyncMetadataManager>(m_file_manager->metadata_path(),
+                                                                                   true,
+                                                                                   std::move(custom_encryption_key));
+                    } else {
+                        throw;
+                    }
+                }
+                break;
+            case MetadataMode::NoMetadata:
+                return;
+        }
+
+        REALM_ASSERT(m_metadata_manager);
+        // Perform any necessary file actions.
+        std::vector<SyncFileActionMetadata> completed_actions;
+        SyncFileActionMetadataResults file_actions = m_metadata_manager->all_pending_actions();
+        for (size_t i = 0; i < file_actions.size(); i++) {
+            auto file_action = file_actions.get(i);
+            if (run_file_action(file_action)) {
+                completed_actions.emplace_back(std::move(file_action));
+            }
+        }
+        for (auto& action : completed_actions) {
+            action.remove();
+        }
+        // Load persisted users into the users map.
+        SyncUserMetadataResults users = m_metadata_manager->all_unmarked_users();
+        for (size_t i = 0; i < users.size(); i++) {
+            // Note that 'admin' style users are not persisted.
+            auto user_data = users.get(i);
+            auto user_token = user_data.user_token();
+            auto identity = user_data.identity();
+            auto server_url = user_data.auth_server_url();
+            bool is_admin = user_data.is_admin();
+            if (user_token) {
+                UserCreationData data = {
+                    std::move(identity),
+                    std::move(*user_token),
+                    std::move(server_url),
+                    is_admin,
+                };
+                users_to_add.emplace_back(std::move(data));
+            }
+        }
+        // Delete any users marked for death.
+        std::vector<SyncUserMetadata> dead_users;
+        SyncUserMetadataResults users_to_remove = m_metadata_manager->all_users_marked_for_removal();
+        dead_users.reserve(users_to_remove.size());
+        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.)
+            try {
+                m_file_manager->remove_user_directory(user.local_uuid());
+                dead_users.emplace_back(std::move(user));
+            } catch (util::File::AccessError const&) {
+                continue;
+            }
+        }
+        for (auto& user : dead_users) {
+            user.remove();
+        }
+    }
+    {
+        std::lock_guard<std::mutex> lock(m_user_mutex);
+        for (auto& user_data : users_to_add) {
+            auto& identity = user_data.identity;
+            auto& server_url = user_data.server_url;
+            auto user = std::make_shared<SyncUser>(user_data.user_token, identity, server_url);
+            user->set_is_admin(user_data.is_admin);
+            m_users.insert({ {identity, server_url}, std::move(user) });
+        }
+    }
+}
+
+bool SyncManager::immediately_run_file_actions(const std::string& realm_path)
+{
+    if (!m_metadata_manager) {
+        return false;
+    }
+    if (auto metadata = m_metadata_manager->get_file_action_metadata(realm_path)) {
+        if (run_file_action(*metadata)) {
+            metadata->remove();
+            return true;
+        }
+    }
+    return false;
+}
+
+// Perform a file action. Returns whether or not the file action can be removed.
+bool SyncManager::run_file_action(const SyncFileActionMetadata& md)
+{
+    switch (md.action()) {
+        case SyncFileActionMetadata::Action::DeleteRealm:
+            // Delete all the files for the given Realm.
+            m_file_manager->remove_realm(md.original_name());
+            return true;
+        case SyncFileActionMetadata::Action::BackUpThenDeleteRealm:
+            // Copy the primary Realm file to the recovery dir, and then delete the Realm.
+            auto new_name = md.new_name();
+            auto original_name = md.original_name();
+            if (!util::File::exists(original_name)) {
+                // The Realm file doesn't exist anymore.
+                return true;
+            }
+            if (new_name && !util::File::exists(*new_name) && m_file_manager->copy_realm_file(original_name, *new_name)) {
+                // We successfully copied the Realm file to the recovery directory.
+                m_file_manager->remove_realm(original_name);
+                return true;
+            }
+            return false;
+    }
+    return false;
+}
+
+void SyncManager::reset_for_testing()
+{
+    std::lock_guard<std::mutex> lock(m_file_system_mutex);
+    m_file_manager = nullptr;
+    m_metadata_manager = nullptr;
+    {
+        // Destroy all the users.
+        std::lock_guard<std::mutex> lock(m_user_mutex);
+        m_users.clear();
+        m_admin_token_users.clear();
+    }
+    {
+        std::lock_guard<std::mutex> lock(m_mutex);
+
+        // Stop the client. This will abort any uploads that inactive sessions are waiting for.
+        if (m_sync_client)
+            m_sync_client->stop();
+
+        {
+            std::lock_guard<std::mutex> lock(m_session_mutex);
+
+            // Callers of `SyncManager::reset_for_testing` should ensure there are no active sessions
+            // prior to calling `reset_for_testing`.
+            auto no_active_sessions = std::none_of(m_sessions.begin(), m_sessions.end(), [](auto& element){
+                return element.second->existing_external_reference();
+            });
+            REALM_ASSERT_RELEASE(no_active_sessions);
+
+            // Destroy any inactive sessions.
+            // FIXME: We shouldn't have any inactive sessions at this point! Sessions are expected to
+            // remain inactive until their final upload completes, at which point they are unregistered
+            // and destroyed. Our call to `sync::Client::stop` above aborts all uploads, so all sessions
+            // should have already been destroyed.
+            m_sessions.clear();
+        }
+
+        // Destroy the client now that we have no remaining sessions.
+        m_sync_client = nullptr;
+
+        // Reset even more state.
+        // NOTE: these should always match the defaults.
+        m_log_level = util::Logger::Level::info;
+        m_logger_factory = nullptr;
+        m_client_reconnect_mode = ReconnectMode::normal;
+        m_multiplex_sessions = false;
+    }
+}
+
+void SyncManager::set_log_level(util::Logger::Level level) noexcept
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    m_log_level = level;
+}
+
+void SyncManager::set_logger_factory(SyncLoggerFactory& factory) noexcept
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    m_logger_factory = &factory;
+}
+
+void SyncManager::set_client_should_reconnect_immediately(bool reconnect_immediately)
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    m_client_reconnect_mode = reconnect_immediately ? ReconnectMode::immediate : ReconnectMode::normal;
+}
+
+bool SyncManager::client_should_reconnect_immediately() const noexcept
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    return m_client_reconnect_mode == ReconnectMode::immediate;
+}
+
+void SyncManager::reconnect()
+{
+    std::lock_guard<std::mutex> lock(m_session_mutex);
+    for (auto& it : m_sessions) {
+        it.second->handle_reconnect();
+    }
+}
+
+util::Logger::Level SyncManager::log_level() const noexcept
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    return m_log_level;
+}
+
+bool SyncManager::perform_metadata_update(std::function<void(const SyncMetadataManager&)> update_function) const
+{
+    std::lock_guard<std::mutex> lock(m_file_system_mutex);
+    if (!m_metadata_manager) {
+        return false;
+    }
+    update_function(*m_metadata_manager);
+    return true;
+}
+
+std::shared_ptr<SyncUser> SyncManager::get_user(const SyncUserIdentifier& identifier, std::string refresh_token)
+{
+    std::lock_guard<std::mutex> lock(m_user_mutex);
+    auto it = m_users.find(identifier);
+    if (it == m_users.end()) {
+        // No existing user.
+        auto new_user = std::make_shared<SyncUser>(std::move(refresh_token),
+                                                   identifier.user_id,
+                                                   identifier.auth_server_url,
+                                                   none,
+                                                   SyncUser::TokenType::Normal);
+        m_users.insert({ identifier, new_user });
+        return new_user;
+    } else {
+        auto user = it->second;
+        if (user->state() == SyncUser::State::Error) {
+            return nullptr;
+        }
+        user->update_refresh_token(std::move(refresh_token));
+        return user;
+    }
+}
+
+std::shared_ptr<SyncUser> SyncManager::get_admin_token_user_from_identity(const std::string& identity,
+                                                                          util::Optional<std::string> server_url,
+                                                                          const std::string& token)
+{
+    if (server_url)
+        return get_admin_token_user(*server_url, token, identity);
+
+    std::lock_guard<std::mutex> lock(m_user_mutex);
+    // Look up the user based off the identity.
+    // No server URL, so no migration possible.
+    auto it = m_admin_token_users.find(identity);
+    if (it == m_admin_token_users.end()) {
+        // No existing user.
+        auto new_user = std::make_shared<SyncUser>(token,
+                                                   c_admin_identity,
+                                                   std::move(server_url),
+                                                   identity,
+                                                   SyncUser::TokenType::Admin);
+        m_admin_token_users.insert({ identity, new_user });
+        return new_user;
+    } else {
+        return it->second;
+    }
+}
+
+std::shared_ptr<SyncUser> SyncManager::get_admin_token_user(const std::string& server_url,
+                                                            const std::string& token,
+                                                            util::Optional<std::string> old_identity)
+{
+    std::shared_ptr<SyncUser> user;
+    {
+        std::lock_guard<std::mutex> lock(m_user_mutex);
+        // Look up the user based off the server URL.
+        auto it = m_admin_token_users.find(server_url);
+        if (it != m_admin_token_users.end())
+            return it->second;
+
+        // No existing user.
+        user = std::make_shared<SyncUser>(token,
+                                          c_admin_identity,
+                                          server_url,
+                                          c_admin_identity + server_url,
+                                          SyncUser::TokenType::Admin);
+        m_admin_token_users.insert({ server_url, user });
+    }
+    if (old_identity) {
+        // Try renaming the user's directory to use our new naming standard, if applicable.
+        std::lock_guard<std::mutex> fm_lock(m_file_system_mutex);
+        if (m_file_manager)
+            m_file_manager->try_rename_user_directory(*old_identity, c_admin_identity + server_url);
+    }
+    return user;
+}
+
+std::vector<std::shared_ptr<SyncUser>> SyncManager::all_logged_in_users() const
+{
+    std::lock_guard<std::mutex> lock(m_user_mutex);
+    std::vector<std::shared_ptr<SyncUser>> users;
+    users.reserve(m_users.size() + m_admin_token_users.size());
+    for (auto& it : m_users) {
+        auto user = it.second;
+        if (user->state() == SyncUser::State::Active) {
+            users.emplace_back(std::move(user));
+        }
+    }
+    for (auto& it : m_admin_token_users) {
+        users.emplace_back(std::move(it.second));
+    }
+    return users;
+}
+
+std::shared_ptr<SyncUser> SyncManager::get_current_user() const
+{
+    std::lock_guard<std::mutex> lock(m_user_mutex);
+
+    auto is_active_user = [](auto& el) { return el.second->state() == SyncUser::State::Active; };
+    auto it = std::find_if(m_users.begin(), m_users.end(), is_active_user);
+    if (it == m_users.end())
+        return nullptr;
+
+    if (std::find_if(std::next(it), m_users.end(), is_active_user) != m_users.end())
+        throw std::logic_error("Current user is not valid if more that one valid, logged-in user exists.");
+
+    return it->second;
+}
+
+std::shared_ptr<SyncUser> SyncManager::get_existing_logged_in_user(const SyncUserIdentifier& identifier) const
+{
+    std::lock_guard<std::mutex> lock(m_user_mutex);
+    auto it = m_users.find(identifier);
+    if (it == m_users.end())
+        return nullptr;
+
+    auto user = it->second;
+    return user->state() == SyncUser::State::Active ? user : nullptr;
+}
+
+std::string SyncManager::path_for_realm(const SyncUser& user, const std::string& raw_realm_url) const
+{
+    std::lock_guard<std::mutex> lock(m_file_system_mutex);
+    REALM_ASSERT(m_file_manager);
+    const auto& user_local_identity = user.local_identity();
+    util::Optional<SyncUserIdentifier> user_info;
+    if (user.token_type() == SyncUser::TokenType::Normal)
+        user_info = SyncUserIdentifier{ user.identity(), user.server_url() };
+
+    return m_file_manager->path(user_local_identity, raw_realm_url, std::move(user_info));
+}
+
+std::string SyncManager::recovery_directory_path() const
+{
+    std::lock_guard<std::mutex> lock(m_file_system_mutex);
+    REALM_ASSERT(m_file_manager);
+    return m_file_manager->recovery_directory_path();
+}
+
+std::shared_ptr<SyncSession> SyncManager::get_existing_active_session(const std::string& path) const
+{
+    std::lock_guard<std::mutex> lock(m_session_mutex);
+    if (auto session = get_existing_session_locked(path)) {
+        if (auto external_reference = session->existing_external_reference())
+            return external_reference;
+    }
+    return nullptr;
+}
+
+std::shared_ptr<SyncSession> SyncManager::get_existing_session_locked(const std::string& path) const
+{
+    REALM_ASSERT(!m_session_mutex.try_lock());
+    auto it = m_sessions.find(path);
+    return it == m_sessions.end() ? nullptr : it->second;
+}
+
+std::shared_ptr<SyncSession> SyncManager::get_existing_session(const std::string& path) const
+{
+    std::lock_guard<std::mutex> lock(m_session_mutex);
+    if (auto session = get_existing_session_locked(path))
+        return session->external_reference();
+
+    return nullptr;
+}
+
+std::shared_ptr<SyncSession> SyncManager::get_session(const std::string& path, const SyncConfig& sync_config)
+{
+    auto& client = get_sync_client(); // Throws
+
+    std::lock_guard<std::mutex> lock(m_session_mutex);
+    if (auto session = get_existing_session_locked(path)) {
+        sync_config.user->register_session(session);
+        return session->external_reference();
+    }
+
+    auto shared_session = SyncSession::create(client, path, sync_config);
+    m_sessions[path] = shared_session;
+
+    // Create the external reference immediately to ensure that the session will become
+    // inactive if an exception is thrown in the following code.
+    auto external_reference = shared_session->external_reference();
+
+    sync_config.user->register_session(std::move(shared_session));
+
+    return external_reference;
+}
+
+void SyncManager::unregister_session(const std::string& path)
+{
+    std::lock_guard<std::mutex> lock(m_session_mutex);
+    auto it = m_sessions.find(path);
+    REALM_ASSERT(it != m_sessions.end());
+
+    // If the session has an active external reference, leave it be. This will happen if the session
+    // moves to an inactive state while still externally reference, for instance, as a result of
+    // the session's user being logged out.
+    if (it->second->existing_external_reference())
+        return;
+
+    m_sessions.erase(path);
+}
+
+void SyncManager::enable_session_multiplexing()
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    if (m_sync_client)
+        throw std::logic_error("Cannot enable session multiplexing after creating the sync client");
+    m_multiplex_sessions = true;
+}
+
+SyncClient& SyncManager::get_sync_client() const
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    if (!m_sync_client)
+        m_sync_client = create_sync_client(); // Throws
+    return *m_sync_client;
+}
+
+std::unique_ptr<SyncClient> SyncManager::create_sync_client() const
+{
+    REALM_ASSERT(!m_mutex.try_lock());
+
+    std::unique_ptr<util::Logger> logger;
+    if (m_logger_factory) {
+        logger = m_logger_factory->make_logger(m_log_level); // Throws
+    }
+    else {
+        auto stderr_logger = std::make_unique<util::StderrLogger>(); // Throws
+        stderr_logger->set_level_threshold(m_log_level);
+        logger = std::move(stderr_logger);
+    }
+    return std::make_unique<SyncClient>(std::move(logger), m_client_reconnect_mode, m_multiplex_sessions);
+}
+
+std::string SyncManager::client_uuid() const
+{
+    REALM_ASSERT(m_metadata_manager);
+    return m_metadata_manager->client_uuid();
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_permission.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_permission.cpp
new file mode 100644 (file)
index 0000000..d53dad0
--- /dev/null
@@ -0,0 +1,370 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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/sync_permission.hpp"
+
+#include "impl/notification_wrapper.hpp"
+#include "impl/object_accessor_impl.hpp"
+#include "object_schema.hpp"
+#include "property.hpp"
+
+#include "sync/sync_config.hpp"
+#include "sync/sync_manager.hpp"
+#include "sync/sync_session.hpp"
+#include "sync/sync_user.hpp"
+#include "util/event_loop_signal.hpp"
+#include "util/uuid.hpp"
+
+#include <realm/query_expression.hpp>
+
+using namespace realm;
+using namespace std::chrono;
+
+// MARK: - Utility
+
+namespace {
+
+// Make a handler that extracts either an exception pointer, or the string value
+// of the property with the specified name.
+Permissions::AsyncOperationHandler make_handler_extracting_property(std::string property,
+                                                                    Permissions::PermissionOfferCallback callback)
+{
+    return [property=std::move(property),
+            callback=std::move(callback)](Object* object, std::exception_ptr exception) {
+        if (exception) {
+            callback(none, exception);
+        } else {
+            CppContext context;
+            auto token = any_cast<std::string>(object->get_property_value<util::Any>(context, property));
+            callback(util::make_optional<std::string>(std::move(token)), nullptr);
+        }
+    };
+}
+
+AccessLevel extract_access_level(Object& permission, CppContext& context)
+{
+    auto may_manage = permission.get_property_value<util::Any>(context, "mayManage");
+    if (may_manage.has_value() && any_cast<bool>(may_manage))
+        return AccessLevel::Admin;
+
+    auto may_write = permission.get_property_value<util::Any>(context, "mayWrite");
+    if (may_write.has_value() && any_cast<bool>(may_write))
+        return AccessLevel::Write;
+
+    auto may_read = permission.get_property_value<util::Any>(context, "mayRead");
+    if (may_read.has_value() && any_cast<bool>(may_read))
+        return AccessLevel::Read;
+
+    return AccessLevel::None;
+}
+
+/// Turn a system time point value into the 64-bit integer representing ns since the Unix epoch.
+int64_t ns_since_unix_epoch(const system_clock::time_point& point)
+{
+    tm unix_epoch{};
+    unix_epoch.tm_year = 70;
+    time_t epoch_time = mktime(&unix_epoch);
+    auto epoch_point = system_clock::from_time_t(epoch_time);
+    return duration_cast<nanoseconds>(point - epoch_point).count();
+}
+
+} // anonymous namespace
+
+// MARK: - Permission
+
+Permission::Permission(Object& permission)
+{
+    CppContext context;
+    path = any_cast<std::string>(permission.get_property_value<util::Any>(context, "path"));
+    access = extract_access_level(permission, context);
+    condition = Condition(any_cast<std::string>(permission.get_property_value<util::Any>(context, "userId")));
+    updated_at = any_cast<Timestamp>(permission.get_property_value<util::Any>(context, "updatedAt"));
+}
+
+Permission::Permission(std::string path, AccessLevel access, Condition condition, Timestamp updated_at)
+: path(std::move(path))
+, access(access)
+, condition(std::move(condition))
+, updated_at(std::move(updated_at))
+{ }
+
+std::string Permission::description_for_access_level(AccessLevel level)
+{
+    switch (level) {
+        case AccessLevel::None: return "none";
+        case AccessLevel::Read: return "read";
+        case AccessLevel::Write: return "write";
+        case AccessLevel::Admin: return "admin";
+    }
+    REALM_UNREACHABLE();
+}
+
+bool Permission::paths_are_equivalent(std::string path_1, std::string path_2,
+                                      const std::string& user_id_1, const std::string& user_id_2)
+{
+    REALM_ASSERT_DEBUG(path_1.length() > 0);
+    REALM_ASSERT_DEBUG(path_2.length() > 0);
+    if (path_1 == path_2) {
+        // If both paths are identical and contain `/~/`, the user IDs must match.
+        return (path_1.find("/~/") == std::string::npos) || (user_id_1 == user_id_2);
+    }
+    // Make substitutions for the first `/~/` in the string.
+    size_t index = path_1.find("/~/");
+    if (index != std::string::npos)
+        path_1.replace(index + 1, 1, user_id_1);
+
+    index = path_2.find("/~/");
+    if (index != std::string::npos)
+        path_2.replace(index + 1, 1, user_id_2);
+
+    return path_1 == path_2;
+}
+
+// MARK: - Permissions
+
+void Permissions::get_permissions(std::shared_ptr<SyncUser> user,
+                                  PermissionResultsCallback callback,
+                                  const ConfigMaker& make_config)
+{
+    auto realm = Permissions::permission_realm(user, make_config);
+    auto table = ObjectStore::table_for_object_type(realm->read_group(), "Permission");
+    auto results = std::make_shared<_impl::NotificationWrapper<Results>>(std::move(realm), *table);
+
+    // `get_permissions` works by temporarily adding an async notifier to the permission Realm.
+    // This notifier will run the `async` callback until the Realm contains permissions or
+    // an error happens. When either of these two things happen, the notifier will be
+    // unregistered by nulling out the `results_wrapper` container.
+    auto async = [results, callback=std::move(callback)](CollectionChangeSet, std::exception_ptr ex) mutable {
+        if (ex) {
+            callback(Results(), ex);
+            results.reset();
+            return;
+        }
+        if (results->size() > 0) {
+            // We monitor the raw results. The presence of a `__management` Realm indicates
+            // that the permissions have been downloaded (hence, we wait until size > 0).
+            TableRef table = ObjectStore::table_for_object_type(results->get_realm()->read_group(), "Permission");
+            size_t col_idx = table->get_descriptor()->get_column_index("path");
+            auto query = !(table->column<StringData>(col_idx).ends_with("/__permission")
+                           || table->column<StringData>(col_idx).ends_with("/__perm")
+                           || table->column<StringData>(col_idx).ends_with("/__management"));
+            // Call the callback with our new permissions object. This object will exclude the
+            // private Realms.
+            callback(results->filter(std::move(query)), nullptr);
+            results.reset();
+        }
+    };
+    results->add_notification_callback(std::move(async));
+}
+
+void Permissions::set_permission(std::shared_ptr<SyncUser> user,
+                                 Permission permission,
+                                 PermissionChangeCallback callback,
+                                 const ConfigMaker& make_config)
+{
+    auto props = AnyDict{
+        {"userId", permission.condition.user_id},
+        {"realmUrl", user->server_url() + permission.path},
+        {"mayRead", permission.access != AccessLevel::None},
+        {"mayWrite", permission.access == AccessLevel::Write || permission.access == AccessLevel::Admin},
+        {"mayManage", permission.access == AccessLevel::Admin},
+    };
+    if (permission.condition.type == Permission::Condition::Type::KeyValue) {
+        props.insert({"metadataKey", permission.condition.key_value.first});
+        props.insert({"metadataValue", permission.condition.key_value.second});
+    }
+    auto cb = [callback=std::move(callback)](Object*, std::exception_ptr exception) {
+        callback(exception);
+    };
+    perform_async_operation("PermissionChange", std::move(user), std::move(cb), std::move(props), make_config);
+}
+
+void Permissions::delete_permission(std::shared_ptr<SyncUser> user,
+                                    Permission permission,
+                                    PermissionChangeCallback callback,
+                                    const ConfigMaker& make_config)
+{
+    permission.access = AccessLevel::None;
+    set_permission(std::move(user), std::move(permission), std::move(callback), make_config);
+}
+
+void Permissions::make_offer(std::shared_ptr<SyncUser> user,
+                             PermissionOffer offer,
+                             PermissionOfferCallback callback,
+                             const ConfigMaker& make_config)
+{
+    auto props = AnyDict{
+        {"expiresAt", std::move(offer.expiration)},
+        {"userId", user->identity()},
+        {"realmUrl", user->server_url() + offer.path},
+        {"mayRead", offer.access != AccessLevel::None},
+        {"mayWrite", offer.access == AccessLevel::Write || offer.access == AccessLevel::Admin},
+        {"mayManage", offer.access == AccessLevel::Admin},
+    };
+    perform_async_operation("PermissionOffer",
+                            std::move(user),
+                            make_handler_extracting_property("token", std::move(callback)),
+                            std::move(props),
+                            make_config);
+}
+
+void Permissions::accept_offer(std::shared_ptr<SyncUser> user,
+                               const std::string& token,
+                               PermissionOfferCallback callback,
+                               const ConfigMaker& make_config)
+{
+    perform_async_operation("PermissionOfferResponse",
+                            std::move(user),
+                            make_handler_extracting_property("realmUrl", std::move(callback)),
+                            AnyDict{ {"token", token} },
+                            make_config);
+}
+
+void Permissions::perform_async_operation(const std::string& object_type,
+                                          std::shared_ptr<SyncUser> user,
+                                          AsyncOperationHandler handler,
+                                          AnyDict additional_props,
+                                          const ConfigMaker& make_config)
+{;
+    auto realm = Permissions::management_realm(std::move(user), make_config);
+    CppContext context;
+
+    // Get the current time.
+    int64_t ns_since_epoch = ns_since_unix_epoch(system_clock::now());
+    int64_t s_arg = ns_since_epoch / (int64_t)Timestamp::nanoseconds_per_second;
+    int32_t ns_arg = ns_since_epoch % Timestamp::nanoseconds_per_second;
+
+    auto props = AnyDict{
+        {"id", util::uuid_string()},
+        {"createdAt", Timestamp(s_arg, ns_arg)},
+        {"updatedAt", Timestamp(s_arg, ns_arg)},
+    };
+    props.insert(additional_props.begin(), additional_props.end());
+
+    // Write the permission object.
+    realm->begin_transaction();
+    auto raw = Object::create<util::Any>(context, realm, *realm->schema().find(object_type), std::move(props), false);
+    auto object = std::make_shared<_impl::NotificationWrapper<Object>>(std::move(raw));
+    realm->commit_transaction();
+
+    // Observe the permission object until the permission change has been processed or failed.
+    // The notifier is automatically unregistered upon the completion of the permission
+    // change, one way or another.
+    auto block = [object, handler=std::move(handler)](CollectionChangeSet, std::exception_ptr ex) mutable {
+        if (ex) {
+            handler(nullptr, ex);
+            object.reset();
+            return;
+        }
+
+        CppContext context;
+        auto status_code = object->get_property_value<util::Any>(context, "statusCode");
+        if (!status_code.has_value()) {
+            // Continue waiting for the sync server to complete the operation.
+            return;
+        }
+
+        // Determine whether an error happened or not.
+        if (auto code = any_cast<long long>(status_code)) {
+            // The permission change failed because an error was returned from the server.
+            auto status = object->get_property_value<util::Any>(context, "statusMessage");
+            std::string error_str = (status.has_value()
+                                     ? any_cast<std::string>(status)
+                                     : util::format("Error code: %1", code));
+            handler(nullptr, std::make_exception_ptr(PermissionActionException(error_str, code)));
+        }
+        else {
+            handler(object.get(), nullptr);
+        }
+        object.reset();
+    };
+    object->add_notification_callback(std::move(block));
+}
+
+SharedRealm Permissions::management_realm(std::shared_ptr<SyncUser> user, const ConfigMaker& make_config)
+{
+    // FIXME: maybe we should cache the management Realm on the user, so we don't need to open it every time.
+    const auto realm_url = util::format("realm%1/~/__management", user->server_url().substr(4));
+    Realm::Config config = make_config(user, std::move(realm_url));
+    config.sync_config->stop_policy = SyncSessionStopPolicy::Immediately;
+    config.schema = Schema{
+        {"PermissionChange", {
+            Property{"id",                PropertyType::String, Property::IsPrimary{true}},
+            Property{"createdAt",         PropertyType::Date},
+            Property{"updatedAt",         PropertyType::Date},
+            Property{"statusCode",        PropertyType::Int|PropertyType::Nullable},
+            Property{"statusMessage",     PropertyType::String|PropertyType::Nullable},
+            Property{"userId",            PropertyType::String},
+            Property{"metadataKey",       PropertyType::String|PropertyType::Nullable},
+            Property{"metadataValue",     PropertyType::String|PropertyType::Nullable},
+            Property{"metadataNameSpace", PropertyType::String|PropertyType::Nullable},
+            Property{"realmUrl",          PropertyType::String},
+            Property{"mayRead",           PropertyType::Bool|PropertyType::Nullable},
+            Property{"mayWrite",          PropertyType::Bool|PropertyType::Nullable},
+            Property{"mayManage",         PropertyType::Bool|PropertyType::Nullable},
+        }},
+        {"PermissionOffer", {
+            Property{"id",                PropertyType::String, Property::IsPrimary{true}},
+            Property{"createdAt",         PropertyType::Date},
+            Property{"updatedAt",         PropertyType::Date},
+            Property{"expiresAt",         PropertyType::Date|PropertyType::Nullable},
+            Property{"statusCode",        PropertyType::Int|PropertyType::Nullable},
+            Property{"statusMessage",     PropertyType::String|PropertyType::Nullable},
+            Property{"token",             PropertyType::String|PropertyType::Nullable},
+            Property{"realmUrl",          PropertyType::String},
+            Property{"mayRead",           PropertyType::Bool},
+            Property{"mayWrite",          PropertyType::Bool},
+            Property{"mayManage",         PropertyType::Bool},
+        }},
+        {"PermissionOfferResponse", {
+            Property{"id",                PropertyType::String, Property::IsPrimary{true}},
+            Property{"createdAt",         PropertyType::Date},
+            Property{"updatedAt",         PropertyType::Date},
+            Property{"statusCode",        PropertyType::Int|PropertyType::Nullable},
+            Property{"statusMessage",     PropertyType::String|PropertyType::Nullable},
+            Property{"token",             PropertyType::String},
+            Property{"realmUrl",          PropertyType::String|PropertyType::Nullable},
+        }},
+    };
+    config.schema_version = 0;
+    auto shared_realm = Realm::get_shared_realm(std::move(config));
+    user->register_management_session(shared_realm->config().path);
+    return shared_realm;
+}
+
+SharedRealm Permissions::permission_realm(std::shared_ptr<SyncUser> user, const ConfigMaker& make_config)
+{
+    // FIXME: maybe we should cache the permission Realm on the user, so we don't need to open it every time.
+    const auto realm_url = util::format("realm%1/~/__permission", user->server_url().substr(4));
+    Realm::Config config = make_config(user, std::move(realm_url));
+    config.sync_config->stop_policy = SyncSessionStopPolicy::Immediately;
+    config.schema = Schema{
+        {"Permission", {
+            {"updatedAt", PropertyType::Date},
+            {"userId", PropertyType::String},
+            {"path", PropertyType::String},
+            {"mayRead", PropertyType::Bool},
+            {"mayWrite", PropertyType::Bool},
+            {"mayManage", PropertyType::Bool},
+        }}
+    };
+    config.schema_version = 0;
+    auto shared_realm = Realm::get_shared_realm(std::move(config));
+    user->register_permission_session(shared_realm->config().path);
+    return shared_realm;
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_session.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_session.cpp
new file mode 100644 (file)
index 0000000..dea0b41
--- /dev/null
@@ -0,0 +1,901 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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/sync_session.hpp"
+
+#include "sync/impl/sync_client.hpp"
+#include "sync/impl/sync_file.hpp"
+#include "sync/impl/sync_metadata.hpp"
+#include "sync/sync_manager.hpp"
+#include "sync/sync_user.hpp"
+
+#include <realm/sync/client.hpp>
+#include <realm/sync/protocol.hpp>
+
+
+using namespace realm;
+using namespace realm::_impl;
+using namespace realm::_impl::sync_session_states;
+
+using SessionWaiterPointer = void(sync::Session::*)(std::function<void(std::error_code)>);
+
+constexpr const char SyncError::c_original_file_path_key[];
+constexpr const char SyncError::c_recovery_file_path_key[];
+
+/// 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: 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.
+///
+/// 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
+///
+/// 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
+///
+/// INACTIVE: the user owning this session has logged out, the `sync::Session`
+/// owned by this session is destroyed, and the session is quiescent.
+/// Note that a session briefly enters this state before being destroyed, but
+/// it can also enter this state and stay there if the user has been logged out.
+/// From: initial, WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING
+/// To:
+///    * WAITING_FOR_ACCESS_TOKEN: if the session is revived
+///
+struct SyncSession::State {
+    virtual ~State() { }
+
+    // Move the given session into this state. All state transitions MUST be carried out through this method.
+    virtual void enter_state(std::unique_lock<std::mutex>&, SyncSession&) const { }
+
+    virtual void refresh_access_token(std::unique_lock<std::mutex>&,
+                                      SyncSession&, std::string,
+                                      const util::Optional<std::string>&) const { }
+
+    // Returns true iff the lock is still locked when the method returns.
+    virtual bool access_token_expired(std::unique_lock<std::mutex>&, SyncSession&) const { return true; }
+
+    virtual void nonsync_transact_notify(std::unique_lock<std::mutex>&, SyncSession&, sync::Session::version_type) const { }
+
+    // Perform any work needed to reactivate a session that is not already active.
+    // Returns true iff the session should ask the binding to get a token for `bind()`.
+    virtual bool revive_if_needed(std::unique_lock<std::mutex>&, SyncSession&) const { return false; }
+
+    // Perform any work needed to respond to the application regaining network connectivity.
+    virtual void handle_reconnect(std::unique_lock<std::mutex>&, SyncSession&) const { };
+
+    // The user that owns this session has been logged out, and the session should take appropriate action.
+    virtual void log_out(std::unique_lock<std::mutex>&, SyncSession&) const { }
+
+    // The session should be closed and moved to `inactive`, in accordance with its stop policy and other state.
+    virtual void close(std::unique_lock<std::mutex>&, SyncSession&) const { }
+
+    // Returns true iff the error has been fully handled and the error handler should immediately return.
+    virtual bool handle_error(std::unique_lock<std::mutex>&, SyncSession&, const SyncError&) const { return false; }
+
+    // Register a handler to wait for sync session uploads, downloads, or synchronization.
+    // PRECONDITION: the session state lock must be held at the time this method is called, until after it returns.
+    // Returns true iff the handler was registered, either immediately or placed in a queue for later registration.
+    virtual bool wait_for_completion(SyncSession&,
+                                     std::function<void(std::error_code)>,
+                                     SessionWaiterPointer) const {
+        return false;
+    }
+
+    virtual void override_server(std::unique_lock<std::mutex>&, SyncSession&, std::string, int) const { }
+
+    static const State& waiting_for_access_token;
+    static const State& active;
+    static const State& dying;
+    static const State& inactive;
+};
+
+struct sync_session_states::WaitingForAccessToken : public SyncSession::State {
+    void enter_state(std::unique_lock<std::mutex>&, SyncSession& session) const override
+    {
+        session.m_deferred_close = false;
+    }
+
+    void refresh_access_token(std::unique_lock<std::mutex>& lock, SyncSession& session,
+                              std::string access_token,
+                              const util::Optional<std::string>& server_url) const override
+    {
+        session.create_sync_session();
+
+        // 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 = server_url;
+        }
+        if (session.m_session_has_been_bound) {
+            session.m_session->refresh(std::move(access_token));
+            session.m_session->cancel_reconnect_delay();
+        } else {
+            session.m_session->bind(*session.m_server_url, std::move(access_token));
+            session.m_session_has_been_bound = true;
+        }
+
+        if (session.m_server_override)
+            session.m_session->override_server(session.m_server_override->address, session.m_server_override->port);
+
+        // Register all the pending wait-for-completion blocks.
+        for (auto& package : session.m_completion_wait_packages) {
+            (*session.m_session.*package.waiter)(std::move(package.callback));
+        }
+        session.m_completion_wait_packages.clear();
+
+        // Handle any deferred commit notification.
+        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_state->close(lock, session);
+        }
+    }
+
+    void log_out(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
+    {
+        session.advance_state(lock, inactive);
+    }
+
+    bool revive_if_needed(std::unique_lock<std::mutex>&, SyncSession& session) const override
+    {
+        session.m_deferred_close = false;
+        return false;
+    }
+
+    void handle_reconnect(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
+    {
+        // Ask the binding to retry getting the token for this session.
+        std::shared_ptr<SyncSession> session_ptr = session.shared_from_this();
+        lock.unlock();
+        session.m_config.bind_session_handler(session_ptr->m_realm_path, session_ptr->m_config, session_ptr);
+    }
+
+    void nonsync_transact_notify(std::unique_lock<std::mutex>&,
+                                 SyncSession& session,
+                                 sync::Session::version_type version) const override
+    {
+        // Notify at first available opportunity.
+        session.m_deferred_commit_notification = version;
+    }
+
+    void close(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
+    {
+        switch (session.m_config.stop_policy) {
+            case SyncSessionStopPolicy::Immediately:
+                // Immediately kill the session.
+                session.advance_state(lock, inactive);
+                break;
+            case SyncSessionStopPolicy::LiveIndefinitely:
+            case SyncSessionStopPolicy::AfterChangesUploaded:
+                // Defer handling closing the session until after the login response succeeds.
+                session.m_deferred_close = true;
+                break;
+        }
+    }
+
+    bool wait_for_completion(SyncSession& session,
+                             std::function<void(std::error_code)> callback,
+                             SessionWaiterPointer waiter) const override
+    {
+        session.m_completion_wait_packages.push_back({ waiter, std::move(callback) });
+        return true;
+    }
+
+    void override_server(std::unique_lock<std::mutex>&, SyncSession& session,
+                         std::string address, int port) const override
+    {
+        session.m_server_override = SyncSession::ServerOverride{address, port};
+    }
+};
+
+struct sync_session_states::Active : public SyncSession::State {
+    void refresh_access_token(std::unique_lock<std::mutex>&, SyncSession& session,
+                              std::string access_token,
+                              const util::Optional<std::string>&) const override
+    {
+        session.m_session->refresh(std::move(access_token));
+        // Cancel the session's reconnection delay. This is important if the
+        // token is being refreshed as a response to a 202 (token expired)
+        // error, or similar non-fatal sync errors.
+        session.m_session->cancel_reconnect_delay();
+    }
+
+    bool access_token_expired(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
+    {
+        session.advance_state(lock, waiting_for_access_token);
+        std::shared_ptr<SyncSession> session_ptr = session.shared_from_this();
+        lock.unlock();
+        session.m_config.bind_session_handler(session_ptr->m_realm_path, session_ptr->m_config, session_ptr);
+        return false;
+    }
+
+    void log_out(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
+    {
+        session.advance_state(lock, inactive);
+    }
+
+    void nonsync_transact_notify(std::unique_lock<std::mutex>&, 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<std::mutex>& 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;
+        }
+    }
+
+    bool wait_for_completion(SyncSession& session,
+                             std::function<void(std::error_code)> callback,
+                             SessionWaiterPointer waiter) const override
+    {
+        REALM_ASSERT(session.m_session);
+        (*session.m_session.*waiter)(std::move(callback));
+        return true;
+    }
+
+    void handle_reconnect(std::unique_lock<std::mutex>&, SyncSession& session) const override
+    {
+        session.m_session->cancel_reconnect_delay();
+    }
+
+    void override_server(std::unique_lock<std::mutex>&, SyncSession& session,
+                         std::string address, int port) const override
+    {
+        session.m_server_override = SyncSession::ServerOverride{address, port};
+        session.m_session->override_server(address, port);
+    }
+};
+
+struct sync_session_states::Dying : public SyncSession::State {
+    void enter_state(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
+    {
+        // If we have no session, we cannot possibly upload anything.
+        if (!session.m_session) {
+            session.advance_state(lock, inactive);
+            return;
+        }
+
+        size_t current_death_count = ++session.m_death_count;
+        std::weak_ptr<SyncSession> weak_session = session.shared_from_this();
+        session.m_session->async_wait_for_upload_completion([weak_session, current_death_count](std::error_code) {
+            if (auto session = weak_session.lock()) {
+                std::unique_lock<std::mutex> lock(session->m_state_mutex);
+                if (session->m_state == &State::dying && session->m_death_count == current_death_count) {
+                    session->advance_state(lock, inactive);
+                }
+            }
+        });
+    }
+
+    bool handle_error(std::unique_lock<std::mutex>& lock, SyncSession& session, const SyncError& error) const override
+    {
+        if (error.is_fatal) {
+            session.advance_state(lock, inactive);
+        }
+        // If the error isn't fatal, don't change state, but don't
+        // allow it to be reported either.
+        // FIXME: What if the token expires while a session is dying?
+        // Should we allow the token to be refreshed so that changes
+        // can finish being uploaded?
+        return true;
+    }
+
+    bool revive_if_needed(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
+    {
+        // Revive.
+        session.advance_state(lock, active);
+        return false;
+    }
+
+    void log_out(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
+    {
+        session.advance_state(lock, inactive);
+    }
+
+    bool wait_for_completion(SyncSession& session,
+                             std::function<void(std::error_code)> callback,
+                             SessionWaiterPointer waiter) const override
+    {
+        REALM_ASSERT(session.m_session);
+        (*session.m_session.*waiter)(std::move(callback));
+        return true;
+    }
+
+    void override_server(std::unique_lock<std::mutex>&, SyncSession& session,
+                         std::string address, int port) const override
+    {
+        session.m_server_override = SyncSession::ServerOverride{address, port};
+        session.m_session->override_server(address, port);
+    }
+};
+
+struct sync_session_states::Inactive : public SyncSession::State {
+    void enter_state(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
+    {
+        // Inform any queued-up completion handlers that they were cancelled.
+        for (auto& package : session.m_completion_wait_packages) {
+            package.callback(util::error::operation_aborted);
+        }
+        session.m_completion_wait_packages.clear();
+        session.m_session = nullptr;
+        session.unregister(lock);
+    }
+
+    bool revive_if_needed(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
+    {
+        session.advance_state(lock, waiting_for_access_token);
+        return true;
+    }
+
+    bool wait_for_completion(SyncSession& session,
+                             std::function<void(std::error_code)> callback,
+                             SessionWaiterPointer waiter) const override
+    {
+        session.m_completion_wait_packages.push_back({ waiter, std::move(callback) });
+        return true;
+    }
+
+    void override_server(std::unique_lock<std::mutex>&, SyncSession& session,
+                         std::string address, int port) const override
+    {
+        session.m_server_override = SyncSession::ServerOverride{address, port};
+    }
+};
+
+
+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();
+
+SyncSession::SyncSession(SyncClient& 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(client)
+{
+    // Sync history validation ensures that the history within the Realm file is in a format that can be used
+    // by the version of realm-sync that we're using. Validation is enabled by default when the binding manually
+    // opens a sync session (via `SyncManager::get_session`), but is disabled when the sync session is opened
+    // as a side effect of opening a `Realm`. In that case, the sync history has already been validated by the
+    // act of opening the `Realm` so it's not necessary to repeat it here.
+    if (m_config.validate_sync_history) {
+        Realm::Config realm_config;
+        realm_config.path = m_realm_path;
+        realm_config.schema_mode = SchemaMode::Additive;
+        realm_config.force_sync_history = true;
+        realm_config.cache = false;
+
+        if (m_config.realm_encryption_key) {
+            realm_config.encryption_key.resize(64);
+            std::copy(m_config.realm_encryption_key->begin(), m_config.realm_encryption_key->end(),
+                      realm_config.encryption_key.begin());
+        }
+
+        // FIXME: Opening a Realm only to discard it is relatively expensive. It may be preferable to have
+        // realm-sync open the Realm when the `sync::Session` is created since it can continue to use it.
+        Realm::get_shared_realm(realm_config); // Throws
+   }
+}
+
+std::string SyncSession::get_recovery_file_path()
+{
+    return util::reserve_unique_file_name(SyncManager::shared().recovery_directory_path(),
+                                          util::create_timestamped_template("recovered_realm"));
+}
+
+void SyncSession::update_error_and_mark_file_for_deletion(SyncError& error, ShouldBackup should_backup)
+{
+    // Add a SyncFileActionMetadata marking the Realm as needing to be deleted.
+    std::string recovery_path;
+    auto original_path = path();
+    error.user_info[SyncError::c_original_file_path_key] = original_path;
+    if (should_backup == ShouldBackup::yes) {
+        recovery_path = get_recovery_file_path();
+        error.user_info[SyncError::c_recovery_file_path_key] = recovery_path;
+    }
+    using Action = SyncFileActionMetadata::Action;
+    auto action = should_backup == ShouldBackup::yes ? Action::BackUpThenDeleteRealm : Action::DeleteRealm;
+    SyncManager::shared().perform_metadata_update([this,
+                                                   action,
+                                                   original_path=std::move(original_path),
+                                                   recovery_path=std::move(recovery_path)](const auto& manager) {
+        manager.make_file_action_metadata(original_path, m_config.realm_url(), m_config.user->identity(),
+                                          action, std::move(recovery_path));
+    });
+}
+
+// This method should only be called from within the error handler callback registered upon the underlying `m_session`.
+void SyncSession::handle_error(SyncError error)
+{
+    enum class NextStateAfterError { none, inactive, error };
+    auto next_state = error.is_fatal ? NextStateAfterError::error : NextStateAfterError::none;
+    auto error_code = error.error_code;
+
+    {
+        // See if the current state wishes to take responsibility for handling the error.
+        std::unique_lock<std::mutex> lock(m_state_mutex);
+        if (m_state->handle_error(lock, *this, error)) {
+            return;
+        }
+    }
+
+    if (error_code.category() == realm::sync::protocol_error_category()) {
+        using ProtocolError = realm::sync::ProtocolError;
+        switch (static_cast<ProtocolError>(error_code.value())) {
+            // Connection level errors
+            case ProtocolError::connection_closed:
+            case ProtocolError::other_error:
+                // Not real errors, don't need to be reported to the binding.
+                return;
+            case ProtocolError::unknown_message:
+            case ProtocolError::bad_syntax:
+            case ProtocolError::limits_exceeded:
+            case ProtocolError::wrong_protocol_version:
+            case ProtocolError::bad_session_ident:
+            case ProtocolError::reuse_of_session_ident:
+            case ProtocolError::bound_in_other_session:
+            case ProtocolError::bad_message_order:
+            case ProtocolError::bad_client_version:
+            case ProtocolError::illegal_realm_path:
+            case ProtocolError::no_such_realm:
+            case ProtocolError::bad_changeset:
+            case ProtocolError::bad_changeset_header_syntax:
+            case ProtocolError::bad_changeset_size:
+            case ProtocolError::bad_changesets:
+            case ProtocolError::bad_decompression:
+            case ProtocolError::partial_sync_disabled:
+                break;
+            // Session errors
+            case ProtocolError::session_closed:
+            case ProtocolError::other_session_error:
+            case ProtocolError::disabled_session:
+                // The binding doesn't need to be aware of these because they are strictly informational, and do not
+                // represent actual errors.
+                return;
+            case ProtocolError::token_expired: {
+                std::unique_lock<std::mutex> 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 ProtocolError::bad_authentication: {
+                std::shared_ptr<SyncUser> user_to_invalidate;
+                next_state = NextStateAfterError::none;
+                {
+                    std::unique_lock<std::mutex> lock(m_state_mutex);
+                    user_to_invalidate = user();
+                    cancel_pending_waits();
+                }
+                if (user_to_invalidate)
+                    user_to_invalidate->invalidate();
+                break;
+            }
+            case ProtocolError::permission_denied: {
+                next_state = NextStateAfterError::inactive;
+                update_error_and_mark_file_for_deletion(error, ShouldBackup::no);
+                break;
+            }
+            case ProtocolError::bad_server_file_ident:
+            case ProtocolError::bad_client_file_ident:
+            case ProtocolError::bad_server_version:
+            case ProtocolError::diverging_histories:
+                next_state = NextStateAfterError::inactive;
+                update_error_and_mark_file_for_deletion(error, ShouldBackup::yes);
+                break;
+        }
+    } else if (error_code.category() == realm::sync::client_error_category()) {
+        using ClientError = realm::sync::Client::Error;
+        switch (static_cast<ClientError>(error_code.value())) {
+            case ClientError::connection_closed:
+            case ClientError::pong_timeout:
+                // Not real errors, don't need to be reported to the binding.
+                return;
+            case ClientError::unknown_message:
+            case ClientError::bad_syntax:
+            case ClientError::limits_exceeded:
+            case ClientError::bad_session_ident:
+            case ClientError::bad_message_order:
+            case ClientError::bad_file_ident_pair:
+            case ClientError::bad_progress:
+            case ClientError::bad_changeset_header_syntax:
+            case ClientError::bad_changeset_size:
+            case ClientError::bad_origin_file_ident:
+            case ClientError::bad_server_version:
+            case ClientError::bad_changeset:
+            case ClientError::bad_request_ident:
+            case ClientError::bad_error_code:
+            case ClientError::bad_compression:
+            case ClientError::bad_client_version:
+            case ClientError::ssl_server_cert_rejected:
+                // Don't do anything special for these errors.
+                // Future functionality may require special-case handling for existing
+                // errors, or newly introduced error codes.
+                break;
+        }
+    } else {
+        // Unrecognized error code.
+        error.is_unrecognized_by_client = true;
+    }
+    switch (next_state) {
+        case NextStateAfterError::none:
+            break;
+        case NextStateAfterError::inactive: {
+            std::unique_lock<std::mutex> lock(m_state_mutex);
+            advance_state(lock, State::inactive);
+            break;
+        }
+        case NextStateAfterError::error: {
+            std::unique_lock<std::mutex> lock(m_state_mutex);
+            cancel_pending_waits();
+            break;
+        }
+    }
+    if (m_config.error_handler) {
+        m_config.error_handler(shared_from_this(), std::move(error));
+    }
+}
+
+void SyncSession::cancel_pending_waits()
+{
+    // Inform any queued-up completion handlers that they were cancelled.
+    for (auto& package : m_completion_wait_packages) {
+        package.callback(util::error::operation_aborted);
+    }
+    m_completion_wait_packages.clear();
+}
+
+void SyncSession::handle_progress_update(uint64_t downloaded, uint64_t downloadable,
+                                         uint64_t uploaded, uint64_t uploadable, bool is_fresh)
+{
+    std::vector<std::function<void()>> invocations;
+    {
+        std::lock_guard<std::mutex> lock(m_progress_notifier_mutex);
+        m_current_progress = Progress{uploadable, downloadable, uploaded, downloaded};
+        m_latest_progress_data_is_fresh = is_fresh;
+
+        for (auto it = m_notifiers.begin(); it != m_notifiers.end();) {
+            auto& package = it->second;
+            package.update(*m_current_progress, is_fresh);
+
+            bool should_delete = false;
+            invocations.emplace_back(package.create_invocation(*m_current_progress, should_delete));
+
+            it = (should_delete ? m_notifiers.erase(it) : std::next(it));
+        }
+    }
+    // Run the notifiers only after we've released the lock.
+    for (auto& invocation : invocations) {
+        invocation();
+    }
+}
+
+void SyncSession::NotifierPackage::update(const Progress& current_progress, bool data_is_fresh)
+{
+    if (is_streaming || captured_transferrable || !data_is_fresh)
+        return;
+
+    captured_transferrable = direction == NotifierType::download ? current_progress.downloadable
+                                                                 : current_progress.uploadable;
+}
+
+// PRECONDITION: `update()` must first be called on the same package.
+std::function<void()> SyncSession::NotifierPackage::create_invocation(const Progress& current_progress,
+                                                                      bool& is_expired) const
+{
+    // It's possible for a non-streaming notifier to not yet have fresh transferrable bytes data.
+    // In that case, we don't call it at all.
+    // NOTE: `update()` is always called before `create_invocation()`, and will
+    // set `captured_transferrable` on the notifier package if fresh data has
+    // been received and the package is for a non-streaming notifier.
+    if (!is_streaming && !captured_transferrable)
+        return [](){ };
+
+    bool is_download = direction == NotifierType::download;
+    uint64_t transferred = is_download ? current_progress.downloaded : current_progress.uploaded;
+    uint64_t transferrable;
+    if (is_streaming) {
+        transferrable = is_download ? current_progress.downloadable : current_progress.uploadable;
+    } else {
+        transferrable = *captured_transferrable;
+    }
+    // A notifier is expired if at least as many bytes have been transferred
+    // as were originally considered transferrable.
+    is_expired = !is_streaming && transferred >= *captured_transferrable;
+    return [=, package=*this](){
+        package.notifier(transferred, transferrable);
+    };
+}
+
+void SyncSession::create_sync_session()
+{
+    if (m_session)
+        return;
+
+    sync::Session::Config session_config;
+    session_config.changeset_cooker = m_config.transformer;
+    session_config.encryption_key = m_config.realm_encryption_key;
+    session_config.verify_servers_ssl_certificate = m_config.client_validate_ssl;
+    session_config.ssl_trust_certificate_path = m_config.ssl_trust_certificate_path;
+    session_config.ssl_verify_callback = m_config.ssl_verify_callback;
+    session_config.multiplex_ident = m_multiplex_identity;
+    m_session = m_client.make_session(m_realm_path, std::move(session_config));
+
+    // The next time we get a token, call `bind()` instead of `refresh()`.
+    m_session_has_been_bound = false;
+
+    // Configure the error handler.
+    std::weak_ptr<SyncSession> weak_self = shared_from_this();
+    auto wrapped_handler = [this, weak_self](std::error_code error_code, bool is_fatal, std::string message) {
+        auto self = weak_self.lock();
+        if (!self) {
+            // An error was delivered after the session it relates to was destroyed. There's nothing useful
+            // we can do with it.
+            return;
+        }
+        handle_error(SyncError{error_code, std::move(message), is_fatal});
+    };
+    m_session->set_error_handler(std::move(wrapped_handler));
+
+    // Configure the sync transaction callback.
+    auto wrapped_callback = [this, weak_self](VersionID old_version, VersionID new_version) {
+        if (auto self = weak_self.lock()) {
+            if (m_sync_transact_callback) {
+                m_sync_transact_callback(old_version, new_version);
+            }
+        }
+    };
+    m_session->set_sync_transact_callback(std::move(wrapped_callback));
+
+    // Set up the wrapped progress handler callback
+    auto wrapped_progress_handler = [this, weak_self](uint_fast64_t downloaded, uint_fast64_t downloadable,
+                                                      uint_fast64_t uploaded, uint_fast64_t uploadable,
+                                                      bool is_fresh, uint_fast64_t /*snapshot_version*/) {
+        if (auto self = weak_self.lock()) {
+            handle_progress_update(downloaded, downloadable, uploaded, uploadable, is_fresh);
+        }
+    };
+    m_session->set_progress_handler(std::move(wrapped_progress_handler));
+}
+
+void SyncSession::set_sync_transact_callback(std::function<sync::Session::SyncTransactCallback> callback)
+{
+    m_sync_transact_callback = std::move(callback);
+}
+
+void SyncSession::advance_state(std::unique_lock<std::mutex>& 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<std::mutex> lock(m_state_mutex);
+    m_state->nonsync_transact_notify(lock, *this, version);
+}
+
+void SyncSession::revive_if_needed()
+{
+    util::Optional<std::function<SyncBindSessionHandler>&> handler;
+    {
+        std::unique_lock<std::mutex> lock(m_state_mutex);
+        if (m_state->revive_if_needed(lock, *this))
+            handler = m_config.bind_session_handler;
+    }
+    if (handler)
+        handler.value()(m_realm_path, m_config, shared_from_this());
+}
+
+void SyncSession::handle_reconnect()
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+    m_state->handle_reconnect(lock, *this);
+}
+
+void SyncSession::log_out()
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+    m_state->log_out(lock, *this);
+}
+
+void SyncSession::close()
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+    m_state->close(lock, *this);
+}
+
+void SyncSession::unregister(std::unique_lock<std::mutex>& 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);
+}
+
+bool SyncSession::wait_for_upload_completion(std::function<void(std::error_code)> callback)
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+    return m_state->wait_for_completion(*this, std::move(callback), &sync::Session::async_wait_for_upload_completion);
+}
+
+bool SyncSession::wait_for_download_completion(std::function<void(std::error_code)> callback)
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+    return m_state->wait_for_completion(*this, std::move(callback), &sync::Session::async_wait_for_download_completion);
+}
+
+uint64_t SyncSession::register_progress_notifier(std::function<SyncProgressNotifierCallback> notifier,
+                                                 NotifierType direction, bool is_streaming)
+{
+    std::function<void()> invocation;
+    uint64_t token_value = 0;
+    {
+        std::lock_guard<std::mutex> lock(m_progress_notifier_mutex);
+        token_value = m_progress_notifier_token++;
+        NotifierPackage package{std::move(notifier), is_streaming, direction};
+        if (!m_current_progress) {
+            // Simply register the package, since we have no data yet.
+            m_notifiers.emplace(token_value, std::move(package));
+            return token_value;
+        }
+        package.update(*m_current_progress, m_latest_progress_data_is_fresh);
+        bool skip_registration = false;
+        invocation = package.create_invocation(*m_current_progress, skip_registration);
+        if (skip_registration) {
+            token_value = 0;
+        } else {
+            m_notifiers.emplace(token_value, std::move(package));
+        }
+    }
+    invocation();
+    return token_value;
+}
+
+void SyncSession::unregister_progress_notifier(uint64_t token)
+{
+    std::lock_guard<std::mutex> lock(m_progress_notifier_mutex);
+    m_notifiers.erase(token);
+}
+
+void SyncSession::refresh_access_token(std::string access_token, util::Optional<std::string> server_url)
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+    if (!m_server_url && !server_url) {
+        // The first time this method is called, the server URL must be provided.
+        return;
+    }
+    m_state->refresh_access_token(lock, *this, std::move(access_token), server_url);
+}
+
+void SyncSession::override_server(std::string address, int port)
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+    m_state->override_server(lock, *this, std::move(address), port);
+}
+
+void SyncSession::set_multiplex_identifier(std::string multiplex_identity)
+{
+    m_multiplex_identity = std::move(multiplex_identity);
+}
+
+SyncSession::PublicState SyncSession::state() const
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+    if (m_state == &State::waiting_for_access_token) {
+        return PublicState::WaitingForAccessToken;
+    } else if (m_state == &State::active) {
+        return PublicState::Active;
+    } else if (m_state == &State::dying) {
+        return PublicState::Dying;
+    } else if (m_state == &State::inactive) {
+        return PublicState::Inactive;
+    }
+    REALM_UNREACHABLE();
+}
+
+// Represents a reference to the SyncSession from outside of the sync subsystem.
+// We attempt to keep the SyncSession in an active state as long as it has an external reference.
+class SyncSession::ExternalReference {
+public:
+    ExternalReference(std::shared_ptr<SyncSession> session) : m_session(std::move(session))
+    {}
+
+    ~ExternalReference()
+    {
+        m_session->did_drop_external_reference();
+    }
+
+private:
+    std::shared_ptr<SyncSession> m_session;
+};
+
+std::shared_ptr<SyncSession> SyncSession::external_reference()
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+
+    if (auto external_reference = m_external_reference.lock())
+        return std::shared_ptr<SyncSession>(external_reference, this);
+
+    auto external_reference = std::make_shared<ExternalReference>(shared_from_this());
+    m_external_reference = external_reference;
+    return std::shared_ptr<SyncSession>(external_reference, this);
+}
+
+std::shared_ptr<SyncSession> SyncSession::existing_external_reference()
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+
+    if (auto external_reference = m_external_reference.lock())
+        return std::shared_ptr<SyncSession>(external_reference, this);
+
+    return nullptr;
+}
+
+void SyncSession::did_drop_external_reference()
+{
+    std::unique_lock<std::mutex> lock(m_state_mutex);
+
+    // If the session is being resurrected we should not close the session.
+    if (!m_external_reference.expired())
+        return;
+
+    m_state->close(lock, *this);
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_user.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/sync_user.cpp
new file mode 100644 (file)
index 0000000..1adf8b1
--- /dev/null
@@ -0,0 +1,265 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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/sync_user.hpp"
+
+#include "sync/impl/sync_metadata.hpp"
+#include "sync/sync_manager.hpp"
+#include "sync/sync_session.hpp"
+
+namespace realm {
+
+SyncUserContextFactory SyncUser::s_binding_context_factory;
+std::mutex SyncUser::s_binding_context_factory_mutex;
+
+SyncUser::SyncUser(std::string refresh_token,
+                   std::string identity,
+                   util::Optional<std::string> server_url,
+                   util::Optional<std::string> local_identity,
+                   TokenType token_type)
+: m_state(State::Active)
+, m_server_url(server_url.value_or(""))
+, m_token_type(token_type)
+, m_refresh_token(std::move(refresh_token))
+, m_identity(std::move(identity))
+{
+    {
+        std::lock_guard<std::mutex> lock(s_binding_context_factory_mutex);
+        if (s_binding_context_factory) {
+            m_binding_context = s_binding_context_factory();
+        }
+    }
+    if (token_type == TokenType::Normal) {
+        REALM_ASSERT(m_server_url.length() > 0);
+        bool updated = SyncManager::shared().perform_metadata_update([=](const auto& manager) {
+            auto metadata = manager.get_or_make_user_metadata(m_identity, m_server_url);
+            metadata->set_user_token(m_refresh_token);
+            m_is_admin = metadata->is_admin();
+            m_local_identity = metadata->local_uuid();
+        });
+        if (!updated)
+            m_local_identity = m_identity;
+    } else {
+        // Admin token users. The local identity serves as the directory path.
+        REALM_ASSERT(local_identity);
+        m_local_identity = std::move(*local_identity);
+    }
+}
+
+std::vector<std::shared_ptr<SyncSession>> SyncUser::all_sessions()
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    std::vector<std::shared_ptr<SyncSession>> sessions;
+    if (m_state == State::Error) {
+        return sessions;
+    }
+    for (auto it = m_sessions.begin(); it != m_sessions.end();) {
+        if (auto ptr_to_session = it->second.lock()) {
+            if (!ptr_to_session->is_in_error_state()) {
+                sessions.emplace_back(std::move(ptr_to_session));
+                it++;
+                continue;
+            }
+        }
+        // This session is bad, destroy it.
+        it = m_sessions.erase(it);
+    }
+    return sessions;
+}
+
+std::shared_ptr<SyncSession> SyncUser::session_for_on_disk_path(const std::string& path)
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    if (m_state == State::Error) {
+        return nullptr;
+    }
+    auto it = m_sessions.find(path);
+    if (it == m_sessions.end()) {
+        return nullptr;
+    }
+    auto locked = it->second.lock();
+    if (!locked) {
+        // Remove the session from the map, because it has fatally errored out or the entry is invalid.
+        m_sessions.erase(it);
+    }
+    return locked;
+}
+
+void SyncUser::update_refresh_token(std::string token)
+{
+    std::vector<std::shared_ptr<SyncSession>> sessions_to_revive;
+    {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        if (auto session = m_management_session.lock())
+            sessions_to_revive.emplace_back(std::move(session));
+
+        if (auto session = m_permission_session.lock())
+            sessions_to_revive.emplace_back(std::move(session));
+
+        switch (m_state) {
+            case State::Error:
+                return;
+            case State::Active:
+                m_refresh_token = token;
+                break;
+            case State::LoggedOut: {
+                sessions_to_revive.reserve(m_waiting_sessions.size());
+                m_refresh_token = token;
+                m_state = State::Active;
+                for (auto& pair : m_waiting_sessions) {
+                    if (auto ptr = pair.second.lock()) {
+                        m_sessions[pair.first] = ptr;
+                        sessions_to_revive.emplace_back(std::move(ptr));
+                    }
+                }
+                m_waiting_sessions.clear();
+                break;
+            }
+        }
+        // Update persistent user metadata.
+        if (m_token_type != TokenType::Admin) {
+            SyncManager::shared().perform_metadata_update([=](const auto& manager) {
+                auto metadata = manager.get_or_make_user_metadata(m_identity, m_server_url);
+                metadata->set_user_token(token);
+            });
+        }
+    }
+    // (Re)activate all pending sessions.
+    // Note that we do this after releasing the lock, since the session may
+    // need to access protected User state in the process of binding itself.
+    for (auto& session : sessions_to_revive) {
+        session->revive_if_needed();
+    }
+}
+
+void SyncUser::log_out()
+{
+    if (m_token_type == TokenType::Admin) {
+        // Admin-token users cannot be logged out.
+        return;
+    }
+    std::lock_guard<std::mutex> lock(m_mutex);
+    if (m_state == State::LoggedOut) {
+        return;
+    }
+    m_state = State::LoggedOut;
+    // Move all active sessions into the waiting sessions pool. If the user is
+    // logged back in, they will automatically be reactivated.
+    for (auto& pair : m_sessions) {
+        if (auto ptr = pair.second.lock()) {
+            ptr->log_out();
+            m_waiting_sessions[pair.first] = ptr;
+        }
+    }
+    m_sessions.clear();
+    // Deactivate the sessions for the management and admin Realms.
+    if (auto session = m_management_session.lock())
+        session->log_out();
+
+    if (auto session = m_permission_session.lock())
+        session->log_out();
+
+    // Mark the user as 'dead' in the persisted metadata Realm.
+    SyncManager::shared().perform_metadata_update([=](const auto& manager) {
+        auto metadata = manager.get_or_make_user_metadata(m_identity, m_server_url, false);
+        if (metadata)
+            metadata->mark_for_removal();
+    });
+}
+
+void SyncUser::set_is_admin(bool is_admin)
+{
+    if (m_token_type == TokenType::Admin) {
+        return;
+    }
+    m_is_admin = is_admin;
+    SyncManager::shared().perform_metadata_update([=](const auto& manager) {
+        auto metadata = manager.get_or_make_user_metadata(m_identity, m_server_url);
+        metadata->set_is_admin(is_admin);
+    });
+}
+
+void SyncUser::invalidate()
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    m_state = State::Error;
+}
+
+std::string SyncUser::refresh_token() const
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    return m_refresh_token;
+}
+
+SyncUser::State SyncUser::state() const
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    return m_state;
+}
+
+void SyncUser::register_session(std::shared_ptr<SyncSession> session)
+{
+    const std::string& path = session->path();
+    std::unique_lock<std::mutex> lock(m_mutex);
+    switch (m_state) {
+        case State::Active:
+            // Immediately ask the session to come online.
+            m_sessions[path] = session;
+            lock.unlock();
+            session->revive_if_needed();
+            break;
+        case State::LoggedOut:
+            m_waiting_sessions[path] = session;
+            break;
+        case State::Error:
+            break;
+    }
+}
+
+void SyncUser::set_binding_context_factory(SyncUserContextFactory factory)
+{
+    std::lock_guard<std::mutex> lock(s_binding_context_factory_mutex);
+    s_binding_context_factory = std::move(factory);
+}
+
+void SyncUser::register_management_session(const std::string& path)
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    if (m_management_session.lock() || m_state == State::Error)
+        return;
+
+    m_management_session = SyncManager::shared().get_existing_session(path);
+}
+
+void SyncUser::register_permission_session(const std::string& path)
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    if (m_permission_session.lock() || m_state == State::Error)
+        return;
+
+    m_permission_session = SyncManager::shared().get_existing_session(path);
+}
+
+}
+
+namespace std {
+size_t hash<realm::SyncUserIdentifier>::operator()(const realm::SyncUserIdentifier& k) const
+{
+    return ((hash<string>()(k.user_id) ^ (hash<string>()(k.auth_server_url) << 1)) >> 1);
+}
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/thread_safe_reference.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/thread_safe_reference.cpp
new file mode 100644 (file)
index 0000000..9469d89
--- /dev/null
@@ -0,0 +1,123 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_safe_reference.hpp"
+
+#include "impl/realm_coordinator.hpp"
+#include "list.hpp"
+#include "object.hpp"
+#include "object_schema.hpp"
+#include "results.hpp"
+
+#include <realm/util/scope_exit.hpp>
+
+using namespace realm;
+
+ThreadSafeReferenceBase::ThreadSafeReferenceBase(SharedRealm source_realm) : m_source_realm(std::move(source_realm))
+{
+    m_source_realm->verify_thread();
+    if (m_source_realm->is_in_transaction()) {
+        throw InvalidTransactionException("Cannot obtain thread safe reference during a write transaction.");
+    }
+
+    try {
+        m_version_id = get_source_shared_group().pin_version();
+    } catch (...) {
+        invalidate();
+        throw;
+    }
+}
+
+ThreadSafeReferenceBase::~ThreadSafeReferenceBase()
+{
+    if (!is_invalidated())
+        invalidate();
+}
+
+template <typename V, typename T>
+V ThreadSafeReferenceBase::invalidate_after_import(Realm& destination_realm, T construct_with_shared_group) {
+    destination_realm.verify_thread();
+    REALM_ASSERT_DEBUG(!m_source_realm->is_in_transaction());
+    REALM_ASSERT_DEBUG(!is_invalidated());
+
+    SharedGroup& destination_shared_group = *Realm::Internal::get_shared_group(destination_realm);
+    auto unpin_version = util::make_scope_exit([&]() noexcept { invalidate(); });
+
+    return construct_with_shared_group(destination_shared_group);
+}
+
+SharedGroup& ThreadSafeReferenceBase::get_source_shared_group() const {
+    return *Realm::Internal::get_shared_group(*m_source_realm);
+}
+
+bool ThreadSafeReferenceBase::has_same_config(Realm& realm) const {
+    return &Realm::Internal::get_coordinator(*m_source_realm) == &Realm::Internal::get_coordinator(realm);
+}
+
+void ThreadSafeReferenceBase::invalidate() {
+    REALM_ASSERT_DEBUG(m_source_realm);
+    SharedRealm thread_local_realm = Realm::Internal::get_coordinator(*m_source_realm).get_realm();
+    Realm::Internal::get_shared_group(*thread_local_realm)->unpin_version(m_version_id);
+    m_source_realm = nullptr;
+}
+
+ThreadSafeReference<List>::ThreadSafeReference(List const& list)
+: ThreadSafeReferenceBase(list.get_realm())
+, m_link_view(get_source_shared_group().export_linkview_for_handover(list.m_link_view))
+, m_table(get_source_shared_group().export_table_for_handover(list.m_table))
+{ }
+
+List ThreadSafeReference<List>::import_into_realm(SharedRealm realm) && {
+    return invalidate_after_import<List>(*realm, [&](SharedGroup& shared_group) {
+        if (auto link_view = shared_group.import_linkview_from_handover(std::move(m_link_view)))
+            return List(std::move(realm), std::move(link_view));
+        return List(std::move(realm), shared_group.import_table_from_handover(std::move(m_table)));
+    });
+}
+
+ThreadSafeReference<Object>::ThreadSafeReference(Object const& object)
+: ThreadSafeReferenceBase(object.realm())
+, m_row(get_source_shared_group().export_for_handover(Row(object.row())))
+, m_object_schema_name(object.get_object_schema().name) { }
+
+Object ThreadSafeReference<Object>::import_into_realm(SharedRealm realm) && {
+    return invalidate_after_import<Object>(*realm, [&](SharedGroup& shared_group) {
+        Row row = *shared_group.import_from_handover(std::move(m_row));
+        auto object_schema = realm->schema().find(m_object_schema_name);
+        REALM_ASSERT_DEBUG(object_schema != realm->schema().end());
+        return Object(std::move(realm), *object_schema, row);
+    });
+}
+
+ThreadSafeReference<Results>::ThreadSafeReference(Results const& results)
+: ThreadSafeReferenceBase(results.get_realm())
+, m_query(get_source_shared_group().export_for_handover(results.get_query(), ConstSourcePayload::Copy))
+, m_ordering_patch([&]() {
+    DescriptorOrdering::HandoverPatch ordering_patch;
+    DescriptorOrdering::generate_patch(results.get_descriptor_ordering(), ordering_patch);
+    return ordering_patch;
+}()){ }
+
+Results ThreadSafeReference<Results>::import_into_realm(SharedRealm realm) && {
+    return invalidate_after_import<Results>(*realm, [&](SharedGroup& shared_group) {
+        Query query = *shared_group.import_from_handover(std::move(m_query));
+        Table& table = *query.get_table();
+        DescriptorOrdering descriptors = DescriptorOrdering::create_from_and_consume_patch(m_ordering_patch, table);
+        return Results(std::move(realm), std::move(query), std::move(descriptors));
+    });
+}
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/util/uuid.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/util/uuid.cpp
new file mode 100644 (file)
index 0000000..0110002
--- /dev/null
@@ -0,0 +1,80 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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/uuid.hpp"
+
+#include <algorithm>
+#include <array>
+#include <functional>
+#include <random>
+#include <stdio.h>
+
+namespace {
+
+// Seed `engine` with as much random state as it requires, based on the approach outlined in P0205R0.
+// <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html>
+template <typename T>
+T create_and_seed_engine()
+{
+    constexpr auto bytes_needed = T::state_size * sizeof(typename T::result_type);
+
+    constexpr auto numbers_needed = sizeof(std::random_device::result_type) < sizeof(std::seed_seq::result_type)
+                                    ? (bytes_needed / sizeof(std::random_device::result_type))
+                                    : (bytes_needed / sizeof(std::seed_seq::result_type));
+
+    std::array<std::random_device::result_type, numbers_needed> state;
+    std::random_device rd;
+    std::generate(begin(state), end(state), std::ref(rd));
+    std::seed_seq seeds(begin(state), end(state));
+
+    T engine;
+    engine.seed(seeds);
+    return engine;
+}
+
+} // unnamed namespace
+
+namespace realm {
+namespace util {
+
+std::string uuid_string()
+{
+    static auto engine = create_and_seed_engine<std::mt19937>();
+
+    std::array<uint8_t, 16> uuid_bytes;
+    std::uniform_int_distribution<unsigned int> distribution(0, std::numeric_limits<uint8_t>::max());
+    std::generate(begin(uuid_bytes), end(uuid_bytes), [&] { return distribution(engine); });
+
+    // Version 4 UUID.
+    uuid_bytes[6] = (uuid_bytes[6] & 0x0f) | 0x40;
+    // IETF variant.
+    uuid_bytes[8] = (uuid_bytes[8] & 0x3f) | 0x80;
+
+    std::array<char, 37> uuid_formatted;
+    snprintf(uuid_formatted.data(), uuid_formatted.size(),
+             "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+             uuid_bytes[0], uuid_bytes[1], uuid_bytes[2], uuid_bytes[3],
+             uuid_bytes[4], uuid_bytes[5], uuid_bytes[6], uuid_bytes[7],
+             uuid_bytes[8], uuid_bytes[9], uuid_bytes[10], uuid_bytes[11],
+             uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]);
+
+    return std::string(uuid_formatted.data(), uuid_formatted.size() - 1);
+}
+
+} // namespace util
+} // namespace realm
diff --git a/iOS/Pods/Realm/Realm/RLMAccessor.mm b/iOS/Pods/Realm/Realm/RLMAccessor.mm
new file mode 100644 (file)
index 0000000..fc530a8
--- /dev/null
@@ -0,0 +1,800 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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.hpp"
+
+#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.hpp"
+#import "RLMSchema_Private.h"
+#import "RLMUtil.hpp"
+#import "results.hpp"
+#import "property.hpp"
+
+#import <objc/runtime.h>
+#import <objc/message.h>
+#import <realm/descriptor.hpp>
+
+#pragma mark - Helper functions
+
+namespace {
+template<typename T>
+T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
+    RLMVerifyAttached(obj);
+    return obj->_row.get<T>(obj->_info->objectSchema->persisted_properties[index].table_column);
+}
+
+template<typename T>
+id getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
+    RLMVerifyAttached(obj);
+    auto& prop = obj->_info->objectSchema->persisted_properties[index];
+    auto col = prop.table_column;
+    if (obj->_row.is_null(col)) {
+        return nil;
+    }
+
+    RLMAccessorContext ctx(obj, &prop);
+    return ctx.box(obj->_row.get<T>(col));
+}
+
+template<typename T>
+void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, T val) {
+    RLMVerifyInWriteTransaction(obj);
+    obj->_row.set(colIndex, val);
+}
+
+template<typename Fn>
+void translateError(Fn&& fn) {
+    try {
+        fn();
+    }
+    catch (std::exception const& e) {
+        @throw RLMException(e);
+    }
+}
+
+void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+              __unsafe_unretained NSString *const val) {
+    RLMVerifyInWriteTransaction(obj);
+    translateError([&] {
+        obj->_row.set(colIndex, RLMStringDataWithNSString(val));
+    });
+}
+
+[[gnu::noinline]]
+void setNull(realm::Row& row, size_t col) {
+    translateError([&] { row.set_null(col); });
+}
+
+void setValue(__unsafe_unretained RLMObjectBase *const obj,
+              NSUInteger colIndex, __unsafe_unretained NSDate *const date) {
+    RLMVerifyInWriteTransaction(obj);
+    if (date) {
+        obj->_row.set(colIndex, RLMTimestampForNSDate(date));
+    }
+    else {
+        setNull(obj->_row, colIndex);
+    }
+}
+
+void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+              __unsafe_unretained NSData *const data) {
+    RLMVerifyInWriteTransaction(obj);
+    translateError([&] {
+        obj->_row.set(colIndex, RLMBinaryDataForNSData(data));
+    });
+}
+
+void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+              __unsafe_unretained RLMObjectBase *const val) {
+    if (!val) {
+        RLMVerifyInWriteTransaction(obj);
+        obj->_row.nullify_link(colIndex);
+        return;
+    }
+
+    RLMAddObjectToRealm(val, obj->_realm, false);
+
+    // make sure it is the correct type
+    if (val->_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.set_link(colIndex, val->_row.get_index());
+}
+
+// array getter/setter
+RLMArray *getArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger propIndex) {
+    RLMVerifyAttached(obj);
+    auto prop = obj->_info->rlmObjectSchema.properties[propIndex];
+    return [[RLMManagedArray alloc] initWithParent:obj property:prop];
+}
+
+void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+              __unsafe_unretained id<NSFastEnumeration> const value) {
+    RLMVerifyInWriteTransaction(obj);
+    auto prop = obj->_info->propertyForTableColumn(colIndex);
+    RLMValidateValueForProperty(value, obj->_info->rlmObjectSchema, prop, true);
+
+    realm::List list(obj->_realm->_realm, *obj->_row.get_table(), colIndex, obj->_row.get_index());
+    RLMClassInfo *info = obj->_info;
+    if (list.get_type() == realm::PropertyType::Object) {
+        info = &obj->_info->linkTargetType(prop.index);
+    }
+    RLMAccessorContext ctx(obj->_realm, *info);
+    translateError([&] { list.assign(ctx, value, false); });
+}
+
+void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+              __unsafe_unretained NSNumber<RLMInt> *const intObject) {
+    RLMVerifyInWriteTransaction(obj);
+
+    if (intObject) {
+        obj->_row.set(colIndex, intObject.longLongValue);
+    }
+    else {
+        setNull(obj->_row, colIndex);
+    }
+}
+
+void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+              __unsafe_unretained NSNumber<RLMFloat> *const floatObject) {
+    RLMVerifyInWriteTransaction(obj);
+
+    if (floatObject) {
+        obj->_row.set(colIndex, floatObject.floatValue);
+    }
+    else {
+        setNull(obj->_row, colIndex);
+    }
+}
+
+void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+              __unsafe_unretained NSNumber<RLMDouble> *const doubleObject) {
+    RLMVerifyInWriteTransaction(obj);
+
+    if (doubleObject) {
+        obj->_row.set(colIndex, doubleObject.doubleValue);
+    }
+    else {
+        setNull(obj->_row, colIndex);
+    }
+}
+
+void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
+              __unsafe_unretained NSNumber<RLMBool> *const boolObject) {
+    RLMVerifyInWriteTransaction(obj);
+
+    if (boolObject) {
+        obj->_row.set(colIndex, (bool)boolObject.boolValue);
+    }
+    else {
+        setNull(obj->_row, colIndex);
+    }
+}
+
+RLMLinkingObjects *getLinkingObjects(__unsafe_unretained RLMObjectBase *const obj,
+                                     __unsafe_unretained RLMProperty *const property) {
+    RLMVerifyAttached(obj);
+    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
+template<typename Type, typename StorageType=Type>
+id makeGetter(NSUInteger index) {
+    return ^(__unsafe_unretained RLMObjectBase *const obj) {
+        return static_cast<Type>(get<StorageType>(obj, index));
+    };
+}
+
+template<typename Type>
+id makeBoxedGetter(NSUInteger index) {
+    return ^(__unsafe_unretained RLMObjectBase *const obj) {
+        return getBoxed<Type>(obj, index);
+    };
+}
+template<typename Type>
+id makeOptionalGetter(NSUInteger index) {
+    return ^(__unsafe_unretained RLMObjectBase *const obj) {
+        return getBoxed<realm::util::Optional<Type>>(obj, index);
+    };
+}
+template<typename Type>
+id makeNumberGetter(NSUInteger index, bool boxed, bool optional) {
+    if (optional) {
+        return makeOptionalGetter<Type>(index);
+    }
+    if (boxed) {
+        return makeBoxedGetter<Type>(index);
+    }
+    return makeGetter<Type>(index);
+}
+
+// dynamic getter with column closure
+id managedGetter(RLMProperty *prop, const char *type) {
+    NSUInteger index = prop.index;
+    if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) {
+        return ^id(__unsafe_unretained RLMObjectBase *const obj) {
+            return getArray(obj, index);
+        };
+    }
+
+    bool boxed = *type == '@';
+    switch (prop.type) {
+        case RLMPropertyTypeInt:
+            if (prop.optional || boxed) {
+                return makeNumberGetter<long long>(index, boxed, prop.optional);
+            }
+            switch (*type) {
+                case 'c': return makeGetter<char, int64_t>(index);
+                case 's': return makeGetter<short, int64_t>(index);
+                case 'i': return makeGetter<int, int64_t>(index);
+                case 'l': return makeGetter<long, int64_t>(index);
+                case 'q': return makeGetter<long long, int64_t>(index);
+                default:
+                    @throw RLMException(@"Unexpected property type for Objective-C type code");
+            }
+        case RLMPropertyTypeFloat:
+            return makeNumberGetter<float>(index, boxed, prop.optional);
+        case RLMPropertyTypeDouble:
+            return makeNumberGetter<double>(index, boxed, prop.optional);
+        case RLMPropertyTypeBool:
+            return makeNumberGetter<bool>(index, boxed, prop.optional);
+        case RLMPropertyTypeString:
+            return makeBoxedGetter<realm::StringData>(index);
+        case RLMPropertyTypeDate:
+            return makeBoxedGetter<realm::Timestamp>(index);
+        case RLMPropertyTypeData:
+            return makeBoxedGetter<realm::BinaryData>(index);
+        case RLMPropertyTypeObject:
+            return makeBoxedGetter<realm::RowExpr>(index);
+        case RLMPropertyTypeAny:
+            @throw RLMException(@"Cannot create accessor class for schema with Mixed properties");
+        case RLMPropertyTypeLinkingObjects:
+            return ^(__unsafe_unretained RLMObjectBase *const obj) {
+                return getLinkingObjects(obj, prop);
+            };
+    }
+}
+
+template<typename ArgType, typename StorageType=ArgType>
+id makeSetter(__unsafe_unretained RLMProperty *const prop) {
+    NSUInteger index = prop.index;
+    NSString *name = prop.name;
+    if (prop.isPrimary) {
+        return ^(__unused RLMObjectBase *obj, __unused ArgType val) {
+            @throw RLMException(@"Primary key can't be changed after an object is inserted.");
+        };
+    }
+
+    return ^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) {
+        auto set = [&] {
+            setValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column,
+                     static_cast<StorageType>(val));
+        };
+        if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo,
+                                                             obj->_row.get_index(), *obj->_info)) {
+            info->willChange(name);
+            set();
+            info->didChange(name);
+        }
+        else {
+            set();
+        }
+    };
+}
+
+// dynamic setter with column closure
+id managedSetter(RLMProperty *prop, const char *type) {
+    if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) {
+        return makeSetter<id<NSFastEnumeration>>(prop);
+    }
+
+    bool boxed = prop.optional || *type == '@';
+    switch (prop.type) {
+        case RLMPropertyTypeInt:
+            if (boxed) {
+                return makeSetter<NSNumber<RLMInt> *>(prop);
+            }
+            switch (*type) {
+                case 'c': return makeSetter<char, long long>(prop);
+                case 's': return makeSetter<short, long long>(prop);
+                case 'i': return makeSetter<int, long long>(prop);
+                case 'l': return makeSetter<long, long long>(prop);
+                case 'q': return makeSetter<long long>(prop);
+                default:
+                    @throw RLMException(@"Unexpected property type for Objective-C type code");
+            }
+        case RLMPropertyTypeFloat:
+            return boxed ? makeSetter<NSNumber<RLMFloat> *>(prop) : makeSetter<float>(prop);
+        case RLMPropertyTypeDouble:
+            return boxed ? makeSetter<NSNumber<RLMDouble> *>(prop) : makeSetter<double>(prop);
+        case RLMPropertyTypeBool:
+            return boxed ? makeSetter<NSNumber<RLMBool> *>(prop) : makeSetter<BOOL, bool>(prop);
+        case RLMPropertyTypeString:         return makeSetter<NSString *>(prop);
+        case RLMPropertyTypeDate:           return makeSetter<NSDate *>(prop);
+        case RLMPropertyTypeData:           return makeSetter<NSData *>(prop);
+        case RLMPropertyTypeAny:            return nil;
+        case RLMPropertyTypeLinkingObjects: return nil;
+        case RLMPropertyTypeObject:         return makeSetter<RLMObjectBase *>(prop);
+    }
+}
+
+// call getter for superclass for property at colIndex
+id superGet(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
+void superSet(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
+id unmanagedGetter(RLMProperty *prop, const char *) {
+    // only override getters for RLMArray and linking objects properties
+    if (prop.type == RLMPropertyTypeLinkingObjects) {
+        return ^(RLMObjectBase *) { return [RLMResults emptyDetachedResults]; };
+    }
+    if (prop.array) {
+        NSString *propName = prop.name;
+        if (prop.type == RLMPropertyTypeObject) {
+            NSString *objectClassName = prop.objectClassName;
+            return ^(RLMObjectBase *obj) {
+                id val = superGet(obj, propName);
+                if (!val) {
+                    val = [[RLMArray alloc] initWithObjectClassName:objectClassName];
+                    superSet(obj, propName, val);
+                }
+                return val;
+            };
+        }
+        auto type = prop.type;
+        auto optional = prop.optional;
+        return ^(RLMObjectBase *obj) {
+            id val = superGet(obj, propName);
+            if (!val) {
+                val = [[RLMArray alloc] initWithObjectType:type optional:optional];
+                superSet(obj, propName, val);
+            }
+            return val;
+        };
+    }
+    return nil;
+}
+
+id unmanagedSetter(RLMProperty *prop, const char *) {
+    // Only RLMArray needs special handling for the unmanaged setter
+    if (!prop.array) {
+        return nil;
+    }
+
+    NSString *propName = prop.name;
+    return ^(RLMObjectBase *obj, id<NSFastEnumeration> values) {
+        auto prop = obj->_objectSchema[propName];
+        RLMValidateValueForProperty(values, obj->_objectSchema, prop, true);
+
+        // make copy when setting (as is the case for all other variants)
+        RLMArray *ar;
+        if (prop.type == RLMPropertyTypeObject)
+            ar = [[RLMArray alloc] initWithObjectClassName:prop.objectClassName];
+        else
+            ar = [[RLMArray alloc] initWithObjectType:prop.type optional:prop.optional];
+        [ar addObjects:values];
+        superSet(obj, propName, ar);
+    };
+}
+
+void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop,
+               id (*getter)(RLMProperty *, const char *),
+               id (*setter)(RLMProperty *, const char *)) {
+    SEL sel = prop.getterSel;
+    auto getterMethod = class_getInstanceMethod(cls, sel);
+    if (!getterMethod) {
+        return;
+    }
+
+    const char *getterType = method_getTypeEncoding(getterMethod);
+    if (id block = getter(prop, getterType)) {
+        class_addMethod(cls, sel, imp_implementationWithBlock(block), getterType);
+    }
+
+    if (!(sel = prop.setterSel)) {
+        return;
+    }
+    auto setterMethod = class_getInstanceMethod(cls, sel);
+    if (!setterMethod) {
+        return;
+    }
+    if (id block = setter(prop, getterType)) { // note: deliberately getterType as it's easier to grab the relevant type from
+        class_addMethod(cls, sel, imp_implementationWithBlock(block), method_getTypeEncoding(setterMethod));
+    }
+}
+
+Class createAccessorClass(Class objectClass,
+                          RLMObjectSchema *schema,
+                          const char *accessorClassName,
+                          id (*getterGetter)(RLMProperty *, const char *),
+                          id (*setterGetter)(RLMProperty *, const char *)) {
+    REALM_ASSERT_DEBUG(RLMIsObjectOrSubclass(objectClass));
+
+    // create and register proxy class which derives from object class
+    Class accClass = objc_allocateClassPair(objectClass, accessorClassName, 0);
+    if (!accClass) {
+        // Class with that name already exists, so just return the pre-existing one
+        // This should only happen for our standalone "accessors"
+        return objc_lookUpClass(accessorClassName);
+    }
+
+    // override getters/setters for each propery
+    for (RLMProperty *prop in schema.properties) {
+        addMethod(accClass, prop, getterGetter, setterGetter);
+    }
+    for (RLMProperty *prop in schema.computedProperties) {
+        addMethod(accClass, prop, getterGetter, setterGetter);
+    }
+
+    objc_registerClassPair(accClass);
+
+    return accClass;
+}
+} // anonymous namespace
+
+#pragma mark - Public Interface
+
+Class RLMManagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, const char *name) {
+    return createAccessorClass(objectClass, schema, name, managedGetter, managedSetter);
+}
+
+Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema) {
+    return createAccessorClass(objectClass, schema,
+                               [@"RLM:Unmanaged " stringByAppendingString:schema.className].UTF8String,
+                               unmanagedGetter, unmanagedSetter);
+}
+
+// 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);
+        }
+
+        static const char accessorClassPrefix[] = "RLM:";
+        if (!strncmp(class_getName(cls), accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) {
+            return schema;
+        }
+
+        return [RLMSchema sharedSchemaForClass:cls];
+    });
+    class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:");
+}
+
+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);
+    }
+    RLMValidateValueForProperty(val, schema, prop, true);
+    RLMDynamicSet(obj, prop, RLMCoerceToNil(val));
+}
+
+// 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) {
+    REALM_ASSERT_DEBUG(!prop.isPrimary);
+    realm::Object o(obj->_info->realm->_realm, *obj->_info->objectSchema, obj->_row);
+    RLMAccessorContext c(obj);
+    translateError([&] {
+        o.set_property_value(c, prop.name.UTF8String, val ?: NSNull.null, false);
+    });
+}
+
+id RLMDynamicGet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop) {
+    realm::Object o(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
+    RLMAccessorContext c(obj);
+    c.currentProperty = prop;
+    return RLMCoerceToNil(o.get_property_value<id>(c, prop.name.UTF8String));
+}
+
+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.array && prop.swiftIvar) {
+        RLMListBase *list = object_getIvar(obj, prop.swiftIvar);
+        if (prop.type != RLMPropertyTypeLinkingObjects && !list._rlmArray) {
+            list._rlmArray = RLMDynamicGet(obj, prop);
+        }
+        return list;
+    }
+
+    return RLMDynamicGet(obj, prop);
+}
+
+RLMAccessorContext::RLMAccessorContext(RLMAccessorContext& parent, realm::Property const& property)
+: _realm(parent._realm)
+, _info(property.type == realm::PropertyType::Object ? parent._info.linkTargetType(property) : parent._info)
+, _promote_existing(parent._promote_existing)
+{
+}
+
+RLMAccessorContext::RLMAccessorContext(RLMRealm *realm, RLMClassInfo& info, bool promote)
+: _realm(realm), _info(info), _promote_existing(promote)
+{
+}
+
+RLMAccessorContext::RLMAccessorContext(__unsafe_unretained RLMObjectBase *const parent,
+                                       const realm::Property *prop)
+: _realm(parent->_realm)
+, _info(prop && prop->type == realm::PropertyType::Object ? parent->_info->linkTargetType(*prop)
+                                                          : *parent->_info)
+, _parentObject(parent)
+{
+}
+
+id RLMAccessorContext::defaultValue(__unsafe_unretained NSString *const key) {
+    if (!_defaultValues) {
+        _defaultValues = RLMDefaultValuesForObjectSchema(_info.rlmObjectSchema);
+    }
+    return _defaultValues[key];
+}
+
+id RLMAccessorContext::propertyValue(__unsafe_unretained id const obj, size_t propIndex,
+                                     __unsafe_unretained RLMProperty *const prop) {
+    // Property value from an NSArray
+    if ([obj respondsToSelector:@selector(objectAtIndex:)]) {
+        return propIndex < [obj count] ? [obj objectAtIndex:propIndex] : nil;
+    }
+
+    // Property value from an NSDictionary
+    if ([obj respondsToSelector:@selector(objectForKey:)]) {
+        return [obj objectForKey:prop.name];
+    }
+
+    // Property value from an instance of this object type
+    id value;
+    if ([obj isKindOfClass:_info.rlmObjectSchema.objectClass] && prop.swiftIvar) {
+        if (prop.array) {
+            return static_cast<RLMListBase *>(object_getIvar(obj, prop.swiftIvar))._rlmArray;
+        }
+        else { // optional
+            value = static_cast<RLMOptionalBase *>(object_getIvar(obj, prop.swiftIvar)).underlyingValue;
+        }
+    }
+    else {
+    // Property value from some object that's KVC-compatible
+        value = RLMValidatedValueForProperty(obj, [obj respondsToSelector:prop.getterSel] ? prop.getterName : prop.name,
+                                             _info.rlmObjectSchema.className);
+    }
+    return value ?: NSNull.null;
+}
+
+id RLMAccessorContext::box(realm::List&& l) {
+    REALM_ASSERT(_parentObject);
+    REALM_ASSERT(currentProperty);
+    return [[RLMManagedArray alloc] initWithList:std::move(l) realm:_realm
+                                      parentInfo:_parentObject->_info
+                                        property:currentProperty];
+}
+
+id RLMAccessorContext::box(realm::Object&& o) {
+    REALM_ASSERT(currentProperty);
+    return RLMCreateObjectAccessor(_realm, _info.linkTargetType(currentProperty.index), o.row());
+}
+
+id RLMAccessorContext::box(realm::RowExpr r) {
+    return RLMCreateObjectAccessor(_realm, _info, r);
+}
+
+id RLMAccessorContext::box(realm::Results&& r) {
+    REALM_ASSERT(currentProperty);
+    return [RLMResults resultsWithObjectInfo:_realm->_info[currentProperty.objectClassName]
+                                     results:std::move(r)];
+}
+
+template<>
+realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, bool, bool) {
+    id v = RLMCoerceToNil(value);
+    return RLMTimestampForNSDate(v);
+}
+
+template<>
+bool RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+    return [v boolValue];
+}
+template<>
+double RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+    return [v doubleValue];
+}
+template<>
+float RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+    return [v floatValue];
+}
+template<>
+long long RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+    return [v longLongValue];
+}
+template<>
+realm::BinaryData RLMAccessorContext::unbox(id v, bool, bool) {
+    v = RLMCoerceToNil(v);
+    return RLMBinaryDataForNSData(v);
+}
+template<>
+realm::StringData RLMAccessorContext::unbox(id v, bool, bool) {
+    v = RLMCoerceToNil(v);
+    return RLMStringDataWithNSString(v);
+}
+
+template<typename Fn>
+static auto to_optional(__unsafe_unretained id const value, Fn&& fn) {
+    id v = RLMCoerceToNil(value);
+    return v && v != NSNull.null ? realm::util::make_optional(fn(v)) : realm::util::none;
+}
+
+template<>
+realm::util::Optional<bool> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+    return to_optional(v, [&](__unsafe_unretained id v) { return (bool)[v boolValue]; });
+}
+template<>
+realm::util::Optional<double> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+    return to_optional(v, [&](__unsafe_unretained id v) { return [v doubleValue]; });
+}
+template<>
+realm::util::Optional<float> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+    return to_optional(v, [&](__unsafe_unretained id v) { return [v floatValue]; });
+}
+template<>
+realm::util::Optional<int64_t> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+    return to_optional(v, [&](__unsafe_unretained id v) { return [v longLongValue]; });
+}
+
+template<>
+realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, bool create, bool update) {
+    RLMObjectBase *link = RLMDynamicCast<RLMObjectBase>(v);
+    if (!link) {
+        if (!create)
+            return realm::RowExpr();
+        return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, update)->_row;
+    }
+
+    if (link.isInvalidated) {
+        if (create) {
+            @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted");
+        }
+        else {
+            @throw RLMException(@"Object has been invalidated");
+        }
+    }
+
+    if (![link->_objectSchema.className isEqualToString:_info.rlmObjectSchema.className]) {
+        if (create && !_promote_existing)
+            return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, update)->_row;
+        return link->_row;
+    }
+
+    if (!link->_realm) {
+        if (!create)
+            return realm::RowExpr();
+        if (!_promote_existing)
+            return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, update)->_row;
+        RLMAddObjectToRealm(link, _realm, update);
+    }
+    else if (link->_realm != _realm) {
+        if (_promote_existing)
+            @throw RLMException(@"Object is already managed by another Realm. Use create instead to copy it into this Realm.");
+        return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, update)->_row;
+    }
+    return link->_row;
+}
+
+void RLMAccessorContext::will_change(realm::Row const& row, realm::Property const& prop) {
+    _observationInfo = RLMGetObservationInfo(nullptr, row.get_index(), _info);
+    if (_observationInfo) {
+        _kvoPropertyName = @(prop.name.c_str());
+        _observationInfo->willChange(_kvoPropertyName);
+    }
+}
+
+void RLMAccessorContext::did_change() {
+    if (_observationInfo) {
+        _observationInfo->didChange(_kvoPropertyName);
+        _kvoPropertyName = nil;
+        _observationInfo = nullptr;
+    }
+}
+
+RLMOptionalId RLMAccessorContext::value_for_property(__unsafe_unretained id const obj,
+                                                     std::string const&, size_t propIndex) {
+    auto prop = _info.rlmObjectSchema.properties[propIndex];
+    id value = propertyValue(obj, propIndex, prop);
+    if (value) {
+        RLMValidateValueForProperty(value, _info.rlmObjectSchema, prop);
+    }
+
+    if (_promote_existing && [obj isKindOfClass:_info.rlmObjectSchema.objectClass] && !prop.swiftIvar) {
+        // 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) {
+            ((void(*)(id, SEL, id))objc_msgSend)(obj, prop.setterSel, nil);
+        }
+    }
+
+    return RLMOptionalId{value};
+}
+
+RLMOptionalId RLMAccessorContext::default_value_for_property(realm::ObjectSchema const&,
+                                                             std::string const& prop)
+{
+    return RLMOptionalId{defaultValue(@(prop.c_str()))};
+}
+
+bool RLMAccessorContext::is_same_list(realm::List const& list, __unsafe_unretained id const v) const noexcept {
+    return [v respondsToSelector:@selector(isBackedByList:)] && [v isBackedByList:list];
+}
diff --git a/iOS/Pods/Realm/Realm/RLMAnalytics.mm b/iOS/Pods/Realm/Realm/RLMAnalytics.mm
new file mode 100644 (file)
index 0000000..b6d750a
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+#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 <array>
+#import <sys/socket.h>
+#import <sys/sysctl.h>
+#import <net/if.h>
+#import <net/if_dl.h>
+
+#import <CommonCrypto/CommonDigest.h>
+
+#ifndef REALM_COCOA_VERSION
+#import "RLMVersion.h"
+#endif
+
+#import <realm/sync/version.hpp>
+
+// 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<void, decltype(&free)> 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<int, 2> 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<CC_LONG>(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<int>(if_nametoindex("en0"));
+    if (!en0) {
+        return nil;
+    }
+
+    std::array<int, 6> 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<sockaddr_dl *>(static_cast<if_msghdr *>(buffer.get()) + 1);
+    auto mac = reinterpret_cast<const unsigned char *>(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/iOS/Pods/Realm/Realm/RLMArray.mm b/iOS/Pods/Realm/Realm/RLMArray.mm
new file mode 100644 (file)
index 0000000..2262e78
--- /dev/null
@@ -0,0 +1,587 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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.h"
+#import "RLMObjectStore.h"
+#import "RLMObject_Private.h"
+#import "RLMProperty_Private.h"
+#import "RLMQueryUtil.hpp"
+#import "RLMSchema_Private.h"
+#import "RLMSwiftSupport.h"
+#import "RLMThreadSafeReference_Private.hpp"
+#import "RLMUtil.hpp"
+
+// See -countByEnumeratingWithState:objects:count
+@interface RLMArrayHolder : NSObject {
+@public
+    std::unique_ptr<id[]> items;
+}
+@end
+@implementation RLMArrayHolder
+@end
+
+@interface RLMArray () <RLMThreadConfined_Private>
+@end
+
+@implementation RLMArray {
+@public
+    // Backing array when this instance is unmanaged
+    NSMutableArray *_backingArray;
+}
+
+#pragma mark - Initializers
+
+- (instancetype)initWithObjectClassName:(__unsafe_unretained NSString *const)objectClassName {
+    REALM_ASSERT([objectClassName length] > 0);
+    self = [super init];
+    if (self) {
+        _objectClassName = objectClassName;
+        _type = RLMPropertyTypeObject;
+    }
+    return self;
+}
+
+- (instancetype)initWithObjectType:(RLMPropertyType)type optional:(BOOL)optional {
+    self = [super init];
+    if (self) {
+        _type = type;
+        _optional = optional;
+    }
+    return self;
+}
+
+#pragma mark - Convenience wrappers used for all RLMArray types
+
+- (void)addObjects:(id<NSFastEnumeration>)objects {
+    for (id obj in objects) {
+        [self addObject:obj];
+    }
+}
+
+- (void)addObject:(id)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];
+}
+
+- (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending {
+    return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:keyPath ascending:ascending]]];
+}
+
+- (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 - Unmanaged RLMArray implementation
+
+- (RLMRealm *)realm {
+    return nil;
+}
+
+- (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;
+}
+
+- (id)objectAtIndex:(NSUInteger)index {
+    validateArrayBounds(self, index);
+    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<id[]>(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;
+}
+
+
+template<typename IndexSetFactory>
+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; });
+}
+
+void RLMArrayValidateMatchingObjectType(__unsafe_unretained RLMArray *const array,
+                                        __unsafe_unretained id const value) {
+    if (!value && !array->_optional) {
+        @throw RLMException(@"Invalid nil value for array of '%@'.",
+                            array->_objectClassName ?: RLMTypeToString(array->_type));
+    }
+    if (array->_type != RLMPropertyTypeObject) {
+        if (!RLMValidateValue(value, array->_type, array->_optional, false, nil)) {
+            @throw RLMException(@"Invalid value '%@' of type '%@' for expected type '%@%s'.",
+                                value, [value class], RLMTypeToString(array->_type),
+                                array->_optional ? "?" : "");
+        }
+        return;
+    }
+
+    auto object = RLMDynamicCast<RLMObjectBase>(value);
+    if (!object) {
+        return;
+    }
+    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 of type '%@' does not match RLMArray type '%@'.",
+                            object->_objectSchema.className, array->_objectClassName);
+    }
+}
+
+static void validateArrayBounds(__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);
+    }
+}
+
+- (void)addObjectsFromArray:(NSArray *)array {
+    for (id obj in array) {
+        RLMArrayValidateMatchingObjectType(self, obj);
+    }
+    changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(_backingArray.count, array.count), ^{
+        [_backingArray addObjectsFromArray:array];
+    });
+}
+
+- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
+    RLMArrayValidateMatchingObjectType(self, anObject);
+    validateArrayBounds(self, index, true);
+    changeArray(self, NSKeyValueChangeInsertion, index, ^{
+        [_backingArray insertObject:anObject atIndex:index];
+    });
+}
+
+- (void)insertObjects:(id<NSFastEnumeration>)objects atIndexes:(NSIndexSet *)indexes {
+    changeArray(self, NSKeyValueChangeInsertion, indexes, ^{
+        NSUInteger currentIndex = [indexes firstIndex];
+        for (RLMObject *obj in objects) {
+            RLMArrayValidateMatchingObjectType(self, obj);
+            [_backingArray insertObject:obj atIndex:currentIndex];
+            currentIndex = [indexes indexGreaterThanIndex:currentIndex];
+        }
+    });
+}
+
+- (void)removeObjectAtIndex:(NSUInteger)index {
+    validateArrayBounds(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 {
+    RLMArrayValidateMatchingObjectType(self, anObject);
+    validateArrayBounds(self, index);
+    changeArray(self, NSKeyValueChangeReplacement, index, ^{
+        [_backingArray replaceObjectAtIndex:index withObject:anObject];
+    });
+}
+
+- (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex {
+    validateArrayBounds(self, sourceIndex);
+    validateArrayBounds(self, destinationIndex);
+    id 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 {
+    validateArrayBounds(self, index1);
+    validateArrayBounds(self, index2);
+
+    changeArray(self, NSKeyValueChangeReplacement, ^{
+        [_backingArray exchangeObjectAtIndex:index1 withObjectAtIndex:index2];
+    }, [=] {
+        NSMutableIndexSet *set = [[NSMutableIndexSet alloc] initWithIndex:index1];
+        [set addIndex:index2];
+        return set;
+    });
+}
+
+- (NSUInteger)indexOfObject:(id)object {
+    RLMArrayValidateMatchingObjectType(self, object);
+    if (!_backingArray) {
+        return NSNotFound;
+    }
+    if (_type != RLMPropertyTypeObject) {
+        return [_backingArray indexOfObject:object];
+    }
+
+    NSUInteger index = 0;
+    for (RLMObjectBase *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]];
+}
+
+static bool canAggregate(RLMPropertyType type, bool allowDate) {
+    switch (type) {
+        case RLMPropertyTypeInt:
+        case RLMPropertyTypeFloat:
+        case RLMPropertyTypeDouble:
+            return true;
+        case RLMPropertyTypeDate:
+            return allowDate;
+        default:
+            return false;
+    }
+}
+
+- (RLMPropertyType)typeForProperty:(NSString *)propertyName {
+    if ([propertyName isEqualToString:@"self"]) {
+        return _type;
+    }
+
+    RLMObjectSchema *objectSchema;
+    if (_backingArray.count) {
+        objectSchema = [_backingArray[0] objectSchema];
+    }
+    else {
+        objectSchema = [RLMSchema.partialPrivateSharedSchema schemaForClassName:_objectClassName];
+    }
+
+    return RLMValidatedProperty(objectSchema, propertyName).type;
+}
+
+- (id)aggregateProperty:(NSString *)key operation:(NSString *)op method:(SEL)sel {
+    // Although delegating to valueForKeyPath: here would allow to support
+    // nested key paths as well, limiting functionality gives consistency
+    // between unmanaged and managed arrays.
+    if ([key rangeOfString:@"."].location != NSNotFound) {
+        @throw RLMException(@"Nested key paths are not supported yet for KVC collection operators.");
+    }
+
+    bool allowDate = false;
+    bool sum = false;
+    if ([op isEqualToString:@"@min"] || [op isEqualToString:@"@max"]) {
+        allowDate = true;
+    }
+    else if ([op isEqualToString:@"@sum"]) {
+        sum = true;
+    }
+    else if (![op isEqualToString:@"@avg"]) {
+        // Just delegate to NSArray for all other operators
+        return [_backingArray valueForKeyPath:[op stringByAppendingPathExtension:key]];
+    }
+
+    RLMPropertyType type = [self typeForProperty:key];
+    if (!canAggregate(type, allowDate)) {
+        NSString *method = sel ? NSStringFromSelector(sel) : op;
+        if (_type == RLMPropertyTypeObject) {
+            @throw RLMException(@"%@: is not supported for %@ property '%@.%@'",
+                                method, RLMTypeToString(type), _objectClassName, key);
+        }
+        else {
+            @throw RLMException(@"%@ is not supported for %@%s array",
+                                method, RLMTypeToString(_type), _optional ? "?" : "");
+        }
+    }
+
+    NSArray *values = [key isEqualToString:@"self"] ? _backingArray : [_backingArray valueForKey:key];
+    if (_optional) {
+        // Filter out NSNull values to match our behavior on managed arrays
+        NSIndexSet *nonnull = [values indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger, BOOL *) {
+            return obj != NSNull.null;
+        }];
+        if (nonnull.count < values.count) {
+            values = [values objectsAtIndexes:nonnull];
+        }
+    }
+    id result = [values valueForKeyPath:[op stringByAppendingString:@".self"]];
+    return sum && !result ? @0 : result;
+}
+
+- (id)valueForKeyPath:(NSString *)keyPath {
+    if ([keyPath characterAtIndex:0] != '@') {
+        return _backingArray ? [_backingArray valueForKeyPath:keyPath] : [super valueForKeyPath:keyPath];
+    }
+
+    if (!_backingArray) {
+        _backingArray = [NSMutableArray new];
+    }
+
+    NSUInteger dot = [keyPath rangeOfString:@"."].location;
+    if (dot == NSNotFound) {
+        return [_backingArray valueForKeyPath:keyPath];
+    }
+
+    NSString *op = [keyPath substringToIndex:dot];
+    NSString *key = [keyPath substringFromIndex:dot + 1];
+    return [self aggregateProperty:key operation:op method:nil];
+}
+
+- (id)valueForKey:(NSString *)key {
+    if ([key isEqualToString:RLMInvalidatedKey]) {
+        return @NO; // Unmanaged arrays are never invalidated
+    }
+    if (!_backingArray) {
+        _backingArray = [NSMutableArray new];
+    }
+    return [_backingArray valueForKey:key];
+}
+
+- (void)setValue:(id)value forKey:(NSString *)key {
+    if ([key isEqualToString:@"self"]) {
+        RLMArrayValidateMatchingObjectType(self, value);
+        for (NSUInteger i = 0, count = _backingArray.count; i < count; ++i) {
+            _backingArray[i] = value;
+        }
+        return;
+    }
+    else if (_type == RLMPropertyTypeObject) {
+        [_backingArray setValue:value forKey:key];
+    }
+    else {
+        [self setValue:value forUndefinedKey:key];
+    }
+}
+
+- (id)minOfProperty:(NSString *)property {
+    return [self aggregateProperty:property operation:@"@min" method:_cmd];
+}
+
+- (id)maxOfProperty:(NSString *)property {
+    return [self aggregateProperty:property operation:@"@max" method:_cmd];
+}
+
+- (id)sumOfProperty:(NSString *)property {
+    return [self aggregateProperty:property operation:@"@sum" method:_cmd];
+}
+
+- (id)averageOfProperty:(NSString *)property {
+    return [self aggregateProperty:property operation:@"@avg" method:_cmd];
+}
+
+- (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];
+}
+
+#pragma mark - 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 *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)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 mark - Thread Confined Protocol Conformance
+
+- (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
+    REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`");
+}
+
+- (id)objectiveCMetadata {
+    REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`");
+}
+
++ (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
+                                     metadata:(id)metadata
+                                        realm:(RLMRealm *)realm {
+    REALM_TERMINATE("Unexpected handover of unmanaged `RLMArray`");
+}
+
+#pragma clang diagnostic pop // unused parameter warning
+
+#pragma mark - Superclass Overrides
+
+- (NSString *)description {
+    return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth];
+}
+
+- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth {
+    return RLMDescriptionWithMaxDepth(@"RLMArray", self, depth);
+}
+@end
+
+@implementation RLMSortDescriptor
+
++ (instancetype)sortDescriptorWithKeyPath:(NSString *)keyPath ascending:(BOOL)ascending {
+    RLMSortDescriptor *desc = [[RLMSortDescriptor alloc] init];
+    desc->_keyPath = keyPath;
+    desc->_ascending = ascending;
+    return desc;
+}
+
+- (instancetype)reversedSortDescriptor {
+    return [self.class sortDescriptorWithKeyPath:_keyPath ascending:!_ascending];
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMClassInfo.mm b/iOS/Pods/Realm/Realm/RLMClassInfo.mm
new file mode 100644 (file)
index 0000000..6063a55
--- /dev/null
@@ -0,0 +1,130 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_Private.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 "shared_realm.hpp"
+
+#import <realm/table.hpp>
+
+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 propertyIndex) {
+    if (propertyIndex < m_linkTargets.size() && m_linkTargets[propertyIndex]) {
+        return *m_linkTargets[propertyIndex];
+    }
+    if (m_linkTargets.size() <= propertyIndex) {
+        m_linkTargets.resize(propertyIndex + 1);
+    }
+    m_linkTargets[propertyIndex] = &realm->_info[rlmObjectSchema.properties[propertyIndex].objectClassName];
+    return *m_linkTargets[propertyIndex];
+}
+
+RLMClassInfo &RLMClassInfo::linkTargetType(realm::Property const& property) {
+    REALM_ASSERT(property.type == PropertyType::Object);
+    return linkTargetType(&property - &objectSchema->persisted_properties[0]);
+}
+
+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;
+    realm::Schema const& schema = realm->_realm->schema();
+    REALM_ASSERT(rlmSchema.objectSchema.count == schema.size());
+
+    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.objectName.UTF8String)));
+    }
+}
+
+RLMSchemaInfo RLMSchemaInfo::clone(realm::Schema const& source_schema,
+                                   __unsafe_unretained RLMRealm *const target_realm) {
+    RLMSchemaInfo info;
+    info.m_objects.reserve(m_objects.size());
+
+    auto& schema = target_realm->_realm->schema();
+    for (auto& pair : m_objects) {
+        size_t idx = pair.second.objectSchema - &*source_schema.begin();
+        info.m_objects.emplace(std::piecewise_construct,
+                               std::forward_as_tuple(pair.first),
+                               std::forward_as_tuple(target_realm, pair.second.rlmObjectSchema,
+                                                     &*schema.begin() + idx));
+    }
+    return info;
+}
diff --git a/iOS/Pods/Realm/Realm/RLMCollection.mm b/iOS/Pods/Realm/Realm/RLMCollection.mm
new file mode 100644 (file)
index 0000000..360789a
--- /dev/null
@@ -0,0 +1,423 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMAccessor.hpp"
+#import "RLMArray_Private.hpp"
+#import "RLMListBase.h"
+#import "RLMObjectSchema_Private.hpp"
+#import "RLMObjectStore.h"
+#import "RLMObject_Private.hpp"
+#import "RLMProperty_Private.h"
+
+#import "collection_notifications.hpp"
+#import "list.hpp"
+#import "results.hpp"
+
+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;
+
+    // A pointer to either _snapshot or a Results from the source collection,
+    // to avoid having to copy the Results when not in a write transaction
+    realm::Results *_results;
+    realm::Results _snapshot;
+
+    // A strong reference to the collection being enumerated to ensure it stays
+    // alive when we're holding a pointer to a member in it
+    id _collection;
+}
+
+- (instancetype)initWithList:(realm::List&)list
+                  collection:(id)collection
+                       realm:(RLMRealm *)realm
+                   classInfo:(RLMClassInfo&)info
+{
+    self = [super init];
+    if (self) {
+        if (realm.inWriteTransaction) {
+            _snapshot = list.snapshot();
+        }
+        else {
+            _snapshot = list.as_results();
+            _collection = collection;
+            [realm registerEnumerator:self];
+        }
+        _results = &_snapshot;
+        _realm = realm;
+        _info = &info;
+    }
+    return self;
+}
+
+- (instancetype)initWithResults:(realm::Results&)results
+                     collection:(id)collection
+                          realm:(RLMRealm *)realm
+                      classInfo:(RLMClassInfo&)info
+{
+    self = [super init];
+    if (self) {
+        if (realm.inWriteTransaction) {
+            _snapshot = results.snapshot();
+            _results = &_snapshot;
+        }
+        else {
+            _results = &results;
+            _collection = collection;
+            [realm registerEnumerator:self];
+        }
+        _realm = realm;
+        _info = &info;
+    }
+    return self;
+}
+
+- (void)dealloc {
+    if (_collection) {
+        [_realm unregisterEnumerator:self];
+    }
+}
+
+- (void)detach {
+    _snapshot = _results->snapshot();
+    _results = &_snapshot;
+    _collection = nil;
+}
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
+                                    count:(NSUInteger)len {
+    [_realm verifyThread];
+    if (!_results->is_valid()) {
+        @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];
+
+    @autoreleasepool {
+        RLMAccessorContext ctx(_realm, *_info);
+        for (NSUInteger index = state->state; index < count && batchCount < len; ++index) {
+            _strongBuffer[batchCount] = _results->get(ctx, index);
+            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
+        if (_collection) {
+            _collection = nil;
+            [_realm unregisterEnumerator:self];
+        }
+        _snapshot = {};
+    }
+
+    state->itemsPtr = (__unsafe_unretained id *)(void *)_strongBuffer;
+    state->state += batchCount;
+    state->mutationsPtr = state->extra+1;
+
+    return batchCount;
+}
+@end
+
+NSUInteger RLMFastEnumerate(NSFastEnumerationState *state, NSUInteger len, id<RLMFastEnumerable> collection) {
+    __autoreleasing RLMFastEnumerator *enumerator;
+    if (state->state == 0) {
+        enumerator = collection.fastEnumerator;
+        state->extra[0] = (long)enumerator;
+        state->extra[1] = collection.count;
+    }
+    else {
+        enumerator = (__bridge id)(void *)state->extra[0];
+    }
+
+    return [enumerator countByEnumeratingWithState:state count:len];
+}
+
+template<typename Collection>
+NSArray *RLMCollectionValueForKey(Collection& collection, NSString *key,
+                                  RLMRealm *realm, RLMClassInfo& info) {
+    size_t count = collection.size();
+    if (count == 0) {
+        return @[];
+    }
+
+    NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
+    if ([key isEqualToString:@"self"]) {
+        RLMAccessorContext context(realm, info);
+        for (size_t i = 0; i < count; ++i) {
+            [array addObject:collection.get(context, i) ?: NSNull.null];
+        }
+        return array;
+    }
+
+    if (collection.get_type() != realm::PropertyType::Object) {
+        RLMAccessorContext context(realm, info);
+        for (size_t i = 0; i < count; ++i) {
+            [array addObject:[collection.get(context, i) valueForKey:key] ?: NSNull.null];
+        }
+        return array;
+    }
+
+    RLMObject *accessor = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info);
+
+    // List properties need to be handled specially since we need to create a
+    // new List each time
+    if (info.rlmObjectSchema.isSwiftClass) {
+        auto prop = info.rlmObjectSchema[key];
+        if (prop && prop.array && prop.swiftIvar) {
+            // Grab the actual class for the generic List from an instance of it
+            // so that we can make instances of the List without creating a new
+            // object accessor each time
+            Class cls = [object_getIvar(accessor, prop.swiftIvar) class];
+            RLMAccessorContext context(realm, info);
+            for (size_t i = 0; i < count; ++i) {
+                RLMListBase *list = [[cls alloc] init];
+                list._rlmArray = [[RLMManagedArray alloc] initWithList:realm::List(realm->_realm, *info.table(),
+                                                                                   info.tableColumn(prop),
+                                                                                   collection.get(i).get_index())
+                                                                 realm:realm parentInfo:&info
+                                                              property:prop];
+                [array addObject:list];
+            }
+            return array;
+        }
+    }
+
+    for (size_t i = 0; i < count; i++) {
+        accessor->_row = collection.get(i);
+        RLMInitializeSwiftAccessorGenerics(accessor);
+        [array addObject:[accessor valueForKey:key] ?: NSNull.null];
+    }
+    return array;
+}
+
+template NSArray *RLMCollectionValueForKey(realm::Results&, NSString *, RLMRealm *, RLMClassInfo&);
+template NSArray *RLMCollectionValueForKey(realm::List&, NSString *, RLMRealm *, RLMClassInfo&);
+
+void RLMCollectionSetValueForKey(id<RLMFastEnumerable> 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<RLMCollection> collection,
+                                     NSUInteger depth) {
+    if (depth == 0) {
+        return @"<Maximum depth exceeded>";
+    }
+
+    const NSUInteger maxObjects = 100;
+    auto str = [NSMutableString stringWithFormat:@"%@<%@> <%p> (\n", name,
+                [collection objectClassName] ?: RLMTypeToString([collection type]),
+                (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;
+}
+
+std::vector<std::pair<std::string, bool>> RLMSortDescriptorsToKeypathArray(NSArray<RLMSortDescriptor *> *properties) {
+    std::vector<std::pair<std::string, bool>> keypaths;
+    keypaths.reserve(properties.count);
+    for (RLMSortDescriptor *desc in properties) {
+        if ([desc.keyPath rangeOfString:@"@"].location != NSNotFound) {
+            @throw RLMException(@"Cannot sort on key path '%@': KVC collection operators are not supported.", desc.keyPath);
+        }
+        keypaths.push_back({desc.keyPath.UTF8String, desc.ascending});
+    }
+    return keypaths;
+}
+
+@implementation RLMCancellationToken {
+    realm::NotificationToken _token;
+    __unsafe_unretained RLMRealm *_realm;
+}
+- (instancetype)initWithToken:(realm::NotificationToken)token realm:(RLMRealm *)realm {
+    self = [super init];
+    if (self) {
+        _token = std::move(token);
+        _realm = realm;
+    }
+    return self;
+}
+
+- (RLMRealm *)realm {
+    return _realm;
+}
+
+- (void)suppressNextNotification {
+    _token.suppress_next();
+}
+
+- (void)invalidate {
+    _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<NSIndexPath *> *)deletionsInSection:(NSUInteger)section {
+    return toIndexPathArray(_indices.deletions, section);
+}
+
+- (NSArray<NSIndexPath *> *)insertionsInSection:(NSUInteger)section {
+    return toIndexPathArray(_indices.insertions, section);
+
+}
+
+- (NSArray<NSIndexPath *> *)modificationsInSection:(NSUInteger)section {
+    return toIndexPathArray(_indices.modifications, section);
+
+}
+@end
+
+template<typename Collection>
+RLMNotificationToken *RLMAddNotificationBlock(id objcCollection,
+                                              Collection& collection,
+                                              void (^block)(id, RLMCollectionChange *, NSError *),
+                                              bool suppressInitialChange) {
+    auto skip = suppressInitialChange ? std::make_shared<bool>(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 (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)
+                                                 realm:(RLMRealm *)[objcCollection realm]];
+}
+
+// Explicitly instantiate the templated function for the two types we'll use it on
+template RLMNotificationToken *RLMAddNotificationBlock<realm::List>(id, realm::List&, void (^)(id, RLMCollectionChange *, NSError *), bool);
+template RLMNotificationToken *RLMAddNotificationBlock<realm::Results>(id, realm::Results&, void (^)(id, RLMCollectionChange *, NSError *), bool);
diff --git a/iOS/Pods/Realm/Realm/RLMConstants.m b/iOS/Pods/Realm/Realm/RLMConstants.m
new file mode 100644 (file)
index 0000000..b45638f
--- /dev/null
@@ -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 <Realm/RLMConstants.h>
+
+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";
+
+NSString * const RLMBackupRealmConfigurationErrorKey = @"RLMBackupRealmConfiguration";
diff --git a/iOS/Pods/Realm/Realm/RLMJSONModels.m b/iOS/Pods/Realm/Realm/RLMJSONModels.m
new file mode 100644 (file)
index 0000000..7360d5d
--- /dev/null
@@ -0,0 +1,218 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 "RLMJSONModels.h"
+#import "RLMSyncUtil_Private.h"
+#import "RLMSyncUser.h"
+
+#pragma mark - Constants
+
+static const NSString *const kRLMSyncAccessTokenKey     = @"access_token";
+static const NSString *const kRLMSyncAccountsKey        = @"accounts";
+static const NSString *const kRLMSyncErrorCodeKey       = @"code";
+static const NSString *const kRLMSyncExpiresKey         = @"expires";
+static const NSString *const kRLMSyncErrorHintKey       = @"hint";
+static const NSString *const kRLMSyncIdKey              = @"id";
+static const NSString *const kRLMSyncKeyKey             = @"key";
+static const NSString *const kRLMSyncMetadataKey        = @"metadata";
+static const NSString *const kRLMSyncRefreshTokenKey    = @"refresh_token";
+static const NSString *const kRLMSyncErrorStatusKey     = @"status";
+static const NSString *const kRLMSyncErrorTitleKey      = @"title";
+static const NSString *const kRLMSyncTokenDataKey       = @"token_data";
+static const NSString *const kRLMSyncUserKey            = @"user";
+static const NSString *const kRLMSyncValueKey           = @"value";
+
+#pragma mark - RLMTokenDataModel
+
+@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) BOOL isAdmin;
+//@property (nonatomic, readwrite) NSArray *access;
+
+@end
+
+@implementation RLMTokenDataModel
+
+- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary {
+    if (self = [super init]) {
+        self.isAdmin = NO;
+        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_OPTIONAL_BOOL(jsonDictionary, kRLMSyncIsAdminKey, isAdmin);
+        RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncExpiresKey, expires);
+        return self;
+    }
+    return nil;
+}
+
+@end
+
+#pragma mark - RLMTokenModel
+
+@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
+
+#pragma mark - RLMAuthResponseModel
+
+@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
+
+#pragma mark - RLMUserInfoResponseModel
+
+@interface RLMSyncUserAccountInfo ()
+@property (nonatomic, readwrite) NSString *provider;
+@property (nonatomic, readwrite) NSString *providerUserIdentity;
+@end
+
+@implementation RLMSyncUserAccountInfo
+
+- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary {
+    if (self = [super init]) {
+        RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncProviderKey, provider);
+        RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncProviderIDKey, providerUserIdentity);
+        return self;
+    }
+    return nil;
+}
+
+@end
+
+@interface RLMUserResponseModel ()
+
+@property (nonatomic, readwrite) NSString *identity;
+@property (nonatomic, readwrite) NSArray *accounts;
+@property (nonatomic, readwrite) NSDictionary *metadata;
+@property (nonatomic, readwrite) BOOL isAdmin;
+
+@end
+
+@implementation RLMUserResponseModel
+
+- (void)parseMetadataFromJSON:(NSDictionary *)jsonDictionary {
+    NSMutableDictionary *buffer = [NSMutableDictionary dictionary];
+    NSArray *metadataArray = jsonDictionary[kRLMSyncMetadataKey];
+    if (![metadataArray isKindOfClass:[NSArray class]]) {
+        self.metadata = @{};
+        return;
+    }
+    for (NSDictionary *object in metadataArray) {
+        if (![object isKindOfClass:[NSDictionary class]]) {
+            continue;
+        }
+        NSString *key = object[kRLMSyncKeyKey];
+        NSString *value = object[kRLMSyncValueKey];
+        if (!key || !value) {
+            continue;
+        }
+        buffer[key] = value;
+    }
+    self.metadata = [buffer copy];
+}
+
+- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary {
+    if (self = [super init]) {
+        self.isAdmin = NO;
+        RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncUserIDKey, identity);
+        RLM_SYNC_PARSE_OPTIONAL_BOOL(jsonDictionary, kRLMSyncIsAdminKey, isAdmin);
+        RLM_SYNC_PARSE_MODEL_ARRAY_OR_ABORT(jsonDictionary, kRLMSyncAccountsKey, RLMSyncUserAccountInfo, accounts);
+        [self parseMetadataFromJSON:jsonDictionary];
+        return self;
+    }
+    return nil;
+}
+
+@end
+
+#pragma mark - RLMSyncErrorResponseModel
+
+@interface RLMSyncErrorResponseModel ()
+
+@property (nonatomic, readwrite) NSInteger status;
+@property (nonatomic, readwrite) NSInteger code;
+@property (nonatomic, readwrite) NSString *title;
+@property (nonatomic, readwrite) NSString *hint;
+
+@end
+
+@implementation RLMSyncErrorResponseModel
+
+- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary {
+    if (self = [super init]) {
+        RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncErrorStatusKey, status);
+        RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncErrorCodeKey, code);
+        RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncErrorTitleKey, title);
+        RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncErrorHintKey, hint);
+        return self;
+    }
+    return nil;
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMListBase.mm b/iOS/Pods/Realm/Realm/RLMListBase.mm
new file mode 100644 (file)
index 0000000..6988acc
--- /dev/null
@@ -0,0 +1,64 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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<RLMObservationInfo> _observationInfo;
+}
+
+- (instancetype)initWithArray:(RLMArray *)array {
+    self = [super init];
+    if (self) {
+        __rlmArray = array;
+    }
+    return self;
+}
+
+- (id)valueForKey:(NSString *)key {
+    return [__rlmArray valueForKey:key];
+}
+
+- (id)valueForKeyPath:(NSString *)keyPath {
+    return [__rlmArray valueForKeyPath:keyPath];
+}
+
+- (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/iOS/Pods/Realm/Realm/RLMManagedArray.mm b/iOS/Pods/Realm/Realm/RLMManagedArray.mm
new file mode 100644 (file)
index 0000000..2593618
--- /dev/null
@@ -0,0 +1,545 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMAccessor.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 "RLMThreadSafeReference_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "list.hpp"
+#import "results.hpp"
+#import "shared_realm.hpp"
+
+#import <realm/table_view.hpp>
+#import <objc/runtime.h>
+
+@interface RLMManagedArrayHandoverMetadata : NSObject
+@property (nonatomic) NSString *parentClassName;
+@property (nonatomic) NSString *key;
+@end
+
+@implementation RLMManagedArrayHandoverMetadata
+@end
+
+@interface RLMManagedArray () <RLMThreadConfined_Private>
+@end
+
+//
+// RLMArray implementation
+//
+@implementation RLMManagedArray {
+@public
+    realm::List _backingList;
+    RLMRealm *_realm;
+    RLMClassInfo *_objectInfo;
+    RLMClassInfo *_ownerInfo;
+    std::unique_ptr<RLMObservationInfo> _observationInfo;
+}
+
+- (RLMManagedArray *)initWithList:(realm::List)list
+                            realm:(__unsafe_unretained RLMRealm *const)realm
+                       parentInfo:(RLMClassInfo *)parentInfo
+                         property:(__unsafe_unretained RLMProperty *const)property {
+    if (property.type == RLMPropertyTypeObject)
+        self = [self initWithObjectClassName:property.objectClassName];
+    else
+        self = [self initWithObjectType:property.type optional:property.optional];
+    if (self) {
+        _realm = realm;
+        REALM_ASSERT(list.get_realm() == realm->_realm);
+        _backingList = std::move(list);
+        _ownerInfo = parentInfo;
+        if (property.type == RLMPropertyTypeObject)
+            _objectInfo = &parentInfo->linkTargetType(property.index);
+        else
+            _objectInfo = _ownerInfo;
+        _key = property.name;
+    }
+    return self;
+}
+
+- (RLMManagedArray *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject
+                           property:(__unsafe_unretained RLMProperty *const)property {
+    __unsafe_unretained RLMRealm *const realm = parentObject->_realm;
+    auto col = parentObject->_info->tableColumn(property);
+    auto& row = parentObject->_row;
+    return [self initWithList:realm::List(realm->_realm, *row.get_table(), col, row.get_index())
+                        realm:realm
+                   parentInfo:parentObject->_info
+                     property:property];
+}
+
+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<RLMObservationInfo>& info,
+                                   __unsafe_unretained NSString *const keyPath,
+                                   __unsafe_unretained RLMArray *const array,
+                                   __unsafe_unretained id const observed) {
+    RLMValidateArrayObservationKey(keyPath, array);
+    if (!info && array.class == [RLMManagedArray class]) {
+        auto lv = static_cast<RLMManagedArray *>(array);
+        info = std::make_unique<RLMObservationInfo>(*lv->_ownerInfo,
+                                                    lv->_backingList.get_origin_row_index(),
+                                                    observed);
+    }
+}
+
+//
+// validation helpers
+//
+[[gnu::noinline]]
+[[noreturn]]
+static void throwError(__unsafe_unretained RLMManagedArray *const ar, NSString *aggregateMethod) {
+    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);
+    }
+    catch (realm::Results::UnsupportedColumnTypeException const& e) {
+        if (ar->_backingList.get_type() == realm::PropertyType::Object) {
+            @throw RLMException(@"%@: is not supported for %s%s property '%s'.",
+                                aggregateMethod,
+                                string_for_property_type(e.property_type),
+                                is_nullable(e.property_type) ? "?" : "",
+                                e.column_name.data());
+        }
+        @throw RLMException(@"%@: is not supported for %s%s array '%@.%@'.",
+                            aggregateMethod,
+                            string_for_property_type(e.property_type),
+                            is_nullable(e.property_type) ? "?" : "",
+                            ar->_ownerInfo->rlmObjectSchema.className, ar->_key);
+    }
+    catch (std::logic_error const& e) {
+        @throw RLMException(e);
+    }
+}
+
+template<typename Function>
+static auto translateErrors(__unsafe_unretained RLMManagedArray *const ar,
+                            Function&& f, NSString *aggregateMethod=nil) {
+    try {
+        return f();
+    }
+    catch (...) {
+        throwError(ar, aggregateMethod);
+    }
+}
+
+template<typename Function>
+static auto translateErrors(Function&& f) {
+    try {
+        return f();
+    }
+    catch (...) {
+        throwError(nil, nil);
+    }
+}
+
+template<typename IndexSetFactory>
+static void changeArray(__unsafe_unretained RLMManagedArray *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(ar, nil);
+        }
+        info->didChange(ar->_key, kind, indexes);
+    }
+    else {
+        translateErrors([&] { f(); });
+    }
+}
+
+static void changeArray(__unsafe_unretained RLMManagedArray *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) {
+    changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; });
+}
+
+static void changeArray(__unsafe_unretained RLMManagedArray *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) {
+    changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; });
+}
+
+static void changeArray(__unsafe_unretained RLMManagedArray *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)isBackedByList:(realm::List const&)list {
+    return _backingList == list;
+}
+
+- (BOOL)isEqual:(id)object {
+    return [object respondsToSelector:@selector(isBackedByList:)] && [object isBackedByList:_backingList];
+}
+
+- (NSUInteger)hash {
+    return std::hash<realm::List>()(_backingList);
+}
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
+                                  objects:(__unused __unsafe_unretained id [])buffer
+                                    count:(NSUInteger)len {
+    return RLMFastEnumerate(state, len, self);
+}
+
+- (id)objectAtIndex:(NSUInteger)index {
+    return translateErrors([&] {
+        RLMAccessorContext context(_realm, *_objectInfo);
+        return _backingList.get(context, index);
+    });
+}
+
+static void RLMInsertObject(RLMManagedArray *ar, id object, NSUInteger index) {
+    RLMArrayValidateMatchingObjectType(ar, object);
+    if (index == NSUIntegerMax) {
+        index = translateErrors([&] { return ar->_backingList.size(); });
+    }
+
+    changeArray(ar, NSKeyValueChangeInsertion, index, ^{
+        RLMAccessorContext context(ar->_realm, *ar->_objectInfo);
+        ar->_backingList.insert(context, index, object);
+    });
+}
+
+- (void)addObject:(id)object {
+    RLMInsertObject(self, object, NSUIntegerMax);
+}
+
+- (void)insertObject:(id)object atIndex:(NSUInteger)index {
+    RLMInsertObject(self, object, index);
+}
+
+- (void)insertObjects:(id<NSFastEnumeration>)objects atIndexes:(NSIndexSet *)indexes {
+    changeArray(self, NSKeyValueChangeInsertion, indexes, ^{
+        NSUInteger index = [indexes firstIndex];
+        RLMAccessorContext context(_realm, *_objectInfo);
+        for (id obj in objects) {
+            RLMArrayValidateMatchingObjectType(self, obj);
+            _backingList.insert(context, index, obj);
+            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), ^{
+        RLMAccessorContext context(_realm, *_objectInfo);
+        for (id obj in array) {
+            RLMArrayValidateMatchingObjectType(self, obj);
+            _backingList.add(context, obj);
+        }
+    });
+}
+
+- (void)removeAllObjects {
+    changeArray(self, NSKeyValueChangeRemoval, NSMakeRange(0, self.count), ^{
+        _backingList.remove_all();
+    });
+}
+
+- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)object {
+    RLMArrayValidateMatchingObjectType(self, object);
+    changeArray(self, NSKeyValueChangeReplacement, index, ^{
+        RLMAccessorContext context(_realm, *_objectInfo);
+        _backingList.set(context, index, object);
+    });
+}
+
+- (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:(id)object {
+    RLMArrayValidateMatchingObjectType(self, object);
+    return translateErrors([&] {
+        RLMAccessorContext context(_realm, *_objectInfo);
+        return RLMConvertNotFound(_backingList.find(context, object));
+    });
+}
+
+- (id)valueForKeyPath:(NSString *)keyPath {
+    if ([keyPath hasPrefix:@"@"]) {
+        // Delegate KVC collection operators to RLMResults
+        return translateErrors([&] {
+            auto results = [RLMResults resultsWithObjectInfo:*_objectInfo results:_backingList.as_results()];
+            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 RLMManagedArray *can't* have objects where
+    // invalidated is true, so we're not losing much.
+    return translateErrors([&]() -> id {
+        if ([key isEqualToString:RLMInvalidatedKey]) {
+            return @(!_backingList.is_valid());
+        }
+
+        _backingList.verify_attached();
+        return RLMCollectionValueForKey(_backingList, key, _realm, *_objectInfo);
+    });
+}
+
+- (void)setValue:(id)value forKey:(NSString *)key {
+    if ([key isEqualToString:@"self"]) {
+        RLMArrayValidateMatchingObjectType(self, value);
+        RLMAccessorContext context(_realm, *_objectInfo);
+        translateErrors([&] {
+            for (size_t i = 0, count = _backingList.size(); i < count; ++i) {
+                _backingList.set(context, i, value);
+            }
+        });
+        return;
+    }
+    else if (_type == RLMPropertyTypeObject) {
+        RLMArrayValidateMatchingObjectType(self, value);
+        translateErrors([&] { _backingList.verify_in_transaction(); });
+        RLMCollectionSetValueForKey(self, key, value);
+    }
+    else {
+        [self setValue:value forUndefinedKey:key];
+    }
+}
+
+- (size_t)columnForProperty:(NSString *)propertyName {
+    if (_backingList.get_type() == realm::PropertyType::Object) {
+        return _objectInfo->tableColumn(propertyName);
+    }
+    if (![propertyName isEqualToString:@"self"]) {
+        @throw RLMException(@"Arrays of '%@' can only be aggregated on \"self\"", RLMTypeToString(_type));
+    }
+    return 0;
+}
+
+- (id)minOfProperty:(NSString *)property {
+    size_t column = [self columnForProperty:property];
+    auto value = translateErrors(self, [&] { return _backingList.min(column); }, @"minOfProperty");
+    return value ? RLMMixedToObjc(*value) : nil;
+}
+
+- (id)maxOfProperty:(NSString *)property {
+    size_t column = [self columnForProperty:property];
+    auto value = translateErrors(self, [&] { return _backingList.max(column); }, @"maxOfProperty");
+    return value ? RLMMixedToObjc(*value) : nil;
+}
+
+- (id)sumOfProperty:(NSString *)property {
+    size_t column = [self columnForProperty:property];
+    return RLMMixedToObjc(translateErrors(self, [&] { return _backingList.sum(column); }, @"sumOfProperty"));
+}
+
+- (id)averageOfProperty:(NSString *)property {
+    size_t column = [self columnForProperty:property];
+    auto value = translateErrors(self, [&] { return _backingList.average(column); }, @"averageOfProperty");
+    return value ? @(*value) : nil;
+}
+
+- (void)deleteObjectsFromRealm {
+    if (_type != RLMPropertyTypeObject) {
+        @throw RLMException(@"Cannot delete objects from RLMArray<%@>: only RLMObjects can be deleted.", RLMTypeToString(_type));
+    }
+    // delete all target rows from the realm
+    RLMTrackDeletions(_realm, ^{
+        translateErrors([&] { _backingList.delete_all(); });
+    });
+}
+
+- (RLMResults *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties {
+    return translateErrors([&] {
+        return [RLMResults resultsWithObjectInfo:*_objectInfo
+                                         results:_backingList.sort(RLMSortDescriptorsToKeypathArray(properties))];
+    });
+}
+
+- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
+    if (_type != RLMPropertyTypeObject) {
+        @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
+    }
+    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 {
+    if (_type != RLMPropertyTypeObject) {
+        @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
+    }
+    realm::Query query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema,
+                                             _realm.schema, _realm.group);
+
+    return translateErrors([&] {
+        return RLMConvertNotFound(_backingList.find(std::move(query)));
+    });
+}
+
+- (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];
+}
+
+- (realm::TableView)tableView {
+    return translateErrors([&] { return _backingList.get_query(); }).find_all();
+}
+
+- (RLMFastEnumerator *)fastEnumerator {
+    return translateErrors([&] {
+        return [[RLMFastEnumerator alloc] initWithList:_backingList collection:self
+                                                 realm:_realm classInfo:*_objectInfo];
+    });
+}
+
+// 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:true];
+    return RLMAddNotificationBlock(self, _backingList, block);
+}
+#pragma clang diagnostic pop
+
+#pragma mark - Thread Confined Protocol Conformance
+
+- (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
+    auto list_reference = _realm->_realm->obtain_thread_safe_reference(_backingList);
+    return std::make_unique<realm::ThreadSafeReference<realm::List>>(std::move(list_reference));
+}
+
+- (RLMManagedArrayHandoverMetadata *)objectiveCMetadata {
+    RLMManagedArrayHandoverMetadata *metadata = [[RLMManagedArrayHandoverMetadata alloc] init];
+    metadata.parentClassName = _ownerInfo->rlmObjectSchema.className;
+    metadata.key = _key;
+    return metadata;
+}
+
++ (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
+                                     metadata:(RLMManagedArrayHandoverMetadata *)metadata
+                                        realm:(RLMRealm *)realm {
+    REALM_ASSERT_DEBUG(dynamic_cast<realm::ThreadSafeReference<realm::List> *>(reference.get()));
+    auto list_reference = static_cast<realm::ThreadSafeReference<realm::List> *>(reference.get());
+
+    auto list = realm->_realm->resolve_thread_safe_reference(std::move(*list_reference));
+    if (!list.is_valid()) {
+        return nil;
+    }
+    RLMClassInfo *parentInfo = &realm->_info[metadata.parentClassName];
+    return [[RLMManagedArray alloc] initWithList:std::move(list)
+                                            realm:realm
+                                       parentInfo:parentInfo
+                                         property:parentInfo->rlmObjectSchema[metadata.key]];
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMMigration.mm b/iOS/Pods/Realm/Realm/RLMMigration.mm
new file mode 100644 (file)
index 0000000..778150a
--- /dev/null
@@ -0,0 +1,202 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMObject_Private.hpp"
+#import "RLMObjectSchema_Private.hpp"
+#import "RLMObjectStore.h"
+#import "RLMProperty_Private.h"
+#import "RLMRealm_Dynamic.h"
+#import "RLMRealm_Private.hpp"
+#import "RLMResults_Private.hpp"
+#import "RLMSchema_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "object_store.hpp"
+#import "shared_realm.hpp"
+#import "schema.hpp"
+
+#import <realm/table.hpp>
+
+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;
+    NSMutableDictionary *deletedObjectIndices;
+    NSMutableSet *deletedClasses;
+}
+
+- (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);
+        deletedObjectIndices = [NSMutableDictionary dictionary];
+        deletedClasses = [NSMutableSet set];
+    }
+    return self;
+}
+
+- (RLMSchema *)oldSchema {
+    return self.oldRealm.schema;
+}
+
+- (RLMSchema *)newSchema {
+    return self.realm.schema;
+}
+
+- (void)enumerateObjects:(NSString *)className block:(RLMObjectMigrationBlock)block {
+    if ([deletedClasses containsObject:className]) {
+        return;
+    }
+    
+    // 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) {
+        NSArray *deletedObjects = deletedObjectIndices[className];
+        if (!deletedObjects) {
+            deletedObjects = [NSMutableArray array];
+            deletedObjectIndices[className] = deletedObjects;
+        }
+
+        for (long i = oldObjects.count - 1; i >= 0; i--) {
+            @autoreleasepool {
+                if ([deletedObjects containsObject:@(i)]) {
+                    continue;
+                }
+                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());
+
+        [self deleteObjectsMarkedForDeletion];
+        [self deleteDataMarkedForDeletion];
+
+        _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 {
+    [deletedObjectIndices[object.objectSchema.className] addObject:@(object->_row.get_index())];
+}
+
+- (void)deleteObjectsMarkedForDeletion {
+    for (NSString *className in deletedObjectIndices.allKeys) {
+        RLMResults *objects = [_realm allObjects:className];
+        for (NSNumber *index in deletedObjectIndices[className]) {
+            RLMObject *object = objects[index.longValue];
+            [_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;
+    }
+
+    [deletedClasses addObject:name];
+    return true;
+}
+
+- (void)deleteDataMarkedForDeletion {
+    for (NSString *className in deletedClasses) {
+        TableRef table = ObjectStore::table_for_object_type(_realm.group, className.UTF8String);
+        if (!table) {
+            continue;
+        }
+        if ([_realm.schema schemaForClassName:className]) {
+            table->clear();
+        }
+        else {
+            realm::ObjectStore::delete_data_for_object(_realm.group, className.UTF8String);
+        }
+    }
+}
+
+- (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/iOS/Pods/Realm/Realm/RLMNetworkClient.mm b/iOS/Pods/Realm/Realm/RLMNetworkClient.mm
new file mode 100644 (file)
index 0000000..1e33bca
--- /dev/null
@@ -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.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMNetworkClient.h"
+
+#import "RLMRealmConfiguration.h"
+#import "RLMJSONModels.h"
+#import "RLMSyncUtil_Private.hpp"
+#import "RLMUtil.hpp"
+
+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);
+}
+
+@interface RLMSyncServerEndpoint ()
+- (instancetype)initPrivate NS_DESIGNATED_INITIALIZER;
+
+/// The HTTP method the endpoint expects. Defaults to POST.
+- (NSString *)httpMethod;
+
+/// The URL to which the request should be made. Must be implemented.
+- (NSURL *)urlForAuthServer:(NSURL *)authServerURL payload:(NSDictionary *)json;
+
+/// The body for the request, if any.
+- (NSData *)httpBodyForPayload:(NSDictionary *)json error:(NSError **)error;
+
+/// The HTTP headers to be added to the request, if any.
+- (NSDictionary<NSString *, NSString *> *)httpHeadersForPayload:(NSDictionary *)json;
+@end
+
+@implementation RLMSyncServerEndpoint
+
+- (instancetype)initPrivate {
+    return (self = [super init]);
+}
+
+- (NSString *)httpMethod {
+    return @"POST";
+}
+
+- (NSURL *)urlForAuthServer:(__unused NSURL *)authServerURL payload:(__unused NSDictionary *)json {
+    NSAssert(NO, @"This method must be overriden by concrete subclasses.");
+    return nil;
+}
+
+- (NSData *)httpBodyForPayload:(NSDictionary *)json error:(NSError **)error {
+    NSError *localError = nil;
+    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json
+                                                       options:(NSJSONWritingOptions)0
+                                                         error:&localError];
+    if (jsonData && !localError) {
+        return jsonData;
+    }
+    NSAssert(localError, @"If there isn't a converted data object there must be an error.");
+    if (error) {
+        *error = localError;
+    }
+    return nil;
+}
+
+- (NSDictionary<NSString *, NSString *> *)httpHeadersForPayload:(__unused NSDictionary *)json {
+    return @{@"Content-Type":   @"application/json;charset=utf-8",
+             @"Accept":         @"application/json"};
+}
+
+@end
+
+@implementation RLMSyncAuthEndpoint
+
++ (instancetype)endpoint {
+    return [[RLMSyncAuthEndpoint alloc] initPrivate];
+}
+
+- (NSURL *)urlForAuthServer:(NSURL *)authServerURL payload:(__unused NSDictionary *)json {
+    return [authServerURL URLByAppendingPathComponent:@"auth"];
+}
+
+@end
+
+@implementation RLMSyncChangePasswordEndpoint
+
++ (instancetype)endpoint {
+    return [[RLMSyncChangePasswordEndpoint alloc] initPrivate];
+}
+
+- (NSString *)httpMethod {
+    return @"PUT";
+}
+
+- (NSURL *)urlForAuthServer:(NSURL *)authServerURL payload:(__unused NSDictionary *)json {
+    return [authServerURL URLByAppendingPathComponent:@"auth/password"];
+}
+
+- (NSDictionary *)httpHeadersForPayload:(NSDictionary *)json {
+    NSString *authToken = [json objectForKey:kRLMSyncTokenKey];
+    if (!authToken) {
+        @throw RLMException(@"Malformed request; this indicates an internal error.");
+    }
+    NSMutableDictionary *headers = [[super httpHeadersForPayload:json] mutableCopy];
+    [headers setObject:authToken forKey:@"Authorization"];
+    return [headers copy];
+}
+
+@end
+
+@implementation RLMSyncGetUserInfoEndpoint
+
++ (instancetype)endpoint {
+    return [[RLMSyncGetUserInfoEndpoint alloc] initPrivate];
+}
+
+- (NSString *)httpMethod {
+    return @"GET";
+}
+
+- (NSURL *)urlForAuthServer:(NSURL *)authServerURL payload:(NSDictionary *)json {
+    NSString *provider = json[kRLMSyncProviderKey];
+    NSString *providerID = json[kRLMSyncProviderIDKey];
+    NSAssert([provider isKindOfClass:[NSString class]] && [providerID isKindOfClass:[NSString class]],
+             @"malformed request; this indicates a logic error in the binding.");
+    NSCharacterSet *allowed = [NSCharacterSet URLQueryAllowedCharacterSet];
+    NSString *pathComponent = [NSString stringWithFormat:@"auth/users/%@/%@",
+                               [provider stringByAddingPercentEncodingWithAllowedCharacters:allowed],
+                               [providerID stringByAddingPercentEncodingWithAllowedCharacters:allowed]];
+    return [authServerURL URLByAppendingPathComponent:pathComponent];
+}
+
+- (NSData *)httpBodyForPayload:(__unused NSDictionary *)json error:(__unused NSError **)error {
+    return nil;
+}
+
+- (NSDictionary<NSString *, NSString *> *)httpHeadersForPayload:(NSDictionary *)json {
+    NSString *authToken = [json objectForKey:kRLMSyncTokenKey];
+    if (!authToken) {
+        @throw RLMException(@"Malformed request; this indicates an internal error.");
+    }
+    return @{@"Authorization": authToken};
+}
+
+@end
+
+
+@implementation RLMNetworkClient
+
++ (NSURLSession *)session {
+    return [NSURLSession sharedSession];
+}
+
++ (void)sendRequestToEndpoint:(RLMSyncServerEndpoint *)endpoint
+                       server:(NSURL *)serverURL
+                         JSON:(NSDictionary *)jsonDictionary
+                   completion:(RLMSyncCompletionBlock)completionBlock {
+    static NSTimeInterval const defaultTimeout = 60;
+    [self sendRequestToEndpoint:endpoint
+                         server:serverURL
+                           JSON:jsonDictionary
+                        timeout:defaultTimeout
+                     completion:completionBlock];
+}
+
++ (void)sendRequestToEndpoint:(RLMSyncServerEndpoint *)endpoint
+                       server:(NSURL *)serverURL
+                         JSON:(NSDictionary *)jsonDictionary
+                      timeout:(NSTimeInterval)timeout
+                   completion:(RLMSyncCompletionBlock)completionBlock {
+    // Create the request
+    NSError *localError = nil;
+    NSURL *requestURL = [endpoint urlForAuthServer:serverURL payload:jsonDictionary];
+    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
+    request.HTTPBody = [endpoint httpBodyForPayload:jsonDictionary error:&localError];
+    if (localError) {
+        completionBlock(localError, nil);
+        return;
+    }
+    request.HTTPMethod = [endpoint httpMethod];
+    request.timeoutInterval = MAX(timeout, 10);
+    NSDictionary<NSString *, NSString *> *headers = [endpoint httpHeadersForPayload:jsonDictionary];
+    for (NSString *key in headers) {
+        [request addValue:headers[key] forHTTPHeaderField:key];
+    }
+    RLMServerURLSessionCompletionBlock handler = ^(NSData *data,
+                                                   NSURLResponse *response,
+                                                   NSError *error) {
+        if (error != nil) {
+            // Network error
+            completionBlock(error, nil);
+            return;
+        }
+
+        NSError *localError = nil;
+
+        if (![self validateResponse:response data:data error:&localError]) {
+            // Response error
+            completionBlock(localError, nil);
+            return;
+        }
+
+        // Parse out the JSON
+        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 = make_auth_error_bad_response(json);
+            completionBlock(localError, nil);
+        } else {
+            // JSON parsed successfully
+            completionBlock(nil, (NSDictionary *)json);
+        }
+    };
+
+    // Add the request to a task and start it
+    NSURLSessionTask *task = [self.session dataTaskWithRequest:request
+                                             completionHandler:handler];
+    [task resume];
+}
+
++ (BOOL)validateResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error {
+    __autoreleasing NSError *localError = nil;
+    if (!error) {
+        error = &localError;
+    }
+
+    if (![response isKindOfClass:[NSHTTPURLResponse class]]) {
+        // FIXME: Provide error message
+        *error = make_auth_error_bad_response();
+        return NO;
+    }
+
+    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
+    BOOL badResponse = (NSLocationInRange(httpResponse.statusCode, RLM_rangeForErrorType(ClientError))
+                        || NSLocationInRange(httpResponse.statusCode, RLM_rangeForErrorType(ServerError)));
+    if (badResponse) {
+        if (RLMSyncErrorResponseModel *responseModel = [self responseModelFromData:data]) {
+            switch (responseModel.code) {
+                case RLMSyncAuthErrorInvalidCredential:
+                case RLMSyncAuthErrorUserDoesNotExist:
+                case RLMSyncAuthErrorUserAlreadyExists:
+                case RLMSyncAuthErrorAccessDeniedOrInvalidPath:
+                case RLMSyncAuthErrorInvalidAccessToken:
+                case RLMSyncAuthErrorExpiredPermissionOffer:
+                case RLMSyncAuthErrorAmbiguousPermissionOffer:
+                case RLMSyncAuthErrorFileCannotBeShared:
+                    *error = make_auth_error(responseModel);
+                    break;
+                default:
+                    // Right now we assume that any codes not described
+                    // above are generic HTTP error codes.
+                    *error = make_auth_error_http_status(responseModel.status);
+                    break;
+            }
+        } else {
+            *error = make_auth_error_http_status(httpResponse.statusCode);
+        }
+
+        return NO;
+    }
+
+    if (!data) {
+        // FIXME: provide error message
+        *error = make_auth_error_bad_response();
+        return NO;
+    }
+
+    return YES;
+}
+
++ (RLMSyncErrorResponseModel *)responseModelFromData:(NSData *)data {
+    if (data.length == 0) {
+        return nil;
+    }
+    id json = [NSJSONSerialization JSONObjectWithData:data
+                                              options:(NSJSONReadingOptions)0
+                                                error:nil];
+    if (!json || ![json isKindOfClass:[NSDictionary class]]) {
+        return nil;
+    }
+    return [[RLMSyncErrorResponseModel alloc] initWithDictionary:json];
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMObject.mm b/iOS/Pods/Realm/Realm/RLMObject.mm
new file mode 100644 (file)
index 0000000..1ace411
--- /dev/null
@@ -0,0 +1,411 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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.h"
+#import "RLMCollection_Private.hpp"
+#import "RLMObjectBase_Private.h"
+#import "RLMObjectSchema_Private.hpp"
+#import "RLMObjectStore.h"
+#import "RLMProperty.h"
+#import "RLMQueryUtil.hpp"
+#import "RLMRealm_Private.hpp"
+#import "RLMSchema_Private.h"
+
+#import "collection_notifications.hpp"
+#import "object.hpp"
+
+@interface RLMPropertyChange ()
+@property (nonatomic, readwrite, strong) NSString *name;
+@property (nonatomic, readwrite, strong, nullable) id previousValue;
+@property (nonatomic, readwrite, strong, nullable) id value;
+@end
+
+// 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 {
+    return [super initWithValue:value schema:RLMSchema.partialPrivateSharedSchema];
+}
+
+#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:(__unsafe_unretained RLMRealm *const)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);
+}
+
+- (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block {
+    return RLMObjectAddNotificationBlock(self, ^(NSArray<NSString *> *propertyNames,
+                                                 NSArray *oldValues, NSArray *newValues, NSError *error) {
+        if (error) {
+            block(false, nil, error);
+        }
+        else if (!propertyNames) {
+            block(true, nil, nil);
+        }
+        else {
+            auto properties = [NSMutableArray arrayWithCapacity:propertyNames.count];
+            for (NSUInteger i = 0, count = propertyNames.count; i < count; ++i) {
+                auto prop = [RLMPropertyChange new];
+                prop.name = propertyNames[i];
+                prop.previousValue = RLMCoerceToNil(oldValues[i]);
+                prop.value = RLMCoerceToNil(newValues[i]);
+                [properties addObject:prop];
+            }
+            block(false, properties, nil);
+        }
+    });
+}
+
++ (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;
+}
+
+- (id)copyWithZone:(__unused NSZone *)zone {
+    RLMWeakObjectHandle *copy = [[RLMWeakObjectHandle alloc] init];
+    copy->_row = _row;
+    copy->_info = _info;
+    copy->_objectClass = _objectClass;
+    return copy;
+}
+
+@end
+
+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) {
+    auto isSubclass = [](Class class1, Class class2) {
+        class1 = class_getSuperclass(class1);
+        return RLMIsKindOfClass(class1, class2);
+    };
+    if (isSubclass(class_getSuperclass(klass), RLMObjectBase.class)) {
+        return YES;
+    }
+
+    if (treatFakeObjectAsRLMObject) {
+        static Class FakeObjectClass = NSClassFromString(@"FakeObject");
+        return isSubclass(klass, FakeObjectClass);
+    }
+    return NO;
+}
+
+@interface RLMObjectNotificationToken : RLMCancellationToken
+@end
+@implementation RLMObjectNotificationToken {
+@public
+    realm::Object _object;
+}
+@end
+
+RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block) {
+    if (!obj->_realm) {
+        @throw RLMException(@"Only objects which are managed by a Realm support change notifications");
+    }
+    [obj->_realm verifyNotificationsAreSupported:true];
+
+    struct {
+        void (^block)(NSArray<NSString *> *, NSArray *, NSArray *, NSError *);
+        RLMObjectBase *object;
+
+        NSArray<NSString *> *propertyNames = nil;
+        NSArray *oldValues = nil;
+        bool deleted = false;
+
+        void populateProperties(realm::CollectionChangeSet const& c) {
+            if (propertyNames) {
+                return;
+            }
+            if (!c.deletions.empty()) {
+                deleted = true;
+                return;
+            }
+            if (c.columns.empty()) {
+                return;
+            }
+
+            auto properties = [NSMutableArray new];
+            for (size_t i = 0; i < c.columns.size(); ++i) {
+                if (c.columns[i].empty()) {
+                    continue;
+                }
+                if (auto prop = object->_info->propertyForTableColumn(i)) {
+                    [properties addObject:prop.name];
+                }
+            }
+            if (properties.count) {
+                propertyNames = properties;
+            }
+        }
+
+        NSArray *readValues(realm::CollectionChangeSet const& c) {
+            if (c.empty()) {
+                return nil;
+            }
+            populateProperties(c);
+            if (!propertyNames) {
+                return nil;
+            }
+
+            auto values = [NSMutableArray arrayWithCapacity:propertyNames.count];
+            for (NSString *name in propertyNames) {
+                id value = [object valueForKey:name];
+                if (!value || [value isKindOfClass:[RLMArray class]]) {
+                    [values addObject:NSNull.null];
+                }
+                else {
+                    [values addObject:value];
+                }
+            }
+            return values;
+        }
+
+        void before(realm::CollectionChangeSet const& c) {
+            @autoreleasepool {
+                oldValues = readValues(c);
+            }
+        }
+
+        void after(realm::CollectionChangeSet const& c) {
+            @autoreleasepool {
+                auto newValues = readValues(c);
+                if (deleted) {
+                    block(nil, nil, nil, nil);
+                }
+                else if (newValues) {
+                    block(propertyNames, oldValues, newValues, nil);
+                }
+                propertyNames = nil;
+                oldValues = nil;
+            }
+        }
+
+        void error(std::exception_ptr err) {
+            @autoreleasepool {
+                try {
+                    rethrow_exception(err);
+                }
+                catch (...) {
+                    NSError *error = nil;
+                    RLMRealmTranslateException(&error);
+                    block(nil, nil, nil, error);
+                }
+            }
+        }
+    } callback{block, obj};
+
+    realm::Object object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
+    auto token = [[RLMObjectNotificationToken alloc] initWithToken:object.add_notification_callback(callback) realm:obj->_realm];
+    token->_object = std::move(object);
+    return token;
+}
+
+@implementation RLMPropertyChange
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMObjectBase.mm b/iOS/Pods/Realm/Realm/RLMObjectBase.mm
new file mode 100644 (file)
index 0000000..434475f
--- /dev/null
@@ -0,0 +1,538 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMThreadSafeReference_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "object.hpp"
+#import "shared_realm.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;
+}
+
+@interface RLMObjectBase () <RLMThreadConfined, RLMThreadConfined_Private>
+@end
+
+@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 coerceToObjectType(id obj, Class cls, RLMSchema *schema) {
+    return [obj isKindOfClass:cls] ? obj : [[cls alloc] initWithValue:obj schema:schema];
+}
+
+static id validatedObjectForProperty(__unsafe_unretained id const obj,
+                                     __unsafe_unretained RLMObjectSchema *const objectSchema,
+                                     __unsafe_unretained RLMProperty *const prop,
+                                     __unsafe_unretained RLMSchema *const schema) {
+    RLMValidateValueForProperty(obj, objectSchema, prop);
+    if (!obj || obj == NSNull.null) {
+        return nil;
+    }
+    if (prop.type == RLMPropertyTypeObject) {
+        Class objectClass = schema[prop.objectClassName].objectClass;
+        if (prop.array) {
+            NSMutableArray *ret = [[NSMutableArray alloc] init];
+            for (id el in obj) {
+                [ret addObject:coerceToObjectType(el, objectClass, schema)];
+            }
+            return ret;
+        }
+        return coerceToObjectType(obj, objectClass, schema);
+    }
+    return obj;
+}
+
+- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema {
+    if (!(self = [super init])) {
+        return self;
+    }
+
+    if (!value || value == NSNull.null) {
+        @throw RLMException(@"Must provide a non-nil value.");
+    }
+
+    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<NSArray>(value)) {
+        if (array.count > properties.count) {
+            @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).",
+                                (unsigned long long)array.count, (unsigned long long)properties.count);
+        }
+        NSUInteger i = 0;
+        for (id val in array) {
+            RLMProperty *prop = properties[i++];
+            [self setValue:validatedObjectForProperty(RLMCoerceToNil(val), _objectSchema, prop, schema)
+                    forKey:prop.name];
+        }
+    }
+    else {
+        // assume our object is an NSDictionary or an object with kvc properties
+        for (RLMProperty *prop in properties) {
+            id obj = RLMValidatedValueForProperty(value, prop.name, _objectSchema.className);
+
+            // don't set unspecified properties
+            if (!obj) {
+                continue;
+            }
+
+            [self setValue:validatedObjectForProperty(RLMCoerceToNil(obj), _objectSchema, prop, schema)
+                    forKey:prop.name];
+        }
+    }
+
+    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 {
+    value = RLMCoerceToNil(value);
+    RLMProperty *property = _objectSchema[key];
+    if (Ivar ivar = property.swiftIvar) {
+        if (property.array && (!value || [value conformsToProtocol:@protocol(NSFastEnumeration)])) {
+            RLMArray *array = [object_getIvar(self, ivar) _rlmArray];
+            [array removeAllObjects];
+
+            if (value) {
+                [array addObjects:validatedObjectForProperty(value, _objectSchema, property,
+                                                             RLMSchema.partialPrivateSharedSchema)];
+            }
+        }
+        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];
+}
+
++ (void)initializeLinkedObjectSchemas {
+    for (RLMProperty *prop in self.sharedSchema.properties) {
+        if (prop.type == RLMPropertyTypeObject && !RLMSchema.partialPrivateSharedSchema[prop.objectClassName]) {
+            [[RLMSchema classForString:prop.objectClassName] initializeLinkedObjectSchemas];
+        }
+    }
+}
+
++ (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 @"<Maximum depth exceeded>";
+    }
+
+    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<RLMObjectBase>(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);
+}
+
++ (NSString *)_realmObjectName {
+    return nil;
+}
+
+- (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[] = "RLM:Managed";
+    if (!strncmp(className, accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) {
+        if ([class_getSuperclass(self.class) sharedSchema][key]) {
+            return NO;
+        }
+    }
+
+    return [super automaticallyNotifiesObserversForKey:key];
+}
+
+#pragma mark - Thread Confined Protocol Conformance
+
+- (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
+    Object object(_realm->_realm, *_info->objectSchema, _row);
+    realm::ThreadSafeReference<Object> reference = _realm->_realm->obtain_thread_safe_reference(std::move(object));
+    return std::make_unique<realm::ThreadSafeReference<Object>>(std::move(reference));
+}
+
+- (id)objectiveCMetadata {
+    return nil;
+}
+
++ (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
+                                     metadata:(__unused id)metadata
+                                        realm:(RLMRealm *)realm {
+    REALM_ASSERT_DEBUG(dynamic_cast<realm::ThreadSafeReference<Object> *>(reference.get()));
+    auto object_reference = static_cast<realm::ThreadSafeReference<Object> *>(reference.get());
+
+    Object object = realm->_realm->resolve_thread_safe_reference(std::move(*object_reference));
+    if (!object.is_valid()) {
+        return nil;
+    }
+    NSString *objectClassName = @(object.get_object_schema().name.c_str());
+
+    return RLMCreateObjectAccessor(realm, realm->_info[objectClassName], object.row().get_index());
+}
+
+@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 *)getSwiftProperties:(__unused id)obj {
+    return nil;
+}
+
++ (NSDictionary *)getOptionalProperties:(__unused id)obj {
+    return nil;
+}
+
++ (NSArray *)requiredPropertiesForClass:(Class)cls {
+    return [cls requiredProperties];
+}
+
+@end
+
+@implementation RLMSwiftPropertyMetadata
+
++ (instancetype)metadataForOtherProperty:(NSString *)propertyName {
+    RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
+    md.propertyName = propertyName;
+    md.kind = RLMSwiftPropertyKindOther;
+    return md;
+}
+
++ (instancetype)metadataForListProperty:(NSString *)propertyName {
+    RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
+    md.propertyName = propertyName;
+    md.kind = RLMSwiftPropertyKindList;
+    return md;
+}
+
++ (instancetype)metadataForLinkingObjectsProperty:(NSString *)propertyName
+                                        className:(NSString *)className
+                               linkedPropertyName:(NSString *)linkedPropertyName {
+    RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
+    md.propertyName = propertyName;
+    md.className = className;
+    md.linkedPropertyName = linkedPropertyName;
+    md.kind = RLMSwiftPropertyKindLinkingObjects;
+    return md;
+}
+
++ (instancetype)metadataForOptionalProperty:(NSString *)propertyName type:(RLMPropertyType)type {
+    RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
+    md.propertyName = propertyName;
+    md.propertyType = type;
+    md.kind = RLMSwiftPropertyKindOptional;
+    return md;
+}
+
++ (instancetype)metadataForNilLiteralOptionalProperty:(NSString *)propertyName {
+    RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
+    md.propertyName = propertyName;
+    md.kind = RLMSwiftPropertyKindNilLiteralOptional;
+    return md;
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMObjectSchema.mm b/iOS/Pods/Realm/Realm/RLMObjectSchema.mm
new file mode 100644 (file)
index 0000000..48516d6
--- /dev/null
@@ -0,0 +1,452 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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<id, RLMProperty *> *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 hasSwiftName = [RLMSwiftSupport isSwiftClassName:className];
+    if (hasSwiftName) {
+        className = [RLMSwiftSupport demangleClassName:className];
+    }
+
+    static Class s_swiftObjectClass = NSClassFromString(@"RealmSwiftObject");
+    bool isSwift = hasSwiftName || [objectClass isSubclassOfClass:s_swiftObjectClass];
+
+    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"]];
+        NSArray *duplicatePropertyNames = [countedPropertyNames filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *) {
+            return [countedPropertyNames countForObject:object] > 1;
+        }]].allObjects;
+
+        if (duplicatePropertyNames.count == 1) {
+            @throw RLMException(@"Property '%@' is declared multiple times in the class hierarchy of '%@'", duplicatePropertyNames.firstObject, className);
+        } else {
+            @throw RLMException(@"Object '%@' has properties that are declared multiple times in its class hierarchy: '%@'", className, [duplicatePropertyNames 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(@"Property '%@' cannot be made the primary key of '%@' because it is not a 'string' or 'int' property.",
+                                primaryKey, className);
+        }
+    }
+
+    for (RLMProperty *prop in schema.properties) {
+        if (prop.optional && prop.array && (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeLinkingObjects)) {
+            // FIXME: message is awkward
+            @throw RLMException(@"Property '%@.%@' cannot be made optional because optional '%@' properties are not supported.",
+                                className, prop.name, RLMTypeToString(prop.type));
+        }
+    }
+
+    return schema;
+}
+
++ (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;
+    std::unique_ptr<objc_property_t[], decltype(&free)> props(class_copyPropertyList(objectClass, &count), &free);
+    NSMutableArray<RLMProperty *> *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];
+        }
+    }
+
+    if (isSwiftClass) {
+        [self addSwiftProperties:propArray objectUtil:objectUtil instance:swiftObjectInstance indexed:indexed];
+    }
+
+    if (auto requiredProperties = [objectUtil requiredPropertiesForClass:objectClass]) {
+        for (RLMProperty *property in propArray) {
+            bool required = [requiredProperties containsObject:property.name];
+            if (required && property.type == RLMPropertyTypeObject && !property.array) {
+                @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 && !property.array) {
+            @throw RLMException(@"The `%@.%@` property must be marked as being optional.",
+                                [objectClass className], property.name);
+        }
+    }
+
+    return propArray;
+}
+
++ (void)addSwiftProperties:(NSMutableArray<RLMProperty *> *)propArray
+                objectUtil:(Class)objectUtil
+                  instance:(id)instance
+                   indexed:(NSSet<NSString *> *)indexed {
+    // The property list reported to the obj-c runtime for Swift objects is
+    // incomplete and doesn't include Swift generics like List<> and
+    // RealmOptional<>, and is missing information for some properties that
+    // are reported, such as the difference between `String` and `String?`. To
+    // deal with this, we also get the properties from Swift reflection, and
+    // merge the results.
+
+    NSArray<RLMSwiftPropertyMetadata *> *props = [objectUtil getSwiftProperties:instance];
+    if (!props) {
+        // A Swift subclass of RLMObject, which operates under obj-c rules
+        return;
+    }
+
+    // Track the index that we expect the next property to go in, for inserting
+    // generic properties into the correct place
+    NSUInteger nextIndex = 0;
+    for (RLMSwiftPropertyMetadata *md in props) {
+        // In theory existing should only ever be nextIndex or NSNotFound, and
+        // this search is just a waste of time.
+        // FIXME: verify if this is actually true
+        NSUInteger existing = [propArray indexOfObjectPassingTest:^(RLMProperty *obj, NSUInteger, BOOL *) {
+            return [obj.name isEqualToString:md.propertyName];
+        }];
+
+        switch (md.kind) {
+            case RLMSwiftPropertyKindList: // List<>
+                [propArray insertObject:[[RLMProperty alloc] initSwiftListPropertyWithName:md.propertyName
+                                                                                  instance:instance]
+                                atIndex:nextIndex];
+                break;
+            case RLMSwiftPropertyKindLinkingObjects: { // LinkingObjects<>
+                Ivar ivar = class_getInstanceVariable([instance class], md.propertyName.UTF8String);
+                [propArray insertObject:[[RLMProperty alloc] initSwiftLinkingObjectsPropertyWithName:md.propertyName
+                                                                                                ivar:ivar
+                                                                                     objectClassName:md.className
+                                                                              linkOriginPropertyName:md.linkedPropertyName]
+                                atIndex:nextIndex];
+                break;
+            }
+            case RLMSwiftPropertyKindOptional: {
+                if (existing != NSNotFound) {
+                    // String?, Data?, Date? with a non-nil default value
+                    // We already know about this property from obj-c and we
+                    // defaulted to optional, so nothing to do
+                    break;
+                }
+
+                // RealmOptional<>
+                Ivar ivar = class_getInstanceVariable([instance class], md.propertyName.UTF8String);
+                BOOL isIndexed = [indexed containsObject:md.propertyName];
+                [propArray insertObject:[[RLMProperty alloc] initSwiftOptionalPropertyWithName:md.propertyName
+                                                                                       indexed:isIndexed
+                                                                                          ivar:ivar
+                                                                                  propertyType:md.propertyType]
+                                atIndex:nextIndex];
+                break;
+            }
+
+            case RLMSwiftPropertyKindOther:
+            case RLMSwiftPropertyKindNilLiteralOptional:
+                // This might be a property which wasn't reported to obj-c and
+                // isn't one of our supported generic types, in which case we
+                // ignore it
+                if (existing == NSNotFound) {
+                    --nextIndex;
+                }
+                // or it might be a String?, Data?, Date? or object field with
+                // a nil default value
+                else if (md.kind == RLMSwiftPropertyKindNilLiteralOptional) {
+                    propArray[existing].optional = true;
+                }
+                // or it may be some non-optional property which may have been
+                // previously marked as optional due to that being the default
+                // in obj-c
+                else {
+                    propArray[existing].optional = false;
+                }
+                break;
+        }
+
+        ++nextIndex;
+    }
+}
+
+- (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];
+}
+
+- (NSString *)objectName {
+    return [self.objectClass _realmObjectName] ?: _className;
+}
+
+- (realm::ObjectSchema)objectStoreCopy {
+    ObjectSchema objectSchema;
+    objectSchema.name = self.objectName.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/iOS/Pods/Realm/Realm/RLMObjectStore.mm b/iOS/Pods/Realm/Realm/RLMObjectStore.mm
new file mode 100644 (file)
index 0000000..db9950d
--- /dev/null
@@ -0,0 +1,263 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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.hpp"
+#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 <objc/message.h>
+
+using namespace realm;
+
+void RLMRealmCreateAccessors(RLMSchema *schema) {
+    const size_t bufferSize = sizeof("RLM:Managed  ") // includes null terminator
+                            + std::numeric_limits<unsigned long long>::digits10
+                            + realm::Group::max_table_name_length;
+
+    char className[bufferSize] = "RLM:Managed ";
+    char *const start = className + strlen(className);
+
+    for (RLMObjectSchema *objectSchema in schema.objectSchema) {
+        if (objectSchema.accessorClass != objectSchema.objectClass) {
+            continue;
+        }
+
+        static unsigned long long count = 0;
+        sprintf(start, "%llu %s", count++, objectSchema.className.UTF8String);
+        objectSchema.accessorClass = RLMManagedAccessorClassForObjectClass(objectSchema.objectClass, objectSchema, className);
+    }
+}
+
+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 == RLMPropertyTypeLinkingObjects) {
+            id linkingObjects = object_getIvar(object, prop.swiftIvar);
+            [linkingObjects setObject:(id)[[RLMWeakObjectHandle alloc] initWithObject:object]];
+            [linkingObjects setProperty:prop];
+        }
+        else if (prop.array) {
+            RLMArray *array = [[RLMManagedArray alloc] initWithParent:object property:prop];
+            [object_getIvar(object, prop.swiftIvar) set_rlmArray:array];
+        }
+        else {
+            RLMOptionalBase *optional = object_getIvar(object, prop.swiftIvar);
+            optional.property = prop;
+            optional.object = object;
+        }
+    }
+}
+
+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) {
+            // Adding an object to the Realm it's already manged by is a 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. Use create instead to copy it into this Realm.");
+    }
+    if (object->_observationInfo && object->_observationInfo->hasObservers()) {
+        @throw RLMException(@"Cannot add an object with observers to a Realm");
+    }
+
+    auto& info = realm->_info[object->_objectSchema.className];
+    RLMAccessorContext c{realm, info, true};
+    object->_info = &info;
+    object->_realm = realm;
+    object->_objectSchema = info.rlmObjectSchema;
+    try {
+        realm::Object::create(c, realm->_realm, *info.objectSchema, (id)object,
+                              createOrUpdate, &object->_row);
+    }
+    catch (std::exception const& e) {
+        @throw RLMException(e);
+    }
+    object_setClass(object, info.rlmObjectSchema.accessorClass);
+    RLMInitializeSwiftAccessorGenerics(object);
+}
+
+RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className,
+                                               id value, bool createOrUpdate = false) {
+    RLMVerifyInWriteTransaction(realm);
+
+    if (createOrUpdate && RLMIsObjectSubclass([value class])) {
+        RLMObjectBase *obj = value;
+        if (obj->_realm == realm && [obj->_objectSchema.className isEqualToString:className]) {
+            // This is a no-op if value is an RLMObject of the same type already backed by the target realm.
+            return value;
+        }
+    }
+
+    if (!value || value == NSNull.null) {
+        @throw RLMException(@"Must provide a non-nil value.");
+    }
+
+    auto& info = realm->_info[className];
+    if ([value isKindOfClass:[NSArray class]] && [value count] > info.objectSchema->persisted_properties.size()) {
+        @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).",
+                            (unsigned long long)[value count],
+                            (unsigned long long)info.objectSchema->persisted_properties.size());
+    }
+
+    RLMAccessorContext c{realm, info, false};
+    RLMObjectBase *object = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info);
+    try {
+        object->_row = realm::Object::create(c, realm->_realm, *info.objectSchema,
+                                             (id)value, createOrUpdate).row();
+    }
+    catch (std::exception const& e) {
+        @throw RLMException(e);
+    }
+    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.move_last_over();
+        });
+    }
+
+    // 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(__unsafe_unretained RLMRealm *const 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);
+
+    RLMAccessorContext *c = nullptr;
+    auto& info = realm->_info[objectClassName];
+    if (RLMProperty *prop = info.propertyForPrimaryKey()) {
+        RLMValidateValueForProperty(key, info.rlmObjectSchema, prop);
+    }
+    try {
+        auto obj = realm::Object::get_for_primary_key(*c, realm->_realm, *info.objectSchema,
+                                                      key ?: NSNull.null);
+        if (!obj.is_valid())
+            return nil;
+        return RLMCreateObjectAccessor(realm, info, obj.row());
+    }
+    catch (std::exception const& e) {
+        @throw RLMException(e);
+    }
+}
+
+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/iOS/Pods/Realm/Realm/RLMObservation.mm b/iOS/Pods/Realm/Realm/RLMObservation.mm
new file mode 100644 (file)
index 0000000..ed95129
--- /dev/null
@@ -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 <realm/group.hpp>
+
+using namespace realm;
+
+namespace {
+    template<typename Iterator>
+    struct IteratorPair {
+        Iterator first;
+        Iterator second;
+    };
+    template<typename Iterator>
+    Iterator begin(IteratorPair<Iterator> const& p) {
+        return p.first;
+    }
+    template<typename Iterator>
+    Iterator end(IteratorPair<Iterator> const& p) {
+        return p.second;
+    }
+
+    template<typename Container>
+    auto reverse(Container const& c) {
+        return IteratorPair<typename Container::const_reverse_iterator>{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.array) {
+        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<RLMOptionalBase>(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<id(*)(id, SEL, NSString *)>([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.array) {
+        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<std::vector<RLMObservationInfo *> *> 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<change> changes;
+    std::vector<RLMObservationInfo *> 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<typename Func>
+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<RLMObservationInfo *>(state.info));
+        }
+    }
+}
+}
+
+std::vector<realm::BindingContext::ObserverState> RLMGetObservedRows(RLMSchemaInfo const& schema) {
+    std::vector<realm::BindingContext::ObserverState> 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<realm::BindingContext::ObserverState> const& observed,
+                   std::vector<void *> const& invalidated) {
+    for (auto info : invalidated) {
+        static_cast<RLMObservationInfo *>(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<RLMObservationInfo *>(info)->prepareForInvalidation();
+    }
+}
+
+void RLMDidChange(std::vector<realm::BindingContext::ObserverState> const& observed,
+                  std::vector<void *> 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<RLMObservationInfo *>(info)->didChange(RLMInvalidatedKey);
+    }
+}
diff --git a/iOS/Pods/Realm/Realm/RLMOptionalBase.mm b/iOS/Pods/Realm/Realm/RLMOptionalBase.mm
new file mode 100644 (file)
index 0000000..f48ec94
--- /dev/null
@@ -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 <objc/runtime.h>
+
+@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);
+    }
+    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/iOS/Pods/Realm/Realm/RLMPredicateUtil.mm b/iOS/Pods/Realm/Realm/RLMPredicateUtil.mm
new file mode 100644 (file)
index 0000000..830a9b2
--- /dev/null
@@ -0,0 +1,127 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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"
+
+#include <realm/util/assert.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: {
+            NSExpression *collection = expression.collection;
+            // NSExpression.collection is declared as id, but appears to always hold an NSExpression for subqueries.
+            REALM_ASSERT([collection isKindOfClass:[NSExpression class]]);
+            return [NSExpression expressionForSubquery:visit(collection) 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:
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+            return [NSExpression expressionForConditional:visit(expression.predicate) trueExpression:visit(expression.trueExpression) falseExpression:visit(expression.falseExpression)];
+#pragma clang diagnostic pop
+
+        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/iOS/Pods/Realm/Realm/RLMProperty.mm b/iOS/Pods/Realm/Realm/RLMProperty.mm
new file mode 100644 (file)
index 0000000..e9d4c08
--- /dev/null
@@ -0,0 +1,641 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_Private.hpp"
+#import "RLMListBase.h"
+#import "RLMObject.h"
+#import "RLMObject_Private.h"
+#import "RLMOptionalBase.h"
+#import "RLMSchema_Private.h"
+#import "RLMSwiftSupport.h"
+#import "RLMUtil.hpp"
+
+static_assert((int)RLMPropertyTypeInt    == (int)realm::PropertyType::Int, "");
+static_assert((int)RLMPropertyTypeBool   == (int)realm::PropertyType::Bool, "");
+static_assert((int)RLMPropertyTypeFloat  == (int)realm::PropertyType::Float, "");
+static_assert((int)RLMPropertyTypeDouble == (int)realm::PropertyType::Double, "");
+static_assert((int)RLMPropertyTypeString == (int)realm::PropertyType::String, "");
+static_assert((int)RLMPropertyTypeData   == (int)realm::PropertyType::Data, "");
+static_assert((int)RLMPropertyTypeDate   == (int)realm::PropertyType::Date, "");
+static_assert((int)RLMPropertyTypeObject == (int)realm::PropertyType::Object, "");
+
+BOOL RLMPropertyTypeIsComputed(RLMPropertyType propertyType) {
+    return propertyType == RLMPropertyTypeLinkingObjects;
+}
+
+// Swift obeys the ARC naming conventions for method families (except for init)
+// but the end result doesn't really work (using KVC on a method returning a
+// retained value results in a leak, but not returning a retained value results
+// in crashes). Objective-C makes properties with naming fitting the method
+// families a compile error, so we just disallow them in Swift as well.
+// http://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families
+void RLMValidateSwiftPropertyName(NSString *name) {
+    // To belong to a method family, the property name must begin with the family
+    // name followed by a non-lowercase letter (or nothing), with an optional
+    // leading underscore
+    const char *str = name.UTF8String;
+    if (str[0] == '_')
+        ++str;
+    auto nameSize = strlen(str);
+
+    // Note that "init" is deliberately not in this list because Swift does not
+    // infer family membership for it.
+    for (auto family : {"alloc", "new", "copy", "mutableCopy"}) {
+        auto familySize = strlen(family);
+        if (nameSize < familySize || !std::equal(str, str + familySize, family)) {
+            continue;
+        }
+        if (familySize == nameSize || !islower(str[familySize])) {
+            @throw RLMException(@"Property names beginning with '%s' are not "
+                                 "supported. Swift follows ARC's ownership "
+                                 "rules for methods based on their name, which "
+                                 "results in memory leaks when accessing "
+                                 "properties which return retained values via KVC.",
+                                family);
+        }
+        return;
+    }
+}
+
+static bool rawTypeShouldBeTreatedAsComputedProperty(NSString *rawType) {
+    return [rawType isEqualToString:@"@\"RLMLinkingObjects\""] || [rawType hasPrefix:@"@\"RLMLinkingObjects<"];
+}
+
+@implementation RLMProperty
+
++ (instancetype)propertyForObjectStoreProperty:(const realm::Property &)prop {
+    auto ret = [[RLMProperty alloc] initWithName:@(prop.name.c_str())
+                                            type:static_cast<RLMPropertyType>(prop.type & ~realm::PropertyType::Flags)
+                                 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:is_nullable(prop.type)];
+    if (is_array(prop.type)) {
+        ret->_array = true;
+    }
+    return ret;
+}
+
+- (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 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);
+}
+
+static realm::util::Optional<RLMPropertyType> typeFromProtocolString(const char *type) {
+    if (strncmp(type, "RLM", 3)) {
+        return realm::none;
+    }
+    type += 3;
+    if (strcmp(type, "Int>\"") == 0) {
+        return RLMPropertyTypeInt;
+    }
+    if (strcmp(type, "Float>\"") == 0) {
+        return RLMPropertyTypeFloat;
+    }
+    if (strcmp(type, "Double>\"") == 0) {
+        return RLMPropertyTypeDouble;
+    }
+    if (strcmp(type, "Bool>\"") == 0) {
+        return RLMPropertyTypeBool;
+    }
+    if (strcmp(type, "String>\"") == 0) {
+        return RLMPropertyTypeString;
+    }
+    if (strcmp(type, "Data>\"") == 0) {
+        return RLMPropertyTypeData;
+    }
+    if (strcmp(type, "Date>\"") == 0) {
+        return RLMPropertyTypeDate;
+    }
+    return realm::none;
+}
+
+// determine RLMPropertyType from objc code - returns true if valid type was found/set
+- (BOOL)setTypeFromRawType:(NSString *)rawType {
+    const char *code = rawType.UTF8String;
+    switch (*code) {
+        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 '@':
+            break;
+        default:
+            return NO;
+    }
+
+    _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) {
+        _array = true;
+        if (auto type = typeFromProtocolString(code + arrayPrefixLen)) {
+            _type = *type;
+            return YES;
+        }
+
+        // get object class from type string - @"RLMArray<objectClassName>"
+        _objectClassName = [[NSString alloc] initWithBytes:code + arrayPrefixLen
+                                                    length:strlen(code + arrayPrefixLen) - 2 // drop trailing >"
+                                                  encoding:NSUTF8StringEncoding];
+
+        if ([RLMSchema classForString:_objectClassName]) {
+            _optional = false;
+            _type = RLMPropertyTypeObject;
+            return YES;
+        }
+        @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) {
+        auto type = typeFromProtocolString(code + numberPrefixLen);
+        if (type && (*type == RLMPropertyTypeInt || *type == RLMPropertyTypeFloat || *type == RLMPropertyTypeDouble || *type == RLMPropertyTypeBool)) {
+            _type = *type;
+            return YES;
+        }
+        @throw RLMException(@"Property '%@' is of type %s 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, code + 1);
+    }
+    else if (strncmp(code, linkingObjectsPrefix, linkingObjectsPrefixLen) == 0 &&
+             (code[linkingObjectsPrefixLen] == '"' || code[linkingObjectsPrefixLen] == '<')) {
+        _type = RLMPropertyTypeLinkingObjects;
+        _optional = false;
+        _array = true;
+
+        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<RLMInt>.", _name);
+    }
+    else if (strcmp(code, "@\"RLMArray\"") == 0) {
+        @throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: RLMArray<Person>.", _name);
+    }
+    else {
+        NSString *className;
+        Class cls = nil;
+        if (code[1] == '\0') {
+            className = @"id";
+        }
+        else {
+            // for objects strip the quotes and @
+            className = [rawType substringWithRange:NSMakeRange(2, rawType.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;
+}
+
+- (void)parseObjcProperty:(objc_property_t)property
+                 readOnly:(bool *)readOnly
+                 computed:(bool *)computed
+                  rawType:(NSString **)rawType {
+    unsigned int count;
+    objc_property_attribute_t *attrs = property_copyAttributeList(property, &count);
+
+    *computed = true;
+    for (size_t i = 0; i < count; ++i) {
+        switch (*attrs[i].name) {
+            case 'T':
+                *rawType = @(attrs[i].value);
+                break;
+            case 'R':
+                *readOnly = true;
+                break;
+            case 'N':
+                // nonatomic
+                break;
+            case 'D':
+                // dynamic
+                break;
+            case 'G':
+                _getterName = @(attrs[i].value);
+                break;
+            case 'S':
+                _setterName = @(attrs[i].value);
+                break;
+            case 'V': // backing ivar name
+                *computed = false;
+                break;
+            default:
+                break;
+        }
+    }
+    free(attrs);
+}
+
+- (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;
+    }
+
+    RLMValidateSwiftPropertyName(name);
+
+    _name = name;
+    _indexed = indexed;
+
+    if (linkPropertyDescriptor) {
+        _objectClassName = [linkPropertyDescriptor.objectClass className];
+        _linkOriginPropertyName = linkPropertyDescriptor.propertyName;
+    }
+
+    NSString *rawType;
+    bool readOnly = false;
+    bool isComputed = false;
+    [self parseObjcProperty:property readOnly:&readOnly computed:&isComputed rawType:&rawType];
+    if (!readOnly && isComputed) {
+        // Check for lazy property.
+        NSString *backingPropertyName = [NSString stringWithFormat:@"%@.storage", name];
+        if (class_getInstanceVariable([obj class], backingPropertyName.UTF8String)) {
+            isComputed = false;
+        }
+    }
+
+    if (readOnly || isComputed) {
+        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 ([rawType isEqualToString:@"@"]) {
+        if (propertyValue) {
+            rawType = [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
+            rawType = @"@\"RLMLinkingObjects\"";
+        }
+    }
+
+    // convert array types to objc variant
+    if ([rawType isEqualToString:@"@\"RLMArray\""]) {
+        RLMArray *value = propertyValue;
+        _type = value.type;
+        _optional = value.optional;
+        _array = true;
+        _objectClassName = value.objectClassName;
+        if (_type == RLMPropertyTypeObject && ![RLMSchema classForString:_objectClassName]) {
+            @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 ([rawType 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.");
+        }
+        _optional = true;
+        switch (*numberType) {
+            case 'i': case 'l': case 'q':
+                _type = RLMPropertyTypeInt;
+                break;
+            case 'f':
+                _type = RLMPropertyTypeFloat;
+                break;
+            case 'd':
+                _type = RLMPropertyTypeDouble;
+                break;
+            case 'B': case 'c':
+                _type = RLMPropertyTypeBool;
+                break;
+            default:
+                @throw RLMException(@"Can't persist NSNumber of type '%s': only integers, floats, doubles, and bools are currently supported.", numberType);
+        }
+    }
+    else if (![self setTypeFromRawType:rawType]) {
+        @throw RLMException(@"Can't persist property '%@' with incompatible type. "
+                            "Add to Object.ignoredProperties() class method to ignore.",
+                            self.name);
+    }
+
+    if ([rawType isEqualToString:@"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;
+    }
+
+    NSString *rawType;
+    bool isReadOnly = false;
+    bool isComputed = false;
+    [self parseObjcProperty:property readOnly:&isReadOnly computed:&isComputed rawType:&rawType];
+    bool shouldBeTreatedAsComputedProperty = rawTypeShouldBeTreatedAsComputedProperty(rawType);
+    if ((isReadOnly || isComputed) && !shouldBeTreatedAsComputedProperty) {
+        return nil;
+    }
+
+    if (![self setTypeFromRawType:rawType]) {
+        @throw RLMException(@"Can't persist property '%@' with incompatible type. "
+                             "Add to ignoredPropertyNames: method to ignore.", self.name);
+    }
+
+    if (!isReadOnly && shouldBeTreatedAsComputedProperty) {
+        @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
+                                     instance:(id)object {
+    self = [super init];
+    if (!self) {
+        return nil;
+    }
+    _name = name;
+    _array = true;
+    _swiftIvar = class_getInstanceVariable([object class], name.UTF8String);
+
+    RLMArray *array = [object_getIvar(object, _swiftIvar) _rlmArray];
+    _type = array.type;
+    _optional = array.optional;
+    _objectClassName = array.objectClassName;
+
+    // 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;
+    _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;
+    _array = true;
+    _objectClassName = objectClassName;
+    _linkOriginPropertyName = linkOriginPropertyName;
+    _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->_objectClassName = _objectClassName;
+    prop->_array = _array;
+    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"
+             "\tarray = %@;\n"
+             "\toptional = %@;\n"
+             "}",
+            self.name, RLMTypeToString(self.type), self.objectClassName,
+            self.linkOriginPropertyName,
+            self.indexed ? @"YES" : @"NO",
+            self.isPrimary ? @"YES" : @"NO",
+            self.array ? @"YES" : @"NO",
+            self.optional ? @"YES" : @"NO"];
+}
+
+- (realm::Property)objectStoreCopy {
+    realm::Property p;
+    p.name = _name.UTF8String;
+    p.object_type = _objectClassName ? _objectClassName.UTF8String : "";
+    p.is_indexed = (bool)_indexed;
+    p.link_origin_property_name = _linkOriginPropertyName ? _linkOriginPropertyName.UTF8String : "";
+    p.type = static_cast<realm::PropertyType>(_type);
+    if (_array) {
+        p.type |= realm::PropertyType::Array;
+    }
+    if (_optional) {
+        p.type |= realm::PropertyType::Nullable;
+    }
+    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/iOS/Pods/Realm/Realm/RLMQueryUtil.mm b/iOS/Pods/Realm/Realm/RLMQueryUtil.mm
new file mode 100644 (file)
index 0000000..82d8ccd
--- /dev/null
@@ -0,0 +1,1489 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_Private.h"
+#import "RLMObject_Private.hpp"
+#import "RLMPredicateUtil.hpp"
+#import "RLMProperty_Private.h"
+#import "RLMSchema.h"
+#import "RLMUtil.hpp"
+
+#import "object_store.hpp"
+#import "results.hpp"
+
+#include <realm/query_engine.hpp>
+#include <realm/query_expression.hpp>
+#include <realm/util/cf_ptr.hpp>
+#include <realm/util/overload.hpp>
+
+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;
+    }
+}
+
+// Equal and ContainsSubstring are used by QueryBuilder::add_string_constraint as the comparator
+// for performing diacritic-insensitive comparisons.
+
+bool equal(CFStringCompareFlags options, StringData v1, StringData v2)
+{
+    if (v1.is_null() || v2.is_null()) {
+        return v1.is_null() == v2.is_null();
+    }
+
+    auto s1 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v1.data(), v1.size(),
+                                                          kCFStringEncodingUTF8, false, kCFAllocatorNull));
+    auto s2 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v2.data(), v2.size(),
+                                                          kCFStringEncodingUTF8, false, kCFAllocatorNull));
+
+    return CFStringCompare(s1.get(), s2.get(), options) == kCFCompareEqualTo;
+}
+
+template <CFStringCompareFlags options>
+struct Equal {
+    using CaseSensitive = Equal<options & ~kCFCompareCaseInsensitive>;
+    using CaseInsensitive = Equal<options | kCFCompareCaseInsensitive>;
+
+    bool operator()(StringData v1, StringData v2, bool v1_null, bool v2_null) const
+    {
+        REALM_ASSERT_DEBUG(v1_null == v1.is_null());
+        REALM_ASSERT_DEBUG(v2_null == v2.is_null());
+
+        return equal(options, v1, v2);
+    }
+
+    // FIXME: Consider the options.
+    static const char* description() { return "equal"; }
+};
+
+bool contains_substring(CFStringCompareFlags options, StringData v1, StringData v2)
+{
+    if (v2.is_null()) {
+        // Everything contains NULL
+        return true;
+    }
+
+    if (v1.is_null()) {
+        // NULL contains nothing (except NULL, handled above)
+        return false;
+    }
+
+    if (v2.size() == 0) {
+        // Everything (except NULL, handled above) contains the empty string
+        return true;
+    }
+
+    auto s1 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v1.data(), v1.size(),
+                                                          kCFStringEncodingUTF8, false, kCFAllocatorNull));
+    auto s2 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v2.data(), v2.size(),
+                                                          kCFStringEncodingUTF8, false, kCFAllocatorNull));
+
+    return CFStringFind(s1.get(), s2.get(), options).location != kCFNotFound;
+}
+
+template <CFStringCompareFlags options>
+struct ContainsSubstring {
+    using CaseSensitive = ContainsSubstring<options & ~kCFCompareCaseInsensitive>;
+    using CaseInsensitive = ContainsSubstring<options | kCFCompareCaseInsensitive>;
+
+    bool operator()(StringData v1, StringData v2, bool v1_null, bool v2_null) const
+    {
+        REALM_ASSERT_DEBUG(v1_null == v1.is_null());
+        REALM_ASSERT_DEBUG(v2_null == v2.is_null());
+
+        return contains_substring(options, v1, v2);
+    }
+
+    // FIXME: Consider the options.
+    static const char* description() { return "contains"; }
+};
+
+
+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.objectName.UTF8String);
+}
+
+// A reference to a column within a query. Can be resolved to a Columns<T> for use in query expressions.
+class ColumnReference {
+public:
+    ColumnReference(Query& query, Group& group, RLMSchema *schema, RLMProperty* property, const std::vector<RLMProperty*>& 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 <typename T, typename... SubQuery>
+    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<T>(index(), std::forward<SubQuery>(subquery)...);
+        }
+        else {
+            return resolve_backlink<T>(std::forward<SubQuery>(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 RLMPropertyTypeLinkingObjects:
+                return m_schema[property().objectClassName];
+            default:
+                REALM_UNREACHABLE();
+        }
+    }
+
+    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.array; });
+    }
+
+    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 <typename T, typename... SubQuery>
+    auto resolve_backlink(SubQuery&&... subquery) const
+    {
+        // We actually just want `if constexpr (std::is_same<T, Link>::value) { ... }`,
+        // so fake it by tag-dispatching on the conditional
+        return do_resolve_backlink<T>(std::is_same<T, Link>(), std::forward<SubQuery>(subquery)...);
+    }
+
+    template <typename T, typename... SubQuery>
+    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<T>(table, col, std::forward<SubQuery>(subquery)...);
+        });
+    }
+
+    template <typename T, typename... SubQuery>
+    Columns<T> 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<typename Func>
+    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<typename Func>
+    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<RLMProperty*> 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<ColumnReference> column)
+        : m_type(type)
+        , m_link_column(std::move(link_column))
+        , m_column(std::move(column))
+    {
+        RLMPrecondition(m_link_column.property().array,
+                        @"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<ColumnReference> 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<ColumnReference> 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 <typename A, typename B>
+    void add_numeric_constraint(RLMPropertyType datatype,
+                                NSPredicateOperatorType operatorType,
+                                A&& lhs, B&& rhs);
+
+    template <typename A, typename B>
+    void add_bool_constraint(NSPredicateOperatorType operatorType, A lhs, B rhs);
+
+    void add_substring_constraint(null, Query condition);
+    template<typename T>
+    void add_substring_constraint(const T& value, Query condition);
+    template<typename T>
+    void add_substring_constraint(const Columns<T>& value, Query condition);
+
+    template <typename T>
+    void add_string_constraint(NSPredicateOperatorType operatorType,
+                               NSComparisonPredicateOptions predicateOptions,
+                               Columns<String> &&column,
+                               T value);
+
+    void add_string_constraint(NSPredicateOperatorType operatorType,
+                               NSComparisonPredicateOptions predicateOptions,
+                               StringData value,
+                               Columns<String>&& column);
+
+    template <typename L, typename R>
+    void add_constraint(RLMPropertyType type,
+                        NSPredicateOperatorType operatorType,
+                        NSComparisonPredicateOptions predicateOptions,
+                        L lhs, R rhs);
+    template <typename... T>
+    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);
+
+    void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, BinaryData value);
+    void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, id value);
+    void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, null);
+    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<typename T>
+    void add_link_constraint(NSPredicateOperatorType operatorType, T obj, const ColumnReference& column);
+    void add_link_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&);
+
+    template <CollectionOperation::Type Operation, typename... T>
+    void add_collection_operation_constraint(RLMPropertyType propertyType, NSPredicateOperatorType operatorType, T... values);
+    template <typename... T>
+    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 <typename A, typename B>
+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 <typename A, typename B>
+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));
+    }
+}
+
+void QueryBuilder::add_substring_constraint(null, Query) {
+    // Foundation always returns false for substring operations with a RHS of null or "".
+    m_query.and_query(std::unique_ptr<Expression>(new FalseExpression));
+}
+
+template<typename T>
+void QueryBuilder::add_substring_constraint(const T& value, Query condition) {
+    // Foundation always returns false for substring operations with a RHS of null or "".
+    m_query.and_query(value.size()
+                      ? std::move(condition)
+                      : std::unique_ptr<Expression>(new FalseExpression));
+}
+
+template<typename T>
+void QueryBuilder::add_substring_constraint(const Columns<T>& value, Query condition) {
+    // Foundation always returns false for substring operations with a RHS of null or "".
+    // We don't need to concern ourselves with the possibility of value traversing a link list
+    // and producing multiple values per row as such expressions will have been rejected.
+    m_query.and_query(const_cast<Columns<String>&>(value).size() != 0 && std::move(condition));
+}
+
+template <typename T>
+void QueryBuilder::add_string_constraint(NSPredicateOperatorType operatorType,
+                                         NSComparisonPredicateOptions predicateOptions,
+                                         Columns<String> &&column,
+                                         T value) {
+    bool caseSensitive = !(predicateOptions & NSCaseInsensitivePredicateOption);
+    bool diacriticSensitive = !(predicateOptions & NSDiacriticInsensitivePredicateOption);
+
+    if (diacriticSensitive) {
+        switch (operatorType) {
+            case NSBeginsWithPredicateOperatorType:
+                add_substring_constraint(value, column.begins_with(value, caseSensitive));
+                break;
+            case NSEndsWithPredicateOperatorType:
+                add_substring_constraint(value, column.ends_with(value, caseSensitive));
+                break;
+            case NSContainsPredicateOperatorType:
+                add_substring_constraint(value, 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;
+            case NSLikePredicateOperatorType:
+                m_query.and_query(column.like(value, caseSensitive));
+                break;
+            default:
+                @throw RLMPredicateException(@"Invalid operator type",
+                                             @"Operator '%@' not supported for string type",
+                                             operatorName(operatorType));
+        }
+        return;
+    }
+
+    auto as_subexpr = util::overload([](StringData value) { return make_subexpr<ConstantStringValue>(value); },
+                                     [](const Columns<String>& c) { return c.clone(); });
+    auto left = as_subexpr(column);
+    auto right = as_subexpr(value);
+
+    auto make_constraint = [&](auto comparator) {
+        using Comparator = decltype(comparator);
+        using CompareCS = Compare<typename Comparator::CaseSensitive, StringData>;
+        using CompareCI = Compare<typename Comparator::CaseInsensitive, StringData>;
+        if (caseSensitive) {
+            return make_expression<CompareCS>(std::move(left), std::move(right));
+        }
+        else {
+            return make_expression<CompareCI>(std::move(left), std::move(right));
+        }
+    };
+
+    switch (operatorType) {
+        case NSBeginsWithPredicateOperatorType: {
+            using C = ContainsSubstring<kCFCompareDiacriticInsensitive | kCFCompareAnchored>;
+            add_substring_constraint(value, make_constraint(C{}));
+            break;
+        }
+        case NSEndsWithPredicateOperatorType: {
+            using C = ContainsSubstring<kCFCompareDiacriticInsensitive | kCFCompareAnchored | kCFCompareBackwards>;
+            add_substring_constraint(value, make_constraint(C{}));
+            break;
+        }
+        case NSContainsPredicateOperatorType: {
+            using C = ContainsSubstring<kCFCompareDiacriticInsensitive>;
+            add_substring_constraint(value, make_constraint(C{}));
+            break;
+        }
+        case NSNotEqualToPredicateOperatorType:
+            m_query.Not();
+            REALM_FALLTHROUGH;
+        case NSEqualToPredicateOperatorType:
+            m_query.and_query(make_constraint(Equal<kCFCompareDiacriticInsensitive>{}));
+            break;
+        case NSLikePredicateOperatorType:
+            @throw RLMPredicateException(@"Invalid operator type",
+                                         @"Operator 'LIKE' not supported with diacritic-insensitive modifier.");
+        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<String>&& 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<NSExpression>(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<NSArray>(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<Link>(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();
+}
+
+void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType,
+                                         const ColumnReference& column,
+                                         BinaryData value) {
+    RLMPrecondition(!column.has_links(), @"Unsupported operator", @"NSData properties cannot be queried over an object link.");
+
+    size_t index = column.index();
+    Query query = m_query.get_table()->where();
+
+    switch (operatorType) {
+        case NSBeginsWithPredicateOperatorType:
+            add_substring_constraint(value, query.begins_with(index, value));
+            break;
+        case NSEndsWithPredicateOperatorType:
+            add_substring_constraint(value, query.ends_with(index, value));
+            break;
+        case NSContainsPredicateOperatorType:
+            add_substring_constraint(value, 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, const ColumnReference& column, null) {
+    add_binary_constraint(operatorType, column, BinaryData());
+}
+
+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<Link>() == obj->_row);
+    }
+    else {
+        m_query.and_query(column.resolve<Link>() != obj->_row);
+    }
+}
+
+void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType,
+                                       const ColumnReference& column,
+                                       realm::null) {
+    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<Link>() == null());
+    }
+    else {
+        m_query.and_query(column.resolve<Link>() != null());
+    }
+}
+
+template<typename T>
+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<typename Func>
+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<Expression>(new FalseExpression));
+    }
+
+    query.end_group();
+}
+
+template <typename RequestedType>
+RequestedType convert(id value);
+
+template <>
+Timestamp convert<Timestamp>(id value) {
+    return RLMTimestampForNSDate(value);
+}
+
+template <>
+bool convert<bool>(id value) {
+    return [value boolValue];
+}
+
+template <>
+Double convert<Double>(id value) {
+    return [value doubleValue];
+}
+
+template <>
+Float convert<Float>(id value) {
+    return [value floatValue];
+}
+
+template <>
+Int convert<Int>(id value) {
+    return [value longLongValue];
+}
+
+template <>
+String convert<String>(id value) {
+    return RLMStringDataWithNSString(value);
+}
+
+template <typename>
+realm::null value_of_type(realm::null) {
+    return realm::null();
+}
+
+template <typename RequestedType>
+auto value_of_type(id value) {
+    return ::convert<RequestedType>(value);
+}
+
+template <typename RequestedType>
+auto value_of_type(const ColumnReference& column) {
+    return column.resolve<RequestedType>();
+}
+
+
+template <typename... T>
+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<bool>(values)...);
+            break;
+        case RLMPropertyTypeDate:
+            add_numeric_constraint(type, operatorType, value_of_type<realm::Timestamp>(values)...);
+            break;
+        case RLMPropertyTypeDouble:
+            add_numeric_constraint(type, operatorType, value_of_type<Double>(values)...);
+            break;
+        case RLMPropertyTypeFloat:
+            add_numeric_constraint(type, operatorType, value_of_type<Float>(values)...);
+            break;
+        case RLMPropertyTypeInt:
+            add_numeric_constraint(type, operatorType, value_of_type<Int>(values)...);
+            break;
+        case RLMPropertyTypeString:
+            add_string_constraint(operatorType, predicateOptions, value_of_type<String>(values)...);
+            break;
+        case RLMPropertyTypeData:
+            add_binary_constraint(operatorType, values...);
+            break;
+        case RLMPropertyTypeObject:
+        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<typename T>
+bool is_nsnull(T) {
+    return false;
+}
+
+template <typename L, typename R>
+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);
+    }
+}
+
+struct KeyPath {
+    std::vector<RLMProperty *> links;
+    RLMProperty *property;
+    bool containsToManyRelationship;
+};
+
+KeyPath key_path_from_string(RLMSchema *schema, RLMObjectSchema *objectSchema, NSString *keyPath)
+{
+    RLMProperty *property;
+    std::vector<RLMProperty *> 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.array)
+            keyPathContainsToManyRelationship = true;
+
+        if (end != NSNotFound) {
+            RLMPrecondition(property.type == RLMPropertyTypeObject || 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 = schema[property.objectClassName];
+        }
+
+        start = end + 1;
+    } while (end != NSNotFound);
+
+    return {std::move(links), property, keyPathContainsToManyRelationship};
+}
+
+ColumnReference QueryBuilder::column_reference_from_key_path(RLMObjectSchema *objectSchema,
+                                                             NSString *keyPathString, bool isAggregate)
+{
+    auto keyPath = key_path_from_string(m_schema, objectSchema, keyPathString);
+
+    if (isAggregate && !keyPath.containsToManyRelationship) {
+        @throw RLMPredicateException(@"Invalid predicate",
+                                     @"Aggregate operations can only be used on key paths that include an array property");
+    } else if (!isAggregate && keyPath.containsToManyRelationship) {
+        @throw RLMPredicateException(@"Invalid predicate",
+                                     @"Key paths that include an array property must use aggregate operations");
+    }
+
+    return ColumnReference(m_query, m_group, m_schema, keyPath.property, std::move(keyPath.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.array) {
+        RLMPrecondition([RLMObjectBaseObjectSchema(RLMDynamicCast<RLMObjectBase>(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<RLMObjectBase>(value)) {
+        RLMPrecondition(!obj->_row.is_attached() || &column.group() == &obj->_realm.group,
+                        @"Invalid value origin", @"Object must be from the Realm being queried");
+    }
+}
+
+template <typename RequestedType, CollectionOperation::Type OperationType>
+struct ValueOfTypeWithCollectionOperationHelper;
+
+template <>
+struct ValueOfTypeWithCollectionOperationHelper<Int, CollectionOperation::Count> {
+    static auto convert(const CollectionOperation& operation)
+    {
+        assert(operation.type() == CollectionOperation::Count);
+        return operation.link_column().resolve<Link>().count();
+    }
+};
+
+#define VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(OperationType, function) \
+template <typename T> \
+struct ValueOfTypeWithCollectionOperationHelper<T, OperationType> { \
+    static auto convert(const CollectionOperation& operation) \
+    { \
+        REALM_ASSERT(operation.type() == OperationType); \
+        auto targetColumn = operation.link_column().resolve<Link>().template column<T>(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 <typename Requested, CollectionOperation::Type OperationType, typename T>
+auto value_of_type_with_collection_operation(T&& value) {
+    return value_of_type<Requested>(std::forward<T>(value));
+}
+
+template <typename Requested, CollectionOperation::Type OperationType>
+auto value_of_type_with_collection_operation(CollectionOperation operation) {
+    using helper = ValueOfTypeWithCollectionOperationHelper<Requested, OperationType>;
+    return helper::convert(operation);
+}
+
+template <CollectionOperation::Type Operation, typename... T>
+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<Int, Operation>(values)...);
+            break;
+        case RLMPropertyTypeFloat:
+            add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation<Float, Operation>(values)...);
+            break;
+        case RLMPropertyTypeDouble:
+            add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation<Double, Operation>(values)...);
+            break;
+        default:
+            REALM_ASSERT(false && "Only numeric property types should hit this path.");
+    }
+}
+
+template <typename... T>
+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<Int, CollectionOperation::Count>(values)...);
+            break;
+        case CollectionOperation::Minimum:
+            add_collection_operation_constraint<CollectionOperation::Minimum>(collectionOperation.column().type(), operatorType, values...);
+            break;
+        case CollectionOperation::Maximum:
+            add_collection_operation_constraint<CollectionOperation::Maximum>(collectionOperation.column().type(), operatorType, values...);
+            break;
+        case CollectionOperation::Sum:
+            add_collection_operation_constraint<CollectionOperation::Sum>(collectionOperation.column().type(), operatorType, values...);
+            break;
+        case CollectionOperation::Average:
+            add_collection_operation_constraint<CollectionOperation::Average>(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<ColumnReference> 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<LinkList>(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<Expression>(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<Expression>(new TrueExpression));
+    } else if ([predicate isEqual:[NSPredicate predicateWithValue:NO]]) {
+        m_query.and_query(std::unique_ptr<Expression>(new FalseExpression));
+    }
+    else {
+        // invalid predicate type
+        @throw RLMPredicateException(@"Invalid predicate",
+                                     @"Only support compound, comparison, and constant predicates");
+    }
+}
+} // 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;
+}
diff --git a/iOS/Pods/Realm/Realm/RLMRealm+Sync.mm b/iOS/Pods/Realm/Realm/RLMRealm+Sync.mm
new file mode 100644 (file)
index 0000000..2d83f8c
--- /dev/null
@@ -0,0 +1,54 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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+Sync.h"
+
+#import "RLMObjectBase.h"
+#import "RLMObjectSchema.h"
+#import "RLMRealm_Private.hpp"
+#import "RLMResults_Private.hpp"
+#import "RLMSchema.h"
+
+#import "results.hpp"
+#import "sync/partial_sync.hpp"
+#import "shared_realm.hpp"
+
+using namespace realm;
+
+@implementation RLMRealm (Sync)
+
+- (void)subscribeToObjects:(Class)type where:(NSString *)query callback:(RLMPartialSyncFetchCallback)callback {
+    NSString *className = [type className];
+    auto cb = [=](Results results, std::exception_ptr err) {
+        if (err) {
+            try {
+                rethrow_exception(err);
+            }
+            catch (...) {
+                NSError *error = nil;
+                RLMRealmTranslateException(&error);
+                callback(nil, error);
+            }
+            return;
+        }
+        callback([RLMResults resultsWithObjectInfo:_info[className] results:std::move(results)], nil);
+    };
+    partial_sync::register_query(_realm, className.UTF8String, query.UTF8String, std::move(cb));
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMRealm.mm b/iOS/Pods/Realm/Realm/RLMRealm.mm
new file mode 100644 (file)
index 0000000..fa2f728
--- /dev/null
@@ -0,0 +1,881 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMMigration_Private.h"
+#import "RLMObject_Private.h"
+#import "RLMObject_Private.hpp"
+#import "RLMObjectSchema_Private.hpp"
+#import "RLMObjectStore.h"
+#import "RLMObservation.hpp"
+#import "RLMProperty.h"
+#import "RLMProperty_Private.h"
+#import "RLMQueryUtil.hpp"
+#import "RLMRealmConfiguration_Private.hpp"
+#import "RLMRealmUtil.hpp"
+#import "RLMSchema_Private.hpp"
+#import "RLMSyncManager_Private.h"
+#import "RLMSyncUtil_Private.hpp"
+#import "RLMThreadSafeReference_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 <realm/disable_sync_to_disk.hpp>
+#include <realm/util/scope_exit.hpp>
+#include <realm/version.hpp>
+
+#import "sync/sync_session.hpp"
+
+using namespace realm;
+using util::File;
+
+@interface RLMRealmNotificationToken : RLMNotificationToken
+@property (nonatomic, strong) RLMRealm *realm;
+@property (nonatomic, copy) RLMNotificationBlock block;
+@end
+
+@interface RLMRealm ()
+@property (nonatomic, strong) NSHashTable<RLMRealmNotificationToken *> *notificationHandlers;
+- (void)sendNotifications:(RLMNotification)notification;
+@end
+
+void RLMDisableSyncToDisk() {
+    realm::disable_sync_to_disk();
+}
+
+static void RLMAddSkipBackupAttributeToItemAtPath(std::string const& path) {
+    [[NSURL fileURLWithPath:@(path.c_str())] setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
+}
+
+@implementation RLMRealmNotificationToken
+- (void)invalidate {
+    [_realm verifyThread];
+    [_realm.notificationHandlers removeObject:self];
+    _realm = nil;
+    _block = nil;
+}
+
+- (void)suppressNextNotification {
+    // Temporarily replace the block with one which restores the old block
+    // rather than producing a notification.
+
+    // This briefly creates a retain cycle but it's fine because the block will
+    // be synchronously called shortly after this method is called. Unlike with
+    // collection notifications, this does not have to go through the object
+    // store or do fancy things to handle transaction coalescing because it's
+    // called synchronously by the obj-c code and not by the object store.
+    auto notificationBlock = _block;
+    _block = ^(RLMNotification, RLMRealm *) {
+        _block = notificationBlock;
+    };
+}
+
+- (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 invalidate] 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 && key.length != 64) {
+        @throw RLMException(@"Encryption key must be exactly 64 bytes long");
+    }
+
+    return key;
+}
+
+@implementation RLMRealm {
+    NSHashTable<RLMFastEnumerator *> *_collectionEnumerators;
+    bool _sendingNotifications;
+}
+
++ (BOOL)isCoreDebug {
+    return realm::Version::has_feature(realm::feature_Debug);
+}
+
++ (void)initialize {
+    static bool initialized;
+    if (initialized) {
+        return;
+    }
+    initialized = true;
+
+    RLMCheckForUpdates();
+    RLMSendAnalytics();
+}
+
+- (instancetype)initPrivate {
+    self = [super init];
+    return self;
+}
+
+- (BOOL)isEmpty {
+    return realm::ObjectStore::is_empty(self.group);
+}
+
+- (void)verifyThread {
+    try {
+        _realm->verify_thread();
+    }
+    catch (std::exception const& e) {
+        @throw RLMException(e);
+    }
+}
+
+- (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);
+}
+
++ (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];
+}
+
++ (void)asyncOpenWithConfiguration:(RLMRealmConfiguration *)configuration
+                     callbackQueue:(dispatch_queue_t)callbackQueue
+                          callback:(RLMAsyncOpenRealmCallback)callback {
+    RLMRealm *strongReferenceToSyncedRealm = nil;
+    if (configuration.config.sync_config) {
+        NSError *error = nil;
+        strongReferenceToSyncedRealm = [RLMRealm uncachedSchemalessRealmWithConfiguration:configuration error:&error];
+        if (error) {
+            dispatch_async(callbackQueue, ^{
+                callback(nil, error);
+            });
+            return;
+        }
+    }
+    static dispatch_queue_t queue = dispatch_queue_create("io.realm.asyncOpenDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
+    dispatch_async(queue, ^{
+        @autoreleasepool {
+            if (strongReferenceToSyncedRealm) {
+                // Sync behavior: get the raw session, then wait for it to download.
+                if (auto session = sync_session_for_realm(strongReferenceToSyncedRealm)) {
+                    // Wait for the session to download, then open it.
+                    session->wait_for_download_completion([=](std::error_code error_code) {
+                        dispatch_async(callbackQueue, ^{
+                            (void)strongReferenceToSyncedRealm;
+                            NSError *error = nil;
+                            if (error_code == std::error_code{}) {
+                                // Success
+                                @autoreleasepool {
+                                    // Try opening the Realm on the destination queue.
+                                    RLMRealm *localRealm = [RLMRealm realmWithConfiguration:configuration error:&error];
+                                    callback(localRealm, error);
+                                }
+                            } else {
+                                // Failure
+                                callback(nil, make_sync_error(RLMSyncSystemErrorKindSession,
+                                                              @(error_code.message().c_str()),
+                                                              error_code.value(),
+                                                              nil));
+                            }
+                        });
+                    });
+                } else {
+                    dispatch_async(callbackQueue, ^{
+                        callback(nil, make_sync_error(RLMSyncSystemErrorKindSession,
+                                                      @"Cannot asynchronously open synced Realm, because the associated session previously experienced a fatal error",
+                                                      NSNotFound,
+                                                      nil));
+                    });
+                    return;
+                }
+            } else {
+                // Default behavior: just dispatch onto the destination queue and open the Realm.
+                dispatch_async(callbackQueue, ^{
+                    @autoreleasepool {
+                        NSError *error = nil;
+                        RLMRealm *localRealm = [RLMRealm realmWithConfiguration:configuration error:&error];
+                        callback(localRealm, error);
+                    }
+                });
+                return;
+            }
+        }
+    });
+}
+
+// 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(__unsafe_unretained id value) {
+    // +1 __bridge_retained, -1 CFAutorelease
+    return value ? (__bridge id)CFAutorelease((__bridge_retained CFTypeRef)value) : nil;
+}
+
++ (instancetype)realmWithSharedRealm:(SharedRealm)sharedRealm schema:(RLMSchema *)schema {
+    RLMRealm *realm = [[RLMRealm alloc] initPrivate];
+    realm->_realm = sharedRealm;
+    realm->_dynamic = YES;
+    realm->_schema = schema;
+    realm->_info = RLMSchemaInfo(realm);
+    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::BadHistoryError: {
+                NSString *err = @"Realm file's history format is incompatible with the "
+                                 "settings in the configuration object being used to open "
+                                 "the Realm. Note that Realms configured for sync cannot be "
+                                 "opened as non-synced Realms, and vice versa. Otherwise, the "
+                                 "file may be corrupt.";
+                RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileAccess,
+                                                File::AccessError(err.UTF8String, ex.path())), 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);
+    }
+}
+
+REALM_NOINLINE static void translateSharedGroupOpenException(RLMRealmConfiguration *originalConfiguration, NSError **error) {
+    try {
+        throw;
+    }
+    catch (RealmFileException const& ex) {
+        switch (ex.kind()) {
+            case RealmFileException::Kind::IncompatibleSyncedRealm: {
+                RLMRealmConfiguration *configuration = [originalConfiguration copy];
+                configuration.fileURL = [NSURL fileURLWithPath:@(ex.path().data())];
+                configuration.readOnly = YES;
+
+                NSError *intermediateError = RLMMakeError(RLMErrorIncompatibleSyncedFile, ex);
+                NSMutableDictionary *userInfo = [intermediateError.userInfo mutableCopy];
+                userInfo[RLMBackupRealmConfigurationErrorKey] = configuration;
+                NSError *finalError = [NSError errorWithDomain:intermediateError.domain code:intermediateError.code
+                                                      userInfo:userInfo];
+                RLMSetErrorOrThrow(finalError, error);
+                break;
+            }
+            default:
+                RLMRealmTranslateException(error);
+                break;
+        }
+    }
+    catch (...) {
+        RLMRealmTranslateException(error);
+    }
+}
+
+
++ (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error {
+    bool dynamic = configuration.dynamic;
+    bool cache = configuration.cache;
+    bool readOnly = configuration.readOnly;
+
+    {
+        Realm::Config& config = configuration.config;
+
+        // try to reuse existing realm first
+        if (cache || dynamic) {
+            if (RLMRealm *realm = RLMGetThreadLocalCachedRealmForPath(config.path)) {
+                auto const& old_config = realm->_realm->config();
+                if (old_config.immutable() != config.immutable()
+                    || old_config.read_only_alternative() != config.read_only_alternative()) {
+                    @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 alloc] initPrivate];
+    realm->_dynamic = dynamic;
+
+    // protects the realm cache and accessors cache
+    static std::mutex& initLock = *new std::mutex();
+    std::lock_guard<std::mutex> lock(initLock);
+
+    try {
+        realm->_realm = Realm::get_shared_realm(config);
+    }
+    catch (...) {
+        translateSharedGroupOpenException(configuration, error);
+        return nil;
+    }
+
+    // if we have a cached realm on another thread we can skip a few steps and
+    // just grab its schema
+    @autoreleasepool {
+        // ensure that cachedRealm doesn't end up in this thread's autorelease pool
+        if (auto cachedRealm = RLMGetAnyCachedRealmForPath(config.path)) {
+            realm->_realm->set_schema_subset(cachedRealm->_realm->schema());
+            realm->_schema = cachedRealm.schema;
+            realm->_info = cachedRealm->_info.clone(cachedRealm->_realm->schema(), realm);
+        }
+    }
+
+    if (realm->_schema) { }
+    else if (dynamic) {
+        realm->_schema = [RLMSchema dynamicSchemaFromObjectStoreSchema:realm->_realm->schema()];
+        realm->_info = RLMSchemaInfo(realm);
+    }
+    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;
+        }
+
+        realm->_schema = schema;
+        realm->_info = RLMSchemaInfo(realm);
+        RLMRealmCreateAccessors(realm.schema);
+
+        if (!readOnly) {
+            // initializing the schema started a read transaction, so end it
+            [realm invalidate];
+        }
+    }
+
+    if (cache) {
+        RLMCacheRealm(config.path, realm);
+    }
+
+    if (!readOnly) {
+        realm->_realm->m_binding_context = RLMCreateBindingContext(realm);
+        realm->_realm->m_binding_context->realm = realm->_realm;
+
+        RLMAddSkipBackupAttributeToItemAtPath(config.path + ".management");
+        RLMAddSkipBackupAttributeToItemAtPath(config.path + ".lock");
+        RLMAddSkipBackupAttributeToItemAtPath(config.path + ".note");
+    }
+
+    return RLMAutorelease(realm);
+}
+
++ (instancetype)uncachedSchemalessRealmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error {
+    RLMRealm *realm = [[RLMRealm alloc] initPrivate];
+    try {
+        realm->_realm = Realm::get_shared_realm(configuration.config);
+    }
+    catch (...) {
+        translateSharedGroupOpenException(configuration, error);
+        return nil;
+    }
+    return realm;
+}
+
++ (void)resetRealmState {
+    RLMClearRealmCache();
+    realm::_impl::RealmCoordinator::clear_cache();
+    [RLMRealmConfiguration resetRealmConfigurationState];
+}
+
+- (void)verifyNotificationsAreSupported:(bool)isCollection {
+    [self verifyThread];
+    if (_realm->config().immutable()) {
+        @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.");
+    }
+    if (isCollection && _realm->is_in_transaction()) {
+        @throw RLMException(@"Cannot register notification blocks from within write transactions.");
+    }
+}
+
+- (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block {
+    if (!block) {
+        @throw RLMException(@"The notification block should not be nil");
+    }
+    [self verifyNotificationsAreSupported:false];
+
+    _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().immutable(), @"Read-only realms do not have notifications");
+    if (_sendingNotifications) {
+        return;
+    }
+    NSUInteger count = _notificationHandlers.count;
+    if (count == 0) {
+        return;
+    }
+
+    _sendingNotifications = true;
+    auto cleanup = realm::util::make_scope_exit([&]() noexcept {
+        _sendingNotifications = false;
+    });
+
+    // call this realm's 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;
+    }
+}
+
+- (BOOL)commitWriteTransactionWithoutNotifying:(NSArray<RLMNotificationToken *> *)tokens error:(NSError **)error {
+    for (RLMNotificationToken *token in tokens) {
+        if (token.realm != self) {
+            @throw RLMException(@"Incorrect Realm: only notifications for the Realm being modified can be skipped.");
+        }
+        [token suppressNextNotification];
+    }
+
+    try {
+        _realm->commit_transaction();
+        return YES;
+    }
+    catch (...) {
+        RLMRealmTranslateException(error);
+        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();
+    }
+}
+
+- (nullable id)resolveThreadSafeReference:(RLMThreadSafeReference *)reference {
+    return [reference resolveReferenceInRealm:self];
+}
+
+/**
+ 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<NSFastEnumeration>)objects {
+    for (RLMObject *obj in objects) {
+        if (![obj isKindOfClass:RLMObjectBase.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)addOrUpdateObjects:(id<NSFastEnumeration>)objects {
+    for (RLMObject *obj in objects) {
+        if (![obj isKindOfClass:RLMObjectBase.class]) {
+            @throw RLMException(@"Cannot add or update objects of type %@ with addOrUpdateObjects:. Only RLMObjects are"
+                                " supported.",
+                                NSStringFromClass(obj.class));
+        }
+        [self addOrUpdateObject:obj];
+    }
+}
+
+- (void)deleteObject:(RLMObject *)object {
+    RLMDeleteObjectFromRealm(object, self);
+}
+
+- (void)deleteObjects:(id<NSFastEnumeration>)objects {
+    id idObjects = objects;
+    if ([idObjects respondsToSelector:@selector(realm)]
+        && [idObjects respondsToSelector:@selector(deleteObjectsFromRealm)]) {
+        if (self != (RLMRealm *)[idObjects realm]) {
+            @throw RLMException(@"Can only delete objects from the Realm they belong to.");
+        }
+        [idObjects deleteObjectsFromRealm];
+        return;
+    }
+    if (auto array = RLMDynamicCast<RLMArray>(objects)) {
+        if (array.type != RLMPropertyTypeObject) {
+            @throw RLMException(@"Cannot delete objects from RLMArray<%@>: only RLMObjects can be deleted.",
+                                RLMTypeToString(array.type));
+        }
+    }
+    for (RLMObject *obj in objects) {
+        if (![obj isKindOfClass:RLMObjectBase.class]) {
+            @throw RLMException(@"Cannot delete objects of type %@ with deleteObjects:. Only RLMObjects can be deleted.",
+                                NSStringFromClass(obj.class));
+        }
+        RLMDeleteObjectFromRealm(obj, self);
+    }
+}
+
+- (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 {
+    RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
+    try {
+        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 (...) {
+        translateSharedGroupOpenException(config, error);
+        return RLMNotVersioned;
+    }
+}
+
++ (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<const char *>(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/iOS/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm b/iOS/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm
new file mode 100644 (file)
index 0000000..8c80fc4
--- /dev/null
@@ -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 "RLMRealmConfiguration+Sync.h"
+
+#import "RLMRealmConfiguration_Private.hpp"
+#import "RLMSyncConfiguration_Private.hpp"
+#import "RLMSyncUser_Private.hpp"
+#import "RLMSyncManager_Private.h"
+#import "RLMSyncUtil_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "sync/sync_config.hpp"
+#import "sync/sync_manager.hpp"
+
+@implementation RLMRealmConfiguration (Sync)
+
+#pragma mark - API
+
+- (void)setSyncConfiguration:(RLMSyncConfiguration *)syncConfiguration {
+    if (self.config.should_compact_on_launch_function) {
+        @throw RLMException(@"Cannot set `syncConfiguration` when `shouldCompactOnLaunch` is set.");
+    }
+    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.");
+    if (syncConfiguration.customFileURL) {
+        self.config.path = syncConfiguration.customFileURL.path.UTF8String;
+    } else {
+        self.config.path = SyncManager::shared().path_for_realm(*[user _syncUser],
+                                                                [realmURL.absoluteString UTF8String]);
+    }
+    self.config.in_memory = false;
+    self.config.sync_config = std::make_shared<realm::SyncConfig>([syncConfiguration rawConfiguration]);
+    self.config.schema_mode = realm::SchemaMode::Additive;
+    if (!self.config.encryption_key.empty()) {
+        auto& sync_encryption_key = self.config.sync_config->realm_encryption_key;
+        sync_encryption_key = std::array<char, 64>();
+        std::copy_n(self.config.encryption_key.begin(), 64, sync_encryption_key->begin());
+    }
+}
+
+- (RLMSyncConfiguration *)syncConfiguration {
+    if (!self.config.sync_config) {
+        return nil;
+    }
+    realm::SyncConfig& sync_config = *self.config.sync_config;
+    return [[RLMSyncConfiguration alloc] initWithRawConfig:sync_config];
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMRealmConfiguration.mm b/iOS/Pods/Realm/Realm/RLMRealmConfiguration.mm
new file mode 100644 (file)
index 0000000..f0cf4a0
--- /dev/null
@@ -0,0 +1,305 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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"
+#import "sync/sync_config.hpp"
+
+static NSString *const c_RLMRealmConfigurationProperties[] = {
+    @"fileURL",
+    @"inMemoryIdentifier",
+    @"encryptionKey",
+    @"readOnly",
+    @"schemaVersion",
+    @"migrationBlock",
+    @"deleteRealmIfMigrationNeeded",
+    @"shouldCompactOnLaunch",
+    @"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 {
+    RLMRealmConfiguration *configuration;
+    @synchronized(c_defaultRealmFileName) {
+        if (!s_defaultConfiguration) {
+            s_defaultConfiguration = [[RLMRealmConfiguration alloc] init];
+        }
+        configuration = s_defaultConfiguration;
+    }
+    return configuration;
+}
+
++ (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;
+        self.cache = YES;
+
+        // We have our own caching of RLMRealm instances, so the ObjectStore
+        // cache is at best pointless, and may result in broken behavior when
+        // a realm::Realm instance outlives the RLMRealm (due to collection
+        // notifiers being in the middle of running when the RLMRealm is
+        // dealloced) and then reused for a new RLMRealm
+        _config.cache = false;
+    }
+
+    return self;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+    RLMRealmConfiguration *configuration = [[[self class] allocWithZone:zone] init];
+    configuration->_config = _config;
+    configuration->_cache = _cache;
+    configuration->_dynamic = _dynamic;
+    configuration->_migrationBlock = _migrationBlock;
+    configuration->_shouldCompactOnLaunch = _shouldCompactOnLaunch;
+    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<const char *>(key.bytes);
+        _config.encryption_key.assign(bytes, bytes + key.length);
+        if (_config.sync_config) {
+            auto& sync_encryption_key = self.config.sync_config->realm_encryption_key;
+            sync_encryption_key = std::array<char, 64>();
+            std::copy_n(_config.encryption_key.begin(), 64, sync_encryption_key->begin());
+        }
+    }
+    else {
+        _config.encryption_key.clear();
+        if (_config.sync_config)
+            _config.sync_config->realm_encryption_key = realm::util::none;
+    }
+}
+
+- (BOOL)readOnly {
+    return _config.immutable();
+}
+
+- (void)setReadOnly:(BOOL)readOnly {
+    if (readOnly) {
+        if (self.deleteRealmIfMigrationNeeded) {
+            @throw RLMException(@"Cannot set `readOnly` when `deleteRealmIfMigrationNeeded` is set.");
+        } else if (self.shouldCompactOnLaunch) {
+            @throw RLMException(@"Cannot set `readOnly` when `shouldCompactOnLaunch` is set.");
+        }
+        _config.schema_mode = realm::SchemaMode::Immutable;
+    }
+    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;
+    self.cache = !dynamic;
+}
+
+- (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;
+}
+
+- (NSString *)pathOnDisk {
+    return @(_config.path.c_str());
+}
+
+- (void)setShouldCompactOnLaunch:(RLMShouldCompactOnLaunchBlock)shouldCompactOnLaunch {
+    if (shouldCompactOnLaunch) {
+        if (self.readOnly) {
+            @throw RLMException(@"Cannot set `shouldCompactOnLaunch` when `readOnly` is set.");
+        } else if (_config.sync_config) {
+            @throw RLMException(@"Cannot set `shouldCompactOnLaunch` when `syncConfiguration` is set.");
+        }
+        _config.should_compact_on_launch_function = [=](size_t totalBytes, size_t usedBytes) {
+            return shouldCompactOnLaunch(totalBytes, usedBytes);
+        };
+    }
+    else {
+        _config.should_compact_on_launch_function = nullptr;
+    }
+    _shouldCompactOnLaunch = shouldCompactOnLaunch;
+}
+
+- (void)setCustomSchemaWithoutCopying:(RLMSchema *)schema {
+    _customSchema = schema;
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMRealmUtil.mm b/iOS/Pods/Realm/Realm/RLMRealmUtil.mm
new file mode 100644 (file)
index 0000000..10d6ff3
--- /dev/null
@@ -0,0 +1,147 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMConstants.h>
+#import <Realm/RLMSchema.h>
+
+#import "binding_context.hpp"
+
+#import <map>
+#import <mutex>
+#import <sys/event.h>
+#import <sys/stat.h>
+#import <sys/time.h>
+#import <unistd.h>
+
+// Global realm state
+static std::mutex& s_realmCacheMutex = *new std::mutex();
+static std::map<std::string, NSMapTable *>& s_realmsPerPath = *new std::map<std::string, NSMapTable *>();
+
+void RLMCacheRealm(std::string const& path, __unsafe_unretained RLMRealm *const realm) {
+    std::lock_guard<std::mutex> lock(s_realmCacheMutex);
+    NSMapTable *realms = s_realmsPerPath[path];
+    if (!realms) {
+        s_realmsPerPath[path] = realms = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsOpaquePersonality|NSPointerFunctionsOpaqueMemory
+                                                               valueOptions:NSPointerFunctionsWeakMemory];
+    }
+    [realms setObject:realm forKey:(__bridge id)pthread_self()];
+}
+
+RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path) {
+    std::lock_guard<std::mutex> lock(s_realmCacheMutex);
+    return [s_realmsPerPath[path] objectEnumerator].nextObject;
+}
+
+RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path) {
+    std::lock_guard<std::mutex> lock(s_realmCacheMutex);
+    return [s_realmsPerPath[path] objectForKey:(__bridge id)pthread_self()];
+}
+
+void RLMClearRealmCache() {
+    std::lock_guard<std::mutex> lock(s_realmCacheMutex);
+    s_realmsPerPath.clear();
+}
+
+bool RLMIsInRunLoop() {
+    // 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;
+}
+
+namespace {
+class RLMNotificationHelper : public realm::BindingContext {
+public:
+    RLMNotificationHelper(RLMRealm *realm) : _realm(realm) { }
+
+    bool can_deliver_notifications() const noexcept override {
+        return RLMIsInRunLoop();
+    }
+
+    void changes_available() override {
+        @autoreleasepool {
+            auto realm = _realm;
+            if (realm && !realm.autorefresh) {
+                [realm sendNotifications:RLMRealmRefreshRequiredNotification];
+            }
+        }
+    }
+
+    std::vector<ObserverState> get_observed_rows() override {
+        @autoreleasepool {
+            if (auto realm = _realm) {
+                [realm detachAllEnumerators];
+                return RLMGetObservedRows(realm->_info);
+            }
+            return {};
+        }
+    }
+
+    void will_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated) override {
+        @autoreleasepool {
+            RLMWillChange(observed, invalidated);
+        }
+    }
+
+    void did_change(std::vector<ObserverState> const& observed, std::vector<void*> const& invalidated, bool version_changed) override {
+        try {
+            @autoreleasepool {
+                RLMDidChange(observed, invalidated);
+                if (version_changed) {
+                    [_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<realm::BindingContext> RLMCreateBindingContext(__unsafe_unretained RLMRealm *const realm) {
+    return std::unique_ptr<realm::BindingContext>(new RLMNotificationHelper(realm));
+}
diff --git a/iOS/Pods/Realm/Realm/RLMResults.mm b/iOS/Pods/Realm/Realm/RLMResults.mm
new file mode 100644 (file)
index 0000000..370acd3
--- /dev/null
@@ -0,0 +1,506 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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.hpp"
+
+#import "RLMAccessor.hpp"
+#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 "RLMThreadSafeReference_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "results.hpp"
+#import "shared_realm.hpp"
+
+#import <objc/message.h>
+#import <realm/table_view.hpp>
+
+using namespace realm;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wincomplete-implementation"
+@implementation RLMNotificationToken
+@end
+#pragma clang diagnostic pop
+
+@interface RLMResults () <RLMThreadConfined_Private>
+@end
+
+//
+// RLMResults implementation
+//
+@implementation RLMResults {
+    RLMRealm *_realm;
+    RLMClassInfo *_info;
+}
+
+- (instancetype)initPrivate {
+    self = [super init];
+    return self;
+}
+
+- (instancetype)initWithResults:(Results)results {
+    if (self = [super init]) {
+        _results = std::move(results);
+    }
+    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.");
+    }
+}
+
+void RLMThrowResultsError(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 of 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 %s%s property '%s'.",
+                            aggregateMethod,
+                            string_for_property_type(e.property_type),
+                            is_nullable(e.property_type) ? "?" : "",
+                            e.column_name.data());
+    }
+    catch (std::exception const& e) {
+        @throw RLMException(e);
+    }
+}
+
++ (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 translateRLMResultsErrors([&] { return !_results.is_valid(); });
+}
+
+- (NSUInteger)count {
+    return translateRLMResultsErrors([&] { return _results.size(); });
+}
+
+- (RLMPropertyType)type {
+    return translateRLMResultsErrors([&] {
+        return static_cast<RLMPropertyType>(_results.get_type() & ~realm::PropertyType::Nullable);
+    });
+}
+
+- (BOOL)isOptional {
+    return translateRLMResultsErrors([&] {
+        return is_nullable(_results.get_type());
+    });
+}
+
+- (NSString *)objectClassName {
+    return translateRLMResultsErrors([&] {
+        return RLMStringDataToNSString(_results.get_object_type());
+    });
+}
+
+- (RLMClassInfo *)objectInfo {
+    return _info;
+}
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
+                                  objects:(__unused __unsafe_unretained id [])buffer
+                                    count:(NSUInteger)len {
+    if (!_info) {
+        return 0;
+    }
+    return RLMFastEnumerate(state, len, self);
+}
+
+- (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;
+    }
+
+    return translateRLMResultsErrors([&] {
+        if (_results.get_type() != realm::PropertyType::Object) {
+            @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
+        }
+        return RLMConvertNotFound(_results.index_of(RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group)));
+    });
+}
+
+- (id)objectAtIndex:(NSUInteger)index {
+    RLMAccessorContext ctx(_realm, *_info);
+    return translateRLMResultsErrors([&] {
+        return _results.get(ctx, index);
+    });
+}
+
+- (id)firstObject {
+    if (!_info) {
+        return nil;
+    }
+    RLMAccessorContext ctx(_realm, *_info);
+    return translateRLMResultsErrors([&] {
+        return _results.first(ctx);
+    });
+}
+
+- (id)lastObject {
+    if (!_info) {
+        return nil;
+    }
+    RLMAccessorContext ctx(_realm, *_info);
+    return translateRLMResultsErrors([&] {
+        return _results.last(ctx);
+    });
+}
+
+- (NSUInteger)indexOfObject:(RLMObject *)object {
+    if (!_info || !object || (!object->_realm && !object.invalidated)) {
+        return NSNotFound;
+    }
+    RLMAccessorContext ctx(_realm, *_info);
+    return translateRLMResultsErrors([&] {
+        return RLMConvertNotFound(_results.index_of(ctx, object));
+    });
+}
+
+- (id)valueForKeyPath:(NSString *)keyPath {
+    if ([keyPath characterAtIndex:0] != '@') {
+        return [super valueForKeyPath:keyPath];
+    }
+    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]);
+    if (![self respondsToSelector:opSelector]) {
+        @throw RLMException(@"Unsupported KVC collection operator found in key path '%@'", keyPath);
+    }
+    if (separatorIndex >= keyPathLength - 1) {
+        @throw RLMException(@"Missing key path for KVC collection operator %@ in key path '%@'",
+                            operatorName, keyPath);
+    }
+    NSString *operatorKeyPath = [keyPath substringFromIndex:separatorIndex + 1];
+    return ((id(*)(id, SEL, id))objc_msgSend)(self, opSelector, operatorKeyPath);
+}
+
+- (id)valueForKey:(NSString *)key {
+    return translateRLMResultsErrors([&] {
+        return RLMCollectionValueForKey(_results, key, _realm, *_info);
+    });
+}
+
+- (void)setValue:(id)value forKey:(NSString *)key {
+    translateRLMResultsErrors([&] { RLMResultsValidateInWriteTransaction(self); });
+    RLMCollectionSetValueForKey(self, key, value);
+}
+
+- (NSNumber *)_aggregateForKeyPath:(NSString *)keyPath
+                            method:(util::Optional<Mixed> (Results::*)(size_t))method
+                        methodName:(NSString *)methodName returnNilForEmpty:(BOOL)returnNilForEmpty {
+    assertKeyPathIsNotNested(keyPath);
+    return [self aggregate:keyPath method:method methodName:methodName returnNilForEmpty:returnNilForEmpty];
+}
+
+- (NSNumber *)_minForKeyPath:(NSString *)keyPath {
+    return [self _aggregateForKeyPath:keyPath method:&Results::min methodName:@"@min" returnNilForEmpty:YES];
+}
+
+- (NSNumber *)_maxForKeyPath:(NSString *)keyPath {
+    return [self _aggregateForKeyPath:keyPath method:&Results::max methodName:@"@max" returnNilForEmpty:YES];
+}
+
+- (NSNumber *)_sumForKeyPath:(NSString *)keyPath {
+    return [self _aggregateForKeyPath:keyPath method:&Results::sum methodName:@"@sum" returnNilForEmpty:NO];
+}
+
+- (NSNumber *)_avgForKeyPath:(NSString *)keyPath {
+    assertKeyPathIsNotNested(keyPath);
+    return [self averageOfProperty:keyPath];
+}
+
+- (NSArray *)_unionOfObjectsForKeyPath:(NSString *)keyPath {
+    assertKeyPathIsNotNested(keyPath);
+    return translateRLMResultsErrors([&] {
+        return RLMCollectionValueForKey(_results, keyPath, _realm, *_info);
+    });
+}
+
+- (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 translateRLMResultsErrors([&] {
+        NSMutableArray *flatArray = [NSMutableArray new];
+        for (id<NSFastEnumeration> array in RLMCollectionValueForKey(_results, keyPath, _realm, *_info)) {
+            for (id value in array) {
+                [flatArray addObject:value];
+            }
+        }
+        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 translateRLMResultsErrors([&] {
+        if (_results.get_mode() == Results::Mode::Empty) {
+            return self;
+        }
+        if (_results.get_type() != realm::PropertyType::Object) {
+            @throw RLMException(@"Querying is currently only implemented for arrays of Realm Objects");
+        }
+        auto query = RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group);
+        return [RLMResults resultsWithObjectInfo:*_info results:_results.filter(std::move(query))];
+    });
+}
+
+- (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath ascending:(BOOL)ascending {
+    return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:keyPath ascending:ascending]]];
+}
+
+- (RLMResults *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties {
+    if (properties.count == 0) {
+        return self;
+    }
+    return translateRLMResultsErrors([&] {
+        if (_results.get_mode() == Results::Mode::Empty) {
+            return self;
+        }
+        return [RLMResults resultsWithObjectInfo:*_info
+                                         results:_results.sort(RLMSortDescriptorsToKeypathArray(properties))];
+    });
+}
+
+- (RLMResults *)distinctResultsUsingKeyPaths:(NSArray<NSString *> *)keyPaths {
+    for (NSString *keyPath in keyPaths) {
+        if ([keyPath rangeOfString:@"@"].location != NSNotFound) {
+            @throw RLMException(@"Cannot distinct on keypath '%@': KVC collection operators are not supported.", keyPath);
+        }
+    }
+    
+    return translateRLMResultsErrors([&] {
+        if (_results.get_mode() == Results::Mode::Empty) {
+            return self;
+        }
+        
+        std::vector<std::string> keyPathsVector;
+        for (NSString *keyPath in keyPaths) {
+            keyPathsVector.push_back(keyPath.UTF8String);
+        }
+        
+        return [RLMResults resultsWithObjectInfo:*_info results:_results.distinct(keyPathsVector)];
+    });
+}
+
+- (id)objectAtIndexedSubscript:(NSUInteger)index {
+    return [self objectAtIndex:index];
+}
+
+- (id)aggregate:(NSString *)property method:(util::Optional<Mixed> (Results::*)(size_t))method
+     methodName:(NSString *)methodName returnNilForEmpty:(BOOL)returnNilForEmpty {
+    if (_results.get_mode() == Results::Mode::Empty) {
+        return returnNilForEmpty ? nil : @0;
+    }
+    size_t column = 0;
+    if (self.type == RLMPropertyTypeObject || ![property isEqualToString:@"self"]) {
+        column = _info->tableColumn(property);
+    }
+
+    auto value = translateRLMResultsErrors([&] { return (_results.*method)(column); }, methodName);
+    return value ? RLMMixedToObjc(*value) : nil;
+}
+
+- (id)minOfProperty:(NSString *)property {
+    return [self aggregate:property method:&Results::min
+                methodName:@"minOfProperty" returnNilForEmpty:YES];
+}
+
+- (id)maxOfProperty:(NSString *)property {
+    return [self aggregate:property method:&Results::max
+                methodName:@"maxOfProperty" returnNilForEmpty:YES];
+}
+
+- (id)sumOfProperty:(NSString *)property {
+    return [self aggregate:property method:&Results::sum
+                methodName:@"sumOfProperty" returnNilForEmpty:NO];
+}
+
+- (id)averageOfProperty:(NSString *)property {
+    if (_results.get_mode() == Results::Mode::Empty) {
+        return nil;
+    }
+    size_t column = 0;
+    if (self.type == RLMPropertyTypeObject || ![property isEqualToString:@"self"]) {
+        column = _info->tableColumn(property);
+    }
+    auto value = translateRLMResultsErrors([&] { return _results.average(column); }, @"averageOfProperty");
+    return value ? @(*value) : nil;
+}
+
+- (void)deleteObjectsFromRealm {
+    if (self.type != RLMPropertyTypeObject) {
+        @throw RLMException(@"Cannot delete objects from RLMResults<%@>: only RLMObjects can be deleted.",
+                            RLMTypeToString(self.type));
+    }
+    return translateRLMResultsErrors([&] {
+        if (_results.get_mode() == Results::Mode::Table) {
+            RLMResultsValidateInWriteTransaction(self);
+            RLMClearTable(*_info);
+        }
+        else {
+            RLMTrackDeletions(_realm, [&] { _results.clear(); });
+        }
+    });
+}
+
+- (NSString *)description {
+    return RLMDescriptionWithMaxDepth(@"RLMResults", self, RLMDescriptionMaxDepth);
+}
+
+- (realm::TableView)tableView {
+    return translateRLMResultsErrors([&] { return _results.get_tableview(); });
+}
+
+- (RLMFastEnumerator *)fastEnumerator {
+    return translateRLMResultsErrors([&] {
+        return [[RLMFastEnumerator alloc] initWithResults:_results collection:self
+                                                    realm:_realm classInfo:*_info];
+    });
+}
+
+// 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:true];
+    return RLMAddNotificationBlock(self, _results, block, true);
+}
+#pragma clang diagnostic pop
+
+- (BOOL)isAttached
+{
+    return !!_realm;
+}
+
+#pragma mark - Thread Confined Protocol Conformance
+
+- (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
+    return std::make_unique<realm::ThreadSafeReference<Results>>(_realm->_realm->obtain_thread_safe_reference(_results));
+}
+
+- (id)objectiveCMetadata {
+    return nil;
+}
+
++ (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
+                                     metadata:(__unused id)metadata
+                                        realm:(RLMRealm *)realm {
+    REALM_ASSERT_DEBUG(dynamic_cast<realm::ThreadSafeReference<Results> *>(reference.get()));
+    auto results_reference = static_cast<realm::ThreadSafeReference<Results> *>(reference.get());
+
+    Results results = realm->_realm->resolve_thread_safe_reference(std::move(*results_reference));
+
+    return [RLMResults resultsWithObjectInfo:realm->_info[RLMStringDataToNSString(results.get_object_type())]
+                                     results:std::move(results)];
+}
+
+@end
+
+@implementation RLMLinkingObjects
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMSchema.mm b/iOS/Pods/Realm/Realm/RLMSchema.mm
new file mode 100644 (file)
index 0000000..4d4c5eb
--- /dev/null
@@ -0,0 +1,361 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMObjectBase_Private.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 <realm/group.hpp>
+
+#import <objc/runtime.h>
+#include <mutex>
+
+using namespace realm;
+
+const uint64_t RLMNotVersioned = realm::ObjectStore::NotVersioned;
+
+// RLMSchema private properties
+@interface RLMSchema ()
+@property (nonatomic, readwrite) NSMutableDictionary *objectSchemaByName;
+@end
+
+// Private RLMSchema subclass that skips class registration on lookup
+@interface RLMPrivateSchema : RLMSchema
+@end
+@implementation RLMPrivateSchema
+- (RLMObjectSchema *)schemaForClassName:(NSString *)className {
+    return self.objectSchemaByName[className];
+}
+
+- (RLMObjectSchema *)objectForKeyedSubscript:(__unsafe_unretained NSString *const)className {
+    return [self schemaForClassName:className];
+}
+@end
+
+static RLMSchema *s_sharedSchema = [[RLMSchema alloc] init];
+static NSMutableDictionary *s_localNameToClass = [[NSMutableDictionary alloc] init];
+static RLMSchema *s_privateSharedSchema = [[RLMPrivateSchema 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_privateSharedSchema[[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_privateSharedSchema.objectSchemaByName[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)) {
+            continue;
+        }
+
+        NSString *className = NSStringFromClass(cls);
+        if ([className hasPrefix:@"RLM:"] || [className hasPrefix:@"NSKVONotifying"]) {
+            continue;
+        }
+
+        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) {
+                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);
+        RLMObjectSchema *objectSchema = RLMRegisterClass(cls);
+        [cls initializeLinkedObjectSchemas];
+        return objectSchema;
+    }
+}
+
++ (instancetype)partialSharedSchema {
+    return s_sharedSchema;
+}
+
++ (instancetype)partialPrivateSharedSchema {
+    return s_privateSharedSchema;
+}
+
+// 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<realm::ObjectSchema> 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/iOS/Pods/Realm/Realm/RLMSwiftSupport.m b/iOS/Pods/Realm/Realm/RLMSwiftSupport.m
new file mode 100644 (file)
index 0000000..e16c79e
--- /dev/null
@@ -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/iOS/Pods/Realm/Realm/RLMSyncConfiguration.mm b/iOS/Pods/Realm/Realm/RLMSyncConfiguration.mm
new file mode 100644 (file)
index 0000000..6beabcb
--- /dev/null
@@ -0,0 +1,199 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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.h"
+#import "RLMSyncSession_Private.hpp"
+#import "RLMSyncSessionRefreshHandle.hpp"
+#import "RLMSyncUser_Private.hpp"
+#import "RLMSyncUtil_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "sync/sync_manager.hpp"
+#import "sync/sync_config.hpp"
+
+#import <realm/sync/protocol.hpp>
+
+using namespace realm;
+
+namespace {
+using ProtocolError = realm::sync::ProtocolError;
+
+RLMSyncSystemErrorKind errorKindForSyncError(SyncError error) {
+    if (error.is_client_reset_requested()) {
+        return RLMSyncSystemErrorKindClientReset;
+    } else if (error.error_code == ProtocolError::permission_denied) {
+        return RLMSyncSystemErrorKindPermissionDenied;
+    } else if (error.error_code == ProtocolError::bad_authentication) {
+        return RLMSyncSystemErrorKindUser;
+    } else if (error.is_session_level_protocol_error()) {
+        return RLMSyncSystemErrorKindSession;
+    } else if (error.is_connection_level_protocol_error()) {
+        return RLMSyncSystemErrorKindConnection;
+    } else if (error.is_client_error()) {
+        return RLMSyncSystemErrorKindClient;
+    } else {
+        return RLMSyncSystemErrorKindUnknown;
+    }
+}
+}
+
+static BOOL isValidRealmURL(NSURL *url) {
+    NSString *scheme = [url scheme];
+    if (![scheme isEqualToString:@"realm"] && ![scheme isEqualToString:@"realms"]) {
+        return NO;
+    }
+    return YES;
+}
+
+@interface RLMSyncConfiguration () {
+    std::unique_ptr<realm::SyncConfig> _config;
+}
+
+- (instancetype)initWithUser:(RLMSyncUser *)user
+                    realmURL:(NSURL *)url
+               customFileURL:(nullable NSURL *)customFileURL
+                   isPartial:(BOOL)isPartial
+                  stopPolicy:(RLMSyncStopPolicy)stopPolicy
+                errorHandler:(std::function<realm::SyncSessionErrorHandler>)errorHandler;
+@end
+
+@implementation RLMSyncConfiguration
+
+@dynamic stopPolicy;
+
+- (instancetype)initWithRawConfig:(realm::SyncConfig)config {
+    if (self = [super init]) {
+        _config = std::make_unique<realm::SyncConfig>(config);
+    }
+    return self;
+}
+
+- (BOOL)isEqual:(id)object {
+    if (![object isKindOfClass:[RLMSyncConfiguration class]]) {
+        return NO;
+    }
+    RLMSyncConfiguration *that = (RLMSyncConfiguration *)object;
+    return [self.realmURL isEqual:that.realmURL]
+        && [self.user isEqual:that.user]
+        && self.stopPolicy == that.stopPolicy;
+}
+
+- (void)setEnableSSLValidation:(BOOL)enableSSLValidation {
+    _config->client_validate_ssl = (bool)enableSSLValidation;
+}
+
+- (BOOL)enableSSLValidation {
+    return (BOOL)_config->client_validate_ssl;
+}
+
+- (void)setIsPartial:(BOOL)isPartial {
+    _config->is_partial = (bool)isPartial;
+}
+
+- (BOOL)isPartial {
+    return (BOOL)_config->is_partial;
+}
+
+- (realm::SyncConfig)rawConfiguration {
+    return *_config;
+}
+
+- (RLMSyncUser *)user {
+    return [[RLMSyncUser alloc] initWithSyncUser:_config->user];
+}
+
+- (RLMSyncStopPolicy)stopPolicy {
+    return translateStopPolicy(_config->stop_policy);
+}
+
+- (void)setStopPolicy:(RLMSyncStopPolicy)stopPolicy {
+    _config->stop_policy = translateStopPolicy(stopPolicy);
+}
+
+- (NSURL *)realmURL {
+    NSString *rawStringURL = @(_config->realm_url().c_str());
+    return [NSURL URLWithString:rawStringURL];
+}
+
+- (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url {
+    return [self initWithUser:user
+                     realmURL:url
+                customFileURL:nil
+                    isPartial:NO
+                   stopPolicy:RLMSyncStopPolicyAfterChangesUploaded
+                 errorHandler:nullptr];
+}
+
+- (instancetype)initWithUser:(RLMSyncUser *)user
+                    realmURL:(NSURL *)url
+               customFileURL:(nullable NSURL *)customFileURL
+                   isPartial:(BOOL)isPartial
+                  stopPolicy:(RLMSyncStopPolicy)stopPolicy
+                errorHandler:(std::function<realm::SyncSessionErrorHandler>)errorHandler {
+    if (self = [super init]) {
+        if (!isValidRealmURL(url)) {
+            @throw RLMException(@"The provided URL (%@) was not a valid Realm URL.", [url absoluteString]);
+        }
+        auto bindHandler = [=](auto&,
+                               const SyncConfig& config,
+                               const std::shared_ptr<SyncSession>& session) {
+            const std::shared_ptr<SyncUser>& user = config.user;
+            NSURL *realmURL = [NSURL URLWithString:@(config.realm_url().c_str())];
+            NSString *path = [realmURL path];
+            REALM_ASSERT(realmURL && path);
+            RLMSyncSessionRefreshHandle *handle = [[RLMSyncSessionRefreshHandle alloc] initWithRealmURL:realmURL
+                                                                                                   user:user
+                                                                                                session:std::move(session)
+                                                                                        completionBlock:[RLMSyncManager sharedManager].sessionCompletionNotifier];
+            context_for(user).register_refresh_handle([path UTF8String], handle);
+        };
+        if (!errorHandler) {
+            errorHandler = [=](std::shared_ptr<SyncSession> errored_session,
+                               SyncError error) {
+                RLMSyncSession *session = [[RLMSyncSession alloc] initWithSyncSession:errored_session];
+                NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:error.user_info.size()];
+                for (auto& pair : error.user_info) {
+                    userInfo[@(pair.first.c_str())] = @(pair.second.c_str());
+                }
+                // FIXME: how should the binding respond if the `is_fatal` bool is true?
+                [[RLMSyncManager sharedManager] _fireErrorWithCode:error.error_code.value()
+                                                           message:@(error.message.c_str())
+                                                           isFatal:error.is_fatal
+                                                           session:session
+                                                          userInfo:userInfo
+                                                        errorClass:errorKindForSyncError(error)];
+            };
+        }
+
+        _config = std::make_unique<SyncConfig>(SyncConfig{
+            [user _syncUser],
+            [[url absoluteString] UTF8String]
+        });
+        _config->stop_policy = translateStopPolicy(stopPolicy);
+        _config->bind_session_handler = std::move(bindHandler);
+        _config->error_handler = std::move(errorHandler);
+        _config->is_partial = isPartial;
+        self.customFileURL = customFileURL;
+        return self;
+    }
+    return nil;
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMSyncCredentials.m b/iOS/Pods/Realm/Realm/RLMSyncCredentials.m
new file mode 100644 (file)
index 0000000..a1521a2
--- /dev/null
@@ -0,0 +1,120 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMSyncCredentials.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 RLMIdentityProviderCloudKit               = @"cloudkit";
+RLMIdentityProvider const RLMIdentityProviderJWT                    = @"jwt";
+RLMIdentityProvider const RLMIdentityProviderAnonymous              = @"anonymous";
+RLMIdentityProvider const RLMIdentityProviderNickname               = @"nickname";
+
+@interface RLMSyncCredentials ()
+
+- (instancetype)initWithCustomToken:(RLMSyncCredentialsToken)token
+                           provider:(RLMIdentityProvider)provider
+                           userInfo:(NSDictionary *)userInfo NS_DESIGNATED_INITIALIZER;
+
+@property (nonatomic, readwrite) RLMSyncCredentialsToken token;
+@property (nonatomic, readwrite) RLMIdentityProvider provider;
+@property (nonatomic, readwrite) NSDictionary *userInfo;
+
+@end
+
+@implementation RLMSyncCredentials
+
++ (instancetype)credentialsWithFacebookToken:(RLMSyncCredentialsToken)token {
+    return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderFacebook userInfo:nil];
+}
+
++ (instancetype)credentialsWithGoogleToken:(RLMSyncCredentialsToken)token {
+    return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderGoogle userInfo:nil];
+}
+
++ (instancetype)credentialsWithCloudKitToken:(RLMSyncCredentialsToken)token {
+    return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderCloudKit userInfo:nil];
+}
+
++ (instancetype)credentialsWithUsername:(NSString *)username
+                               password:(NSString *)password
+                               register:(BOOL)shouldRegister {
+    return [[self alloc] initWithCustomToken:username
+                                    provider:RLMIdentityProviderUsernamePassword
+                                    userInfo:@{kRLMSyncPasswordKey: password,
+                                               kRLMSyncRegisterKey: @(shouldRegister)}];
+}
+
++ (instancetype)credentialsWithJWT:(NSString *)token {
+    return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderJWT userInfo:nil];
+}
+    
++ (instancetype)anonymousCredentials {
+    return [[self alloc] initWithCustomToken:@"" provider:RLMIdentityProviderAnonymous userInfo:nil];
+}
+    
++ (instancetype)credentialsWithNickname:(NSString *)nickname isAdmin:(BOOL)isAdmin {
+    return [[self alloc] initWithCustomToken:nickname
+                                    provider:RLMIdentityProviderNickname
+                                    userInfo:@{kRLMSyncIsAdminKey: @(isAdmin), kRLMSyncDataKey: nickname}];
+}
+
+/// Intended only for testing use. Will only work if the ROS is started with the `debug` provider enabled.
++ (instancetype)credentialsWithDebugUserID:(NSString *)userID isAdmin:(BOOL)isAdmin {
+    return [[self alloc] initWithCustomToken:userID
+                                    provider:RLMIdentityProviderDebug
+                                    userInfo:@{kRLMSyncIsAdminKey: @(isAdmin)}];
+}
+
++ (instancetype)credentialsWithAccessToken:(RLMServerToken)accessToken identity:(NSString *)identity {
+    return [[self alloc] initWithCustomToken:accessToken
+                                    provider:RLMIdentityProviderAccessToken
+                                    userInfo:@{kRLMSyncIdentityKey: identity}];
+}
+
+- (BOOL)isEqual:(id)object {
+    if (![object isKindOfClass:[RLMSyncCredentials class]]) {
+        return NO;
+    }
+    RLMSyncCredentials *that = (RLMSyncCredentials *)object;
+    return ([self.token isEqualToString:that.token]
+            && [self.provider isEqualToString:that.provider]
+            && [self.userInfo isEqual:that.userInfo]);
+}
+
+- (instancetype)initWithCustomToken:(RLMSyncCredentialsToken)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/iOS/Pods/Realm/Realm/RLMSyncManager.mm b/iOS/Pods/Realm/Realm/RLMSyncManager.mm
new file mode 100644 (file)
index 0000000..e35f802
--- /dev/null
@@ -0,0 +1,205 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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.h"
+
+#import "RLMRealmConfiguration+Sync.h"
+#import "RLMSyncConfiguration_Private.hpp"
+#import "RLMSyncSession_Private.hpp"
+#import "RLMSyncUser_Private.hpp"
+#import "RLMSyncUtil_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "sync/sync_config.hpp"
+#import "sync/sync_manager.hpp"
+#import "sync/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.
+}
+
+RLMSyncLogLevel logLevelForLevel(Level logLevel) {
+    switch (logLevel) {
+        case Level::off:    return RLMSyncLogLevelOff;
+        case Level::fatal:  return RLMSyncLogLevelFatal;
+        case Level::error:  return RLMSyncLogLevelError;
+        case Level::warn:   return RLMSyncLogLevelWarn;
+        case Level::info:   return RLMSyncLogLevelInfo;
+        case Level::detail: return RLMSyncLogLevelDetail;
+        case Level::debug:  return RLMSyncLogLevelDebug;
+        case Level::trace:  return RLMSyncLogLevelTrace;
+        case Level::all:    return RLMSyncLogLevelAll;
+    }
+    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<realm::util::Logger> make_logger(realm::util::Logger::Level level) override {
+        auto logger = std::make_unique<CocoaSyncLogger>();
+        logger->set_level_threshold(level);
+        return std::move(logger);
+    }
+} s_syncLoggerFactory;
+
+} // anonymous namespace
+
+@interface RLMSyncManager ()
+- (instancetype)initWithCustomRootDirectory:(nullable NSURL *)rootDirectory NS_DESIGNATED_INITIALIZER;
+@end
+
+@implementation RLMSyncManager
+
+static RLMSyncManager *s_sharedManager = nil;
+
++ (instancetype)sharedManager {
+    std::once_flag flag;
+    std::call_once(flag, [] {
+        try {
+            s_sharedManager = [[RLMSyncManager alloc] initWithCustomRootDirectory:nil];
+        }
+        catch (std::exception const& e) {
+            @throw RLMException(e);
+        }
+    });
+    return s_sharedManager;
+}
+
+- (instancetype)initWithCustomRootDirectory:(NSURL *)rootDirectory {
+    if (self = [super init]) {
+        [RLMSyncUser _setUpBindingContextFactory];
+
+        // Initialize the sync engine.
+        SyncManager::shared().set_logger_factory(s_syncLoggerFactory);
+        bool should_encrypt = !getenv("REALM_DISABLE_METADATA_ENCRYPTION") && !RLMIsRunningInPlayground();
+        auto mode = should_encrypt ? SyncManager::MetadataMode::Encryption : SyncManager::MetadataMode::NoEncryption;
+        rootDirectory = rootDirectory ?: [NSURL fileURLWithPath:RLMDefaultDirectoryForBundleIdentifier(nil)];
+        SyncManager::shared().configure_file_system(rootDirectory.path.UTF8String, mode, none, true);
+        return self;
+    }
+    return nil;
+}
+
+- (NSString *)appID {
+    if (!_appID) {
+        _appID = [[NSBundle mainBundle] bundleIdentifier] ?: @"(none)";
+    }
+    return _appID;
+}
+
+#pragma mark - Passthrough properties
+
+- (RLMSyncLogLevel)logLevel {
+    return logLevelForLevel(realm::SyncManager::shared().log_level());
+}
+
+- (void)setLogLevel:(RLMSyncLogLevel)logLevel {
+    realm::SyncManager::shared().set_log_level(levelForSyncLogLevel(logLevel));
+}
+
+#pragma mark - Private API
+
+- (void)_fireError:(NSError *)error {
+    dispatch_async(dispatch_get_main_queue(), ^{
+        if (self.errorHandler) {
+            self.errorHandler(error, nil);
+        }
+    });
+}
+
+- (void)_fireErrorWithCode:(int)errorCode
+                   message:(NSString *)message
+                   isFatal:(BOOL)fatal
+                   session:(RLMSyncSession *)session
+                  userInfo:(NSDictionary *)userInfo
+                errorClass:(RLMSyncSystemErrorKind)errorClass {
+    NSError *error = nil;
+    BOOL shouldMakeError = YES;
+    NSDictionary *custom = nil;
+    // Note that certain types of errors are 'interactive'; users have several options
+    // as to how to proceed after the error is reported.
+    switch (errorClass) {
+        case RLMSyncSystemErrorKindClientReset: {
+            std::string path = [userInfo[@(realm::SyncError::c_original_file_path_key)] UTF8String];
+            custom = @{kRLMSyncPathOfRealmBackupCopyKey:
+                           userInfo[@(realm::SyncError::c_recovery_file_path_key)],
+                       kRLMSyncErrorActionTokenKey:
+                           [[RLMSyncErrorActionToken alloc] initWithOriginalPath:std::move(path)]
+                       };;
+            break;
+        }
+        case RLMSyncSystemErrorKindPermissionDenied: {
+            std::string path = [userInfo[@(realm::SyncError::c_original_file_path_key)] UTF8String];
+            custom = @{kRLMSyncErrorActionTokenKey:
+                           [[RLMSyncErrorActionToken alloc] initWithOriginalPath:std::move(path)]
+                       };
+            break;
+        }
+        case RLMSyncSystemErrorKindUser:
+        case RLMSyncSystemErrorKindSession:
+            break;
+        case RLMSyncSystemErrorKindConnection:
+        case RLMSyncSystemErrorKindClient:
+        case RLMSyncSystemErrorKindUnknown:
+            // Report the error. There's nothing the user can do about it, though.
+            shouldMakeError = fatal;
+            break;
+    }
+    error = shouldMakeError ? make_sync_error(errorClass, message, errorCode, custom) : nil;
+    dispatch_async(dispatch_get_main_queue(), ^{
+        if (!self.errorHandler || !error) {
+            return;
+        }
+        self.errorHandler(error, session);
+    });
+}
+
+- (NSArray<RLMSyncUser *> *)_allUsers {
+    NSMutableArray<RLMSyncUser *> *buffer = [NSMutableArray array];
+    for (auto user : SyncManager::shared().all_logged_in_users()) {
+        [buffer addObject:[[RLMSyncUser alloc] initWithSyncUser:std::move(user)]];
+    }
+    return buffer;
+}
+
++ (void)resetForTesting {
+    SyncManager::shared().reset_for_testing();
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMSyncPermission.mm b/iOS/Pods/Realm/Realm/RLMSyncPermission.mm
new file mode 100644 (file)
index 0000000..180892d
--- /dev/null
@@ -0,0 +1,189 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 "RLMSyncPermission_Private.hpp"
+#import "RLMSyncUtil_Private.hpp"
+
+#import "RLMUtil.hpp"
+
+using namespace realm;
+using ConditionType = Permission::Condition::Type;
+
+#pragma mark - Permission
+
+@interface RLMSyncPermission () {
+@private
+    NSString *_identity;
+    NSString *_key;
+    NSString *_value;
+    util::Optional<Permission> _underlying;
+    RLMSyncAccessLevel _accessLevel;
+    NSString *_path;
+    NSDate *_updatedAt;
+}
+@end
+
+@implementation RLMSyncPermission
+
+- (instancetype)initWithRealmPath:(NSString *)path
+                         identity:(NSString *)identity
+                      accessLevel:(RLMSyncAccessLevel)accessLevel {
+    if (self = [super init]) {
+        _accessLevel = accessLevel;
+        _path = path;
+        _identity = identity;
+        if (!identity) {
+            @throw RLMException(@"A permission value cannot be created without a valid user ID");
+        }
+        _updatedAt = [NSDate date];
+    }
+    return self;
+}
+
+- (instancetype)initWithRealmPath:(NSString *)path
+                         username:(NSString *)username
+                      accessLevel:(RLMSyncAccessLevel)accessLevel {
+    if (self = [super init]) {
+        _accessLevel = accessLevel;
+        _path = path;
+        _identity = nil;
+        _key = @"email";
+        _value = username;
+        _updatedAt = [NSDate date];
+    }
+    return self;
+}
+
+- (instancetype)initWithPermission:(Permission)permission {
+    if (self = [super init]) {
+        _underlying = util::make_optional<Permission>(std::move(permission));
+        return self;
+    }
+    return nil;
+}
+
+- (NSString *)path {
+    if (!_underlying) {
+        REALM_ASSERT(_path);
+        return _path;
+    }
+    return @(_underlying->path.c_str());
+}
+
+- (RLMSyncAccessLevel)accessLevel {
+    if (!_underlying) {
+        return _accessLevel;
+    }
+    return objCAccessLevelForAccessLevel(_underlying->access);
+}
+
+- (BOOL)mayRead {
+    return self.accessLevel > RLMSyncAccessLevelNone;
+}
+
+- (BOOL)mayWrite {
+    return self.accessLevel > RLMSyncAccessLevelRead;
+}
+
+- (BOOL)mayManage {
+    return self.accessLevel == RLMSyncAccessLevelAdmin;
+}
+
+- (NSString *)identity {
+    if (!_underlying) {
+        return _identity;
+    }
+    if (_underlying->condition.type == ConditionType::UserId) {
+        return @(_underlying->condition.user_id.c_str());
+    }
+    return nil;
+}
+
+- (NSString *)key {
+    if (!_underlying) {
+        return _key;
+    }
+    if (_underlying->condition.type == ConditionType::KeyValue) {
+        return @(_underlying->condition.key_value.first.c_str());
+    }
+    return nil;
+}
+
+- (NSString *)value {
+    if (!_underlying) {
+        return _value;
+    }
+    if (_underlying->condition.type == ConditionType::KeyValue) {
+        return @(_underlying->condition.key_value.second.c_str());
+    }
+    return nil;
+}
+
+- (NSDate *)updatedAt {
+    if (!_underlying) {
+        return _updatedAt;
+    }
+    return RLMTimestampToNSDate(_underlying->updated_at);
+}
+
+- (realm::Permission)rawPermission {
+    if (_underlying) {
+        return *_underlying;
+    }
+    auto condition = (_identity
+                      ? Permission::Condition([_identity UTF8String])
+                      : Permission::Condition([_key UTF8String], [_value UTF8String]));
+    return Permission{
+        [_path UTF8String],
+        accessLevelForObjCAccessLevel(_accessLevel),
+        std::move(condition)
+    };
+}
+
+- (NSUInteger)hash {
+    return [self.identity hash] ^ self.accessLevel;
+}
+
+- (BOOL)isEqual:(id)object {
+    if (self == object) {
+        return YES;
+    }
+    if ([object isKindOfClass:[RLMSyncPermission class]]) {
+        RLMSyncPermission *that = (RLMSyncPermission *)object;
+        return (self.accessLevel == that.accessLevel
+                && Permission::paths_are_equivalent([self.path UTF8String], [that.path UTF8String],
+                                                    [self.identity UTF8String], [that.identity UTF8String])
+                && [self.identity isEqualToString:that.identity]);
+    }
+    return NO;
+}
+
+- (NSString *)description {
+    NSString *typeDescription = nil;
+    if (self.identity) {
+        typeDescription = [NSString stringWithFormat:@"identity: %@", self.identity];
+    } else {
+        typeDescription = [NSString stringWithFormat:@"key: %@, value: %@", self.key, self.value];
+    }
+    return [NSString stringWithFormat:@"<RLMSyncPermission> %@, path: %@, access level: %@",
+            typeDescription,
+            self.path,
+            @(Permission::description_for_access_level(accessLevelForObjCAccessLevel(self.accessLevel)).c_str())];
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMSyncPermissionResults.mm b/iOS/Pods/Realm/Realm/RLMSyncPermissionResults.mm
new file mode 100644 (file)
index 0000000..39354f1
--- /dev/null
@@ -0,0 +1,262 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 "RLMSyncPermissionResults.h"
+
+#import "RLMCollection_Private.hpp"
+#import "RLMObjectSchema_Private.hpp"
+#import "RLMQueryUtil.hpp"
+#import "RLMResults_Private.hpp"
+#import "RLMSchema_Private.hpp"
+#import "RLMSyncPermission_Private.hpp"
+#import "RLMSyncUtil_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "object.hpp"
+
+using namespace realm;
+
+namespace {
+
+bool keypath_is_valid(NSString *keypath)
+{
+    static NSSet<NSString *> *valid = nil;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        valid = [NSSet setWithArray:@[RLMSyncPermissionSortPropertyPath,
+                                      RLMSyncPermissionSortPropertyUserID,
+                                      RLMSyncPermissionSortPropertyUpdated]];
+    });
+    return [valid containsObject:keypath];
+}
+
+}
+
+/// Sort by the Realm Object Server path to the Realm to which the permission applies.
+RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyPath       = @"path";
+/// Sort by the identity of the user to whom the permission applies.
+RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyUserID     = @"userId";
+/// Sort by the date the permissions were last updated.
+RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyUpdated    = @"updatedAt";
+
+@interface RLMSyncPermissionResults ()
+@property (nonatomic, strong) RLMSchema *schema;
+@property (nonatomic, strong) RLMObjectSchema *objectSchema;
+@end
+
+@implementation RLMSyncPermissionResults
+
+#pragma mark - Public API
+
+- (RLMPropertyType)type {
+    return RLMPropertyTypeObject;
+}
+
+- (NSString *)objectClassName {
+    return NSStringFromClass([RLMSyncPermission class]);
+}
+
+- (RLMRealm *)realm {
+    return nil;
+}
+
+- (RLMSyncPermission *)objectAtIndex:(NSUInteger)index {
+    return translateRLMResultsErrors([&] {
+        Object permission(_results.get_realm(), _results.get_object_schema(), _results.get(index));
+        return [[RLMSyncPermission alloc] initWithPermission:Permission(permission)];
+    });
+}
+
+- (RLMSyncPermission *)firstObject {
+    return self.count == 0 ? nil : [self objectAtIndex:0];
+}
+
+- (RLMSyncPermission *)lastObject {
+    return self.count == 0 ? nil : [self objectAtIndex:(self.count - 1)];
+}
+
+- (NSUInteger)indexOfObject:(RLMSyncPermission *)object {
+    if (object.key) {
+        // Key-value permissions are only used for setting; they are never returned.
+        return NSNotFound;
+    }
+    // Canonicalize the path.
+    NSString *path = object.path;
+    if ([path rangeOfString:@"~"].location != NSNotFound) {
+        path = [path stringByReplacingOccurrencesOfString:@"~" withString:object.identity];
+    }
+    NSString *topPrivilege;
+    switch (object.accessLevel) {
+        case RLMSyncAccessLevelNone:
+            // Deleted permissions are removed from the permissions Realm by ROS.
+            return NSNotFound;
+        case RLMSyncAccessLevelRead:
+            topPrivilege = @"mayRead";
+            break;
+        case RLMSyncAccessLevelWrite:
+            topPrivilege = @"mayWrite";
+            break;
+        case RLMSyncAccessLevelAdmin:
+            topPrivilege = @"mayManage";
+            break;
+    }
+    // Build the predicate.
+    NSPredicate *p = [NSPredicate predicateWithFormat:@"%K = %@ AND %K = %@ AND %K == YES",
+                      RLMSyncPermissionSortPropertyPath, path,
+                      RLMSyncPermissionSortPropertyUserID, object.identity,
+                      topPrivilege];
+    return [self indexOfObjectWithPredicate:p];
+}
+
+- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate {
+    return translateRLMResultsErrors([&] {
+        auto& group = _results.get_realm()->read_group();
+        auto query = RLMPredicateToQuery(predicate, self.objectSchema, self.schema, group);
+        return RLMConvertNotFound(_results.index_of(std::move(query)));
+    });
+}
+
+- (RLMResults<RLMSyncPermission *> *)objectsWithPredicate:(NSPredicate *)predicate {
+    return translateRLMResultsErrors([&] {
+        auto query = RLMPredicateToQuery(predicate, self.objectSchema, self.schema, _results.get_realm()->read_group());
+        return [[RLMSyncPermissionResults alloc] initWithResults:_results.filter(std::move(query))];
+    });
+}
+
+- (RLMResults<RLMSyncPermission *> *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties {
+    if (properties.count == 0) {
+        return self;
+    }
+    for (RLMSortDescriptor *descriptor in properties) {
+        if (!keypath_is_valid(descriptor.keyPath)) {
+            @throw RLMException(@"Invalid keypath specified. Use one of the constants defined in "
+                                @" `RLMSyncPermissionSortProperty`.");
+        }
+    }
+    return translateRLMResultsErrors([&] {
+        auto sorted = _results.sort(RLMSortDescriptorsToKeypathArray(properties));
+        return [[RLMSyncPermissionResults alloc] initWithResults:std::move(sorted)];
+    });
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmismatched-parameter-types"
+- (RLMNotificationToken *)addNotificationBlock:(void(^)(RLMSyncPermissionResults *results,
+                                                        RLMCollectionChange *change,
+                                                        NSError *error))block {
+    auto cb = [=](const realm::CollectionChangeSet& changes, std::exception_ptr ptr) {
+        if (ptr) {
+            NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeGet);
+            REALM_ASSERT(error);
+            block(nil, nil, error);
+        } else {
+            // Finished successfully
+            block(self, [[RLMCollectionChange alloc] initWithChanges:changes], nil);
+        }
+    };
+    return [[RLMCancellationToken alloc] initWithToken:_results.add_notification_callback(std::move(cb)) realm:nil];
+}
+#pragma clang diagnostic pop
+
+- (id)aggregate:(__unused NSString *)property
+         method:(__unused util::Optional<Mixed> (Results::*)(size_t))method
+     methodName:(__unused NSString *)methodName returnNilForEmpty:(__unused BOOL)returnNilForEmpty {
+    // We don't support any of the min/max/average/sum APIs; they don't make sense for this collection type.
+    return nil;
+}
+
+- (id)valueForKey:(NSString *)key {
+    size_t count = self.count;
+    if (count == 0) {
+        return @[];
+    }
+    NSMutableArray *results = [NSMutableArray arrayWithCapacity:count];
+    if ([key isEqualToString:@"self"]) {
+        for (size_t i = 0; i < count; i++) {
+            [results addObject:[self objectAtIndex:i]];
+        }
+    } else {
+        for (size_t i = 0; i < count; i++) {
+            [results addObject:[[self objectAtIndex:i] valueForKey:key] ?: NSNull.null];
+        }
+    }
+    return results;
+}
+
+- (void)setValue:(__unused id)value forKey:(__unused NSString *)key {
+    @throw RLMException(@"Cannot set values for the read-only type `RLMSyncPermission`.");
+}
+
+#pragma mark - System
+
+- (RLMSchema *)schema {
+    if (!_schema) {
+        _schema = [RLMSchema dynamicSchemaFromObjectStoreSchema:_results.get_realm()->schema()];
+    }
+    return _schema;
+}
+
+- (RLMObjectSchema *)objectSchema {
+    if (!_objectSchema) {
+        _objectSchema = [RLMObjectSchema objectSchemaForObjectStoreSchema:_results.get_object_schema()];
+    }
+    return _objectSchema;
+}
+
+- (NSString *)description {
+    return RLMDescriptionWithMaxDepth(@"RLMSyncPermissionResults", self, 1);
+}
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
+                                  objects:(id __unsafe_unretained [])buffer
+                                    count:(NSUInteger)len {
+    // FIXME: It would be nice to have a shared fast enumeration implementation for `realm::Results`-only RLMResults.
+    NSUInteger thisSize = self.count;
+    if (state->state == 0) {
+        state->extra[0] = 0;
+        state->extra[1] = (long)thisSize;
+        state->state = 1;
+    }
+    NSUInteger objectsInBuffer = 0;
+    long idx = state->extra[0];
+    if ((unsigned long)idx == thisSize) {
+        // finished
+        return 0;
+    }
+    state->itemsPtr = buffer;
+    state->mutationsPtr = state->extra + 1;
+    while (true) {
+        if (objectsInBuffer == len) {
+            // Buffer is full.
+            state->extra[0] = idx;
+            return objectsInBuffer;
+        }
+        if ((unsigned long)idx == thisSize) {
+            // finished
+            state->extra[0] = idx;
+            return objectsInBuffer;
+        }
+        // Otherwise, add an object and advance the index pointer.
+        RLMSyncPermission * __autoreleasing thisPermission = [self objectAtIndex:idx];
+        buffer[objectsInBuffer] = thisPermission;
+        idx++;
+        objectsInBuffer++;
+    }
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMSyncSession.mm b/iOS/Pods/Realm/Realm/RLMSyncSession.mm
new file mode 100644 (file)
index 0000000..e00af7f
--- /dev/null
@@ -0,0 +1,221 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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.hpp"
+
+#import "RLMSyncConfiguration_Private.hpp"
+#import "RLMSyncUser_Private.hpp"
+#import "RLMSyncUtil_Private.hpp"
+#import "sync/sync_session.hpp"
+
+using namespace realm;
+
+@interface RLMSyncErrorActionToken () {
+@public
+    std::string _originalPath;
+    BOOL _isValid;
+}
+@end
+
+@interface RLMProgressNotificationToken() {
+    uint64_t _token;
+    std::weak_ptr<SyncSession> _session;
+}
+@end
+
+@implementation RLMProgressNotificationToken
+
+- (void)suppressNextNotification {
+    // No-op, but implemented in case this token is passed to
+    // `-[RLMRealm commitWriteTransactionWithoutNotifying:]`.
+}
+
+- (void)invalidate {
+    if (auto session = _session.lock()) {
+        session->unregister_progress_notifier(_token);
+        _session.reset();
+        _token = 0;
+    }
+}
+
+- (void)dealloc {
+    if (_token != 0) {
+        NSLog(@"RLMProgressNotificationToken released without unregistering a notification. "
+              @"You must hold on to the RLMProgressNotificationToken and call "
+              @"-[RLMProgressNotificationToken invalidate] when you no longer wish to receive "
+              @"progress update notifications.");
+    }
+}
+
+- (nullable instancetype)initWithTokenValue:(uint64_t)token
+                                    session:(std::shared_ptr<SyncSession>)session {
+    if (token == 0) {
+        return nil;
+    }
+    if (self = [super init]) {
+        _token = token;
+        _session = session;
+        return self;
+    }
+    return nil;
+}
+
+@end
+
+@interface RLMSyncSession ()
+@property (class, nonatomic, readonly) dispatch_queue_t notificationsQueue;
+@end
+
+@implementation RLMSyncSession
+
++ (dispatch_queue_t)notificationsQueue {
+    static dispatch_queue_t queue;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        queue = dispatch_queue_create("io.realm.sync.sessionsNotificationQueue", DISPATCH_QUEUE_SERIAL);
+    });
+    return queue;
+}
+
+- (instancetype)initWithSyncSession:(std::shared_ptr<SyncSession>)session {
+    if (self = [super init]) {
+        _session = session;
+        return self;
+    }
+    return nil;
+}
+
+- (RLMSyncConfiguration *)configuration {
+    if (auto session = _session.lock()) {
+        if (session->state() != SyncSession::PublicState::Error) {
+            return [[RLMSyncConfiguration alloc] initWithRawConfig:session->config()];
+        }
+    }
+    return nil;
+}
+
+- (NSURL *)realmURL {
+    if (auto session = _session.lock()) {
+        if (auto url = session->full_realm_url()) {
+            return [NSURL URLWithString:@(url->c_str())];
+        }
+    }
+    return nil;
+}
+
+- (RLMSyncUser *)parentUser {
+    if (auto session = _session.lock()) {
+        if (session->state() != SyncSession::PublicState::Error) {
+            return [[RLMSyncUser alloc] initWithSyncUser:session->user()];
+        }
+    }
+    return nil;
+}
+
+- (RLMSyncSessionState)state {
+    if (auto session = _session.lock()) {
+        if (session->state() == SyncSession::PublicState::Inactive) {
+            return RLMSyncSessionStateInactive;
+        }
+        if (session->state() != SyncSession::PublicState::Error) {
+            return RLMSyncSessionStateActive;
+        }
+    }
+    return RLMSyncSessionStateInvalid;
+}
+
+- (BOOL)waitForUploadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(NSError *))callback {
+    if (auto session = _session.lock()) {
+        if (session->state() == SyncSession::PublicState::Error) {
+            return NO;
+        }
+        queue = queue ?: dispatch_get_main_queue();
+        session->wait_for_upload_completion([=](std::error_code err) {
+            NSError *error = (err == std::error_code{}) ? nil : make_sync_error(err);
+            dispatch_async(queue, ^{
+                callback(error);
+            });
+        });
+        return YES;
+    }
+    return NO;
+}
+
+- (BOOL)waitForDownloadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(NSError *))callback {
+    if (auto session = _session.lock()) {
+        if (session->state() == SyncSession::PublicState::Error) {
+            return NO;
+        }
+        queue = queue ?: dispatch_get_main_queue();
+        session->wait_for_download_completion([=](std::error_code err) {
+            NSError *error = (err == std::error_code{}) ? nil : make_sync_error(err);
+            dispatch_async(queue, ^{
+                callback(error);
+            });
+        });
+        return YES;
+    }
+    return NO;
+}
+
+- (RLMProgressNotificationToken *)addProgressNotificationForDirection:(RLMSyncProgressDirection)direction
+                                                                 mode:(RLMSyncProgressMode)mode
+                                                                block:(RLMProgressNotificationBlock)block {
+    if (auto session = _session.lock()) {
+        if (session->state() == SyncSession::PublicState::Error) {
+            return nil;
+        }
+        dispatch_queue_t queue = RLMSyncSession.notificationsQueue;
+        auto notifier_direction = (direction == RLMSyncProgressDirectionUpload
+                                   ? SyncSession::NotifierType::upload
+                                   : SyncSession::NotifierType::download);
+        bool is_streaming = (mode == RLMSyncProgressModeReportIndefinitely);
+        uint64_t token = session->register_progress_notifier([=](uint64_t transferred, uint64_t transferrable) {
+            dispatch_async(queue, ^{
+                block((NSUInteger)transferred, (NSUInteger)transferrable);
+            });
+        }, notifier_direction, is_streaming);
+        return [[RLMProgressNotificationToken alloc] initWithTokenValue:token session:std::move(session)];
+    }
+    return nil;
+}
+
++ (void)immediatelyHandleError:(RLMSyncErrorActionToken *)token {
+    if (!token->_isValid) {
+        return;
+    }
+    token->_isValid = NO;
+    SyncManager::shared().immediately_run_file_actions(std::move(token->_originalPath));
+}
+
+@end
+
+// MARK: - Error action token
+
+@implementation RLMSyncErrorActionToken
+
+- (instancetype)initWithOriginalPath:(std::string)originalPath {
+    if (self = [super init]) {
+        _isValid = YES;
+        _originalPath = std::move(originalPath);
+        return self;
+    }
+    return nil;
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMSyncSessionRefreshHandle.mm b/iOS/Pods/Realm/Realm/RLMSyncSessionRefreshHandle.mm
new file mode 100644 (file)
index 0000000..7bcb8fb
--- /dev/null
@@ -0,0 +1,277 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMSyncSessionRefreshHandle.hpp"
+
+#import "RLMJSONModels.h"
+#import "RLMNetworkClient.h"
+#import "RLMSyncManager_Private.h"
+#import "RLMSyncUser_Private.hpp"
+#import "RLMSyncUtil_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "sync/sync_session.hpp"
+
+using namespace realm;
+
+namespace {
+
+void unregisterRefreshHandle(const std::weak_ptr<SyncUser>& user, const std::string& path) {
+    if (auto strong_user = user.lock()) {
+        context_for(strong_user).unregister_refresh_handle(path);
+    }
+}
+
+void reportInvalidAccessToken(const std::weak_ptr<SyncUser>& user, NSError *error) {
+    if (auto strong_user = user.lock()) {
+        if (RLMUserErrorReportingBlock block = context_for(strong_user).error_handler()) {
+            RLMSyncUser *theUser = [[RLMSyncUser alloc] initWithSyncUser:std::move(strong_user)];
+            [theUser logOut];
+            block(theUser, error);
+        }
+    }
+}
+
+}
+
+static const NSTimeInterval RLMRefreshBuffer = 10;
+
+@interface RLMSyncSessionRefreshHandle () {
+    std::weak_ptr<SyncUser> _user;
+    std::string _path;
+    std::weak_ptr<SyncSession> _session;
+    std::shared_ptr<SyncSession> _strongSession;
+}
+
+@property (nonatomic) NSTimer *timer;
+
+@property (nonatomic) NSURL *realmURL;
+@property (nonatomic) NSURL *authServerURL;
+@property (nonatomic, copy) RLMSyncBasicErrorReportingBlock completionBlock;
+
+@end
+
+@implementation RLMSyncSessionRefreshHandle
+
+- (instancetype)initWithRealmURL:(NSURL *)realmURL
+                            user:(std::shared_ptr<realm::SyncUser>)user
+                         session:(std::shared_ptr<realm::SyncSession>)session
+                 completionBlock:(RLMSyncBasicErrorReportingBlock)completionBlock {
+    if (self = [super init]) {
+        NSString *path = [realmURL path];
+        _path = [path UTF8String];
+        self.authServerURL = [NSURL URLWithString:@(user->server_url().c_str())];
+        if (!self.authServerURL) {
+            @throw RLMException(@"User object isn't configured with an auth server URL.");
+        }
+        self.completionBlock = completionBlock;
+        self.realmURL = realmURL;
+        // For the initial bind, we want to prolong the session's lifetime.
+        _strongSession = std::move(session);
+        _session = _strongSession;
+        _user = user;
+        // Immediately fire off the network request.
+        [self _timerFired:nil];
+        return self;
+    }
+    return nil;
+}
+
+- (void)dealloc {
+    [self.timer invalidate];
+}
+
+- (void)invalidate {
+    _strongSession = nullptr;
+    [self.timer invalidate];
+}
+
++ (NSDate *)fireDateForTokenExpirationDate:(NSDate *)date nowDate:(NSDate *)nowDate {
+    NSDate *fireDate = [date dateByAddingTimeInterval:-RLMRefreshBuffer];
+    // Only fire times in the future are valid.
+    return ([fireDate compare:nowDate] == NSOrderedDescending ? fireDate : nil);
+}
+
+- (void)scheduleRefreshTimer:(NSDate *)dateWhenTokenExpires {
+    // Schedule the timer on the main queue.
+    // It's very likely that this method will be run on a side thread, for example
+    // on the thread that runs `NSURLSession`'s completion blocks. We can't be
+    // guaranteed that there's an existing runloop on those threads, and we don't want
+    // to create and start a new one if one doesn't already exist.
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [self.timer invalidate];
+        NSDate *fireDate = [RLMSyncSessionRefreshHandle fireDateForTokenExpirationDate:dateWhenTokenExpires
+                                                                               nowDate:[NSDate date]];
+        if (!fireDate) {
+            unregisterRefreshHandle(_user, _path);
+            return;
+        }
+        self.timer = [[NSTimer alloc] initWithFireDate:fireDate
+                                              interval:0
+                                                target:self
+                                              selector:@selector(_timerFired:)
+                                              userInfo:nil
+                                               repeats:NO];
+        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
+    });
+}
+
+/// Handler for network requests whose responses successfully parse into an auth response model.
+- (BOOL)_handleSuccessfulRequest:(RLMAuthResponseModel *)model {
+    // Success
+    std::shared_ptr<SyncSession> session = _session.lock();
+    if (!session) {
+        // The session is dead or in a fatal error state.
+        unregisterRefreshHandle(_user, _path);
+        [self invalidate];
+        return NO;
+    }
+    bool success = session->state() != SyncSession::PublicState::Error;
+    if (success) {
+        // Calculate the resolved path.
+        NSString *resolvedURLString = nil;
+        RLMServerPath resolvedPath = model.accessToken.tokenData.path;
+        // Munge the path back onto the original URL, because the `sync` API expects an entire URL.
+        NSURLComponents *urlBuffer = [NSURLComponents componentsWithURL:self.realmURL
+                                                resolvingAgainstBaseURL:YES];
+        urlBuffer.path = resolvedPath;
+        resolvedURLString = [[urlBuffer URL] absoluteString];
+        if (!resolvedURLString) {
+            @throw RLMException(@"Resolved path returned from the server was invalid (%@).", resolvedPath);
+        }
+        // Pass the token and resolved path to the underlying sync subsystem.
+        session->refresh_access_token([model.accessToken.token UTF8String], {resolvedURLString.UTF8String});
+        success = session->state() != SyncSession::PublicState::Error;
+        if (success) {
+            // Schedule a refresh. If we're successful we must already have `bind()`ed the session
+            // initially, so we can null out the strong pointer.
+            _strongSession = nullptr;
+            NSDate *expires = [NSDate dateWithTimeIntervalSince1970:model.accessToken.tokenData.expires];
+            [self scheduleRefreshTimer:expires];
+        } else {
+            // The session is dead or in a fatal error state.
+            unregisterRefreshHandle(_user, _path);
+            [self invalidate];
+        }
+    }
+    if (self.completionBlock) {
+        self.completionBlock(success ? nil : make_auth_error_client_issue());
+    }
+    return success;
+}
+
+/// Handler for network requests that failed before the JSON parsing stage.
+- (void)_handleFailedRequest:(NSError *)error {
+    NSError *authError;
+    if ([error.domain isEqualToString:RLMSyncAuthErrorDomain]) {
+        // Network client may return sync related error
+        authError = error;
+        // Try to report this error to the expiration callback.
+        reportInvalidAccessToken(_user, authError);
+    } else {
+        // Something else went wrong
+        authError = make_auth_error_bad_response();
+    }
+    if (self.completionBlock) {
+        self.completionBlock(authError);
+    }
+    [[RLMSyncManager sharedManager] _fireError:make_sync_error(authError)];
+    // Certain errors related to network connectivity should trigger a retry.
+    NSDate *nextTryDate = nil;
+    if ([error.domain isEqualToString:NSURLErrorDomain]) {
+        switch (error.code) {
+            case NSURLErrorCannotConnectToHost:
+            case NSURLErrorNotConnectedToInternet:
+            case NSURLErrorNetworkConnectionLost:
+            case NSURLErrorTimedOut:
+            case NSURLErrorDNSLookupFailed:
+            case NSURLErrorCannotFindHost:
+                // FIXME: 10 seconds is an arbitrarily chosen value, consider rationalizing it.
+                nextTryDate = [NSDate dateWithTimeIntervalSinceNow:RLMRefreshBuffer + 10];
+                break;
+            default:
+                break;
+        }
+    }
+    if (!nextTryDate) {
+        // This error isn't a network failure error. Just invalidate the refresh handle and stop.
+        unregisterRefreshHandle(_user, _path);
+        [self invalidate];
+        return;
+    }
+    // If we tried to initially bind the session and failed, we'll try again. However, each
+    // subsequent attempt will use a weak pointer to avoid prolonging the session's lifetime
+    // unnecessarily.
+    _strongSession = nullptr;
+    [self scheduleRefreshTimer:nextTryDate];
+    return;
+}
+
+/// Callback handler for network requests.
+- (BOOL)_onRefreshCompletionWithError:(NSError *)error json:(NSDictionary *)json {
+    if (json && !error) {
+        RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json
+                                                                    requireAccessToken:YES
+                                                                   requireRefreshToken:NO];
+        if (model) {
+            return [self _handleSuccessfulRequest:model];
+        }
+        // Otherwise, malformed JSON
+        unregisterRefreshHandle(_user, _path);
+        [self.timer invalidate];
+        NSError *error = make_sync_error(make_auth_error_bad_response(json));
+        if (self.completionBlock) {
+            self.completionBlock(error);
+        }
+        [[RLMSyncManager sharedManager] _fireError:error];
+    } else {
+        REALM_ASSERT(error);
+        [self _handleFailedRequest:error];
+    }
+    return NO;
+}
+
+- (void)_timerFired:(__unused NSTimer *)timer {
+    RLMServerToken refreshToken = nil;
+    if (auto user = _user.lock()) {
+        refreshToken = @(user->refresh_token().c_str());
+    }
+    if (!refreshToken) {
+        unregisterRefreshHandle(_user, _path);
+        [self.timer invalidate];
+        return;
+    }
+
+    NSDictionary *json = @{
+                           kRLMSyncProviderKey: @"realm",
+                           kRLMSyncPathKey: @(_path.c_str()),
+                           kRLMSyncDataKey: refreshToken,
+                           kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID,
+                           };
+
+    __weak RLMSyncSessionRefreshHandle *weakSelf = self;
+    RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) {
+        [weakSelf _onRefreshCompletionWithError:error json:json];
+    };
+    [RLMNetworkClient sendRequestToEndpoint:[RLMSyncAuthEndpoint endpoint]
+                                     server:self.authServerURL
+                                       JSON:json
+                                 completion:handler];
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMSyncUser.mm b/iOS/Pods/Realm/Realm/RLMSyncUser.mm
new file mode 100644 (file)
index 0000000..f843efb
--- /dev/null
@@ -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.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMSyncUser_Private.hpp"
+
+#import "RLMJSONModels.h"
+#import "RLMNetworkClient.h"
+#import "RLMRealmConfiguration+Sync.h"
+#import "RLMRealmConfiguration_Private.hpp"
+#import "RLMRealmUtil.hpp"
+#import "RLMResults_Private.hpp"
+#import "RLMSyncManager_Private.h"
+#import "RLMSyncPermissionResults.h"
+#import "RLMSyncPermission_Private.hpp"
+#import "RLMSyncSessionRefreshHandle.hpp"
+#import "RLMSyncSession_Private.hpp"
+#import "RLMSyncUtil_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "sync/sync_manager.hpp"
+#import "sync/sync_session.hpp"
+#import "sync/sync_user.hpp"
+
+using namespace realm;
+using ConfigMaker = std::function<Realm::Config(std::shared_ptr<SyncUser>, std::string)>;
+
+namespace {
+
+std::function<void(Results, std::exception_ptr)> RLMWrapPermissionResultsCallback(RLMPermissionResultsBlock callback) {
+    return [callback](Results results, std::exception_ptr ptr) {
+        if (ptr) {
+            NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeGet);
+            REALM_ASSERT(error);
+            callback(nil, error);
+        } else {
+            // Finished successfully
+            callback([[RLMSyncPermissionResults alloc] initWithResults:std::move(results)], nil);
+        }
+    };
+}
+
+NSString *tildeSubstitutedPathForRealmURL(NSURL *url, NSString *identity) {
+    return [[url path] stringByReplacingOccurrencesOfString:@"~" withString:identity];
+}
+
+}
+
+void CocoaSyncUserContext::register_refresh_handle(const std::string& path, RLMSyncSessionRefreshHandle *handle)
+{
+    REALM_ASSERT(handle);
+    std::lock_guard<std::mutex> lock(m_mutex);
+    auto it = m_refresh_handles.find(path);
+    if (it != m_refresh_handles.end()) {
+        [it->second invalidate];
+        m_refresh_handles.erase(it);
+    }
+    m_refresh_handles.insert({path, handle});
+}
+
+void CocoaSyncUserContext::unregister_refresh_handle(const std::string& path)
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    m_refresh_handles.erase(path);
+}
+
+void CocoaSyncUserContext::invalidate_all_handles()
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    for (auto& it : m_refresh_handles) {
+        [it.second invalidate];
+    }
+    m_refresh_handles.clear();
+}
+
+RLMUserErrorReportingBlock CocoaSyncUserContext::error_handler() const
+{
+    std::lock_guard<std::mutex> lock(m_error_handler_mutex);
+    return m_error_handler;
+}
+
+void CocoaSyncUserContext::set_error_handler(RLMUserErrorReportingBlock block)
+{
+    std::lock_guard<std::mutex> lock(m_error_handler_mutex);
+    m_error_handler = block;
+}
+
+PermissionChangeCallback RLMWrapPermissionStatusCallback(RLMPermissionStatusBlock callback) {
+    return [callback](std::exception_ptr ptr) {
+        if (ptr) {
+            NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeChange);
+            REALM_ASSERT(error);
+            callback(error);
+        } else {
+            // Finished successfully
+            callback(nil);
+        }
+    };
+}
+
+@interface RLMSyncUserInfo ()
+
+@property (nonatomic, readwrite) NSArray *accounts;
+@property (nonatomic, readwrite) NSDictionary *metadata;
+@property (nonatomic, readwrite) NSString *identity;
+@property (nonatomic, readwrite) BOOL isAdmin;
+
++ (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model;
+
+@end
+
+@interface RLMSyncUser () {
+    std::shared_ptr<SyncUser> _user;
+    // FIXME: remove this when the object store ConfigMaker goes away
+    std::unique_ptr<ConfigMaker> _configMaker;
+}
+
+- (instancetype)initPrivate NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation RLMSyncUser
+
+#pragma mark - static API
+
++ (NSDictionary *)allUsers {
+    NSArray *allUsers = [[RLMSyncManager sharedManager] _allUsers];
+    return [NSDictionary dictionaryWithObjects:allUsers
+                                       forKeys:[allUsers valueForKey:@"identity"]];
+}
+
++ (RLMSyncUser *)currentUser {
+    NSArray *allUsers = [[RLMSyncManager sharedManager] _allUsers];
+    if (allUsers.count > 1) {
+        @throw RLMException(@"+currentUser cannot be called if more that one valid, logged-in user exists.");
+    }
+    return allUsers.firstObject;
+}
+
+#pragma mark - API
+
+- (instancetype)initPrivate {
+    if (self = [super init]) {
+        _configMaker = std::make_unique<ConfigMaker>([](std::shared_ptr<SyncUser> user, std::string url) {
+            RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
+            NSURL *objCUrl = [NSURL URLWithString:@(url.c_str())];
+            RLMSyncUser *objCUser = [[RLMSyncUser alloc] initWithSyncUser:std::move(user)];
+            config.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:objCUser realmURL:objCUrl];
+            return [config config];
+        });
+        return self;
+    }
+    return nil;
+}
+
+- (instancetype)initWithSyncUser:(std::shared_ptr<SyncUser>)user {
+    if (self = [self initPrivate]) {
+        _user = user;
+        return self;
+    }
+    return nil;
+}
+
+- (BOOL)isEqual:(id)object {
+    if (![object isKindOfClass:[RLMSyncUser class]]) {
+        return NO;
+    }
+    return _user == ((RLMSyncUser *)object)->_user;
+}
+
++ (void)logInWithCredentials:(RLMSyncCredentials *)credential
+               authServerURL:(NSURL *)authServerURL
+                onCompletion:(RLMUserCompletionBlock)completion {
+    [self logInWithCredentials:credential
+                 authServerURL:authServerURL
+                       timeout:30
+                 callbackQueue:dispatch_get_main_queue()
+                  onCompletion:completion];
+}
+
++ (void)logInWithCredentials:(RLMSyncCredentials *)credential
+               authServerURL:(NSURL *)authServerURL
+                     timeout:(NSTimeInterval)timeout
+               callbackQueue:(dispatch_queue_t)callbackQueue
+                onCompletion:(RLMUserCompletionBlock)completion {
+    RLMSyncUser *user = [[RLMSyncUser alloc] initPrivate];
+    [RLMSyncUser _performLogInForUser:user
+                          credentials:credential
+                        authServerURL:authServerURL
+                              timeout:timeout
+                        callbackQueue:callbackQueue
+                      completionBlock:completion];
+}
+
+- (void)logOut {
+    if (!_user) {
+        return;
+    }
+    _user->log_out();
+    context_for(_user).invalidate_all_handles();
+}
+
+- (RLMUserErrorReportingBlock)errorHandler {
+    if (!_user) {
+        return nil;
+    }
+    return context_for(_user).error_handler();
+}
+
+- (void)setErrorHandler:(RLMUserErrorReportingBlock)errorHandler {
+    if (!_user) {
+        return;
+    }
+    context_for(_user).set_error_handler([errorHandler copy]);
+}
+
+- (nullable RLMSyncSession *)sessionForURL:(NSURL *)url {
+    if (!_user) {
+        return nil;
+    }
+    auto path = SyncManager::shared().path_for_realm(*_user, [url.absoluteString UTF8String]);
+    if (auto session = _user->session_for_on_disk_path(path)) {
+        return [[RLMSyncSession alloc] initWithSyncSession:session];
+    }
+    return nil;
+}
+
+- (NSArray<RLMSyncSession *> *)allSessions {
+    if (!_user) {
+        return @[];
+    }
+    NSMutableArray<RLMSyncSession *> *buffer = [NSMutableArray array];
+    auto sessions = _user->all_sessions();
+    for (auto session : sessions) {
+        [buffer addObject:[[RLMSyncSession alloc] initWithSyncSession:std::move(session)]];
+    }
+    return [buffer copy];
+}
+
+- (NSString *)identity {
+    if (!_user) {
+        return nil;
+    }
+    return @(_user->identity().c_str());
+}
+
+- (RLMSyncUserState)state {
+    if (!_user) {
+        return RLMSyncUserStateError;
+    }
+    switch (_user->state()) {
+        case SyncUser::State::Active:
+            return RLMSyncUserStateActive;
+        case SyncUser::State::LoggedOut:
+            return RLMSyncUserStateLoggedOut;
+        case SyncUser::State::Error:
+            return RLMSyncUserStateError;
+    }
+}
+
+- (NSURL *)authenticationServer {
+    if (!_user || _user->token_type() == SyncUser::TokenType::Admin) {
+        return nil;
+    }
+    return [NSURL URLWithString:@(_user->server_url().c_str())];
+}
+
+- (BOOL)isAdmin {
+    if (!_user) {
+        return NO;
+    }
+    return _user->is_admin();
+}
+
+#pragma mark - Passwords
+
+- (void)changePassword:(NSString *)newPassword completion:(RLMPasswordChangeStatusBlock)completion {
+    [self changePassword:newPassword forUserID:self.identity completion:completion];
+}
+
+- (void)changePassword:(NSString *)newPassword forUserID:(NSString *)userID completion:(RLMPasswordChangeStatusBlock)completion {
+    if (self.state != RLMSyncUserStateActive) {
+        completion([NSError errorWithDomain:RLMSyncErrorDomain
+                                       code:RLMSyncErrorClientSessionError
+                                   userInfo:nil]);
+        return;
+    }
+    [RLMNetworkClient sendRequestToEndpoint:[RLMSyncChangePasswordEndpoint endpoint]
+                                     server:self.authenticationServer
+                                       JSON:@{kRLMSyncTokenKey: self._refreshToken,
+                                              kRLMSyncUserIDKey: userID,
+                                              kRLMSyncDataKey: @{ kRLMSyncNewPasswordKey: newPassword }
+                                              }
+                                    timeout:60
+                                 completion:^(NSError *error, __unused NSDictionary *json) {
+        completion(error);
+    }];
+}
+
+#pragma mark - Administrator API
+
+- (void)retrieveInfoForUser:(NSString *)providerUserIdentity
+           identityProvider:(RLMIdentityProvider)provider
+                 completion:(RLMRetrieveUserBlock)completion {
+    [RLMNetworkClient sendRequestToEndpoint:[RLMSyncGetUserInfoEndpoint endpoint]
+                                     server:self.authenticationServer
+                                       JSON:@{
+                                              kRLMSyncProviderKey: provider,
+                                              kRLMSyncProviderIDKey: providerUserIdentity,
+                                              kRLMSyncTokenKey: self._refreshToken
+                                              }
+                                 completion:^(NSError *error, NSDictionary *json) {
+                                     if (error) {
+                                         completion(nil, error);
+                                         return;
+                                     }
+                                     RLMUserResponseModel *model = [[RLMUserResponseModel alloc] initWithDictionary:json];
+                                     if (!model) {
+                                         completion(nil, make_auth_error_bad_response(json));
+                                         return;
+                                     }
+                                     completion([RLMSyncUserInfo syncUserInfoWithModel:model], nil);
+                                 }];
+}
+
+#pragma mark - Permissions API
+
+static void verifyInRunLoop() {
+    if (!RLMIsInRunLoop()) {
+        @throw RLMException(@"Can only access or modify permissions from a thread which has a run loop (by default, only the main thread).");
+    }
+}
+
+- (void)retrievePermissionsWithCallback:(RLMPermissionResultsBlock)callback {
+    verifyInRunLoop();
+    if (!_user || _user->state() == SyncUser::State::Error) {
+        callback(nullptr, make_permission_error_get(@"Permissions cannot be retrieved using an invalid user."));
+        return;
+    }
+    Permissions::get_permissions(_user, RLMWrapPermissionResultsCallback(callback), *_configMaker);
+}
+
+- (void)applyPermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback {
+    verifyInRunLoop();
+    if (!_user || _user->state() == SyncUser::State::Error) {
+        callback(make_permission_error_change(@"Permissions cannot be applied using an invalid user."));
+        return;
+    }
+    Permissions::set_permission(_user,
+                                [permission rawPermission],
+                                RLMWrapPermissionStatusCallback(callback),
+                                *_configMaker);
+}
+
+- (void)revokePermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback {
+    verifyInRunLoop();
+    if (!_user || _user->state() == SyncUser::State::Error) {
+        callback(make_permission_error_change(@"Permissions cannot be revoked using an invalid user."));
+        return;
+    }
+    Permissions::delete_permission(_user,
+                                   [permission rawPermission],
+                                   RLMWrapPermissionStatusCallback(callback),
+                                   *_configMaker);
+}
+
+- (void)createOfferForRealmAtURL:(NSURL *)url
+                     accessLevel:(RLMSyncAccessLevel)accessLevel
+                      expiration:(NSDate *)expirationDate
+                        callback:(RLMPermissionOfferStatusBlock)callback {
+    verifyInRunLoop();
+    if (!_user || _user->state() == SyncUser::State::Error) {
+        callback(nil, make_permission_error_change(@"A permission offer cannot be created using an invalid user."));
+        return;
+    }
+    auto cb = [callback](util::Optional<std::string> token, std::exception_ptr ptr) {
+        if (ptr) {
+            NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeOffer);
+            REALM_ASSERT_DEBUG(error);
+            callback(nil, error);
+        } else {
+            REALM_ASSERT_DEBUG(token);
+            callback(@(token->c_str()), nil);
+        }
+    };
+    auto offer = PermissionOffer{
+        [tildeSubstitutedPathForRealmURL(url, self.identity) UTF8String],
+        accessLevelForObjCAccessLevel(accessLevel),
+        RLMTimestampForNSDate(expirationDate),
+    };
+    Permissions::make_offer(_user, std::move(offer), std::move(cb), *_configMaker);
+}
+
+- (void)acceptOfferForToken:(NSString *)token
+                   callback:(RLMPermissionOfferResponseStatusBlock)callback {
+    verifyInRunLoop();
+    if (!_user || _user->state() == SyncUser::State::Error) {
+        callback(nil, make_permission_error_change(@"A permission offer cannot be accepted by an invalid user."));
+        return;
+    }
+    NSURLComponents *baseURL = [NSURLComponents componentsWithURL:self.authenticationServer
+                                          resolvingAgainstBaseURL:YES];
+    if ([baseURL.scheme isEqualToString:@"http"]) {
+        baseURL.scheme = @"realm";
+    } else if ([baseURL.scheme isEqualToString:@"https"]) {
+        baseURL.scheme = @"realms";
+    }
+    auto cb = [baseURL, callback](util::Optional<std::string> raw_path, std::exception_ptr ptr) {
+        if (ptr) {
+            NSError *error = translateSyncExceptionPtrToError(std::move(ptr), RLMPermissionActionTypeAcceptOffer);
+            REALM_ASSERT_DEBUG(error);
+            callback(nil, error);
+        } else {
+            // Note that ROS currently vends the path to the Realm, so we need to construct the full URL ourselves.
+            REALM_ASSERT_DEBUG(raw_path);
+            baseURL.path = @(raw_path->c_str());
+            callback([baseURL URL], nil);
+        }
+    };
+    Permissions::accept_offer(_user, [token UTF8String], std::move(cb), *_configMaker);
+}
+
+#pragma mark - Private API
+
++ (void)_setUpBindingContextFactory {
+    SyncUser::set_binding_context_factory([] {
+        return std::make_shared<CocoaSyncUserContext>();
+    });
+}
+
+- (NSString *)_refreshToken {
+    if (!_user) {
+        return nil;
+    }
+    return @(_user->refresh_token().c_str());
+}
+
+- (std::shared_ptr<SyncUser>)_syncUser {
+    return _user;
+}
+
++ (void)_performLogInForUser:(RLMSyncUser *)user
+                 credentials:(RLMSyncCredentials *)credentials
+               authServerURL:(NSURL *)authServerURL
+                     timeout:(NSTimeInterval)timeout
+               callbackQueue:(dispatch_queue_t)callbackQueue
+             completionBlock:(RLMUserCompletionBlock)completion {
+    // Special credential login should be treated differently.
+    if (credentials.provider == RLMIdentityProviderAccessToken) {
+        [self _performLoginForDirectAccessTokenCredentials:credentials
+                                                      user:user
+                                             authServerURL:authServerURL
+                                           completionBlock:completion];
+        return;
+    }
+    if (!authServerURL) {
+        @throw RLMException(@"A user cannot be logged in without specifying an authentication server URL.");
+    }
+
+    // Prepare login network request
+    NSMutableDictionary *json = [@{
+                                   kRLMSyncProviderKey: credentials.provider,
+                                   kRLMSyncDataKey: credentials.token,
+                                   kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID,
+                                   } mutableCopy];
+    NSMutableDictionary *info = [(credentials.userInfo ?: @{}) mutableCopy];
+
+    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
+                NSError *badResponseError = make_auth_error_bad_response(json);
+                dispatch_async(callbackQueue, ^{
+                    completion(nil, badResponseError);
+                });
+                return;
+            } else {
+                std::string server_url = authServerURL.absoluteString.UTF8String;
+                SyncUserIdentifier identity{[model.refreshToken.tokenData.identity UTF8String], std::move(server_url)};
+                auto sync_user = SyncManager::shared().get_user(identity , [model.refreshToken.token UTF8String]);
+                if (!sync_user) {
+                    NSError *authError = make_auth_error_client_issue();
+                    dispatch_async(callbackQueue, ^{
+                        completion(nil, authError);
+                    });
+                    return;
+                }
+                sync_user->set_is_admin(model.refreshToken.tokenData.isAdmin);
+                user->_user = sync_user;
+                dispatch_async(callbackQueue, ^{
+                    completion(user, nil);
+                });
+            }
+        } else {
+            // Something else went wrong
+            dispatch_async(callbackQueue, ^{
+                completion(nil, error);
+            });
+        }
+    };
+    [RLMNetworkClient sendRequestToEndpoint:[RLMSyncAuthEndpoint endpoint]
+                                     server:authServerURL
+                                       JSON:json
+                                    timeout:timeout
+                                 completion:^(NSError *error, NSDictionary *dictionary) {
+                                     dispatch_async(callbackQueue, ^{
+                                         handler(error, dictionary);
+                                     });
+                                 }];
+}
+
++ (void)_performLoginForDirectAccessTokenCredentials:(RLMSyncCredentials *)credentials
+                                                user:(RLMSyncUser *)user
+                                       authServerURL:(NSURL *)serverURL
+                                     completionBlock:(nonnull RLMUserCompletionBlock)completion {
+    NSString *identity = credentials.userInfo[kRLMSyncIdentityKey];
+    std::shared_ptr<SyncUser> sync_user;
+    if (serverURL) {
+        NSString *scheme = serverURL.scheme;
+        if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) {
+            @throw RLMException(@"The Realm Object Server authentication URL provided for this user, \"%@\", "
+                                @" is invalid. It must begin with http:// or https://.", serverURL);
+        }
+        // Retrieve the user based on the auth server URL.
+        util::Optional<std::string> identity_string;
+        if (identity) {
+            identity_string = std::string(identity.UTF8String);
+        }
+        sync_user = SyncManager::shared().get_admin_token_user([serverURL absoluteString].UTF8String,
+                                                               credentials.token.UTF8String,
+                                                               std::move(identity_string));
+    } else {
+        // Retrieve the user based on the identity.
+        if (!identity) {
+            @throw RLMException(@"A direct access credential must specify either an identity, a server URL, or both.");
+        }
+        sync_user = SyncManager::shared().get_admin_token_user_from_identity(identity.UTF8String,
+                                                                             none,
+                                                                             credentials.token.UTF8String);
+    }
+    if (!sync_user) {
+        completion(nil, make_auth_error_client_issue());
+        return;
+    }
+    user->_user = sync_user;
+    completion(user, nil);
+}
+
+@end
+
+#pragma mark - RLMSyncUserInfo
+
+@implementation RLMSyncUserInfo
+
+- (instancetype)initPrivate {
+    return [super init];
+}
+
++ (instancetype)syncUserInfoWithModel:(RLMUserResponseModel *)model {
+    RLMSyncUserInfo *info = [[RLMSyncUserInfo alloc] initPrivate];
+    info.accounts = model.accounts;
+    info.metadata = model.metadata;
+    info.isAdmin = model.isAdmin;
+    info.identity = model.identity;
+    return info;
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMSyncUtil.mm b/iOS/Pods/Realm/Realm/RLMSyncUtil.mm
new file mode 100644 (file)
index 0000000..6e5fc95
--- /dev/null
@@ -0,0 +1,254 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+#import "RLMJSONModels.h"
+#import "RLMSyncConfiguration_Private.hpp"
+#import "RLMSyncUtil_Private.hpp"
+#import "RLMSyncUser_Private.hpp"
+#import "RLMRealmConfiguration+Sync.h"
+#import "RLMRealmConfiguration_Private.hpp"
+#import "RLMUtil.hpp"
+
+#import "shared_realm.hpp"
+
+#import "sync/sync_permission.hpp"
+#import "sync/sync_user.hpp"
+
+RLMIdentityProvider const RLMIdentityProviderAccessToken = @"_access_token";
+
+NSString *const RLMSyncErrorDomain = @"io.realm.sync";
+NSString *const RLMSyncAuthErrorDomain = @"io.realm.sync.auth";
+NSString *const RLMSyncPermissionErrorDomain = @"io.realm.sync.permission";
+
+NSString *const kRLMSyncPathOfRealmBackupCopyKey            = @"recovered_realm_location_path";
+NSString *const kRLMSyncErrorActionTokenKey                 = @"error_action_token";
+
+NSString *const kRLMSyncAppIDKey                = @"app_id";
+NSString *const kRLMSyncDataKey                 = @"data";
+NSString *const kRLMSyncErrorJSONKey            = @"json";
+NSString *const kRLMSyncErrorStatusCodeKey      = @"statusCode";
+NSString *const kRLMSyncIdentityKey             = @"identity";
+NSString *const kRLMSyncIsAdminKey              = @"is_admin";
+NSString *const kRLMSyncNewPasswordKey          = @"new_password";
+NSString *const kRLMSyncPasswordKey             = @"password";
+NSString *const kRLMSyncPathKey                 = @"path";
+NSString *const kRLMSyncProviderKey             = @"provider";
+NSString *const kRLMSyncProviderIDKey           = @"provider_id";
+NSString *const kRLMSyncRegisterKey             = @"register";
+NSString *const kRLMSyncTokenKey                = @"token";
+NSString *const kRLMSyncUnderlyingErrorKey      = @"underlying_error";
+NSString *const kRLMSyncUserIDKey               = @"user_id";
+
+#pragma mark - C++ APIs
+
+namespace {
+
+NSError *make_permission_error(NSString *description, util::Optional<NSInteger> code, RLMSyncPermissionError type) {
+    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
+    if (description) {
+        userInfo[NSLocalizedDescriptionKey] = description;
+    }
+    if (code) {
+        userInfo[kRLMSyncErrorStatusCodeKey] = @(*code);
+    }
+    return [NSError errorWithDomain:RLMSyncPermissionErrorDomain code:type userInfo:userInfo];
+}
+
+}
+
+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();
+}
+
+NSError *translateSyncExceptionPtrToError(std::exception_ptr ptr, RLMPermissionActionType type) {
+    NSError *error = nil;
+    try {
+        std::rethrow_exception(ptr);
+    } catch (PermissionActionException const& ex) {
+        switch (type) {
+            case RLMPermissionActionTypeGet:
+                error = make_permission_error_get(@(ex.what()), ex.code);
+                break;
+            case RLMPermissionActionTypeChange:
+                error = make_permission_error_change(@(ex.what()), ex.code);
+                break;
+            case RLMPermissionActionTypeOffer:
+                error = make_permission_error_offer(@(ex.what()), ex.code);
+                break;
+            case RLMPermissionActionTypeAcceptOffer:
+                error = make_permission_error_accept_offer(@(ex.what()), ex.code);
+                break;
+        }
+    }
+    catch (const std::exception &exp) {
+        RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, exp), &error);
+    }
+    return error;
+}
+
+std::shared_ptr<SyncSession> sync_session_for_realm(RLMRealm *realm) {
+    Realm::Config realmConfig = realm.configuration.config;
+    if (auto config = realmConfig.sync_config) {
+        std::shared_ptr<SyncUser> user = config->user;
+        if (user && user->state() != SyncUser::State::Error) {
+            return user->session_for_on_disk_path(realmConfig.path);
+        }
+    }
+    return nullptr;
+}
+
+CocoaSyncUserContext& context_for(const std::shared_ptr<realm::SyncUser>& user)
+{
+    return *std::static_pointer_cast<CocoaSyncUserContext>(user->binding_context());
+}
+
+AccessLevel accessLevelForObjCAccessLevel(RLMSyncAccessLevel level) {
+    switch (level) {
+        case RLMSyncAccessLevelNone:
+            return AccessLevel::None;
+        case RLMSyncAccessLevelRead:
+            return AccessLevel::Read;
+        case RLMSyncAccessLevelWrite:
+            return AccessLevel::Write;
+        case RLMSyncAccessLevelAdmin:
+            return AccessLevel::Admin;
+    }
+    REALM_UNREACHABLE();
+}
+
+RLMSyncAccessLevel objCAccessLevelForAccessLevel(AccessLevel level) {
+    switch (level) {
+        case AccessLevel::None:
+            return RLMSyncAccessLevelNone;
+        case AccessLevel::Read:
+            return RLMSyncAccessLevelRead;
+        case AccessLevel::Write:
+            return RLMSyncAccessLevelWrite;
+        case AccessLevel::Admin:
+            return RLMSyncAccessLevelAdmin;
+    }
+    REALM_UNREACHABLE();
+}
+
+NSError *make_auth_error_bad_response(NSDictionary *json) {
+    return [NSError errorWithDomain:RLMSyncAuthErrorDomain
+                               code:RLMSyncAuthErrorBadResponse
+                           userInfo:json ? @{kRLMSyncErrorJSONKey: json} : nil];
+}
+
+NSError *make_auth_error_http_status(NSInteger status) {
+    return [NSError errorWithDomain:RLMSyncAuthErrorDomain
+                               code:RLMSyncAuthErrorHTTPStatusCodeError
+                           userInfo:@{kRLMSyncErrorStatusCodeKey: @(status)}];
+}
+
+NSError *make_auth_error_client_issue() {
+    return [NSError errorWithDomain:RLMSyncAuthErrorDomain
+                               code:RLMSyncAuthErrorClientSessionError
+                           userInfo:nil];
+}
+
+NSError *make_auth_error(RLMSyncErrorResponseModel *model) {
+    NSMutableDictionary<NSString *, NSString *> *userInfo = [NSMutableDictionary dictionaryWithCapacity:2];
+    if (NSString *description = model.title) {
+        [userInfo setObject:description forKey:NSLocalizedDescriptionKey];
+    }
+    if (NSString *hint = model.hint) {
+        [userInfo setObject:hint forKey:NSLocalizedRecoverySuggestionErrorKey];
+    }
+    return [NSError errorWithDomain:RLMSyncAuthErrorDomain code:model.code userInfo:userInfo];
+}
+
+NSError *make_permission_error_get(NSString *description, util::Optional<NSInteger> code) {
+    return make_permission_error(description, std::move(code), RLMSyncPermissionErrorGetFailed);
+}
+
+NSError *make_permission_error_change(NSString *description, util::Optional<NSInteger> code) {
+    return make_permission_error(description, std::move(code), RLMSyncPermissionErrorChangeFailed);
+}
+
+NSError *make_permission_error_offer(NSString *description, util::Optional<NSInteger> code) {
+    return make_permission_error(description, std::move(code), RLMSyncPermissionErrorOfferFailed);
+}
+
+NSError *make_permission_error_accept_offer(NSString *description, util::Optional<NSInteger> code) {
+    return make_permission_error(description, std::move(code), RLMSyncPermissionErrorAcceptOfferFailed);
+}
+
+NSError *make_sync_error(RLMSyncSystemErrorKind kind, NSString *description, NSInteger code, NSDictionary *custom) {
+    NSMutableDictionary *buffer = [custom ?: @{} mutableCopy];
+    buffer[NSLocalizedDescriptionKey] = description;
+    if (code != NSNotFound) {
+        buffer[kRLMSyncErrorStatusCodeKey] = @(code);
+    }
+
+    RLMSyncError errorCode;
+    switch (kind) {
+        case RLMSyncSystemErrorKindClientReset:
+            errorCode = RLMSyncErrorClientResetError;
+            break;
+        case RLMSyncSystemErrorKindPermissionDenied:
+            errorCode = RLMSyncErrorPermissionDeniedError;
+            break;
+        case RLMSyncSystemErrorKindUser:
+            errorCode = RLMSyncErrorClientUserError;
+            break;
+        case RLMSyncSystemErrorKindSession:
+            errorCode = RLMSyncErrorClientSessionError;
+            break;
+        case RLMSyncSystemErrorKindConnection:
+        case RLMSyncSystemErrorKindClient:
+        case RLMSyncSystemErrorKindUnknown:
+            errorCode = RLMSyncErrorClientInternalError;
+            break;
+    }
+    return [NSError errorWithDomain:RLMSyncErrorDomain
+                               code:errorCode
+                           userInfo:[buffer copy]];
+}
+
+NSError *make_sync_error(NSError *wrapped_auth_error) {
+    return [NSError errorWithDomain:RLMSyncErrorDomain
+                               code:RLMSyncErrorUnderlyingAuthError
+                           userInfo:@{kRLMSyncUnderlyingErrorKey: wrapped_auth_error}];
+}
+
+NSError *make_sync_error(std::error_code sync_error, RLMSyncSystemErrorKind kind) {
+    return [NSError errorWithDomain:RLMSyncErrorDomain
+                               code:kind
+                           userInfo:@{
+                                      NSLocalizedDescriptionKey: @(sync_error.message().c_str()),
+                                      kRLMSyncErrorStatusCodeKey: @(sync_error.value())
+                                      }];
+}
diff --git a/iOS/Pods/Realm/Realm/RLMThreadSafeReference.mm b/iOS/Pods/Realm/Realm/RLMThreadSafeReference.mm
new file mode 100644 (file)
index 0000000..7149fa4
--- /dev/null
@@ -0,0 +1,79 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMThreadSafeReference_Private.hpp"
+#import "RLMUtil.hpp"
+
+template<typename Function>
+static auto translateErrors(Function&& f) {
+    try {
+        return f();
+    }
+    catch (std::exception const& e) {
+        @throw RLMException(e);
+    }
+}
+
+@implementation RLMThreadSafeReference {
+    std::unique_ptr<realm::ThreadSafeReferenceBase> _reference;
+    id _metadata;
+    Class _type;
+}
+
+- (instancetype)initWithThreadConfined:(id<RLMThreadConfined>)threadConfined {
+    if (!(self = [super init])) {
+        return nil;
+    }
+
+    REALM_ASSERT_DEBUG([threadConfined conformsToProtocol:@protocol(RLMThreadConfined)]);
+    if (![threadConfined conformsToProtocol:@protocol(RLMThreadConfined_Private)]) {
+        @throw RLMException(@"Illegal custom conformance to `RLMThreadConfined` by `%@`", threadConfined.class);
+    } else if (threadConfined.invalidated) {
+        @throw RLMException(@"Cannot construct reference to invalidated object");
+    } else if (!threadConfined.realm) {
+        @throw RLMException(@"Cannot construct reference to unmanaged object, "
+                            "which can be passed across threads directly");
+    }
+
+    translateErrors([&] {
+        _reference = [(id<RLMThreadConfined_Private>)threadConfined makeThreadSafeReference];
+        _metadata = ((id<RLMThreadConfined_Private>)threadConfined).objectiveCMetadata;
+    });
+    _type = threadConfined.class;
+
+    return self;
+}
+
++ (instancetype)referenceWithThreadConfined:(id<RLMThreadConfined>)threadConfined {
+    return [[self alloc] initWithThreadConfined:threadConfined];
+}
+
+- (id<RLMThreadConfined>)resolveReferenceInRealm:(RLMRealm *)realm {
+    if (!_reference) {
+        @throw RLMException(@"Can only resolve a thread safe reference once.");
+    }
+    return translateErrors([&] {
+        return [_type objectWithThreadSafeReference:std::move(_reference) metadata:_metadata realm:realm];
+    });
+}
+
+- (BOOL)isInvalidated {
+    return !_reference;
+}
+
+@end
diff --git a/iOS/Pods/Realm/Realm/RLMUpdateChecker.mm b/iOS/Pods/Realm/Realm/RLMUpdateChecker.mm
new file mode 100644 (file)
index 0000000..4ff0dd6
--- /dev/null
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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;
+    }
+
+    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"alpha|beta|rc"
+                                                                           options:(NSRegularExpressionOptions)0
+                                                                             error:nil];
+    NSUInteger numberOfMatches = [regex numberOfMatchesInString:REALM_COCOA_VERSION
+                                                        options:(NSMatchingOptions)0
+                                                          range:NSMakeRange(0, REALM_COCOA_VERSION.length)];
+
+    if (numberOfMatches > 0) {
+        // pre-release version, skip update checking
+        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/iOS/Pods/Realm/Realm/RLMUtil.mm b/iOS/Pods/Realm/Realm/RLMUtil.mm
new file mode 100644 (file)
index 0000000..1298602
--- /dev/null
@@ -0,0 +1,418 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <realm/mixed.hpp>
+#import <realm/table_view.hpp>
+
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#if !defined(REALM_COCOA_VERSION)
+#import "RLMVersion.h"
+#endif
+
+static inline bool numberIsInteger(__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 numberIsBool(__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 (numberIsInteger(obj)) {
+        int value = [obj intValue];
+        return value == 0 || value == 1;
+    }
+
+    return false;
+}
+
+static inline bool numberIsFloat(__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 or is NaN.
+           (data_type == *@encode(double) && (ABS([obj doubleValue]) <= FLT_MAX || isnan([obj doubleValue])));
+}
+
+static inline bool numberIsDouble(__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);
+}
+
+static inline RLMArray *asRLMArray(__unsafe_unretained id const value) {
+    return RLMDynamicCast<RLMArray>(value) ?: RLMDynamicCast<RLMListBase>(value)._rlmArray;
+}
+
+static inline bool checkArrayType(__unsafe_unretained RLMArray *const array,
+                                  RLMPropertyType type, bool optional,
+                                  __unsafe_unretained NSString *const objectClassName) {
+    return array.type == type && array.optional == optional
+        && (type != RLMPropertyTypeObject || [array.objectClassName isEqualToString:objectClassName]);
+}
+
+BOOL RLMValidateValue(__unsafe_unretained id const value,
+                      RLMPropertyType type, bool optional, bool array,
+                      __unsafe_unretained NSString *const objectClassName) {
+    if (optional && !RLMCoerceToNil(value)) {
+        return YES;
+    }
+    if (array) {
+        if (auto rlmArray = asRLMArray(value)) {
+            return checkArrayType(rlmArray, type, optional, objectClassName);
+        }
+        if ([value conformsToProtocol:@protocol(NSFastEnumeration)]) {
+            // check each element for compliance
+            for (id el in (id<NSFastEnumeration>)value) {
+                if (!RLMValidateValue(el, type, optional, false, objectClassName)) {
+                    return NO;
+                }
+            }
+            return YES;
+        }
+        if (!value || value == NSNull.null) {
+            return YES;
+        }
+        return NO;
+    }
+
+    switch (type) {
+        case RLMPropertyTypeString:
+            return [value isKindOfClass:[NSString class]];
+        case RLMPropertyTypeBool:
+            if ([value isKindOfClass:[NSNumber class]]) {
+                return numberIsBool(value);
+            }
+            return NO;
+        case RLMPropertyTypeDate:
+            return [value isKindOfClass:[NSDate class]];
+        case RLMPropertyTypeInt:
+            if (NSNumber *number = RLMDynamicCast<NSNumber>(value)) {
+                return numberIsInteger(number);
+            }
+            return NO;
+        case RLMPropertyTypeFloat:
+            if (NSNumber *number = RLMDynamicCast<NSNumber>(value)) {
+                return numberIsFloat(number);
+            }
+            return NO;
+        case RLMPropertyTypeDouble:
+            if (NSNumber *number = RLMDynamicCast<NSNumber>(value)) {
+                return numberIsDouble(number);
+            }
+            return NO;
+        case RLMPropertyTypeData:
+            return [value isKindOfClass:[NSData class]];
+        case RLMPropertyTypeAny:
+            return NO;
+        case RLMPropertyTypeLinkingObjects:
+            return YES;
+        case RLMPropertyTypeObject: {
+            // only NSNull, nil, or objects which derive from RLMObject and match the given
+            // object class are valid
+            RLMObjectBase *objBase = RLMDynamicCast<RLMObjectBase>(value);
+            return objBase && [objBase->_objectSchema.className isEqualToString:objectClassName];
+        }
+    }
+    @throw RLMException(@"Invalid RLMPropertyType specified");
+}
+
+void RLMThrowTypeError(__unsafe_unretained id const obj,
+                       __unsafe_unretained RLMObjectSchema *const objectSchema,
+                       __unsafe_unretained RLMProperty *const prop) {
+    @throw RLMException(@"Invalid value '%@' of type '%@' for '%@%s'%s property '%@.%@'.",
+                        obj, [obj class],
+                        prop.objectClassName ?: RLMTypeToString(prop.type), prop.optional ? "?" : "",
+                        prop.array ? " array" : "", objectSchema.className, prop.name);
+}
+
+void RLMValidateValueForProperty(__unsafe_unretained id const obj,
+                                 __unsafe_unretained RLMObjectSchema *const objectSchema,
+                                 __unsafe_unretained RLMProperty *const prop,
+                                 bool validateObjects) {
+    // This duplicates a lot of the checks in RLMIsObjectValidForProperty()
+    // for the sake of more specific error messages
+    if (prop.array) {
+        // nil is considered equivalent to an empty array for historical reasons
+        // since we don't support null arrays (only arrays containing null),
+        // it's not worth the BC break to change this
+        if (!obj || obj == NSNull.null) {
+            return;
+        }
+        if (![obj conformsToProtocol:@protocol(NSFastEnumeration)]) {
+            @throw RLMException(@"Invalid value (%@) for '%@%s' array property '%@.%@': value is not enumerable.",
+                                obj, prop.objectClassName ?: RLMTypeToString(prop.type), prop.optional ? "?" : "",
+                                objectSchema.className, prop.name);
+        }
+        if (!validateObjects && prop.type == RLMPropertyTypeObject) {
+            return;
+        }
+
+        if (RLMArray *array = asRLMArray(obj)) {
+            if (!checkArrayType(array, prop.type, prop.optional, prop.objectClassName)) {
+                @throw RLMException(@"RLMArray<%@%s> does not match expected type '%@%s' for property '%@.%@'.",
+                                    array.objectClassName ?: RLMTypeToString(array.type), array.optional ? "?" : "",
+                                    prop.objectClassName ?: RLMTypeToString(prop.type), prop.optional ? "?" : "",
+                                    objectSchema.className, prop.name);
+            }
+            return;
+        }
+
+        for (id value in obj) {
+            if (!RLMValidateValue(value, prop.type, prop.optional, false, prop.objectClassName)) {
+                RLMThrowTypeError(value, objectSchema, prop);
+            }
+        }
+        return;
+    }
+
+    // For create() we want to skip the validation logic for objects because
+    // we allow much fuzzier matching (any KVC-compatible object with at least
+    // all the non-defaulted fields), and all the logic for that lives in the
+    // object store rather than here
+    if (prop.type == RLMPropertyTypeObject && !validateObjects) {
+        return;
+    }
+    if (RLMIsObjectValidForProperty(obj, prop)) {
+        return;
+    }
+
+    RLMThrowTypeError(obj, objectSchema, prop);
+}
+
+BOOL RLMIsObjectValidForProperty(__unsafe_unretained id const obj,
+                                 __unsafe_unretained RLMProperty *const property) {
+    return RLMValidateValue(obj, property.type, property.optional, property.array, property.objectClassName);
+}
+
+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(@"%s", 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}];
+}
+
+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});
+    }
+}
+
+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/iOS/Pods/Realm/Realm/Realm.modulemap b/iOS/Pods/Realm/Realm/Realm.modulemap
new file mode 100644 (file)
index 0000000..42845f6
--- /dev/null
@@ -0,0 +1,31 @@
+framework module Realm {
+    umbrella header "Realm.h"
+
+    export *
+    module * { export * }
+
+    explicit module Private {
+        header "RLMAccessor.h"
+        header "RLMArray_Private.h"
+        header "RLMCollection_Private.h"
+        header "RLMListBase.h"
+        header "RLMObject_Private.h"
+        header "RLMObjectBase_Dynamic.h"
+        header "RLMObjectBase_Private.h"
+        header "RLMObjectSchema_Private.h"
+        header "RLMObjectStore.h"
+        header "RLMOptionalBase.h"
+        header "RLMProperty_Private.h"
+        header "RLMRealm_Private.h"
+        header "RLMRealmConfiguration_Private.h"
+        header "RLMResults_Private.h"
+        header "RLMSchema_Private.h"
+        header "RLMSyncConfiguration_Private.h"
+        header "RLMSyncUtil_Private.h"
+    }
+
+    explicit module Dynamic {
+        header "RLMRealm_Dynamic.h"
+        header "RLMObjectBase_Dynamic.h"
+    }
+}
diff --git a/iOS/Pods/Realm/build.sh b/iOS/Pods/Realm/build.sh
new file mode 100755 (executable)
index 0000000..04b73b4
--- /dev/null
@@ -0,0 +1,1516 @@
+#!/bin/bash
+
+##################################################################################
+# 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
+
+usage() {
+cat <<EOF
+Usage: sh $0 command [argument]
+
+command:
+  clean:                clean up/remove all generated files
+  download-core:        downloads core library (binary version)
+  download-object-server:  downloads and installs the Realm Object Server
+  download-sync:        downloads sync library (binary version, core+sync)
+  build:                builds all iOS  and OS X frameworks
+  ios-static:           builds fat iOS static framework
+  ios-dynamic:          builds iOS dynamic frameworks
+  ios-swift:            builds RealmSwift frameworks for iOS
+  watchos:              builds watchOS framwork
+  watchos-swift:        builds RealmSwift framework for watchOS
+  tvos:                 builds tvOS framework
+  tvos-swift:           builds RealmSwift framework for tvOS
+  osx:                  builds OS X framework
+  osx-swift:            builds RealmSwift framework for OS X
+  analyze-osx:          analyzes OS X framework
+  test:                 tests all iOS and OS X frameworks
+  test-all:             tests all iOS and OS X frameworks in both Debug and Release configurations
+  test-ios-static:      tests static iOS framework on 32-bit and 64-bit simulators
+  test-ios-dynamic:     tests dynamic iOS framework on 32-bit and 64-bit simulators
+  test-ios-swift:       tests RealmSwift iOS framework on 32-bit and 64-bit simulators
+  test-ios-devices:     tests ObjC & Swift iOS frameworks on all attached iOS devices
+  test-ios-devices-objc:  tests ObjC iOS framework on all attached iOS devices
+  test-ios-devices-swift: tests Swift iOS framework on all attached iOS devices
+  test-tvos:            tests tvOS framework
+  test-tvos-swift:      tests RealmSwift tvOS framework
+  test-tvos-devices:    tests ObjC & Swift tvOS frameworks on all attached tvOS devices
+  test-osx:             tests OS X framework
+  test-osx-swift:       tests RealmSwift OS X framework
+  verify:               verifies docs, osx, osx-swift, ios-static, ios-dynamic, ios-swift, ios-device in both Debug and Release configurations, swiftlint
+  verify-osx-object-server:  downloads the Realm Object Server and runs the Objective-C and Swift integration tests
+  docs:                 builds docs in docs/output
+  examples:             builds all examples
+  examples-ios:         builds all static iOS examples
+  examples-ios-swift:   builds all Swift iOS examples
+  examples-osx:         builds all OS X examples
+  get-version:          get the current version
+  set-version version:  set the version
+  cocoapods-setup:      download realm-core and create a stub RLMPlatform.h file to enable building via CocoaPods
+
+
+argument:
+  version: version in the x.y.z format
+
+environment variables:
+  XCMODE: xcodebuild (default), xcpretty or xctool
+  CONFIGURATION: Debug or Release (default)
+  REALM_CORE_VERSION: version in x.y.z format or "current" to use local build
+  REALM_EXTRA_BUILD_ARGUMENTS: additional arguments to pass to the build tool
+  REALM_XCODE_VERSION: the version number of Xcode to use (e.g.: 8.1)
+EOF
+}
+
+######################################
+# Xcode Helpers
+######################################
+
+xcode_version_major() {
+    echo "${REALM_XCODE_VERSION%%.*}"
+}
+
+xcode() {
+    mkdir -p build/DerivedData
+    CMD="xcodebuild -IDECustomDerivedDataLocation=build/DerivedData $@"
+    echo "Building with command:" $CMD
+    eval "$CMD"
+}
+
+xc() {
+    # Logs xcodebuild output in realtime
+    : ${NSUnbufferedIO:=YES}
+    args="$@ SWIFT_VERSION=$REALM_SWIFT_VERSION $REALM_EXTRA_BUILD_ARGUMENTS"
+    if [[ "$XCMODE" == "xcodebuild" ]]; then
+        xcode "$args"
+    elif [[ "$XCMODE" == "xcpretty" ]]; then
+        mkdir -p build
+        xcode "$args" | tee build/build.log | xcpretty -c ${XCPRETTY_PARAMS} || {
+            echo "The raw xcodebuild output is available in build/build.log"
+            exit 1
+        }
+    elif [[ "$XCMODE" == "xctool" ]]; then
+        xctool "$args"
+    fi
+}
+
+copy_bcsymbolmap() {
+    find "$1" -name '*.bcsymbolmap' -type f -exec cp {} "$2" \;
+}
+
+build_combined() {
+    local scheme="$1"
+    local module_name="$2"
+    local os="$3"
+    local simulator="$4"
+    local scope_suffix="$5"
+    local version_suffix="$6"
+    local config="$CONFIGURATION"
+
+    local destination=""
+    local os_name=""
+    if [[ "$os" == "iphoneos" ]]; then
+        os_name="ios"
+        destination="iPhone 6"
+    elif [[ "$os" == "watchos"  ]]; then
+        os_name="$os"
+        destination="Apple Watch - 42mm"
+    elif [[ "$os" == "appletvos"  ]]; then
+        os_name="tvos"
+        if (( $(xcode_version_major) >= 9 )); then
+            destination="Apple TV"
+        else
+            destination="Apple TV 1080p"
+        fi
+    fi
+
+    # Derive build paths
+    local build_products_path="build/DerivedData/Realm/Build/Products"
+    local product_name="$module_name.framework"
+    local binary_path="$module_name"
+    local os_path="$build_products_path/$config-$os$scope_suffix/$product_name"
+    local simulator_path="$build_products_path/$config-$simulator$scope_suffix/$product_name"
+    local out_path="build/$os_name$scope_suffix$version_suffix"
+
+    # Build for each platform
+    xc "-scheme '$scheme' -configuration $config -sdk $os"
+    xc "-scheme '$scheme' -configuration $config -sdk $simulator -destination 'name=$destination' ONLY_ACTIVE_ARCH=NO"
+
+    # Combine .swiftmodule
+    if [ -d $simulator_path/Modules/$module_name.swiftmodule ]; then
+      cp $simulator_path/Modules/$module_name.swiftmodule/* $os_path/Modules/$module_name.swiftmodule/
+    fi
+
+    # Copy *.bcsymbolmap to .framework for submitting app with bitcode
+    copy_bcsymbolmap "$build_products_path/$config-$os$scope_suffix" "$os_path"
+
+    # Retrieve build products
+    clean_retrieve $os_path $out_path $product_name
+
+    # Combine ar archives
+    LIPO_OUTPUT="$out_path/$product_name/$module_name"
+    xcrun lipo -create "$simulator_path/$binary_path" "$os_path/$binary_path" -output "$LIPO_OUTPUT"
+
+    if [[ "$destination" != "" && "$config" == "Release" ]]; then
+        sh build.sh binary-has-bitcode "$LIPO_OUTPUT"
+    fi
+}
+
+clean_retrieve() {
+  mkdir -p "$2"
+  rm -rf "$2/$3"
+  cp -R "$1" "$2"
+}
+
+move_to_clean_dir() {
+    rm -rf "$2"
+    mkdir -p "$2"
+    mv "$1" "$2"
+}
+
+test_ios_static() {
+    destination="$1"
+    xc "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' build"
+    if (( $(xcode_version_major) < 9 )); then
+        xc "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'"
+    fi
+
+    # Xcode's depending tracking is lacking and it doesn't realize that the Realm static framework's static library
+    # needs to be recreated when the active architectures change. Help Xcode out by removing the static library.
+    settings=$(xcode "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' -showBuildSettings")
+    path=$(echo "$settings" | awk '/CONFIGURATION_BUILD_DIR/ { cbd = $3; } /EXECUTABLE_PATH/ { ep = $3; } END { printf "%s/%s\n", cbd, ep; }')
+    rm "$path"
+
+    xc "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' test"
+}
+
+######################################
+# Device Test Helper
+######################################
+
+test_devices() {
+    local serial_numbers=()
+    local awk_script="
+    /^ +Vendor ID: / { is_apple = 0; }
+    /^ +Vendor ID: 0x05[aA][cC] / { is_apple = 1; }
+    /^ +Serial Number: / {
+        if (is_apple) {
+            match(\$0, /^ +Serial Number: /);
+            print substr(\$0, RLENGTH + 1);
+        }
+    }
+    "
+    local serial_numbers_text=$(/usr/sbin/system_profiler SPUSBDataType | /usr/bin/awk "$awk_script")
+    while read -r number; do
+        if [[ "$number" != "" ]]; then
+            serial_numbers+=("$number")
+        fi
+    done <<< "$serial_numbers_text"
+    if [[ ${#serial_numbers[@]} == 0 ]]; then
+        echo "At least one iOS/tvOS device must be connected to this computer to run device tests"
+        if [ -z "${JENKINS_HOME}" ]; then
+            # Don't fail if running locally and there's no device
+            exit 0
+        fi
+        exit 1
+    fi
+    local sdk="$1"
+    local scheme="$2"
+    local configuration="$3"
+    local failed=0
+    for device in "${serial_numbers[@]}"; do
+        xc "-scheme '$scheme' -configuration $configuration -destination 'id=$device' -sdk $sdk test" || failed=1
+    done
+    return $failed
+}
+
+######################################
+# Docs
+######################################
+
+build_docs() {
+    local language="$1"
+    local version=$(sh build.sh get-version)
+
+    local xcodebuild_arguments="--objc,Realm/Realm.h,--,-x,objective-c,-isysroot,$(xcrun --show-sdk-path),-I,$(pwd)"
+    local module="Realm"
+    local objc="--objc"
+
+    if [[ "$language" == "swift" ]]; then
+        sh build.sh set-swift-version
+        xcodebuild_arguments="-scheme,RealmSwift"
+        module="RealmSwift"
+        objc=""
+    fi
+
+    touch Realm/RLMPlatform.h # jazzy will fail if it can't find all public header files
+    jazzy \
+      ${objc} \
+      --clean \
+      --author Realm \
+      --author_url https://realm.io \
+      --github_url https://github.com/realm/realm-cocoa \
+      --github-file-prefix https://github.com/realm/realm-cocoa/tree/v${version} \
+      --module-version ${version} \
+      --xcodebuild-arguments ${xcodebuild_arguments} \
+      --module ${module} \
+      --root-url https://realm.io/docs/${language}/${version}/api/ \
+      --output docs/${language}_output \
+      --head "$(cat docs/custom_head.html)"
+
+    rm Realm/RLMPlatform.h
+}
+
+######################################
+# Input Validation
+######################################
+
+if [ "$#" -eq 0 -o "$#" -gt 3 ]; then
+    usage
+    exit 1
+fi
+
+######################################
+# Downloading
+######################################
+
+kill_object_server() {
+# Based on build.sh conventions we always run ROS from a path ending in 'ros/bin/ros'.
+    pkill -f ros/bin/ros\ start
+}
+
+download_object_server() {
+    rm -rf ./test-ros-instance
+    mkdir -p ./test-ros-instance/ros
+    chmod 777 ./test-ros-instance
+    /usr/local/bin/node /usr/local/bin/npm install --scripts-prepend-node-path=auto --prefix ./test-ros-instance/ros \
+        -g realm-object-server@${REALM_OBJECT_SERVER_VERSION}
+}
+
+download_common() {
+    local download_type=$1 tries_left=3 version url error temp_dir temp_path tar_path
+
+    if [ "$download_type" == "core" ]; then
+        version=$REALM_CORE_VERSION
+        url="https://static.realm.io/downloads/core/realm-core-${version}.tar.xz"
+    elif [ "$download_type" == "sync" ]; then
+        version=$REALM_SYNC_VERSION
+        url="https://static.realm.io/downloads/sync/realm-sync-cocoa-${version}.tar.xz"
+    else
+        echo "Unknown dowload_type: $download_type"
+        exit 1
+    fi
+
+    echo "Downloading dependency: ${download_type} ${version}"
+
+    if [ -z "$TMPDIR" ]; then
+        TMPDIR='/tmp'
+    fi
+    temp_dir=$(dirname "$TMPDIR/waste")/${download_type}_bin
+    mkdir -p "$temp_dir"
+    tar_path="${temp_dir}/${download_type}-${version}.tar.xz"
+    temp_path="${tar_path}.tmp"
+
+    while [ 0 -lt $tries_left ] && [ ! -f "$tar_path" ]; do
+        if ! error=$(/usr/bin/curl --fail --silent --show-error --location "$url" --output "$temp_path" 2>&1); then
+            tries_left=$[$tries_left-1]
+        else
+            mv "$temp_path" "$tar_path"
+        fi
+    done
+
+    if [ ! -f "$tar_path" ]; then
+        printf "Downloading ${download_type} failed:\n\t$url\n\t$error\n"
+        exit 1
+    fi
+
+    (
+        cd "$temp_dir"
+        rm -rf "$download_type"
+        tar xf "$tar_path" --xz
+        mv core "${download_type}-${version}"
+    )
+
+    rm -rf "${download_type}-${version}" core
+    mv "${temp_dir}/${download_type}-${version}" .
+    ln -s "${download_type}-${version}" core
+}
+
+download_core() {
+    download_common "core"
+}
+
+download_sync() {
+    download_common "sync"
+}
+
+######################################
+# Variables
+######################################
+
+COMMAND="$1"
+
+# Use Debug config if command ends with -debug, otherwise default to Release
+# Set IS_RUNNING_PACKAGING when running packaging steps to avoid running iOS static tests with Xcode 8.3.3
+case "$COMMAND" in
+    *-debug)
+        COMMAND="${COMMAND%-debug}"
+        CONFIGURATION="Debug"
+        ;;
+    package-*)
+        IS_RUNNING_PACKAGING=1
+        ;;
+esac
+export CONFIGURATION=${CONFIGURATION:-Release}
+export IS_RUNNING_PACKAGING=${IS_RUNNING_PACKAGING:-0}
+
+# Pre-choose Xcode and Swift versions for those operations that do not set them
+REALM_XCODE_VERSION=${xcode_version:-$REALM_XCODE_VERSION}
+REALM_SWIFT_VERSION=${swift_version:-$REALM_SWIFT_VERSION}
+source "${source_root}/scripts/swift-version.sh"
+set_xcode_and_swift_versions
+
+######################################
+# Commands
+######################################
+
+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
+        ;;
+
+    "reset-ros-server-state")
+        rm -rf "./test-ros-instance/data"
+        rm -rf "./test-ros-instance/realm-object-server"
+        exit 0
+        ;;
+
+    "reset-ros-client-state")
+        rm -rf ~/Library/Application\ Support/xctest
+        rm -rf ~/Library/Application\ Support/io.realm.TestHost
+        rm -rf ~/Library/Application\ Support/xctest-child
+        exit 0
+        ;;
+
+    "reset-object-server")
+        kill_object_server
+        # Add a short delay, so file system doesn't complain about files in use
+        sleep 1
+        sh build.sh reset-ros-server-state
+        sh build.sh reset-ros-client-state
+        # Add another delay to ensure files are actually gone from file system
+        sleep 1
+        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:-$REALM_SWIFT_VERSION}
+
+        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-ios-dynamic")
+        xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' build"
+        if (( $(xcode_version_major) < 9 )); then
+            xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'"
+        fi
+        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"
+        if (( $(xcode_version_major) < 9 )); then
+            xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'"
+        fi
+        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" "$CONFIGURATION"
+        exit $?
+        ;;
+
+    "test-ios-devices-swift")
+        test_devices iphoneos "RealmSwift" "$CONFIGURATION"
+        exit $?
+        ;;
+
+    "test-tvos")
+        if (( $(xcode_version_major) >= 9 )); then
+            destination="Apple TV"
+        else
+            destination="Apple TV 1080p"
+        fi
+        xc "-scheme Realm -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination' test"
+        exit $?
+        ;;
+
+    "test-tvos-swift")
+        if (( $(xcode_version_major) >= 9 )); then
+            destination="Apple TV"
+        else
+            destination="Apple TV 1080p"
+        fi
+        xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination' 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-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-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
+        sh build.sh reset-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}"
+        xc "-workspace $workspace -scheme Draw -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")
+        sh build.sh prelaunch-simulator
+        workspace="examples/ios/swift/RealmExamples.xcworkspace"
+        if [[ ! -d "$workspace" ]]; then
+            workspace="${workspace/swift/swift-$REALM_SWIFT_VERSION}"
+        fi
+
+        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}"
+        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"
+        if (( $(xcode_version_major) >= 9 )); then
+            destination="Apple TV"
+        else
+            destination="Apple TV 1080p"
+        fi
+
+        xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}"
+        xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}"
+        exit 0
+        ;;
+
+    "examples-tvos-swift")
+        workspace="examples/tvos/swift/RealmExamples.xcworkspace"
+        if [[ ! -d "$workspace" ]]; then
+            workspace="${workspace/swift/swift-$REALM_SWIFT_VERSION}"
+        fi
+
+        if (( $(xcode_version_major) >= 9 )); then
+            destination="Apple TV"
+        else
+            destination="Apple TV 1080p"
+        fi
+
+        xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}"
+        xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}"
+        exit 0
+        ;;
+
+    ######################################
+    # Versioning
+    ######################################
+    "get-version")
+        version_file="Realm/Realm-Info.plist"
+        echo "$(PlistBuddy -c "Print :CFBundleShortVersionString" "$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
+        # The bundle version can contain only three groups of digits separated by periods,
+        # so strip off any -beta.x tag from the end of the version string.
+        bundle_version=$(echo "$realm_version" | cut -d - -f 1)
+        for version_file in $version_files; do
+            PlistBuddy -c "Set :CFBundleVersion $bundle_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[@]} -lt 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
+          mv core/librealm-ios.a core/librealmcore-ios.a
+          mv core/librealm-macosx.a core/librealmcore-macosx.a
+          mv core/librealm-tvos.a core/librealmcore-tvos.a
+          mv core/librealm-watchos.a core/librealmcore-watchos.a
+        fi
+
+        if [[ "$2" != "swift" ]]; then
+          if [ ! -d Realm/ObjectStore/src ]; then
+            cat >&2 <<EOM
+
+
+ERROR: One of Realm's submodules is missing!
+
+If you're using Realm and/or RealmSwift from a git branch, please add 'submodules: true' to
+their entries in your Podfile.
+
+
+EOM
+            exit 1
+          fi
+
+          rm -rf include
+          mkdir -p include
+          mv core/include include/core
+
+          mkdir -p include/impl/apple include/util/apple include/sync/impl/apple
+          cp Realm/*.hpp include
+          cp Realm/ObjectStore/src/*.hpp include
+          cp Realm/ObjectStore/src/sync/*.hpp include/sync
+          cp Realm/ObjectStore/src/sync/impl/*.hpp include/sync/impl
+          cp Realm/ObjectStore/src/sync/impl/apple/*.hpp include/sync/impl/apple
+          cp Realm/ObjectStore/src/impl/*.hpp include/impl
+          cp Realm/ObjectStore/src/impl/apple/*.hpp include/impl/apple
+          cp Realm/ObjectStore/src/util/*.hpp include/util
+          cp Realm/ObjectStore/src/util/apple/*.hpp include/util/apple
+
+          touch Realm/RLMPlatform.h
+          if [ -n "$COCOAPODS_VERSION" ]; then
+            # This variable is set for the prepare_command available
+            # from the 1.0 prereleases, which requires a different
+            # header layout within the header_mappings_dir.
+            cp Realm/*.h include
+          else
+            # For CocoaPods < 1.0, we need to scope the headers within
+            # the header_mappings_dir by another subdirectory to avoid
+            # Clang from complaining about non-modular headers.
+            mkdir -p include/Realm
+            cp Realm/*.h include/Realm
+          fi
+        else
+          sh build.sh set-swift-version
+        fi
+        ;;
+
+    ######################################
+    # Continuous Integration
+    ######################################
+
+    "ci-pr")
+        mkdir -p build/reports
+        # FIXME: Re-enable once CI can properly unlock the keychain
+        export REALM_DISABLE_METADATA_ENCRYPTION=1
+
+        # strip off the ios|tvos version specifier, e.g. the last part of: `ios-device-objc-ios8`
+        if [[ "$target" =~ ^((ios|tvos)-device(-(objc|swift))?)(-(ios|tvos)[[:digit:]]+)?$ ]]; then
+            export target=${BASH_REMATCH[1]}
+        fi
+
+        if [ "$target" = "docs" ]; then
+            sh build.sh set-swift-version
+            sh build.sh verify-docs
+        elif [ "$target" = "swiftlint" ]; then
+            sh build.sh verify-swiftlint
+        else
+            export sha=$GITHUB_PR_SOURCE_BRANCH
+            export CONFIGURATION=$configuration
+            export REALM_EXTRA_BUILD_ARGUMENTS='GCC_GENERATE_DEBUGGING_SYMBOLS=NO REALM_PREFIX_HEADER=Realm/RLMPrefix.h'
+            sh build.sh prelaunch-simulator
+
+            # Reset CoreSimulator.log
+            mkdir -p ~/Library/Logs/CoreSimulator
+            echo > ~/Library/Logs/CoreSimulator/CoreSimulator.log
+
+            if [ -d ~/Library/Developer/CoreSimulator/Devices/ ]; then
+                # Verify that no Realm files still exist
+                ! find ~/Library/Developer/CoreSimulator/Devices/ -name '*.realm' | grep -q .
+            fi
+
+            failed=0
+            sh build.sh verify-$target 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|operation never finished bootstrapping'; then
+                echo "Known Xcode error detected. Running job again."
+                if cat build/build.log | grep -E 'out of date and needs to be rebuilt'; then
+                    rm -rf build/DerivedData
+                fi
+                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 || true
+                echo "\n\n***\nCoreSimulator.log\n***\n\n" && cat ~/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/<source>\./<source>${WS}/" build/reports/coverage-report.xml
+        fi
+        ;;
+
+    ######################################
+    # Release packaging
+    ######################################
+
+    "package-examples")
+        ./scripts/package_examples.rb
+        zip --symlinks -r realm-examples.zip examples -x "examples/installation/*"
+        ;;
+
+    "package-test-examples")
+        if ! VERSION=$(echo realm-objc-*.zip | egrep -o '\d*\.\d*\.\d*-[a-z]*(\.\d*)?'); then
+            VERSION=$(echo realm-objc-*.zip | egrep -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")
+        sh build.sh prelaunch-simulator
+        sh build.sh ios-static
+
+        cd build/ios-static
+        zip --symlinks -r realm-framework-ios-static.zip Realm.framework
+        ;;
+
+    "package-ios")
+        sh build.sh prelaunch-simulator
+        sh build.sh ios-dynamic
+        cd build/ios
+        zip --symlinks -r realm-framework-ios.zip Realm.framework
+        ;;
+
+    "package-osx")
+        sh build.sh osx
+
+        cd build/DerivedData/Realm/Build/Products/Release
+        zip --symlinks -r realm-framework-osx.zip Realm.framework
+        ;;
+
+    "package-ios-swift")
+        for version in 8.3.3 9.0 9.1 9.2; do
+            REALM_XCODE_VERSION=$version
+            REALM_SWIFT_VERSION=
+            set_xcode_and_swift_versions
+            sh build.sh prelaunch-simulator
+            sh build.sh ios-swift
+        done
+
+        cd build/ios
+        ln -s swift-4.0 swift-3.2
+        ln -s swift-4.0.2 swift-3.2.2
+        ln -s swift-4.0.2 swift-3.2.3
+        ln -s swift-4.0.2 swift-4.0.3
+        zip --symlinks -r realm-swift-framework-ios.zip swift-3.1 swift-3.2 swift-3.2.2 swift-3.2.3 swift-4.0 swift-4.0.2 swift-4.0.3
+        ;;
+
+    "package-osx-swift")
+        for version in 8.3.3 9.0 9.1 9.2; do
+            REALM_XCODE_VERSION=$version
+            REALM_SWIFT_VERSION=
+            set_xcode_and_swift_versions
+            sh build.sh prelaunch-simulator
+            sh build.sh osx-swift
+        done
+
+        cd build/osx
+        ln -s swift-4.0 swift-3.2
+        ln -s swift-4.0.2 swift-3.2.2
+        ln -s swift-4.0.2 swift-3.2.3
+        ln -s swift-4.0.2 swift-4.0.3
+        zip --symlinks -r realm-swift-framework-osx.zip swift-3.1 swift-3.2 swift-3.2.2 swift-3.2.3 swift-4.0 swift-4.0.2 swift-4.0.3
+        ;;
+
+    "package-watchos")
+        sh build.sh prelaunch-simulator
+        sh build.sh watchos
+
+        cd build/watchos
+        zip --symlinks -r realm-framework-watchos.zip Realm.framework
+        ;;
+
+    "package-watchos-swift")
+        for version in 8.3.3 9.0 9.1 9.2; do
+            REALM_XCODE_VERSION=$version
+            REALM_SWIFT_VERSION=
+            set_xcode_and_swift_versions
+            sh build.sh prelaunch-simulator
+            sh build.sh watchos-swift
+        done
+
+        cd build/watchos
+        ln -s swift-4.0 swift-3.2
+        ln -s swift-4.0.2 swift-3.2.2
+        ln -s swift-4.0.2 swift-3.2.3
+        ln -s swift-4.0.2 swift-4.0.3
+        zip --symlinks -r realm-swift-framework-watchos.zip swift-3.1 swift-3.2 swift-3.2.2 swift-3.2.3 swift-4.0 swift-4.0.2 swift-4.0.3
+        ;;
+
+    "package-tvos")
+        sh build.sh prelaunch-simulator
+        sh build.sh tvos
+
+        cd build/tvos
+        zip --symlinks -r realm-framework-tvos.zip Realm.framework
+        ;;
+
+    "package-tvos-swift")
+        for version in 8.3.3 9.0 9.1 9.2; do
+            REALM_XCODE_VERSION=$version
+            REALM_SWIFT_VERSION=
+            set_xcode_and_swift_versions
+            sh build.sh prelaunch-simulator
+            sh build.sh tvos-swift
+        done
+
+        cd build/tvos
+        ln -s swift-4.0 swift-3.2
+        ln -s swift-4.0.2 swift-3.2.2
+        ln -s swift-4.0.2 swift-3.2.3
+        ln -s swift-4.0.2 swift-4.0.3
+        zip --symlinks -r realm-swift-framework-tvos.zip swift-3.1 swift-3.2 swift-3.2.2 swift-3.2.3 swift-4.0 swift-4.0.2 swift-4.0.3
+        ;;
+
+    package-*-swift-3.2)
+        PLATFORM=$(echo $COMMAND | cut -d - -f 2)
+        mkdir -p build/$PLATFORM
+        cd build/$PLATFORM
+        ln -s swift-4.0 swift-3.2
+        zip --symlinks -r realm-swift-framework-$PLATFORM-swift-3.2.zip swift-3.2
+        ;;
+
+    package-*-swift-3.2.2)
+        PLATFORM=$(echo $COMMAND | cut -d - -f 2)
+        mkdir -p build/$PLATFORM
+        cd build/$PLATFORM
+        ln -s swift-4.0.2 swift-3.2.2
+        zip --symlinks -r realm-swift-framework-$PLATFORM-swift-3.2.2.zip swift-3.2.2
+        ;;
+
+    package-*-swift-3.2.3)
+        PLATFORM=$(echo $COMMAND | cut -d - -f 2)
+        mkdir -p build/$PLATFORM
+        cd build/$PLATFORM
+        ln -s swift-4.0.2 swift-3.2.3
+        zip --symlinks -r realm-swift-framework-$PLATFORM-swift-3.2.3.zip swift-3.2.3
+        ;;
+
+    package-*-swift-4.0.3)
+        PLATFORM=$(echo $COMMAND | cut -d - -f 2)
+        mkdir -p build/$PLATFORM
+        cd build/$PLATFORM
+        ln -s swift-4.0.2 swift-4.0.3
+        zip --symlinks -r realm-swift-framework-$PLATFORM-swift-4.0.3.zip swift-4.0.3
+        ;;
+
+    package-*-swift-*)
+        PLATFORM=$(echo $COMMAND | cut -d - -f 2)
+        REALM_SWIFT_VERSION=$(echo $COMMAND | cut -d - -f 4)
+        REALM_XCODE_VERSION=
+
+        set_xcode_and_swift_versions
+        sh build.sh prelaunch-simulator
+        sh build.sh $PLATFORM-swift
+
+        cd build/$PLATFORM
+        zip --symlinks -r realm-swift-framework-$PLATFORM-swift-$REALM_SWIFT_VERSION.zip swift-$REALM_SWIFT_VERSION
+        ;;
+
+    "package-release")
+        LANG="$2"
+        TEMPDIR=$(mktemp -d $TMPDIR/realm-release-package-${LANG}.XXXX)
+
+        VERSION=$(sh build.sh get-version)
+
+        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-static.zip
+            )
+
+            (
+                cd ${FOLDER}/ios/dynamic
+                unzip ${WORKSPACE}/realm-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
+                for f in ${WORKSPACE}/realm-swift-framework-osx-swift-*.zip; do
+                    unzip "$f"
+                done
+            )
+
+            (
+                cd ${FOLDER}/ios
+                for f in ${WORKSPACE}/realm-swift-framework-ios-swift-*.zip; do
+                    unzip "$f"
+                done
+            )
+
+            (
+                cd ${FOLDER}/watchos
+                for f in ${WORKSPACE}/realm-swift-framework-watchos-swift-*.zip; do
+                    unzip "$f"
+                done
+            )
+
+            (
+                cd ${FOLDER}/tvos
+                for f in ${WORKSPACE}/realm-swift-framework-tvos-swift-*.zip; do
+                    unzip "$f"
+                done
+            )
+        fi
+
+        (
+            cd ${WORKSPACE}
+            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 <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>URL</key>
+    <string>https://realm.io/docs/${LANG}/${VERSION}</string>
+</dict>
+</plist>
+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 realm-cocoa
+        cd realm-cocoa
+
+        echo 'Packaging iOS'
+        sh build.sh package-ios-static
+        cp build/ios-static/realm-framework-ios-static.zip ..
+        sh build.sh package-ios
+        cp build/ios/realm-framework-ios.zip ..
+        sh build.sh package-ios-swift
+        cp build/ios/realm-swift-framework-ios.zip ..
+
+        echo 'Packaging OS X'
+        sh build.sh package-osx
+        cp build/DerivedData/Realm/Build/Products/Release/realm-framework-osx.zip ..
+        sh build.sh package-osx-swift
+        cp build/osx/realm-swift-framework-osx.zip ..
+
+        echo 'Packaging watchOS'
+        sh build.sh package-watchos
+        cp build/watchos/realm-framework-watchos.zip ..
+        sh build.sh package-watchos-swift
+        cp build/watchos/realm-swift-framework-watchos.zip ..
+
+        echo 'Packaging tvOS'
+        sh build.sh package-tvos
+        cp build/tvos/realm-framework-tvos.zip ..
+        sh build.sh package-tvos-swift
+        cp build/tvos/realm-swift-framework-tvos.zip ..
+
+        echo 'Packaging examples'
+        sh build.sh package-examples
+        cp realm-examples.zip ..
+
+        echo 'Building final release packages'
+        sh build.sh package-release objc
+        sh build.sh package-release swift
+
+        echo 'Testing packaged examples'
+        sh 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 <<EOS
+x.x.x Release notes (yyyy-MM-dd)
+=============================================================
+
+### Breaking Changes
+
+* None.
+
+### Enhancements
+
+* None.
+
+### Bugfixes
+
+* None.
+EOS)
+        changelog=$(cat CHANGELOG.md)
+        echo "$empty_section" > CHANGELOG.md
+        echo >> CHANGELOG.md
+        echo "$changelog" >> CHANGELOG.md
+        ;;
+
+    *)
+        echo "Unknown command '$COMMAND'"
+        usage
+        exit 1
+        ;;
+esac
diff --git a/iOS/Pods/Realm/core/librealmcore-ios.a b/iOS/Pods/Realm/core/librealmcore-ios.a
new file mode 100644 (file)
index 0000000..5ef8105
Binary files /dev/null and b/iOS/Pods/Realm/core/librealmcore-ios.a differ
diff --git a/iOS/Pods/Realm/include/NSError+RLMSync.h b/iOS/Pods/Realm/include/NSError+RLMSync.h
new file mode 100644 (file)
index 0000000..797db58
--- /dev/null
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class RLMSyncErrorActionToken;
+
+/// NSError category extension providing methods to get data out of Realm's
+/// "client reset" error.
+@interface NSError (RLMSync)
+
+/**
+ Given an appropriate Realm Object Server error, return the token that
+ can be passed into `+[RLMSyncSession immediatelyHandleError:]` to
+ immediately perform error clean-up work, or nil if the error isn't of
+ a type that provides a token.
+ */
+- (nullable RLMSyncErrorActionToken *)rlmSync_errorActionToken NS_REFINED_FOR_SWIFT;
+
+/**
+ Given a Realm Object Server client reset error, return the path where the
+ backup copy of the Realm will be placed once the client reset process is
+ complete.
+ */
+- (nullable NSString *)rlmSync_clientResetBackedUpRealmPath NS_SWIFT_UNAVAILABLE("");
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMAccessor.h b/iOS/Pods/Realm/include/RLMAccessor.h
new file mode 100644 (file)
index 0000000..59c625a
--- /dev/null
@@ -0,0 +1,53 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+@class RLMObjectSchema, RLMProperty, RLMObjectBase;
+
+NS_ASSUME_NONNULL_BEGIN
+
+//
+// Accessors Class Creation/Caching
+//
+
+// get accessor classes for an object class - generates classes if not cached
+Class RLMManagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, const char *name);
+Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema);
+
+//
+// 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);
+
+//
+// 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/iOS/Pods/Realm/include/RLMAccessor.hpp b/iOS/Pods/Realm/include/RLMAccessor.hpp
new file mode 100644 (file)
index 0000000..93d7e68
--- /dev/null
@@ -0,0 +1,116 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 "object_accessor.hpp"
+
+#import "RLMUtil.hpp"
+
+@class RLMRealm;
+class RLMClassInfo;
+class RLMObservationInfo;
+
+// realm::util::Optional<id> doesn't work because Objective-C types can't
+// be members of unions with ARC, so this covers the subset of Optional that we
+// actually need.
+struct RLMOptionalId {
+    id value;
+    RLMOptionalId(id value) : value(value) { }
+    explicit operator bool() const noexcept { return value; }
+    id operator*() const noexcept { return value; }
+};
+
+class RLMAccessorContext {
+public:
+    // Accessor context interface
+    RLMAccessorContext(RLMAccessorContext& parent, realm::Property const& property);
+
+    id box(realm::List&&);
+    id box(realm::Results&&);
+    id box(realm::Object&&);
+    id box(realm::RowExpr);
+
+    id box(bool v) { return @(v); }
+    id box(double v) { return @(v); }
+    id box(float v) { return @(v); }
+    id box(long long v) { return @(v); }
+    id box(realm::StringData v) { return RLMStringDataToNSString(v) ?: NSNull.null; }
+    id box(realm::BinaryData v) { return RLMBinaryDataToNSData(v) ?: NSNull.null; }
+    id box(realm::Timestamp v) { return RLMTimestampToNSDate(v) ?: NSNull.null; }
+    id box(realm::Mixed v) { return RLMMixedToObjc(v); }
+
+    id box(realm::util::Optional<bool> v) { return v ? @(*v) : NSNull.null; }
+    id box(realm::util::Optional<double> v) { return v ? @(*v) : NSNull.null; }
+    id box(realm::util::Optional<float> v) { return v ? @(*v) : NSNull.null; }
+    id box(realm::util::Optional<int64_t> v) { return v ? @(*v) : NSNull.null; }
+
+    void will_change(realm::Row const&, realm::Property const&);
+    void will_change(realm::Object& obj, realm::Property const& prop) { will_change(obj.row(), prop); }
+    void did_change();
+
+    RLMOptionalId value_for_property(id dict, std::string const&, size_t prop_index);
+    RLMOptionalId default_value_for_property(realm::ObjectSchema const&,
+                                             std::string const& prop);
+
+    bool is_same_list(realm::List const& list, id v) const noexcept;
+
+    template<typename Func>
+    void enumerate_list(__unsafe_unretained const id v, Func&& func) {
+        for (id value in v) {
+            func(value);
+        }
+    }
+
+    template<typename T>
+    T unbox(id v, bool create = false, bool update = false);
+
+    bool is_null(id v) { return v == NSNull.null; }
+    id null_value() { return NSNull.null; }
+    id no_value() { return nil; }
+    bool allow_missing(id v) { return [v isKindOfClass:[NSArray class]]; }
+
+    std::string print(id obj) { return [obj description].UTF8String; }
+
+    // Internal API
+    RLMAccessorContext(RLMObjectBase *parentObject, const realm::Property *property = nullptr);
+    RLMAccessorContext(RLMRealm *realm, RLMClassInfo& info, bool promote=true);
+
+    // The property currently being accessed; needed for KVO things for boxing
+    // List and Results
+    RLMProperty *currentProperty;
+
+private:
+    __unsafe_unretained RLMRealm *const _realm;
+    RLMClassInfo& _info;
+    // If true, promote unmanaged RLMObjects passed to box() with create=true
+    // rather than copying them
+    bool _promote_existing = true;
+    // Parent object of the thing currently being processed, for KVO purposes
+    __unsafe_unretained RLMObjectBase *const _parentObject = nil;
+
+    // Cached default values dictionary to avoid having to call the class method
+    // for every property
+    NSDictionary *_defaultValues;
+
+    RLMObservationInfo *_observationInfo = nullptr;
+    NSString *_kvoPropertyName = nil;
+
+    id defaultValue(NSString *key);
+    id propertyValue(id obj, size_t propIndex, __unsafe_unretained RLMProperty *const prop);
+};
diff --git a/iOS/Pods/Realm/include/RLMAnalytics.hpp b/iOS/Pods/Realm/include/RLMAnalytics.hpp
new file mode 100644 (file)
index 0000000..ccb4ea2
--- /dev/null
@@ -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/iOS/Pods/Realm/include/RLMArray.h b/iOS/Pods/Realm/include/RLMArray.h
new file mode 100644 (file)
index 0000000..3ef38eb
--- /dev/null
@@ -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 <Realm/RLMCollection.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class RLMObject, RLMResults<RLMObjectType>;
+
+/**
+ `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<ObjectType *><ObjectType> *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<RLMObjectType> : NSObject<RLMCollection, NSFastEnumeration>
+
+#pragma mark - Properties
+
+/**
+ The number of objects in the array.
+ */
+@property (nonatomic, readonly, assign) NSUInteger count;
+
+/**
+ The type of the objects in the array.
+ */
+@property (nonatomic, readonly, assign) RLMPropertyType type;
+
+/**
+ Indicates whether the objects in the collection can be `nil`.
+ */
+@property (nonatomic, readonly, getter = isOptional) BOOL optional;
+
+/**
+ The class name  of the objects contained in the array.
+
+ Will be `nil` if `type` is not RLMPropertyTypeObject.
+ */
+@property (nonatomic, readonly, copy, nullable) 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 object 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 object 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 object 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 object 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<NSFastEnumeration>)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 object 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.
+
+ This is a no-op if the array is already empty.
+
+ @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<RLMObjectType> *)objectsWhere:(NSString *)predicateFormat, ...;
+
+/// :nodoc:
+- (RLMResults<RLMObjectType> *)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<RLMObjectType> *)objectsWithPredicate:(NSPredicate *)predicate;
+
+/**
+ Returns a sorted `RLMResults` from the array.
+
+ @param keyPath     The key path to sort by.
+ @param ascending   The direction to sort in.
+
+ @return    An `RLMResults` sorted by the specified key path.
+ */
+- (RLMResults<RLMObjectType> *)sortedResultsUsingKeyPath:(NSString *)keyPath 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<RLMObjectType> *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)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<Dog *> *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 `-invalidate` 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<RLMObjectType> *__nullable array,
+                                                         RLMCollectionChange *__nullable changes,
+                                                         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 in the array.
+
+     NSNumber *min = [object.arrayProperty 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, or `nil` if the array is empty.
+ */
+- (nullable id)minOfProperty:(NSString *)property;
+
+/**
+ Returns the maximum (highest) value of the given property among all the objects in the array.
+
+     NSNumber *max = [object.arrayProperty 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, or `nil` if the array is empty.
+ */
+- (nullable id)maxOfProperty:(NSString *)property;
+
+/**
+ Returns the sum of the values of a given property over all the objects in the array.
+
+     NSNumber *sum = [object.arrayProperty 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 in the array.
+
+     NSNumber *average = [object.arrayProperty 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, or `nil` if the array is empty.
+ */
+- (nullable NSNumber *)averageOfProperty:(NSString *)property;
+
+
+#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.
+ */
+- (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.
+ */
++ (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/iOS/Pods/Realm/include/RLMArray_Private.h b/iOS/Pods/Realm/include/RLMArray_Private.h
new file mode 100644 (file)
index 0000000..02b908a
--- /dev/null
@@ -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 <Realm/RLMArray.h>
+#import <Realm/RLMConstants.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface RLMArray ()
+- (instancetype)initWithObjectClassName:(NSString *)objectClassName;
+- (instancetype)initWithObjectType:(RLMPropertyType)type optional:(BOOL)optional;
+- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth;
+@end
+
+void RLMArrayValidateMatchingObjectType(RLMArray *array, id value);
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMArray_Private.hpp b/iOS/Pods/Realm/include/RLMArray_Private.hpp
new file mode 100644 (file)
index 0000000..08a4487
--- /dev/null
@@ -0,0 +1,73 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMResults_Private.hpp"
+
+#import <realm/link_view_fwd.hpp>
+#import <realm/table_ref.hpp>
+
+namespace realm {
+    class Results;
+}
+
+@class RLMObjectBase, RLMObjectSchema, RLMProperty;
+class RLMClassInfo;
+class RLMObservationInfo;
+
+@interface RLMArray () {
+@protected
+    NSString *_objectClassName;
+    RLMPropertyType _type;
+    BOOL _optional;
+@public
+    // The name of the property which this RLMArray represents
+    NSString *_key;
+    __weak RLMObjectBase *_parentObject;
+}
+@end
+
+@interface RLMManagedArray : RLMArray <RLMFastEnumerable>
+- (instancetype)initWithParent:(RLMObjectBase *)parentObject property:(RLMProperty *)property;
+- (RLMManagedArray *)initWithList:(realm::List)list
+                            realm:(__unsafe_unretained RLMRealm *const)realm
+                       parentInfo:(RLMClassInfo *)parentInfo
+                         property:(__unsafe_unretained RLMProperty *const)property;
+
+- (bool)isBackedByList:(realm::List const&)list;
+
+// 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<RLMObservationInfo>& info,
+                                   NSString *keyPath, RLMArray *array, id observed);
+
+
+//
+// RLMResults private methods
+//
+@interface RLMResults () <RLMFastEnumerable>
+- (void)deleteObjectsFromRealm;
+@end
diff --git a/iOS/Pods/Realm/include/RLMClassInfo.hpp b/iOS/Pods/Realm/include/RLMClassInfo.hpp
new file mode 100644 (file)
index 0000000..7d1d23e
--- /dev/null
@@ -0,0 +1,113 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+#import <unordered_map>
+#import <vector>
+
+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<NSString *> {
+    size_t operator()(__unsafe_unretained NSString *const str) const {
+        return [str hash];
+    }
+};
+template<> struct equal_to<NSString *> {
+    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<RLMObservationInfo *> 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;
+
+    // Get the info for the target of the link at the given property index.
+    RLMClassInfo &linkTargetType(size_t propertyIndex);
+
+    // Get the info for the target of the given property
+    RLMClassInfo &linkTargetType(realm::Property const& property);
+
+    void releaseTable() { m_table = nullptr; }
+
+private:
+    mutable realm::Table *_Nullable m_table = nullptr;
+    std::vector<RLMClassInfo *> m_linkTargets;
+};
+
+// A per-RLMRealm object schema map which stores RLMClassInfo keyed on the name
+class RLMSchemaInfo {
+    using impl = std::unordered_map<NSString *, RLMClassInfo>;
+public:
+    RLMSchemaInfo() = default;
+    RLMSchemaInfo(RLMRealm *realm);
+
+    RLMSchemaInfo clone(realm::Schema const& source_schema, RLMRealm *target_realm);
+
+    // 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<NSString *, RLMClassInfo> m_objects;
+};
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMCollection.h b/iOS/Pods/Realm/include/RLMCollection.h
new file mode 100644 (file)
index 0000000..9d3fa7e
--- /dev/null
@@ -0,0 +1,400 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+#import <Realm/RLMThreadSafeReference.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class RLMRealm, RLMResults, RLMSortDescriptor, RLMNotificationToken, RLMCollectionChange;
+typedef NS_ENUM(int32_t, RLMPropertyType);
+
+/**
+ A homogenous collection of Realm-managed objects. Examples of conforming types
+ include `RLMArray`, `RLMResults`, and `RLMLinkingObjects`.
+ */
+@protocol RLMCollection <NSFastEnumeration, RLMThreadConfined>
+
+@required
+
+#pragma mark - Properties
+
+/**
+ The number of objects in the collection.
+ */
+@property (nonatomic, readonly, assign) NSUInteger count;
+
+/**
+ The type of the objects in the collection.
+ */
+@property (nonatomic, readonly, assign) RLMPropertyType type;
+
+/**
+ Indicates whether the objects in the collection can be `nil`.
+ */
+@property (nonatomic, readonly, getter = isOptional) BOOL optional;
+
+/**
+ The class name  of the objects contained in the collection.
+
+ Will be `nil` if `type` is not RLMPropertyTypeObject.
+ */
+@property (nonatomic, readonly, copy, nullable) 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 object 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 object 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 object 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:(id)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 keyPath     The keyPath to sort by.
+ @param ascending   The direction to sort in.
+
+ @return    An `RLMResults` sorted by the specified key path.
+ */
+- (RLMResults *)sortedResultsUsingKeyPath:(NSString *)keyPath 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<RLMSortDescriptor *> *)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<RLMCollection> collection = [Dog allObjects];
+     NSLog(@"dogs.count: %zu", dogs.count); // => 0
+     self.token = [collection addNotificationBlock:^(id<RLMCollection> 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 `-invalidate` 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<RLMCollection> __nullable collection,
+                                                         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
+ in the 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, or `nil` if the Results are empty.
+ */
+- (nullable id)minOfProperty:(NSString *)property;
+
+/**
+ Returns the maximum (highest) value of the given property among all the objects
+ in the 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, or `nil` if the Results are empty.
+ */
+- (nullable id)maxOfProperty:(NSString *)property;
+
+/**
+ Returns the sum of the values of a given property over all the objects in the 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 in the 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, or `nil` if the Results are empty.
+ */
+- (nullable NSNumber *)averageOfProperty:(NSString *)property;
+
+@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 key path which the sort descriptor orders results by.
+ */
+@property (nonatomic, readonly) NSString *keyPath;
+
+/**
+ 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 key path and sort direction.
+ */
++ (instancetype)sortDescriptorWithKeyPath:(NSString *)keyPath 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<NSNumber *> *deletions;
+
+/// The indices in the new version of the collection which were newly inserted.
+@property (nonatomic, readonly) NSArray<NSNumber *> *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<NSNumber *> *modifications;
+
+/// Returns the index paths of the deletion indices in the given section.
+- (NSArray<NSIndexPath *> *)deletionsInSection:(NSUInteger)section;
+
+/// Returns the index paths of the insertion indices in the given section.
+- (NSArray<NSIndexPath *> *)insertionsInSection:(NSUInteger)section;
+
+/// Returns the index paths of the modification indices in the given section.
+- (NSArray<NSIndexPath *> *)modificationsInSection:(NSUInteger)section;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMCollection_Private.h b/iOS/Pods/Realm/include/RLMCollection_Private.h
new file mode 100644 (file)
index 0000000..4c3f882
--- /dev/null
@@ -0,0 +1,26 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMCollection.h>
+
+#import <Realm/RLMRealm.h>
+
+@protocol RLMFastEnumerable;
+
+void RLMCollectionSetValueForKey(id<RLMFastEnumerable> collection, NSString *key, id value);
+FOUNDATION_EXTERN NSString *RLMDescriptionWithMaxDepth(NSString *name, id<RLMCollection> collection, NSUInteger depth);
diff --git a/iOS/Pods/Realm/include/RLMCollection_Private.hpp b/iOS/Pods/Realm/include/RLMCollection_Private.hpp
new file mode 100644 (file)
index 0000000..908c886
--- /dev/null
@@ -0,0 +1,87 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMCollection_Private.h>
+
+#import <vector>
+
+namespace realm {
+    class List;
+    class Results;
+    class TableView;
+    struct CollectionChangeSet;
+    struct NotificationToken;
+}
+class RLMClassInfo;
+@class RLMFastEnumerator;
+
+@protocol RLMFastEnumerable
+@property (nonatomic, readonly) RLMRealm *realm;
+@property (nonatomic, readonly) RLMClassInfo *objectInfo;
+@property (nonatomic, readonly) NSUInteger count;
+
+- (realm::TableView)tableView;
+- (RLMFastEnumerator *)fastEnumerator;
+@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)initWithList:(realm::List&)list
+                  collection:(id)collection
+                       realm:(RLMRealm *)realm
+                   classInfo:(RLMClassInfo&)info;
+- (instancetype)initWithResults:(realm::Results&)results
+                     collection:(id)collection
+                          realm:(RLMRealm *)realm
+                      classInfo:(RLMClassInfo&)info;
+
+// 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
+NSUInteger RLMFastEnumerate(NSFastEnumerationState *state, NSUInteger len, id<RLMFastEnumerable> collection);
+
+@interface RLMNotificationToken ()
+- (void)suppressNextNotification;
+- (RLMRealm *)realm;
+@end
+
+@interface RLMCancellationToken : RLMNotificationToken
+- (instancetype)initWithToken:(realm::NotificationToken)token realm:(RLMRealm *)realm;
+@end
+
+@interface RLMCollectionChange ()
+- (instancetype)initWithChanges:(realm::CollectionChangeSet)indices;
+@end
+
+template<typename Collection>
+RLMNotificationToken *RLMAddNotificationBlock(id objcCollection,
+                                              Collection& collection,
+                                              void (^block)(id, RLMCollectionChange *, NSError *),
+                                              bool suppressInitialChange=false);
+
+template<typename Collection>
+NSArray *RLMCollectionValueForKey(Collection& collection, NSString *key,
+                                  RLMRealm *realm, RLMClassInfo& info);
+
+std::vector<std::pair<std::string, bool>> RLMSortDescriptorsToKeypathArray(NSArray<RLMSortDescriptor *> *properties);
diff --git a/iOS/Pods/Realm/include/RLMConstants.h b/iOS/Pods/Realm/include/RLMConstants.h
new file mode 100644 (file)
index 0000000..f73fe83
--- /dev/null
@@ -0,0 +1,215 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+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) && (!defined(__cplusplus) || !__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).
+ */
+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  = 5,
+    /** Double-precision floating-point numbers: `double`, `Double` (Swift) */
+    RLMPropertyTypeDouble = 6,
+
+#pragma mark - Object types
+
+    /** Strings: `NSString`, `String` (Swift) */
+    RLMPropertyTypeString = 2,
+    /** Binary data: `NSData` */
+    RLMPropertyTypeData   = 3,
+    /**
+     Any object: `id`.
+
+     This property type is no longer supported for new models. However, old files
+     with any-typed properties are still supported for migration purposes.
+     */
+    RLMPropertyTypeAny    = 9,
+    /** Dates: `NSDate` */
+    RLMPropertyTypeDate   = 4,
+
+#pragma mark - Linked object types
+
+    /** Realm model objects. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */
+    RLMPropertyTypeObject = 7,
+    /** Realm linking objects. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */
+    RLMPropertyTypeLinkingObjects = 8,
+};
+
+/** 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,
+
+    /** Denotes an error that occurs when attempting to open an incompatible synchronized Realm file.
+
+     This error occurs when the Realm file was created with an older version of Realm and an automatic migration
+     to the current version is not possible. When such an error occurs, the original file is moved to a backup
+     location, and future attempts to open the synchronized Realm will result in a new file being created.
+     If you wish to migrate any data from the backup Realm, you can open it using the provided Realm configuration.
+     */
+    RLMErrorIncompatibleSyncedFile = 11,
+};
+
+#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 - Error keys
+
+/** Key to identify the associated backup Realm configuration in an error's `userInfo` dictionary */
+extern NSString * const RLMBackupRealmConfigurationErrorKey;
+
+#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/iOS/Pods/Realm/include/RLMJSONModels.h b/iOS/Pods/Realm/include/RLMJSONModels.h
new file mode 100644 (file)
index 0000000..a26d04f
--- /dev/null
@@ -0,0 +1,103 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 <Foundation/Foundation.h>
+
+#import "RLMSyncUtil_Private.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class RLMTokenDataModel, RLMSyncUserAccountInfo;
+
+#pragma mark - RLMTokenModel
+
+@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
+
+#pragma mark - RLMTokenDataModel
+
+@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) BOOL isAdmin;
+//@property (nonatomic, readonly) NSArray *access;
+
+- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary;
+
+@end
+
+#pragma mark - RLMAuthResponseModel
+
+/**
+ An internal class representing a valid JSON response to an auth request.
+
+ ```
+ {
+ "access_token": { ... } // (optional),
+ "refresh_token": { ... } // (optional)
+ }
+ ```
+ */
+@interface RLMAuthResponseModel : NSObject RLM_SYNC_UNINITIALIZABLE
+
+@property (nonatomic, readonly, nullable) RLMTokenModel *accessToken;
+@property (nonatomic, readonly, nullable) RLMTokenModel *refreshToken;
+
+- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary
+                requireAccessToken:(BOOL)requireAccessToken
+               requireRefreshToken:(BOOL)requireRefreshToken;
+
+@end
+
+#pragma mark - RLMUserInfoResponseModel
+
+@interface RLMUserResponseModel : NSObject RLM_SYNC_UNINITIALIZABLE
+
+@property (nonatomic, readonly) NSString *identity;
+@property (nonatomic, readonly) NSArray<RLMSyncUserAccountInfo *> *accounts;
+@property (nonatomic, readonly) NSDictionary *metadata;
+@property (nonatomic, readonly) BOOL isAdmin;
+
+- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary;
+
+@end
+
+#pragma mark - RLMSyncErrorResponseModel
+
+@interface RLMSyncErrorResponseModel : NSObject RLM_SYNC_UNINITIALIZABLE
+
+@property (nonatomic, readonly) NSInteger status;
+@property (nonatomic, readonly) NSInteger code;
+@property (nullable, nonatomic, readonly, copy) NSString *title;
+@property (nullable, nonatomic, readonly, copy) NSString *hint;
+
+- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMListBase.h b/iOS/Pods/Realm/include/RLMListBase.h
new file mode 100644 (file)
index 0000000..0151cfb
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+@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 <NSFastEnumeration>
+@property (nonatomic, strong) RLMArray *_rlmArray;
+
+- (instancetype)initWithArray:(RLMArray *)array;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMMigration.h b/iOS/Pods/Realm/include/RLMMigration.h
new file mode 100644 (file)
index 0000000..e910c42
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+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/iOS/Pods/Realm/include/RLMMigration_Private.h b/iOS/Pods/Realm/include/RLMMigration_Private.h
new file mode 100644 (file)
index 0000000..99699e5
--- /dev/null
@@ -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 <Realm/RLMMigration.h>
+#import <Realm/RLMObjectBase.h>
+#import <Realm/RLMRealm.h>
+
+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/iOS/Pods/Realm/include/RLMNetworkClient.h b/iOS/Pods/Realm/include/RLMNetworkClient.h
new file mode 100644 (file)
index 0000000..858e50b
--- /dev/null
@@ -0,0 +1,62 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+#import "RLMSyncUtil_Private.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// An abstract class representing a server endpoint.
+@interface RLMSyncServerEndpoint : NSObject RLM_SYNC_UNINITIALIZABLE
+@end
+
+/// The authentication endpoint.
+@interface RLMSyncAuthEndpoint : RLMSyncServerEndpoint RLM_SYNC_UNINITIALIZABLE
++ (instancetype)endpoint;
+@end
+
+/// The password change endpoint.
+@interface RLMSyncChangePasswordEndpoint : RLMSyncServerEndpoint RLM_SYNC_UNINITIALIZABLE
++ (instancetype)endpoint;
+@end
+
+/// The get user info endpoint.
+@interface RLMSyncGetUserInfoEndpoint : RLMSyncServerEndpoint RLM_SYNC_UNINITIALIZABLE
++ (instancetype)endpoint;
+@end
+
+/**
+ A simple Realm Object Server network client that wraps `NSURLSession`.
+ */
+@interface RLMNetworkClient : NSObject
+
++ (void)sendRequestToEndpoint:(RLMSyncServerEndpoint *)endpoint
+                       server:(NSURL *)serverURL
+                         JSON:(NSDictionary *)jsonDictionary
+                   completion:(RLMSyncCompletionBlock)completionBlock;
+
++ (void)sendRequestToEndpoint:(RLMSyncServerEndpoint *)endpoint
+                       server:(NSURL *)serverURL
+                         JSON:(NSDictionary *)jsonDictionary
+                      timeout:(NSTimeInterval)timeout
+                   completion:(RLMSyncCompletionBlock)completionBlock;
+
+NS_ASSUME_NONNULL_END
+
+@end
diff --git a/iOS/Pods/Realm/include/RLMObject.h b/iOS/Pods/Realm/include/RLMObject.h
new file mode 100644 (file)
index 0000000..33d0a95
--- /dev/null
@@ -0,0 +1,555 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+#import <Realm/RLMObjectBase.h>
+#import <Realm/RLMThreadSafeReference.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class RLMNotificationToken;
+@class RLMObjectSchema;
+@class RLMPropertyChange;
+@class RLMPropertyDescriptor;
+@class RLMRealm;
+@class RLMResults<RLMObjectType>;
+
+/**
+ `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<X>`, where `X` is one of `RLMInt`, `RLMFloat`, `RLMDouble` or `RLMBool`, for optional number properties
+ - `RLMObject` subclasses, to model many-to-one relationships.
+ - `RLMArray<X>`, 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 <RLMThreadConfined>
+
+#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.
+
+ If the `value` argument is an array, 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.
+
+ If the `value` argument is an array, 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.
+
+ The `value` argument is used to populate the object. It can be a Realm object, 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.
+
+ If the object is being created, an exception will be thrown if any required properties
+ are not present and those properties were not defined with default values.
+
+ If the `value` 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.
+
+ If the object is being updated, all properties defined in its schema will be set by copying from
+ `value` using key-value coding. If the `value` argument does not respond to `valueForKey:` for a
+ given property name (or getter name, if defined), that value will remain untouched.
+ Nullable properties on the object can be set to nil by using `NSNull` as the updated value.
+
+ If the `value` argument is an array, 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.
+
+ The `value` argument is used to populate the object. It can be a Realm object, 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.
+
+ If the object is being created, an exception will be thrown if any required properties
+ are not present and those properties were not defined with default values.
+
+ If the `value` 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.
+
+ If the object is being updated, all properties defined in its schema will be set by copying from
+ `value` using key-value coding. If the `value` argument does not respond to `valueForKey:` for a
+ given property name (or getter name, if defined), that value will remain untouched.
+ Nullable properties on the object can be set to nil by using `NSNull` as the updated value.
+
+ If the `value` argument is an array, 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<NSString *> *)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<NSString *> *)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<NSString *> *)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<NSString *, RLMPropertyDescriptor *> *)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<__kindof RLMObject *> *)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<__kindof RLMObject *> *)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 - Notifications
+
+/**
+ A callback block for `RLMObject` notifications.
+
+ If the object is deleted from the managing Realm, the block is called with
+ `deleted` set to `YES` and the other two arguments are `nil`. The block will
+ never be called again after this.
+
+ If the object is modified, the block will be called with `deleted` set to
+ `NO`, a `nil` error, and an array of `RLMPropertyChange` objects which
+ indicate which properties of the objects were modified.
+
+ If an error occurs, `deleted` will be `NO`, `changes` will be `nil`, and
+ `error` will include information about the error. The block will never be
+ called again after an error occurs.
+ */
+typedef void (^RLMObjectChangeBlock)(BOOL deleted,
+                                     NSArray<RLMPropertyChange *> *_Nullable changes,
+                                     NSError *_Nullable error);
+
+/**
+ Registers a block to be called each time the object changes.
+
+ The block will be asynchronously called after each write transaction which
+ deletes the object or modifies any of the managed properties of the object,
+ including self-assignments that set a property to its existing value.
+
+ For write transactions performed on different threads or in differen
+ processes, the block will be called when the managing Realm is
+ (auto)refreshed to a version including the changes, while for local write
+ transactions it will be called at some point in the future after the write
+ transaction is committed.
+
+ 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.
+
+ Unlike with `RLMArray` and `RLMResults`, there is no "initial" callback made
+ after you add a new notification block.
+
+ Only objects which are managed by a Realm can be observed in this way. You
+ must retain the returned token for as long as you want updates to be sent to
+ the block. To stop receiving updates, call `-invalidate` on the token.
+
+ It is safe to capture a strong reference to the observed object within the
+ callback block. There is no retain cycle due to that the callback is retained
+ by the returned token and not by the object itself.
+
+ @warning This method cannot be called during a write transaction, when the
+          containing Realm is read-only, or on an unmanaged object.
+
+ @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:(RLMObjectChangeBlock)block;
+
+#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 the same logic as 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
+
+/**
+ Information about a specific property which changed in an `RLMObject` change notification.
+ */
+@interface RLMPropertyChange : NSObject
+
+/**
+ The name of the property which changed.
+ */
+@property (nonatomic, readonly, strong) NSString *name;
+
+/**
+ The value of the property before the change occurred. This will always be `nil`
+ if the change happened on the same thread as the notification and for `RLMArray`
+ properties.
+
+ For object properties this will give the object which was previously linked to,
+ but that object will have its new values and not the values it had before the
+ changes. This means that `previousValue` may be a deleted object, and you will
+ need to check `invalidated` before accessing any of its properties.
+ */
+@property (nonatomic, readonly, strong, nullable) id previousValue;
+
+/**
+ The value of the property after the change occurred. This will always be `nil`
+ for `RLMArray` properties.
+ */
+@property (nonatomic, readonly, strong, nullable) id value;
+@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<ObjectType *><ObjectType> *arrayOfObjectTypes;
+  */
+#define RLM_ARRAY_TYPE(RLM_OBJECT_SUBCLASS)\
+@protocol RLM_OBJECT_SUBCLASS <NSObject>   \
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMObjectBase.h b/iOS/Pods/Realm/include/RLMObjectBase.h
new file mode 100644 (file)
index 0000000..6b3271f
--- /dev/null
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+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;
+
++ (nullable NSString *)_realmObjectName;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMObjectBase_Dynamic.h b/iOS/Pods/Realm/include/RLMObjectBase_Dynamic.h
new file mode 100644 (file)
index 0000000..55f64ef
--- /dev/null
@@ -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 <Realm/RLMObject.h>
+
+@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/iOS/Pods/Realm/include/RLMObjectBase_Private.h b/iOS/Pods/Realm/include/RLMObjectBase_Private.h
new file mode 100644 (file)
index 0000000..c2d0722
--- /dev/null
@@ -0,0 +1,28 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 <Realm/RLMObjectBase.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+// RLMObjectBase private
+@interface RLMObjectBase ()
++ (void)initializeLinkedObjectSchemas;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMObjectSchema.h b/iOS/Pods/Realm/include/RLMObjectSchema.h
new file mode 100644 (file)
index 0000000..83a7d84
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+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<NSCopying>
+
+#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<RLMProperty *> *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/iOS/Pods/Realm/include/RLMObjectSchema_Private.h b/iOS/Pods/Realm/include/RLMObjectSchema_Private.h
new file mode 100644 (file)
index 0000000..deca77d
--- /dev/null
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMObjectSchema.h>
+
+#import <objc/runtime.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+// RLMObjectSchema private
+@interface RLMObjectSchema () {
+@public
+    bool _isSwiftClass;
+}
+
+/// The object type name reported to the object store and core.
+@property (nonatomic, readonly) NSString *objectName;
+
+// writable redeclaration
+@property (nonatomic, readwrite, copy) NSArray<RLMProperty *> *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<RLMProperty *> *computedProperties;
+@property (nonatomic, readonly) NSArray<RLMProperty *> *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/iOS/Pods/Realm/include/RLMObjectSchema_Private.hpp b/iOS/Pods/Realm/include/RLMObjectSchema_Private.hpp
new file mode 100644 (file)
index 0000000..3d93be2
--- /dev/null
@@ -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/iOS/Pods/Realm/include/RLMObjectStore.h b/iOS/Pods/Realm/include/RLMObjectStore.h
new file mode 100644 (file)
index 0000000..b44e7e6
--- /dev/null
@@ -0,0 +1,88 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+#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);
+
+
+//
+// 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 RLMManagedArray
+void RLMInitializeSwiftAccessorGenerics(RLMObjectBase *object);
+
+#ifdef __cplusplus
+}
+
+namespace realm {
+    class Table;
+    template<typename T> class BasicRowExpr;
+    using RowExpr = BasicRowExpr<Table>;
+}
+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/iOS/Pods/Realm/include/RLMObject_Private.h b/iOS/Pods/Realm/include/RLMObject_Private.h
new file mode 100644 (file)
index 0000000..33a13ae
--- /dev/null
@@ -0,0 +1,147 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMObjectBase_Dynamic.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class RLMProperty, RLMArray, RLMSwiftPropertyMetadata;
+typedef NS_ENUM(int32_t, RLMPropertyType);
+
+// 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<NSCopying>
+
+- (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);
+
+typedef void (^RLMObjectNotificationCallback)(NSArray<NSString *> *_Nullable propertyNames,
+                                              NSArray *_Nullable oldValues,
+                                              NSArray *_Nullable newValues,
+                                              NSError *_Nullable error);
+FOUNDATION_EXTERN RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block);
+
+// Returns whether the class is a descendent of RLMObjectBase
+FOUNDATION_EXTERN BOOL RLMIsObjectOrSubclass(Class klass);
+
+// Returns whether the class is an indirect descendant of RLMObjectBase
+FOUNDATION_EXTERN BOOL RLMIsObjectSubclass(Class klass);
+
+// 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.
+FOUNDATION_EXTERN void RLMSetTreatFakeObjectAsRLMObject(BOOL flag);
+
+// Get ObjectUil class for objc or swift
+FOUNDATION_EXTERN Class RLMObjectUtilClass(BOOL isSwift);
+
+FOUNDATION_EXTERN const NSUInteger RLMDescriptionMaxDepth;
+
+@interface RLMObjectUtil : NSObject
+
++ (nullable NSArray<NSString *> *)ignoredPropertiesForClass:(Class)cls;
++ (nullable NSArray<NSString *> *)indexedPropertiesForClass:(Class)cls;
++ (nullable NSDictionary<NSString *, NSDictionary<NSString *, NSString *> *> *)linkingObjectsPropertiesForClass:(Class)cls;
+
+// Precondition: these must be returned in ascending order.
++ (nullable NSArray<RLMSwiftPropertyMetadata *> *)getSwiftProperties:(id)obj;
+
++ (nullable NSDictionary<NSString *, NSNumber *> *)getOptionalProperties:(id)obj;
++ (nullable NSArray<NSString *> *)requiredPropertiesForClass:(Class)cls;
+
+@end
+
+typedef NS_ENUM(NSUInteger, RLMSwiftPropertyKind) {
+    RLMSwiftPropertyKindList,
+    RLMSwiftPropertyKindLinkingObjects,
+    RLMSwiftPropertyKindOptional,
+    RLMSwiftPropertyKindNilLiteralOptional,   // For Swift optional properties that reflect as nil
+    RLMSwiftPropertyKindOther,
+};
+
+// Metadata that describes a Swift generic property.
+@interface RLMSwiftPropertyMetadata : NSObject
+
+@property (nonatomic, strong) NSString *propertyName;
+@property (nullable, nonatomic, strong) NSString *className;
+@property (nullable, nonatomic, strong) NSString *linkedPropertyName;
+@property (nonatomic) RLMPropertyType propertyType;
+@property (nonatomic) RLMSwiftPropertyKind kind;
+
++ (instancetype)metadataForOtherProperty:(NSString *)propertyName;
+
++ (instancetype)metadataForListProperty:(NSString *)propertyName;
+
++ (instancetype)metadataForLinkingObjectsProperty:(NSString *)propertyName
+                                        className:(NSString *)className
+                               linkedPropertyName:(NSString *)linkedPropertyName;
+
++ (instancetype)metadataForOptionalProperty:(NSString *)propertyName type:(RLMPropertyType)type;
+
++ (instancetype)metadataForNilLiteralOptionalProperty:(NSString *)propertyName;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMObject_Private.hpp b/iOS/Pods/Realm/include/RLMObject_Private.hpp
new file mode 100644 (file)
index 0000000..7f79dd6
--- /dev/null
@@ -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 <realm/link_view.hpp> // required by row.hpp
+#import <realm/row.hpp>
+
+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/iOS/Pods/Realm/include/RLMObservation.hpp b/iOS/Pods/Realm/include/RLMObservation.hpp
new file mode 100644 (file)
index 0000000..0f5b215
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+#import "binding_context.hpp"
+
+#import <realm/row.hpp>
+#import <realm/table.hpp>
+
+#import <unordered_map>
+
+@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<typename F>
+    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<realm::BindingContext::ObserverState> RLMGetObservedRows(RLMSchemaInfo const& schema);
+void RLMWillChange(std::vector<realm::BindingContext::ObserverState> const& observed, std::vector<void *> const& invalidated);
+void RLMDidChange(std::vector<realm::BindingContext::ObserverState> const& observed, std::vector<void *> const& invalidated);
diff --git a/iOS/Pods/Realm/include/RLMOptionalBase.h b/iOS/Pods/Realm/include/RLMOptionalBase.h
new file mode 100644 (file)
index 0000000..5ee261e
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#import <Realm/RLMConstants.h>
+
+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/iOS/Pods/Realm/include/RLMPlatform.h b/iOS/Pods/Realm/include/RLMPlatform.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/iOS/Pods/Realm/include/RLMPredicateUtil.hpp b/iOS/Pods/Realm/include/RLMPredicateUtil.hpp
new file mode 100644 (file)
index 0000000..71426de
--- /dev/null
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+#import <functional>
+
+using ExpressionVisitor = std::function<NSExpression *(NSExpression *)>;
+NSPredicate *transformPredicate(NSPredicate *, ExpressionVisitor);
diff --git a/iOS/Pods/Realm/include/RLMPrefix.h b/iOS/Pods/Realm/include/RLMPrefix.h
new file mode 100644 (file)
index 0000000..df08ce9
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#endif
+
+#ifdef __cplusplus
+#import <functional>
+#import <map>
+#import <memory>
+#import <string>
+#import <vector>
+
+#import <realm/group.hpp>
+#import <realm/link_view.hpp>
+#import <realm/row.hpp>
+#import <realm/table.hpp>
+#import <realm/table_view.hpp>
+#endif
diff --git a/iOS/Pods/Realm/include/RLMProperty.h b/iOS/Pods/Realm/include/RLMProperty.h
new file mode 100644 (file)
index 0000000..a2bc894
--- /dev/null
@@ -0,0 +1,126 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMConstants.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// :nodoc:
+@protocol RLMInt @end
+/// :nodoc:
+@protocol RLMBool @end
+/// :nodoc:
+@protocol RLMDouble @end
+/// :nodoc:
+@protocol RLMFloat @end
+/// :nodoc:
+@protocol RLMString @end
+/// :nodoc:
+@protocol RLMDate @end
+/// :nodoc:
+@protocol RLMData @end
+
+/// :nodoc:
+@interface NSNumber ()<RLMInt, RLMBool, RLMDouble, RLMFloat>
+@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;
+
+/**
+ Indicates whether this property is an array.
+ */
+@property (nonatomic, readonly) BOOL array;
+
+#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/iOS/Pods/Realm/include/RLMProperty_Private.h b/iOS/Pods/Realm/include/RLMProperty_Private.h
new file mode 100644 (file)
index 0000000..68fc521
--- /dev/null
@@ -0,0 +1,135 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMProperty.h>
+
+#import <objc/runtime.h>
+
+@class RLMObjectBase;
+
+NS_ASSUME_NONNULL_BEGIN
+
+BOOL RLMPropertyTypeIsComputed(RLMPropertyType propertyType);
+FOUNDATION_EXTERN void RLMValidateSwiftPropertyName(NSString *name);
+
+// 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 RLMPropertyTypeLinkingObjects:
+            return @"linking objects";
+    }
+    return @"Unknown";
+}
+
+// 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
+                                     instance:(id)object;
+
+- (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) 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/iOS/Pods/Realm/include/RLMProperty_Private.hpp b/iOS/Pods/Realm/include/RLMProperty_Private.hpp
new file mode 100644 (file)
index 0000000..0e214d5
--- /dev/null
@@ -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 <Realm/RLMProperty_Private.h>
+
+#import "property.hpp"
+
+@interface RLMProperty ()
+
++ (instancetype)propertyForObjectStoreProperty:(const realm::Property&)property;
+
+- (realm::Property)objectStoreCopy;
+
+@end
diff --git a/iOS/Pods/Realm/include/RLMQueryUtil.hpp b/iOS/Pods/Realm/include/RLMQueryUtil.hpp
new file mode 100644 (file)
index 0000000..12df76e
--- /dev/null
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+#import <vector>
+
+namespace realm {
+    class Group;
+    class Query;
+    class SortDescriptor;
+}
+
+@class RLMObjectSchema, RLMProperty, RLMSchema, RLMSortDescriptor;
+class RLMClassInfo;
+
+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);
diff --git a/iOS/Pods/Realm/include/RLMRealm+Sync.h b/iOS/Pods/Realm/include/RLMRealm+Sync.h
new file mode 100644 (file)
index 0000000..6ed4b41
--- /dev/null
@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 <Foundation/Foundation.h>
+
+#import "RLMRealm.h"
+
+@class RLMResults;
+
+/**
+ A callback used to vend the results of a partial sync fetch.
+ */
+typedef void(^RLMPartialSyncFetchCallback)(RLMResults * _Nullable results, NSError * _Nullable error);
+
+NS_ASSUME_NONNULL_BEGIN
+
+///
+@interface RLMRealm (Sync)
+
+/**
+ If the Realm is a partially synchronized Realm, fetch and synchronize the objects
+ of a given object type that match the given query (in string format).
+
+ The results will be returned asynchronously in the callback.
+ Use `-[RLMResults addNotificationBlock:]` to be notified to changes to the set of
+ synchronized objects.
+
+ @warning Partial synchronization is a tech preview. Its APIs are subject to change.
+*/
+- (void)subscribeToObjects:(Class)type where:(NSString *)query callback:(RLMPartialSyncFetchCallback)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMRealm.h b/iOS/Pods/Realm/include/RLMRealm.h
new file mode 100644 (file)
index 0000000..253a74b
--- /dev/null
@@ -0,0 +1,651 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+#import "RLMConstants.h"
+
+@class RLMRealmConfiguration, RLMRealm, RLMObject, RLMSchema, RLMMigration, RLMNotificationToken, RLMThreadSafeReference;
+
+/**
+ A callback block for opening Realms asynchronously.
+
+ Returns the Realm if the open was successful, or an error otherwise.
+ */
+typedef void(^RLMAsyncOpenRealmCallback)(RLMRealm * _Nullable realm, NSError * _Nullable error);
+
+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;
+
+/**
+ Asynchronously open a Realm and deliver it to a block on the given queue.
+
+ Opening a Realm asynchronously will perform all work needed to get the Realm to
+ a usable state (such as running potentially time-consuming migrations) on a
+ background thread before dispatching to the given queue. In addition,
+ synchronized Realms wait for all remote content available at the time the
+ operation began to be downloaded and available locally.
+
+ @param configuration A configuration object to use when opening the Realm.
+ @param callbackQueue The dispatch queue on which the callback should be run.
+ @param callback      A callback block. If the Realm was successfully opened,
+                      it will be passed in as an argument.
+                      Otherwise, an `NSError` describing what went wrong will be
+                      passed to the block instead.
+
+ @note The returned Realm is confined to the thread on which it was created.
+       Because GCD does not guarantee that queues will always use the same
+       thread, accessing the returned Realm outside the callback block (even if
+       accessed from `callbackQueue`) is unsafe.
+ */
++ (void)asyncOpenWithConfiguration:(RLMRealmConfiguration *)configuration
+                     callbackQueue:(dispatch_queue_t)callbackQueue
+                          callback:(RLMAsyncOpenRealmCallback)callback;
+
+/**
+ 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 for each Realm file. 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 for the same Realm file in
+ other threads or other processes will block until the current write transaction
+ completes or is cancelled.
+
+ 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.
+
+ After saving the changes, all notification blocks registered on this specific
+ `RLMRealm` instance are invoked synchronously. Notification blocks registered
+ on other threads or on collections are invoked asynchronously. If you do not
+ want to receive a specific notification for this write tranaction, see
+ `commitWriteTransactionWithoutNotifying:error:`.
+
+ This method can fail if there is insufficient disk space available to save the
+ writes made, or due to unexpected i/o errors. This version of the method throws
+ an exception when errors occur. Use the version with a `NSError` out parameter
+ instead if you wish to handle errors.
+
+ @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.
+
+ After saving the changes, all notification blocks registered on this specific
+ `RLMRealm` instance are invoked synchronously. Notification blocks registered
+ on other threads or on collections are invoked asynchronously. If you do not
+ want to receive a specific notification for this write tranaction, see
+ `commitWriteTransactionWithoutNotifying:error:`.
+
+ This method can fail if there is insufficient disk space available to save the
+ writes made, or due to unexpected i/o errors.
+
+ @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;
+
+/**
+ Commits all write operations in the current write transaction, without
+ notifying specific notification blocks of the changes.
+
+ After saving the changes, all notification blocks registered on this specific
+ `RLMRealm` instance are invoked synchronously. Notification blocks registered
+ on other threads or on collections are scheduled to be invoked asynchronously.
+
+ You can skip notifiying specific notification blocks about the changes made
+ in this write transaction by passing in their associated notification tokens.
+ This is primarily useful when the write transaction is saving changes already
+ made in the UI and you do not want to have the notification block attempt to
+ re-apply the same changes.
+
+ The tokens passed to this method must be for notifications for this specific
+ `RLMRealm` instance. Notifications for different threads cannot be skipped
+ using this method.
+
+ This method can fail if there is insufficient disk space available to save the
+ writes made, or due to unexpected i/o errors.
+
+ @warning This method may only be called during a write transaction.
+
+ @param tokens An array of notification tokens which were returned from adding
+               callbacks which you do not want to be notified for the changes
+               made in this 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)commitWriteTransactionWithoutNotifying:(NSArray<RLMNotificationToken *> *)tokens error:(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.
+
+ KVO observers on any objects which were modified during the transaction will
+ be notified about the change back to their initial values, but no other
+ notifcations are produced by a cancelled write transaction.
+
+ @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.
+
+ If the version of the Realm is actually changed, Realm and collection
+ notifications will be sent to reflect the changes. This may take some time, as
+ collection notifications are prepared on a background thread. As a result,
+ calling this method on the main thread is not advisable.
+
+ @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.
+
+ Write transactions will still always advance a Realm to the latest version and
+ produce local notifications on commit even if autorefresh is disabled.
+
+ 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
+
+/**
+ Returns the same object as the one referenced when the `RLMThreadSafeReference` was first created,
+ but resolved for the current Realm for this thread. Returns `nil` if this object was deleted after
+ the reference was created.
+
+ @param reference The thread-safe reference to the thread-confined object to resolve in this Realm.
+
+ @warning A `RLMThreadSafeReference` object must be resolved at most once.
+          Failing to resolve a `RLMThreadSafeReference` will result in the source version of the
+          Realm being pinned until the reference is deallocated.
+          An exception will be thrown if a reference is resolved more than once.
+
+ @warning Cannot call within a write transaction.
+
+ @note Will refresh this Realm if the source Realm was at a later version than this one.
+
+ @see `+[RLMThreadSafeReference referenceWithThreadConfined:]`
+ */
+- (nullable id)resolveThreadSafeReference:(RLMThreadSafeReference *)reference
+NS_REFINED_FOR_SWIFT;
+
+#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 objects   An enumerable collection such as `NSArray`, `RLMArray`, or `RLMResults`,
+                  containing Realm objects to be added to the Realm.
+
+ @see   `addObject:`
+ */
+- (void)addObjects:(id<NSFastEnumeration>)objects;
+
+/**
+ 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.
+ If there is a property or KVC value on `object` whose value is nil, and it corresponds
+ to a nullable property on an existing object being updated, that nullable property will
+ be set to nil.
+
+ @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 objects  An enumerable collection such as `NSArray`, `RLMArray`, or `RLMResults`,
+                 containing Realm objects to be added to or updated within the Realm.
+
+ @see   `addOrUpdateObject:`
+ */
+- (void)addOrUpdateObjects:(id<NSFastEnumeration>)objects;
+
+/**
+ 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 objects  An enumerable collection such as `NSArray`, `RLMArray`, or `RLMResults`,
+                 containing objects to be deleted from the Realm.
+
+ @see `deleteObject:`
+ */
+- (void)deleteObjects:(id<NSFastEnumeration>)objects;
+
+/**
+ 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
+ */
++ (BOOL)performMigrationForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error;
+
+#pragma mark - Unavailable Methods
+
+/**
+ RLMRealm instances are cached internally by Realm and cannot be created directly.
+
+ Use `+[RLMRealm defaultRealm]`, `+[RLMRealm realmWithConfiguration:error:]` or
+ `+[RLMRealm realmWithURL]` to obtain a reference to an RLMRealm.
+ */
+- (instancetype)init __attribute__((unavailable("Use +defaultRealm, +realmWithConfiguration: or +realmWithURL:.")));
+
+/**
+ RLMRealm instances are cached internally by Realm and cannot be created directly.
+
+ Use `+[RLMRealm defaultRealm]`, `+[RLMRealm realmWithConfiguration:error:]` or
+ `+[RLMRealm realmWithURL]` to obtain a reference to an RLMRealm.
+ */
++ (instancetype)new __attribute__((unavailable("Use +defaultRealm, +realmWithConfiguration: or +realmWithURL:.")));
+
+/// :nodoc:
+- (void)addOrUpdateObjectsFromArray:(id)array __attribute__((unavailable("Renamed to -addOrUpdateObjects:.")));
+
+@end
+
+// MARK: - RLMNotificationToken
+
+/**
+ 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 `-invalidate` method. Notifications are also stopped if
+ the token is deallocated.
+ */
+@interface RLMNotificationToken : NSObject
+/// Stops notifications for the change subscription that returned this token.
+- (void)invalidate;
+
+/// Stops notifications for the change subscription that returned this token.
+- (void)stop __attribute__((unavailable("Renamed to -invalidate."))) NS_REFINED_FOR_SWIFT;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMRealmConfiguration+Sync.h b/iOS/Pods/Realm/include/RLMRealmConfiguration+Sync.h
new file mode 100644 (file)
index 0000000..abd51bf
--- /dev/null
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMRealmConfiguration.h>
+
+#import "RLMSyncUtil.h"
+
+@class RLMSyncConfiguration;
+
+/// Realm configuration options related to Sync.
+@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 any one of the three properties will automatically nil out the other two.
+
+ @see `RLMSyncConfiguration`
+ */
+@property (nullable, nonatomic) RLMSyncConfiguration *syncConfiguration;
+
+NS_ASSUME_NONNULL_END
+
+@end
diff --git a/iOS/Pods/Realm/include/RLMRealmConfiguration.h b/iOS/Pods/Realm/include/RLMRealmConfiguration.h
new file mode 100644 (file)
index 0000000..3ec8fd5
--- /dev/null
@@ -0,0 +1,123 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+#import <Realm/RLMRealm.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A block called when opening a Realm for the first time during the life
+ of a process to determine if it should be compacted before being returned
+ to the user. It is passed the total file size (data + free space) and the total
+ bytes used by data in the file.
+
+ Return `YES` to indicate that an attempt to compact the file should be made.
+ The compaction will be skipped if another process is accessing it.
+ */
+typedef BOOL (^RLMShouldCompactOnLaunchBlock)(NSUInteger totalBytes, NSUInteger bytesUsed);
+
+/**
+ 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<NSCopying>
+
+#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` and `syncConfiguration`;
+/// setting any one of the three properties will automatically nil out the other two.
+@property (nonatomic, copy, nullable) NSURL *fileURL;
+
+/// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL` and `syncConfiguration`;
+/// setting any one of the three properties will automatically nil out the other two.
+@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;
+
+/**
+ A block called when opening a Realm for the first time during the life
+ of a process to determine if it should be compacted before being returned
+ to the user. It is passed the total file size (data + free space) and the total
+ bytes used by data in the file.
+
+ Return `YES` to indicate that an attempt to compact the file should be made.
+ The compaction will be skipped if another process is accessing it.
+ */
+@property (nonatomic, copy, nullable) RLMShouldCompactOnLaunchBlock shouldCompactOnLaunch;
+
+/// The classes managed by the Realm.
+@property (nonatomic, copy, nullable) NSArray *objectClasses;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMRealmConfiguration_Private.h b/iOS/Pods/Realm/include/RLMRealmConfiguration_Private.h
new file mode 100644 (file)
index 0000000..b3e4784
--- /dev/null
@@ -0,0 +1,45 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMRealmConfiguration.h>
+
+@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;
+@property (nonatomic, copy) NSString *pathOnDisk;
+
+// Get the default confiugration without copying it
++ (RLMRealmConfiguration *)rawDefaultConfiguration;
+
++ (void)resetRealmConfigurationState;
+
+- (void)setCustomSchemaWithoutCopying:(nullable RLMSchema *)schema;
+@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/iOS/Pods/Realm/include/RLMRealmConfiguration_Private.hpp b/iOS/Pods/Realm/include/RLMRealmConfiguration_Private.hpp
new file mode 100644 (file)
index 0000000..a89fb0f
--- /dev/null
@@ -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/iOS/Pods/Realm/include/RLMRealmUtil.hpp b/iOS/Pods/Realm/include/RLMRealmUtil.hpp
new file mode 100644 (file)
index 0000000..4960b9c
--- /dev/null
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <memory>
+#import <string>
+
+@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();
+// Check if the current thread is currently within a running CFRunLoop
+bool RLMIsInRunLoop();
+
+std::unique_ptr<realm::BindingContext> RLMCreateBindingContext(RLMRealm *realm);
diff --git a/iOS/Pods/Realm/include/RLMRealm_Dynamic.h b/iOS/Pods/Realm/include/RLMRealm_Dynamic.h
new file mode 100644 (file)
index 0000000..f796ed3
--- /dev/null
@@ -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 <Realm/RLMRealm.h>
+
+#import <Realm/RLMObjectSchema.h>
+#import <Realm/RLMProperty.h>
+
+@class RLMResults<RLMObjectType>;
+
+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<RLMObject *> *)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<RLMObject *> *)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<RLMObject *> *)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/iOS/Pods/Realm/include/RLMRealm_Private.h b/iOS/Pods/Realm/include/RLMRealm_Private.h
new file mode 100644 (file)
index 0000000..59b7c65
--- /dev/null
@@ -0,0 +1,52 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMRealm.h>
+
+@class RLMFastEnumerator;
+
+NS_ASSUME_NONNULL_BEGIN
+
+// Disable syncing files to disk. Cannot be re-enabled. Use only for tests.
+FOUNDATION_EXTERN void RLMDisableSyncToDisk(void);
+
+FOUNDATION_EXTERN NSData * _Nullable RLMRealmValidatedEncryptionKey(NSData *key);
+
+// Translate an in-flight exception resulting from an operation on 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:(bool)isCollection;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMRealm_Private.hpp b/iOS/Pods/Realm/include/RLMRealm_Private.hpp
new file mode 100644 (file)
index 0000000..a1bb294
--- /dev/null
@@ -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::Realm> _realm;
+    RLMSchemaInfo _info;
+}
+
+// FIXME - group should not be exposed
+@property (nonatomic, readonly) realm::Group &group;
+@end
diff --git a/iOS/Pods/Realm/include/RLMResults.h b/iOS/Pods/Realm/include/RLMResults.h
new file mode 100644 (file)
index 0000000..d8d1cf6
--- /dev/null
@@ -0,0 +1,351 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMCollection.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class RLMObject;
+
+/**
+ `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<RLMObjectType> : NSObject<RLMCollection, NSFastEnumeration>
+
+#pragma mark - Properties
+
+/**
+ The number of objects in the results collection.
+ */
+@property (nonatomic, readonly, assign) NSUInteger count;
+
+/**
+ The type of the objects in the results collection.
+ */
+@property (nonatomic, readonly, assign) RLMPropertyType type;
+
+/**
+ Indicates whether the objects in the collection can be `nil`.
+ */
+@property (nonatomic, readwrite, getter = isOptional) BOOL optional;
+
+/**
+ The class name  of the objects contained in the results collection.
+
+ Will be `nil` if `type` is not RLMPropertyTypeObject.
+ */
+@property (nonatomic, readonly, copy, nullable) 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 object 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 object 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 object 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<RLMObjectType> *)objectsWhere:(NSString *)predicateFormat, ...;
+
+/// :nodoc:
+- (RLMResults<RLMObjectType> *)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<RLMObjectType> *)objectsWithPredicate:(NSPredicate *)predicate;
+
+/**
+ Returns a sorted `RLMResults` from an existing results collection.
+
+ @param keyPath     The key path to sort by.
+ @param ascending   The direction to sort in.
+
+ @return    An `RLMResults` sorted by the specified key path.
+ */
+- (RLMResults<RLMObjectType> *)sortedResultsUsingKeyPath:(NSString *)keyPath 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<RLMObjectType> *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties;
+
+/**
+ Returns a distinct `RLMResults` from an existing results collection.
+ @param keyPaths  The key paths used produce distinct results
+ @return    An `RLMResults` made distinct based on the specified key paths
+ */
+- (RLMResults<RLMObjectType> *)distinctResultsUsingKeyPaths:(NSArray<NSString *> *)keyPaths;
+
+#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<Dog *> *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 `-invalidate` 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<RLMObjectType> *__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, or `nil` if the Results are empty.
+ */
+- (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, or `nil` if the Results are empty.
+ */
+- (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, or `nil` if the Results are empty.
+ */
+- (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<RLMObjectType: RLMObject *> : RLMResults
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMResults_Private.h b/iOS/Pods/Realm/include/RLMResults_Private.h
new file mode 100644 (file)
index 0000000..f74b4fd
--- /dev/null
@@ -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 <Realm/RLMResults.h>
+
+@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/iOS/Pods/Realm/include/RLMResults_Private.hpp b/iOS/Pods/Realm/include/RLMResults_Private.hpp
new file mode 100644 (file)
index 0000000..a4f5abf
--- /dev/null
@@ -0,0 +1,63 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 "results.hpp"
+
+class RLMClassInfo;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface RLMResults () {
+@protected
+    realm::Results _results;
+}
+
+/**
+ Initialize a 'raw' `RLMResults` using only an object store level Results.
+ This is only meant for applications where a results collection is being backed
+ by an object store object class that has no binding-level equivalent. The
+ consumer is responsible for bridging between the underlying objects and whatever
+ binding-level class is being vended out.
+ */
+- (instancetype)initWithResults:(realm::Results)results;
+
++ (instancetype)resultsWithObjectInfo:(RLMClassInfo&)info results:(realm::Results)results;
+@end
+
+NS_ASSUME_NONNULL_END
+
+// Utility functions
+
+[[gnu::noinline]]
+[[noreturn]]
+void RLMThrowResultsError(NSString * _Nullable aggregateMethod);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnullability-completeness"
+template<typename Function>
+static auto translateRLMResultsErrors(Function&& f, NSString *aggregateMethod=nil) {
+    try {
+        return f();
+    }
+    catch (...) {
+        RLMThrowResultsError(aggregateMethod);
+    }
+}
+#pragma clang diagnostic pop
diff --git a/iOS/Pods/Realm/include/RLMSchema.h b/iOS/Pods/Realm/include/RLMSchema.h
new file mode 100644 (file)
index 0000000..30325e4
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+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<NSCopying>
+
+#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<RLMObjectSchema *> *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/iOS/Pods/Realm/include/RLMSchema_Private.h b/iOS/Pods/Realm/include/RLMSchema_Private.h
new file mode 100644 (file)
index 0000000..7ef4917
--- /dev/null
@@ -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 <Realm/RLMSchema.h>
+
+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<Class> *)classes;
+
+@property (nonatomic, readwrite, copy) NSArray<RLMObjectSchema *> *objectSchema;
+
+// schema based on runtime objects
++ (instancetype)sharedSchema;
+
+// schema based upon all currently registered object classes
++ (instancetype)partialSharedSchema;
+
+// private schema based upon all currently registered object classes.
+// includes classes that are excluded from the default schema.
++ (instancetype)partialPrivateSharedSchema;
+
+// class for string
++ (nullable Class)classForString:(NSString *)className;
+
++ (nullable RLMObjectSchema *)sharedSchemaForClass:(Class)cls;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMSchema_Private.hpp b/iOS/Pods/Realm/include/RLMSchema_Private.hpp
new file mode 100644 (file)
index 0000000..197ddee
--- /dev/null
@@ -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 <memory>
+
+namespace realm {
+    class Schema;
+    class ObjectSchema;
+}
+
+@interface RLMSchema ()
++ (instancetype)dynamicSchemaFromObjectStoreSchema:(realm::Schema const&)objectStoreSchema;
+- (realm::Schema)objectStoreCopy;
+@end
diff --git a/iOS/Pods/Realm/include/RLMSwiftBridgingHeader.h b/iOS/Pods/Realm/include/RLMSwiftBridgingHeader.h
new file mode 100644 (file)
index 0000000..4758043
--- /dev/null
@@ -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 <Realm/RLMArray.h>
+#import <Realm/RLMObject.h>
+
+@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/iOS/Pods/Realm/include/RLMSwiftSupport.h b/iOS/Pods/Realm/include/RLMSwiftSupport.h
new file mode 100644 (file)
index 0000000..6e45b65
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface RLMSwiftSupport : NSObject
+
++ (BOOL)isSwiftClassName:(NSString *)className;
++ (NSString *)demangleClassName:(NSString *)className;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMSyncConfiguration.h b/iOS/Pods/Realm/include/RLMSyncConfiguration.h
new file mode 100644 (file)
index 0000000..b84a3ee
--- /dev/null
@@ -0,0 +1,78 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+@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;
+
+
+/**
+ Whether SSL certificate validation is enabled for the connection associated
+ with this configuration value. SSL certificate validation is ON by default.
+
+ @warning NEVER disable certificate validation for clients and servers in production.
+ */
+@property (nonatomic) BOOL enableSSLValidation;
+
+/**
+ Whether this Realm should be opened in 'partial synchronization' mode.
+ Partial synchronization mode means that no objects are synchronized from the remote Realm
+ except those matching queries that the user explicitly specifies.
+
+ @warning Partial synchronization is a tech preview. Its APIs are subject to change.
+*/
+@property (nonatomic) BOOL isPartial;
+
+/**
+ 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/iOS/Pods/Realm/include/RLMSyncConfiguration_Private.h b/iOS/Pods/Realm/include/RLMSyncConfiguration_Private.h
new file mode 100644 (file)
index 0000000..1001ae8
--- /dev/null
@@ -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 <Realm/RLMSyncConfiguration.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSUInteger, RLMSyncStopPolicy) {
+    RLMSyncStopPolicyImmediately,
+    RLMSyncStopPolicyLiveIndefinitely,
+    RLMSyncStopPolicyAfterChangesUploaded,
+};
+
+@interface RLMSyncConfiguration ()
+
+@property (nonatomic, readwrite) RLMSyncStopPolicy stopPolicy;
+
+// Internal-only APIs
+@property (nullable, nonatomic) NSURL *customFileURL;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMSyncConfiguration_Private.hpp b/iOS/Pods/Realm/include/RLMSyncConfiguration_Private.hpp
new file mode 100644 (file)
index 0000000..1a80e7f
--- /dev/null
@@ -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/iOS/Pods/Realm/include/RLMSyncCredentials.h b/iOS/Pods/Realm/include/RLMSyncCredentials.h
new file mode 100644 (file)
index 0000000..c90fb4c
--- /dev/null
@@ -0,0 +1,140 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+#import "RLMSyncUtil.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// A token representing an identity provider's credentials.
+typedef NSString *RLMSyncCredentialsToken;
+
+/// 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;
+
+/// A CloudKit account as an identity provider.
+extern RLMIdentityProvider const RLMIdentityProviderCloudKit;
+
+/// A JSON Web Token as an identity provider.
+extern RLMIdentityProvider const RLMIdentityProviderJWT;
+
+/// An Anonymous account as an identity provider.
+extern RLMIdentityProvider const RLMIdentityProviderAnonymous;
+
+/// A Nickname account as an identity provider.
+extern RLMIdentityProvider const RLMIdentityProviderNickname;
+
+/**
+ Opaque credentials representing a specific Realm Object Server user.
+ */
+@interface RLMSyncCredentials : NSObject
+
+/// An opaque credentials token containing information that uniquely identifies a Realm Object Server user.
+@property (nonatomic, readonly) RLMSyncCredentialsToken token;
+
+/// The name of the identity provider which generated the credentials token.
+@property (nonatomic, readonly) RLMIdentityProvider provider;
+
+/// A dictionary containing additional pertinent information. In most cases this is automatically configured.
+@property (nonatomic, readonly) NSDictionary<NSString *, id> *userInfo;
+
+/**
+ Construct and return credentials from a Facebook account token.
+ */
++ (instancetype)credentialsWithFacebookToken:(RLMSyncCredentialsToken)token;
+
+/**
+ Construct and return credentials from a Google account token.
+ */
++ (instancetype)credentialsWithGoogleToken:(RLMSyncCredentialsToken)token;
+
+/**
+ Construct and return credentials from an CloudKit account token.
+ */
++ (instancetype)credentialsWithCloudKitToken:(RLMSyncCredentialsToken)token;
+
+/**
+ Construct and return credentials from a Realm Object Server username and password.
+ */
++ (instancetype)credentialsWithUsername:(NSString *)username
+                               password:(NSString *)password
+                               register:(BOOL)shouldRegister;
+
+/**
+ Construct and return credentials from a JSON Web Token.
+ */
++ (instancetype)credentialsWithJWT:(NSString *)token;
+
+/**
+ Construct and return anonymous credentials
+ */
++ (instancetype)anonymousCredentials;
+    
+/**
+ Construct and return credentials from a nickname
+ */
++ (instancetype)credentialsWithNickname:(NSString *)nickname isAdmin:(BOOL)isAdmin;
+
+/**
+ Construct and return special credentials representing a token that can
+ be directly used to open a Realm. The identity is used to uniquely identify
+ the user across application launches.
+
+ @warning The custom user identity will be deprecated in a future release.
+
+ @warning Do not specify a user identity that is the URL of an authentication
+          server.
+
+ @warning When passing an access token credential into any of `RLMSyncUser`'s
+          login methods, you must always specify the same authentication server
+          URL, or none at all, every time you call the login method.
+ */
++ (instancetype)credentialsWithAccessToken:(RLMServerToken)accessToken identity:(NSString *)identity;
+
+/**
+ Construct and return credentials with a custom token string, identity provider string, and optional user info. In most
+ cases, the convenience initializers should be used instead.
+ */
+- (instancetype)initWithCustomToken:(RLMSyncCredentialsToken)token
+                           provider:(RLMIdentityProvider)provider
+                           userInfo:(nullable NSDictionary *)userInfo NS_DESIGNATED_INITIALIZER;
+
+/// :nodoc:
+- (instancetype)init __attribute__((unavailable("RLMSyncCredentials cannot be created directly")));
+
+/// :nodoc:
++ (instancetype)new __attribute__((unavailable("RLMSyncCredentials cannot be created directly")));
+
+NS_ASSUME_NONNULL_END
+
+@end
diff --git a/iOS/Pods/Realm/include/RLMSyncManager.h b/iOS/Pods/Realm/include/RLMSyncManager.h
new file mode 100644 (file)
index 0000000..909076a
--- /dev/null
@@ -0,0 +1,106 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+#import "RLMSyncUtil.h"
+
+@class RLMSyncSession;
+
+/// 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
+
+/**
+ A block which can optionally be set to report sync-related errors to your application.
+
+ Any error reported through this block will be of the `RLMSyncError` type, and marked
+ with the `RLMSyncErrorDomain` domain.
+
+ Errors reported through this mechanism are fatal, with several exceptions. Please consult
+ `RLMSyncError` for information about the types of errors that can be reported through
+ the block, and for for suggestions on handling recoverable error codes.
+
+ @see `RLMSyncError`
+ */
+@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, copy) NSString *appID;
+
+/**
+ The logging threshold which newly opened synced Realms will use. Defaults to
+ `RLMSyncLogLevelInfo`.
+
+ Logging strings are output to Apple System Logger.
+
+ @warning This property must be set before any synced Realms are opened. Setting it after
+          opening any synced Realm will do nothing.
+ */
+@property (nonatomic) RLMSyncLogLevel logLevel;
+
+/// The sole instance of the singleton.
++ (instancetype)sharedManager NS_REFINED_FOR_SWIFT;
+
+/// :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/iOS/Pods/Realm/include/RLMSyncManager_Private.h b/iOS/Pods/Realm/include/RLMSyncManager_Private.h
new file mode 100644 (file)
index 0000000..98faf03
--- /dev/null
@@ -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 <Realm/RLMSyncManager.h>
+
+#import "RLMSyncUtil_Private.h"
+
+@class RLMSyncUser, RLMSyncConfiguration;
+
+// 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 ()
+
+@property (nullable, nonatomic, copy) RLMSyncBasicErrorReportingBlock sessionCompletionNotifier;
+
+- (void)_fireError:(NSError *)error;
+
+- (void)_fireErrorWithCode:(int)errorCode
+                   message:(NSString *)message
+                   isFatal:(BOOL)fatal
+                   session:(RLMSyncSession *)session
+                  userInfo:(NSDictionary *)userInfo
+                errorClass:(RLMSyncSystemErrorKind)errorClass;
+
+- (NSArray<RLMSyncUser *> *)_allUsers;
+
++ (void)resetForTesting;
+
+NS_ASSUME_NONNULL_END
+
+@end
diff --git a/iOS/Pods/Realm/include/RLMSyncPermission.h b/iOS/Pods/Realm/include/RLMSyncPermission.h
new file mode 100644 (file)
index 0000000..b40f3fc
--- /dev/null
@@ -0,0 +1,168 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 <Foundation/Foundation.h>
+
+/**
+ Access levels which can be granted to Realm Mobile Platform users
+ for specific synchronized Realms, using the permissions APIs.
+
+ Note that each access level guarantees all allowed actions provided
+ by less permissive access levels. Specifically, users with write
+ access to a Realm can always read from that Realm, and users with
+ administrative access can always read or write from the Realm.
+ */
+typedef NS_ENUM(NSUInteger, RLMSyncAccessLevel) {
+    /// No access whatsoever.
+    RLMSyncAccessLevelNone          = 0,
+    /**
+     User can only read the contents of the Realm.
+
+     @warning Users who have read-only access to a Realm should open the
+              Realm using `+[RLMRealm asyncOpenWithConfiguration:callbackQueue:callback:]`.
+              Attempting to directly open the Realm is an error; in this
+              case the Realm must be deleted and re-opened.
+     */
+    RLMSyncAccessLevelRead          = 1,
+    /// User can read and write the contents of the Realm.
+    RLMSyncAccessLevelWrite         = 2,
+    /// User can read, write, and administer the Realm, including
+    /// granting permissions to other users.
+    RLMSyncAccessLevelAdmin         = 3,
+};
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A property on which a `RLMResults<RLMSyncPermission *>` can be queried or filtered.
+
+ @warning If building `NSPredicate`s using format strings including these string
+          constants, use %K instead of %@ as the substitution parameter.
+ */
+typedef NSString * RLMSyncPermissionSortProperty NS_STRING_ENUM;
+
+/// Sort by the Realm Object Server path to the Realm to which the permission applies.
+extern RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyPath;
+/// Sort by the identity of the user to whom the permission applies.
+extern RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyUserID;
+/// Sort by the date the permissions were last updated.
+extern RLMSyncPermissionSortProperty const RLMSyncPermissionSortPropertyUpdated;
+
+/**
+ A value representing a permission granted to the specified user(s) to access the specified Realm(s).
+
+ `RLMSyncPermission` is immutable and can be accessed from any thread.
+
+ See https://realm.io/docs/realm-object-server/#permissions for general documentation.
+ */
+@interface RLMSyncPermission : NSObject
+
+/**
+ The Realm Object Server path to the Realm to which this permission applies (e.g. "/path/to/realm").
+
+ Specify "*" if this permission applies to all Realms managed by the server.
+ */
+@property (nonatomic, readonly) NSString *path;
+
+/**
+ The access level described by this permission.
+ */
+@property (nonatomic, readonly) RLMSyncAccessLevel accessLevel;
+
+/// Whether the access level allows the user to read from the Realm.
+@property (nonatomic, readonly) BOOL mayRead;
+
+/// Whether the access level allows the user to write to the Realm.
+@property (nonatomic, readonly) BOOL mayWrite;
+
+/// Whether the access level allows the user to administer the Realm.
+@property (nonatomic, readonly) BOOL mayManage;
+
+/**
+ Create a new sync permission value, for use with permission APIs.
+
+ @param path        The Realm Object Server path to the Realm whose permission should be modified
+                    (e.g. "/path/to/realm"). Pass "*" to apply to all Realms managed by the user.
+ @param identity    The Realm Object Server identity of the user who should be granted access to
+                    the Realm at `path`.
+                    Pass "*" to apply to all users managed by the server.
+ @param accessLevel The access level to grant.
+ */
+- (instancetype)initWithRealmPath:(NSString *)path
+                         identity:(NSString *)identity
+                      accessLevel:(RLMSyncAccessLevel)accessLevel;
+
+/**
+ Create a new sync permission value, for use with permission APIs.
+
+ @param path        The Realm Object Server path to the Realm whose permission should be modified
+                    (e.g. "/path/to/realm"). Pass "*" to apply to all Realms managed by the user.
+ @param username    The username (often an email address) of the user who should be granted access
+                    to the Realm at `path`.
+ @param accessLevel The access level to grant.
+ */
+- (instancetype)initWithRealmPath:(NSString *)path
+                         username:(NSString *)username
+                      accessLevel:(RLMSyncAccessLevel)accessLevel;
+
+/**
+ The identity of the user to whom this permission is granted, or "*"
+ if all users are granted this permission. Nil if the permission is
+ defined in terms of a key-value pair.
+ */
+@property (nullable, nonatomic, readonly) NSString *identity;
+
+/**
+ If the permission is defined in terms of a key-value pair, the key
+ describing the type of criterion used to determine what users the
+ permission applies to. Otherwise, nil.
+ */
+@property (nullable, nonatomic, readonly) NSString *key;
+
+/**
+ If the permission is defined in terms of a key-value pair, a string
+ describing the criterion value used to determine what users the
+ permission applies to. Otherwise, nil.
+ */
+@property (nullable, nonatomic, readonly) NSString *value;
+
+/**
+ When this permission was last updated.
+ */
+@property (nonatomic, readonly) NSDate *updatedAt;
+
+/// :nodoc:
+- (instancetype)init __attribute__((unavailable("Use the designated initializer")));
+
+/// :nodoc:
++ (instancetype)new __attribute__((unavailable("Use the designated initializer")));
+
+// MARK: - Migration assistance
+
+/// :nodoc:
+@property (nullable, nonatomic, readonly) NSString *userId __attribute__((unavailable("Renamed to `identity`")));
+
+/// :nodoc:
+- (instancetype)initWithRealmPath:(NSString *)path
+                           userID:(NSString *)identity
+                      accessLevel:(RLMSyncAccessLevel)accessLevel
+__attribute__((unavailable("Renamed to `-initWithRealmPath:identity:accessLevel:`")));
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMSyncPermissionResults.h b/iOS/Pods/Realm/include/RLMSyncPermissionResults.h
new file mode 100644 (file)
index 0000000..461da2b
--- /dev/null
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 <Foundation/Foundation.h>
+
+#import "RLMResults.h"
+
+@class RLMSyncPermission;
+
+// A private subclass of `RLMResults`.
+@interface RLMSyncPermissionResults : RLMResults<RLMSyncPermission *>
+@end
diff --git a/iOS/Pods/Realm/include/RLMSyncPermission_Private.hpp b/iOS/Pods/Realm/include/RLMSyncPermission_Private.hpp
new file mode 100644 (file)
index 0000000..9d01adc
--- /dev/null
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 "RLMSyncPermission.h"
+
+#import "sync/sync_permission.hpp"
+
+@interface RLMSyncPermission ()
+
+- (instancetype)initWithPermission:(realm::Permission)permission;
+
+- (realm::Permission)rawPermission;
+
+@end
diff --git a/iOS/Pods/Realm/include/RLMSyncSession.h b/iOS/Pods/Realm/include/RLMSyncSession.h
new file mode 100644 (file)
index 0000000..677e3a9
--- /dev/null
@@ -0,0 +1,186 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+#import "RLMRealm.h"
+
+/**
+ The current state of the session represented by a session object.
+ */
+typedef NS_ENUM(NSUInteger, RLMSyncSessionState) {
+    /// The sync session is bound to the Realm Object Server and communicating with it.
+    RLMSyncSessionStateActive,
+    /// The sync session is not currently communicating with the Realm Object Server.
+    RLMSyncSessionStateInactive,
+    /// The sync session encountered a fatal error and is permanently invalid; it should be discarded.
+    RLMSyncSessionStateInvalid
+};
+
+/**
+ The transfer direction (upload or download) tracked by a given progress notification block.
+
+ Progress notification blocks can be registered on sessions if your app wishes to be informed
+ how many bytes have been uploaded or downloaded, for example to show progress indicator UIs.
+ */
+typedef NS_ENUM(NSUInteger, RLMSyncProgressDirection) {
+    /// For monitoring upload progress.
+    RLMSyncProgressDirectionUpload,
+    /// For monitoring download progress.
+    RLMSyncProgressDirectionDownload,
+};
+
+/**
+ The desired behavior of a progress notification block.
+
+ Progress notification blocks can be registered on sessions if your app wishes to be informed
+ how many bytes have been uploaded or downloaded, for example to show progress indicator UIs.
+ */
+typedef NS_ENUM(NSUInteger, RLMSyncProgressMode) {
+    /**
+     The block will be called indefinitely, or until it is unregistered by calling
+     `-[RLMProgressNotificationToken invalidate]`.
+
+     Notifications will always report the latest number of transferred bytes, and the
+     most up-to-date number of total transferrable bytes.
+     */
+    RLMSyncProgressModeReportIndefinitely,
+    /**
+     The block will, upon registration, store the total number of bytes
+     to be transferred. When invoked, it will always report the most up-to-date number
+     of transferrable bytes out of that original number of transferrable bytes.
+
+     When the number of transferred bytes reaches or exceeds the
+     number of transferrable bytes, the block will be unregistered.
+     */
+    RLMSyncProgressModeForCurrentlyOutstandingWork,
+};
+
+@class RLMSyncUser, RLMSyncConfiguration, RLMSyncErrorActionToken;
+
+/**
+ The type of a progress notification block intended for reporting a session's network
+ activity to the user.
+
+ `transferredBytes` refers to the number of bytes that have been uploaded or downloaded.
+ `transferrableBytes` refers to the total number of bytes transferred, and pending transfer.
+ */
+typedef void(^RLMProgressNotificationBlock)(NSUInteger transferredBytes, NSUInteger transferrableBytes);
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A token object corresponding to a progress notification block on a session object.
+
+ To stop notifications manually, call `-invalidate` on it. Notifications should be stopped before
+ the token goes out of scope or is destroyed.
+ */
+@interface RLMProgressNotificationToken : RLMNotificationToken
+@end
+
+/**
+ 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. Session
+ objects can be accessed from any thread.
+ */
+@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 (nullable, nonatomic, readonly) NSURL *realmURL;
+
+/// The user that owns this session.
+- (nullable 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;
+
+/**
+ Register a progress notification block.
+
+ Multiple blocks can be registered with the same session at once. Each block
+ will be invoked on a side queue devoted to progress notifications.
+
+ If the session has already received progress information from the
+ synchronization subsystem, the block will be called immediately. Otherwise, it
+ will be called as soon as progress information becomes available.
+
+ The token returned by this method must be retained as long as progress
+ notifications are desired, and the `-invalidate` method should be called on it
+ when notifications are no longer needed and before the token is destroyed.
+
+ If no token is returned, the notification block will never be called again.
+ There are a number of reasons this might be true. If the session has previously
+ experienced a fatal error it will not accept progress notification blocks. If
+ the block was configured in the `RLMSyncProgressForCurrentlyOutstandingWork`
+ mode but there is no additional progress to report (for example, the number
+ of transferrable bytes and transferred bytes are equal), the block will not be
+ called again.
+
+ @param direction The transfer direction (upload or download) to track in this progress notification block.
+ @param mode      The desired behavior of this progress notification block.
+ @param block     The block to invoke when notifications are available.
+
+ @return A token which must be held for as long as you want notifications to be delivered.
+
+ @see `RLMSyncProgressDirection`, `RLMSyncProgress`, `RLMProgressNotificationBlock`, `RLMProgressNotificationToken`
+ */
+- (nullable RLMProgressNotificationToken *)addProgressNotificationForDirection:(RLMSyncProgressDirection)direction
+                                                                          mode:(RLMSyncProgressMode)mode
+                                                                         block:(RLMProgressNotificationBlock)block
+NS_REFINED_FOR_SWIFT;
+
+/**
+ Given an error action token, immediately handle the corresponding action.
+ @see `RLMSyncErrorClientResetError`, `RLMSyncErrorPermissionDeniedError`
+ */
++ (void)immediatelyHandleError:(RLMSyncErrorActionToken *)token;
+
+@end
+
+// MARK: - Error action token
+
+#pragma mark - Error action token
+
+/**
+ An opaque token returned as part of certain errors. It can be
+ passed into certain APIs to perform certain actions.
+
+ @see `RLMSyncErrorClientResetError`, `RLMSyncErrorPermissionDeniedError`
+ */
+@interface RLMSyncErrorActionToken : NSObject
+
+/// :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/iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.h b/iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.h
new file mode 100644 (file)
index 0000000..78a278b
--- /dev/null
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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 <Foundation/Foundation.h>
+
+@class RLMSyncUser;
+
+/// An object that handles refreshing a session's token periodically, as long
+/// as the session remains live and valid.
+@interface RLMSyncSessionRefreshHandle : NSObject
+
+- (void)scheduleRefreshTimer:(NSDate *)dateWhenTokenExpires;
+- (void)invalidate;
+
+@end
diff --git a/iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.hpp b/iOS/Pods/Realm/include/RLMSyncSessionRefreshHandle.hpp
new file mode 100644 (file)
index 0000000..26b5bea
--- /dev/null
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMSyncSessionRefreshHandle.h"
+
+#import "RLMSyncUtil_Private.h"
+
+#import <memory>
+
+namespace realm {
+class SyncSession;
+class SyncUser;
+}
+
+@class RLMSyncUser;
+
+@interface RLMSyncSessionRefreshHandle ()
+
+NS_ASSUME_NONNULL_BEGIN
+
+- (instancetype)initWithRealmURL:(NSURL *)realmURL
+                            user:(std::shared_ptr<realm::SyncUser>)user
+                         session:(std::shared_ptr<realm::SyncSession>)session
+                 completionBlock:(nullable RLMSyncBasicErrorReportingBlock)completionBlock;
+
+NS_ASSUME_NONNULL_END
+
+@end
diff --git a/iOS/Pods/Realm/include/RLMSyncSession_Private.hpp b/iOS/Pods/Realm/include/RLMSyncSession_Private.hpp
new file mode 100644 (file)
index 0000000..1f691cb
--- /dev/null
@@ -0,0 +1,51 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMSyncUtil_Private.h"
+#import <memory>
+
+namespace realm {
+class SyncSession;
+}
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface RLMSyncSession () {
+@public     // So it's visible to tests
+    std::weak_ptr<realm::SyncSession> _session;
+} RLM_SYNC_UNINITIALIZABLE
+
+- (instancetype)initWithSyncSession:(std::shared_ptr<realm::SyncSession>)session;
+
+/// 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(^)(NSError * _Nullable))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(^)(NSError * _Nullable))callback;
+
+@end
+
+@interface RLMSyncErrorActionToken ()
+
+- (instancetype)initWithOriginalPath:(std::string)originalPath;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMSyncUser.h b/iOS/Pods/Realm/include/RLMSyncUser.h
new file mode 100644 (file)
index 0000000..c43ac84
--- /dev/null
@@ -0,0 +1,377 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+#import "RLMResults.h"
+#import "RLMSyncCredentials.h"
+#import "RLMSyncPermission.h"
+
+@class RLMSyncUser, RLMSyncUserInfo, RLMSyncCredentials, RLMSyncPermission, RLMSyncSession, RLMRealm;
+
+/**
+ The state of the user object.
+ */
+typedef NS_ENUM(NSUInteger, RLMSyncUserState) {
+    /// The user is logged out. Call `logInWithCredentials:...` with valid credentials 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 an `RLMSyncUser`.
+typedef void(^RLMUserCompletionBlock)(RLMSyncUser * _Nullable, NSError * _Nullable);
+
+/// A block type used to report the status of a password change operation.
+/// If the `NSError` argument is nil, the operation succeeded.
+typedef void(^RLMPasswordChangeStatusBlock)(NSError * _Nullable);
+
+/// A block type used to report the status of a permission apply or revoke operation.
+/// If the `NSError` argument is nil, the operation succeeded.
+typedef void(^RLMPermissionStatusBlock)(NSError * _Nullable);
+
+/// A block type used to report the status of a permission offer operation.
+typedef void(^RLMPermissionOfferStatusBlock)(NSString * _Nullable, NSError * _Nullable);
+
+/// A block type used to report the status of a permission offer response operation.
+typedef void(^RLMPermissionOfferResponseStatusBlock)(NSURL * _Nullable, NSError * _Nullable);
+
+/// A block type used to asynchronously report results of a permissions get operation.
+/// Exactly one of the two arguments will be populated.
+typedef void(^RLMPermissionResultsBlock)(RLMResults<RLMSyncPermission *> * _Nullable, NSError * _Nullable);
+
+/// A block type used to asynchronously report results of a user info retrieval.
+/// Exactly one of the two arguments will be populated.
+typedef void(^RLMRetrieveUserBlock)(RLMSyncUserInfo * _Nullable, NSError * _Nullable);
+
+/// A block type used to report an error related to a specific user.
+typedef void(^RLMUserErrorReportingBlock)(RLMSyncUser * _Nonnull, NSError * _Nonnull);
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A `RLMSyncUser` instance represents a single Realm Object Server user account.
+
+ A user may have one or more credentials associated with it. These credentials
+ uniquely identify the user to the authentication provider, and are used to sign
+ into a Realm Object Server user account.
+
+ Note that user objects are only vended out via SDK APIs, and cannot be directly
+ initialized. User objects can be accessed from any thread.
+ */
+@interface RLMSyncUser : NSObject
+
+/**
+ A dictionary of all valid, logged-in user identities corresponding to their user objects.
+ */
++ (NSDictionary<NSString *, RLMSyncUser *> *)allUsers NS_REFINED_FOR_SWIFT;
+
+/**
+ The logged-in user. `nil` if none exists.
+
+ @warning Throws an exception if more than one logged-in user exists.
+ */
++ (nullable RLMSyncUser *)currentUser NS_REFINED_FOR_SWIFT;
+
+/**
+ The unique Realm Object Server user ID string identifying this user.
+ */
+@property (nullable, nonatomic, readonly) NSString *identity;
+
+/**
+ The URL of the authentication server this user will communicate with.
+ */
+@property (nullable, nonatomic, readonly) NSURL *authenticationServer;
+
+/**
+ Whether the user is a Realm Object Server administrator. Value reflects the
+ state at the time of the last successful login of this user.
+ */
+@property (nonatomic, readonly) BOOL isAdmin;
+
+/**
+ The current state of the user.
+ */
+@property (nonatomic, readonly) RLMSyncUserState state;
+
+#pragma mark - Lifecycle
+
+/**
+ Create, log in, and asynchronously return a new user object, specifying a custom
+ timeout for the network request and a custom queue to run the callback upon.
+ Credentials identifying the user must be passed in. The user becomes available in
+ the completion block, at which point it is ready for use.
+ */
++ (void)logInWithCredentials:(RLMSyncCredentials *)credentials
+               authServerURL:(NSURL *)authServerURL
+                     timeout:(NSTimeInterval)timeout
+               callbackQueue:(dispatch_queue_t)callbackQueue
+                onCompletion:(RLMUserCompletionBlock)completion NS_REFINED_FOR_SWIFT;
+
+/**
+ Create, log in, and asynchronously return a new user object.
+
+ If the login completes successfully, the completion block will invoked with
+ a `RLMSyncUser` object representing the logged-in user. This object can be
+ used to open synchronized Realms. If the login fails, the completion block
+ will be invoked with an error.
+
+ The completion block always runs on the main queue.
+
+ @param credentials     A credentials value identifying the user to be logged in.
+ @param authServerURL   The URL of the authentication server (e.g. "http://realm.example.org:9080").
+ @param completion      A callback block that returns a user object or an error,
+                        indicating the completion of the login operation.
+ */
++ (void)logInWithCredentials:(RLMSyncCredentials *)credentials
+               authServerURL:(NSURL *)authServerURL
+                onCompletion:(RLMUserCompletionBlock)completion
+NS_SWIFT_UNAVAILABLE("Use the full version of this API.");
+
+/**
+ Log a user out, destroying their server state, unregistering them from the SDK,
+ and removing any synced Realms associated with them from on-disk storage on
+ next app launch. If the user is already logged out or in an error state, this
+ method does nothing.
+
+ 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;
+
+/**
+ An optional error handler which can be set to notify the host application when
+ the user encounters an error. Errors reported by this error handler are always
+ `RLMSyncAuthError`s.
+
+ @note Check for `RLMSyncAuthErrorInvalidAccessToken` to see if the user has
+       been remotely logged out because its refresh token expired, or because the
+       third party authentication service providing the user's identity has
+       logged the user out.
+
+ @warning Regardless of whether an error handler is installed, certain user errors
+          will automatically cause the user to enter the logged out state.
+ */
+@property (nullable, nonatomic) RLMUserErrorReportingBlock errorHandler NS_REFINED_FOR_SWIFT;
+
+#pragma mark - Sessions
+
+/**
+ 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<RLMSyncSession *> *)allSessions;
+
+#pragma mark - Passwords
+
+/**
+ Change this user's password asynchronously.
+
+ @warning Changing a user's password using an authentication server that doesn't
+          use HTTPS is a major security flaw, and should only be done while
+          testing.
+
+ @param newPassword The user's new password.
+ @param completion  Completion block invoked when login has completed or failed.
+                    The callback will be invoked on a background queue provided
+                    by `NSURLSession`.
+ */
+- (void)changePassword:(NSString *)newPassword completion:(RLMPasswordChangeStatusBlock)completion;
+
+/**
+ Change an arbitrary user's password asynchronously.
+
+ @note    The current user must be an admin user for this operation to succeed.
+
+ @warning Changing a user's password using an authentication server that doesn't
+          use HTTPS is a major security flaw, and should only be done while
+          testing.
+
+ @param newPassword The user's new password.
+ @param userID      The identity of the user whose password should be changed.
+ @param completion  Completion block invoked when login has completed or failed.
+                    The callback will be invoked on a background queue provided
+                    by `NSURLSession`.
+ */
+- (void)changePassword:(NSString *)newPassword forUserID:(NSString *)userID completion:(RLMPasswordChangeStatusBlock)completion;
+
+#pragma mark - Administrator
+
+/**
+ Given a Realm Object Server authentication provider and a provider identifier for a user
+ (for example, a username), look up and return user information for that user.
+
+ @param providerUserIdentity    The username or identity of the user as issued by the authentication provider.
+                                In most cases this is different from the Realm Object Server-issued identity.
+ @param provider                The authentication provider that manages the user whose information is desired.
+ @param completion              Completion block invoked when request has completed or failed.
+                                The callback will be invoked on a background queue provided
+                                by `NSURLSession`.
+ */
+- (void)retrieveInfoForUser:(NSString *)providerUserIdentity
+           identityProvider:(RLMIdentityProvider)provider
+                 completion:(RLMRetrieveUserBlock)completion;
+
+#pragma mark - Permissions
+
+/**
+ Asynchronously retrieve all permissions associated with the user calling this method.
+
+ The results will be returned through the callback block, or an error if the operation failed.
+ The callback block will be run on the same thread the method was called on.
+
+ @warning This method must be called from a thread with a currently active run loop. Unless
+          you have manually configured a run loop on a side thread, this will usually be the
+          main thread.
+ */
+- (void)retrievePermissionsWithCallback:(RLMPermissionResultsBlock)callback NS_REFINED_FOR_SWIFT;
+
+/**
+ Apply a given permission.
+
+ The operation will take place asynchronously, and the callback will be used to report whether
+ the permission change succeeded or failed. The user calling this method must have the right
+ to grant the given permission, or else the operation will fail.
+
+ @see `RLMSyncPermission`
+ */
+- (void)applyPermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback;
+
+/**
+ Revoke a given permission.
+
+ The operation will take place asynchronously, and the callback will be used to report whether
+ the permission change succeeded or failed. The user calling this method must have the right
+ to grant the given permission, or else the operation will fail.
+
+ @see `RLMSyncPermission`
+ */
+- (void)revokePermission:(RLMSyncPermission *)permission callback:(RLMPermissionStatusBlock)callback;
+
+/**
+ Create a permission offer for a Realm.
+
+ A permission offer is used to grant access to a Realm this user manages to another
+ user. Creating a permission offer produces a string token which can be passed to the
+ recepient in any suitable way (for example, via e-mail).
+
+ The operation will take place asynchronously. The token can be accepted by the recepient
+ using the `-[RLMSyncUser acceptOfferForToken:callback:]` method.
+
+ @param url             The URL of the Realm for which the permission offer should pertain. This
+                        may be the URL of any Realm which this user is allowed to manage. If the URL
+                        has a `~` wildcard it will be replaced with this user's user identity.
+ @param accessLevel     What access level to grant to whoever accepts the token.
+ @param expirationDate  Optionally, a date which indicates when the offer expires. If the
+                        recepient attempts to accept the offer after the date it will be rejected.
+ @param callback        A callback indicating whether the operation succeeded or failed. If it
+                        succeeded the token will be passed in as a string.
+
+ @see `acceptOfferForToken:callback:`
+ */
+- (void)createOfferForRealmAtURL:(NSURL *)url
+                     accessLevel:(RLMSyncAccessLevel)accessLevel
+                      expiration:(nullable NSDate *)expirationDate
+                        callback:(RLMPermissionOfferStatusBlock)callback NS_REFINED_FOR_SWIFT;
+
+/**
+ Accept a permission offer.
+
+ Pass in a token representing a permission offer. The operation will take place asynchronously.
+ If the operation succeeds, the callback will be passed the URL of the Realm for which the
+ offer applied, so the Realm can be opened.
+
+ The token this method accepts can be created by the offering user through the
+ `-[RLMSyncUser createOfferForRealmAtURL:accessLevel:expiration:callback:]` method.
+
+ @see `createOfferForRealmAtURL:accessLevel:expiration:callback:`
+ */
+- (void)acceptOfferForToken:(NSString *)token
+                   callback:(RLMPermissionOfferResponseStatusBlock)callback;
+
+/// :nodoc:
+- (instancetype)init __attribute__((unavailable("RLMSyncUser cannot be created directly")));
+/// :nodoc:
++ (instancetype)new __attribute__((unavailable("RLMSyncUser cannot be created directly")));
+
+@end
+
+#pragma mark - User info classes
+
+/**
+ A data object representing a user account associated with a user.
+
+ @see `RLMSyncUserInfo`
+ */
+@interface RLMSyncUserAccountInfo : NSObject
+
+/// The authentication provider which manages this user account.
+@property (nonatomic, readonly) RLMIdentityProvider provider;
+
+/// The username or identity of this user account.
+@property (nonatomic, readonly) NSString *providerUserIdentity;
+
+/// :nodoc:
+- (instancetype)init __attribute__((unavailable("RLMSyncUserAccountInfo cannot be created directly")));
+/// :nodoc:
++ (instancetype)new __attribute__((unavailable("RLMSyncUserAccountInfo cannot be created directly")));
+
+@end
+
+/**
+ A data object representing information about a user that was retrieved from a user lookup call.
+ */
+@interface RLMSyncUserInfo : NSObject
+
+/**
+ An array of all the user accounts associated with this user.
+ */
+@property (nonatomic, readonly) NSArray<RLMSyncUserAccountInfo *> *accounts;
+
+/**
+ The identity issued to this user by the Realm Object Server.
+ */
+@property (nonatomic, readonly) NSString *identity;
+
+/**
+ Metadata about this user stored on the Realm Object Server.
+ */
+@property (nonatomic, readonly) NSDictionary<NSString *, NSString *> *metadata;
+
+/**
+ Whether the user is flagged on the Realm Object Server as an administrator.
+ */
+@property (nonatomic, readonly) BOOL isAdmin;
+
+/// :nodoc:
+- (instancetype)init __attribute__((unavailable("RLMSyncUserInfo cannot be created directly")));
+/// :nodoc:
++ (instancetype)new __attribute__((unavailable("RLMSyncUserInfo cannot be created directly")));
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMSyncUser_Private.hpp b/iOS/Pods/Realm/include/RLMSyncUser_Private.hpp
new file mode 100644 (file)
index 0000000..05fbdb2
--- /dev/null
@@ -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 "RLMSyncUser.h"
+
+#import "RLMSyncConfiguration.h"
+#import "RLMSyncUtil_Private.h"
+
+#import "sync/sync_config.hpp"
+#import "sync/sync_user.hpp"
+#import "sync/impl/sync_metadata.hpp"
+
+@class RLMSyncConfiguration, RLMSyncSessionRefreshHandle;
+
+using namespace realm;
+
+typedef void(^RLMFetchedRealmCompletionBlock)(NSError * _Nullable, RLMRealm * _Nullable, BOOL * _Nonnull);
+
+NS_ASSUME_NONNULL_BEGIN
+
+class CocoaSyncUserContext : public SyncUserContext {
+public:
+    void register_refresh_handle(const std::string& path, RLMSyncSessionRefreshHandle *handle);
+    void unregister_refresh_handle(const std::string& path);
+    void invalidate_all_handles();
+
+    RLMUserErrorReportingBlock error_handler() const;
+    void set_error_handler(RLMUserErrorReportingBlock);
+
+private:
+    /**
+     A map of paths to 'refresh handles'.
+
+     A refresh handle is an object that encapsulates the concept of periodically
+     refreshing the Realm's access token before it expires. Tokens are indexed by their
+     paths (e.g. `/~/path/to/realm`).
+     */
+    std::unordered_map<std::string, RLMSyncSessionRefreshHandle *> m_refresh_handles;
+    std::mutex m_mutex;
+
+    /**
+     An optional callback invoked when the authentication server reports the user as
+     being in an expired state.
+     */
+    RLMUserErrorReportingBlock m_error_handler;
+    mutable std::mutex m_error_handler_mutex;
+};
+
+@interface RLMSyncUser ()
+
+- (instancetype)initWithSyncUser:(std::shared_ptr<SyncUser>)user;
+- (std::shared_ptr<SyncUser>)_syncUser;
+- (nullable NSString *)_refreshToken;
++ (void)_setUpBindingContextFactory;
+@end
+
+using PermissionChangeCallback = std::function<void(std::exception_ptr)>;
+
+PermissionChangeCallback RLMWrapPermissionStatusCallback(RLMPermissionStatusBlock callback);
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMSyncUtil.h b/iOS/Pods/Realm/include/RLMSyncUtil.h
new file mode 100644 (file)
index 0000000..ab911c2
--- /dev/null
@@ -0,0 +1,216 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMConstants.h>
+
+/// A token originating from the Realm Object Server.
+typedef NSString* RLMServerToken;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// A user info key for use with `RLMSyncErrorClientResetError`.
+extern NSString *const kRLMSyncPathOfRealmBackupCopyKey;
+
+/// A user info key for use with certain error types.
+extern NSString *const kRLMSyncErrorActionTokenKey;
+
+/**
+ The error domain string for all SDK errors related to errors reported
+ by the synchronization manager error handler, as well as general sync
+ errors that don't fall into any of the other categories.
+ */
+extern NSString *const RLMSyncErrorDomain;
+
+/**
+ The error domain string for all SDK errors related to the authentication
+ endpoint.
+ */
+extern NSString *const RLMSyncAuthErrorDomain;
+
+/**
+ The error domain string for all SDK errors related to the permissions
+ system and APIs.
+ */
+extern NSString *const RLMSyncPermissionErrorDomain;
+
+/**
+ An error related to a problem that might be reported by the synchronization manager
+ error handler, or a callback on a sync-related API that performs asynchronous work.
+ */
+typedef RLM_ERROR_ENUM(NSInteger, RLMSyncError, RLMSyncErrorDomain) {
+
+    /// 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, unrecoverable problem
+     with the underlying synchronization engine.
+     */
+    RLMSyncErrorClientInternalError     = 6,
+
+    /**
+     An error that indicates the Realm needs to be reset.
+
+     A synced Realm may need to be reset because the Realm Object Server encountered an
+     error and had to be restored from a backup. If the backup copy of the remote Realm
+     is of an earlier version than the local copy of the Realm, the server will ask the
+     client to reset the Realm.
+
+     The reset process is as follows: the local copy of the Realm is copied into a recovery
+     directory for safekeeping, and then deleted from the original location. The next time
+     the Realm for that URL is opened, the Realm will automatically be re-downloaded from the
+     Realm Object Server, and can be used as normal.
+
+     Data written to the Realm after the local copy of the Realm diverged from the backup
+     remote copy will be present in the local recovery copy of the Realm file. The
+     re-downloaded Realm will initially contain only the data present at the time the Realm
+     was backed up on the server.
+
+     The client reset process can be initiated in one of two ways.
+     
+     The `userInfo` dictionary contains an opaque token object under the key
+     `kRLMSyncErrorActionTokenKey`. This token can be passed into
+     `+[RLMSyncSession immediatelyHandleError:]` in order to immediately perform the client
+     reset process. This should only be done after your app closes and invalidates every
+     instance of the offending Realm on all threads (note that autorelease pools may make this
+     difficult to guarantee).
+
+     If `+[RLMSyncSession immediatelyHandleError:]` is not called, the client reset process
+     will be automatically carried out the next time the app is launched and the
+     `RLMSyncManager` singleton is accessed.
+
+     The value for the `kRLMSyncPathOfRealmBackupCopyKey` key in the `userInfo` dictionary
+     describes the path of the recovered copy of the Realm. This copy will not actually be
+     created until the client reset process is initiated.
+
+     @see `-[NSError rlmSync_errorActionToken]`, `-[NSError rlmSync_clientResetBackedUpRealmPath]`
+     */
+    RLMSyncErrorClientResetError        = 7,
+
+    /**
+     An error that indicates an authentication error occurred.
+
+     The `kRLMSyncUnderlyingErrorKey` key in the user info dictionary will contain the
+     underlying error, which is guaranteed to be under the `RLMSyncAuthErrorDomain`
+     error domain.
+     */
+    RLMSyncErrorUnderlyingAuthError     = 8,
+
+    /**
+     An error that indicates the user does not have permission to perform an operation
+     upon a synced Realm. For example, a user may receive this error if they attempt to
+     open a Realm they do not have at least read access to, or write to a Realm they only
+     have read access to.
+     
+     This error may also occur if a user incorrectly opens a Realm they have read-only
+     permissions to without using the `asyncOpen()` APIs.
+
+     A Realm that suffers a permission denied error is, by default, flagged so that its
+     local copy will be deleted the next time the application starts.
+     
+     The `userInfo` dictionary contains an opaque token object under the key
+     `kRLMSyncErrorActionTokenKey`. This token can be passed into
+     `+[RLMSyncSession immediatelyHandleError:]` in order to immediately delete the local
+     copy. This should only be done after your app closes and invalidates every instance
+     of the offending Realm on all threads (note that autorelease pools may make this
+     difficult to guarantee).
+
+     @warning It is strongly recommended that, if a Realm has encountered a permission denied
+              error, its files be deleted before attempting to re-open it.
+     
+     @see `-[NSError rlmSync_errorActionToken]`
+     */
+    RLMSyncErrorPermissionDeniedError   = 9,
+};
+
+/// An error which is related to authentication to a Realm Object Server.
+typedef RLM_ERROR_ENUM(NSInteger, RLMSyncAuthError, RLMSyncAuthErrorDomain) {
+    /// An error that indicates that the response received from the authentication server was malformed.
+    RLMSyncAuthErrorBadResponse                     = 1,
+
+    /// An error that indicates that the supplied Realm path was invalid, or could not be resolved by the authentication
+    /// server.
+    RLMSyncAuthErrorBadRemoteRealmPath              = 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.
+    RLMSyncAuthErrorHTTPStatusCodeError             = 3,
+
+    /// An error that indicates a problem with the session (a specific Realm opened for sync).
+    RLMSyncAuthErrorClientSessionError              = 4,
+
+    /// An error that indicates that the provided credentials are invalid.
+    RLMSyncAuthErrorInvalidCredential               = 611,
+
+    /// An error that indicates that the user with provided credentials does not exist.
+    RLMSyncAuthErrorUserDoesNotExist                = 612,
+
+    /// An error that indicates that the user cannot be registered as it exists already.
+    RLMSyncAuthErrorUserAlreadyExists               = 613,
+
+    /// An error that indicates the path is invalid or the user doesn't have access to that Realm.
+    RLMSyncAuthErrorAccessDeniedOrInvalidPath       = 614,
+
+    /// An error that indicates the refresh token was invalid.
+    RLMSyncAuthErrorInvalidAccessToken              = 615,
+
+    /// An error that indicates the permission offer is expired.
+    RLMSyncAuthErrorExpiredPermissionOffer          = 701,
+
+    /// An error that indicates the permission offer is ambiguous.
+    RLMSyncAuthErrorAmbiguousPermissionOffer        = 702,
+
+    /// An error that indicates the file at the given path can't be shared.
+    RLMSyncAuthErrorFileCannotBeShared              = 703,
+};
+
+/**
+ An error related to the permissions subsystem.
+ */
+typedef RLM_ERROR_ENUM(NSInteger, RLMSyncPermissionError, RLMSyncPermissionErrorDomain) {
+    /**
+     An error that indicates a permission change operation failed. The `userInfo`
+     dictionary contains the underlying error code and a message (if any).
+     */
+    RLMSyncPermissionErrorChangeFailed          = 1,
+
+    /**
+     An error that indicates that attempting to retrieve permissions failed.
+     */
+    RLMSyncPermissionErrorGetFailed             = 2,
+
+    /**
+     An error that indicates that trying to create a permission offer failed.
+     */
+    RLMSyncPermissionErrorOfferFailed           = 3,
+
+    /**
+     An error that indicates that trying to accept a permission offer failed.
+     */
+    RLMSyncPermissionErrorAcceptOfferFailed     = 4,
+
+    /**
+     An error that indicates that an internal error occurred.
+     */
+    RLMSyncPermissionErrorInternal              = 5,
+};
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMSyncUtil_Private.h b/iOS/Pods/Realm/include/RLMSyncUtil_Private.h
new file mode 100644 (file)
index 0000000..c1b9314
--- /dev/null
@@ -0,0 +1,133 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMSyncUtil.h>
+
+#import <Realm/RLMProperty.h>
+#import <Realm/RLMRealmConfiguration.h>
+#import <Realm/RLMSyncCredentials.h>
+
+typedef NS_ENUM(NSUInteger, RLMSyncSystemErrorKind) {
+    // Specific
+    RLMSyncSystemErrorKindClientReset,
+    RLMSyncSystemErrorKindPermissionDenied,
+    // General
+    RLMSyncSystemErrorKindClient,
+    RLMSyncSystemErrorKindConnection,
+    RLMSyncSystemErrorKindSession,
+    RLMSyncSystemErrorKindUser,
+    RLMSyncSystemErrorKindUnknown,
+};
+
+@class RLMSyncUser;
+
+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 kRLMSyncErrorStatusCodeKey;
+extern NSString *const kRLMSyncIdentityKey;
+extern NSString *const kRLMSyncIsAdminKey;
+extern NSString *const kRLMSyncNewPasswordKey;
+extern NSString *const kRLMSyncPasswordKey;
+extern NSString *const kRLMSyncPathKey;
+extern NSString *const kRLMSyncTokenKey;
+extern NSString *const kRLMSyncProviderKey;
+extern NSString *const kRLMSyncProviderIDKey;
+extern NSString *const kRLMSyncRegisterKey;
+extern NSString *const kRLMSyncUnderlyingErrorKey;
+extern NSString *const kRLMSyncUserIDKey;
+
+#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; \
+} \
+
+#define RLM_SYNC_PARSE_OPTIONAL_BOOL(json_macro_val, key_macro_val, prop_macro_val) \
+{ \
+id data = json_macro_val[key_macro_val]; \
+if (![data isKindOfClass:[NSNumber class]]) { data = @NO; } \
+self.prop_macro_val = [data boolValue]; \
+} \
+
+/// 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; \
+} \
+
+/// A macro to build an array of sub-models out of a JSON dictionary, or return nil.
+#define RLM_SYNC_PARSE_MODEL_ARRAY_OR_ABORT(json_macro_val, key_macro_val, class_macro_val, prop_macro_val) \
+{ \
+NSArray *jsonArray = json_macro_val[key_macro_val]; \
+if (![jsonArray isKindOfClass:[NSArray class]]) { return nil; } \
+NSMutableArray *buffer = [NSMutableArray array]; \
+for (id value in jsonArray) { \
+id next = nil; \
+if ([value isKindOfClass:[NSDictionary class]]) { next = [[class_macro_val alloc] initWithDictionary:value]; } \
+if (!next) { return nil; } \
+[buffer addObject:next]; \
+} \
+self.prop_macro_val = [buffer copy]; \
+} \
+
+#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/iOS/Pods/Realm/include/RLMSyncUtil_Private.hpp b/iOS/Pods/Realm/include/RLMSyncUtil_Private.hpp
new file mode 100644 (file)
index 0000000..3ea2130
--- /dev/null
@@ -0,0 +1,74 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 "RLMSyncPermission.h"
+
+#import "sync/sync_manager.hpp"
+#import "realm/util/optional.hpp"
+
+@class RLMSyncErrorResponseModel;
+class CocoaSyncUserContext;
+
+namespace realm {
+enum class AccessLevel;
+}
+
+realm::SyncSessionStopPolicy translateStopPolicy(RLMSyncStopPolicy stopPolicy);
+RLMSyncStopPolicy translateStopPolicy(realm::SyncSessionStopPolicy stop_policy);
+
+std::shared_ptr<realm::SyncSession> sync_session_for_realm(RLMRealm *realm);
+
+#pragma mark - Get user context
+
+CocoaSyncUserContext& context_for(const std::shared_ptr<realm::SyncUser>& user);
+
+#pragma mark - Access level conversion
+
+realm::AccessLevel accessLevelForObjCAccessLevel(RLMSyncAccessLevel level);
+RLMSyncAccessLevel objCAccessLevelForAccessLevel(realm::AccessLevel level);
+
+#pragma mark - Error conversion
+
+typedef enum : NSUInteger {
+    RLMPermissionActionTypeGet,
+    RLMPermissionActionTypeChange,
+    RLMPermissionActionTypeOffer,
+    RLMPermissionActionTypeAcceptOffer,
+} RLMPermissionActionType;
+
+NSError *translateSyncExceptionPtrToError(std::exception_ptr ptr, RLMPermissionActionType type);
+
+#pragma mark - Error construction
+
+NSError *make_auth_error_bad_response(NSDictionary *json=nil);
+NSError *make_auth_error_http_status(NSInteger status);
+NSError *make_auth_error_client_issue();
+NSError *make_auth_error(RLMSyncErrorResponseModel *responseModel);
+
+NSError *make_permission_error_get(NSString *description, realm::util::Optional<NSInteger> code=none);
+NSError *make_permission_error_change(NSString *description, realm::util::Optional<NSInteger> code=none);
+NSError *make_permission_error_offer(NSString *description, realm::util::Optional<NSInteger> code=none);
+NSError *make_permission_error_accept_offer(NSString *description, realm::util::Optional<NSInteger> code=none);
+
+// Set 'code' to NSNotFound to not actually have an error code.
+NSError *make_sync_error(RLMSyncSystemErrorKind kind, NSString *description, NSInteger code, NSDictionary *custom);
+NSError *make_sync_error(NSError *wrapped_auth_error);
+NSError *make_sync_error(std::error_code, RLMSyncSystemErrorKind kind=RLMSyncSystemErrorKindSession);
diff --git a/iOS/Pods/Realm/include/RLMThreadSafeReference.h b/iOS/Pods/Realm/include/RLMThreadSafeReference.h
new file mode 100644 (file)
index 0000000..a935ebe
--- /dev/null
@@ -0,0 +1,106 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Foundation/Foundation.h>
+
+@class RLMRealm;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Objects of types which conform to `RLMThreadConfined` can be managed by a Realm, which will make
+ them bound to a thread-specific `RLMRealm` instance. Managed objects must be explicitly exported
+ and imported to be passed between threads.
+
+ Managed instances of objects conforming to this protocol can be converted to a thread-safe
+ reference for transport between threads by passing to the
+ `+[RLMThreadSafeReference referenceWithThreadConfined:]` constructor.
+
+ Note that only types defined by Realm can meaningfully conform to this protocol, and defining new
+ classes which attempt to conform to it will not make them work with `RLMThreadSafeReference`.
+ */
+@protocol RLMThreadConfined <NSObject>
+// Conformance to the `RLMThreadConfined_Private` protocol will be enforced at runtime.
+
+/**
+ The Realm which manages the object, or `nil` if the object is unmanaged.
+
+ Unmanaged objects are not confined to a thread and cannot be passed to methods expecting a
+ `RLMThreadConfined` object.
+ */
+@property (nonatomic, readonly, nullable) RLMRealm *realm;
+
+/// Indicates if the object can no longer be accessed because it is now invalid.
+@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
+
+@end
+
+/**
+ An object intended to be passed between threads containing a thread-safe reference to its
+ thread-confined object.
+
+ To resolve a thread-safe reference on a target Realm on a different thread, pass to
+ `-[RLMRealm resolveThreadSafeReference:]`.
+
+ @warning A `RLMThreadSafeReference` object must be resolved at most once.
+          Failing to resolve a `RLMThreadSafeReference` will result in the source version of the
+          Realm being pinned until the reference is deallocated.
+
+ @note Prefer short-lived `RLMThreadSafeReference`s as the data for the version of the source Realm
+       will be retained until all references have been resolved or deallocated.
+
+ @see `RLMThreadConfined`
+ @see `-[RLMRealm resolveThreadSafeReference:]`
+ */
+@interface RLMThreadSafeReference<__covariant Confined : id<RLMThreadConfined>> : NSObject
+
+/**
+ Create a thread-safe reference to the thread-confined object.
+
+ @param threadConfined The thread-confined object to create a thread-safe reference to.
+
+ @note You may continue to use and access the thread-confined object after passing it to this
+       constructor.
+ */
++ (instancetype)referenceWithThreadConfined:(Confined)threadConfined;
+
+/**
+ Indicates if the reference can no longer be resolved because an attempt to resolve it has already
+ occurred. References can only be resolved once.
+ */
+@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;
+
+#pragma mark - Unavailable Methods
+
+/**
+ `-[RLMThreadSafeReference init]` is not available because `RLMThreadSafeReference` cannot be
+ created directly. `RLMThreadSafeReference` instances must be obtained by calling
+ `-[RLMRealm resolveThreadSafeReference:]`.
+ */
+- (instancetype)init __attribute__((unavailable("RLMThreadSafeReference cannot be created directly")));
+
+/**
+ `-[RLMThreadSafeReference new]` is not available because `RLMThreadSafeReference` cannot be
+ created directly. `RLMThreadSafeReference` instances must be obtained by calling
+ `-[RLMRealm resolveThreadSafeReference:]`.
+ */
++ (instancetype)new __attribute__((unavailable("RLMThreadSafeReference cannot be created directly")));
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMThreadSafeReference_Private.hpp b/iOS/Pods/Realm/include/RLMThreadSafeReference_Private.hpp
new file mode 100644 (file)
index 0000000..5cf78ed
--- /dev/null
@@ -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.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMThreadSafeReference.h"
+#import "thread_safe_reference.hpp"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol RLMThreadConfined_Private <NSObject>
+
+// Constructs a new `ThreadSafeReference`
+- (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference;
+
+// The extra information needed to construct an instance of this type from the Object Store type
+@property (nonatomic, readonly, nullable) id objectiveCMetadata;
+
+// Constructs an new instance of this type
++ (nullable instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
+                                              metadata:(nullable id)metadata
+                                                 realm:(RLMRealm *)realm;
+@end
+
+@interface RLMThreadSafeReference ()
+
+- (nullable id<RLMThreadConfined>)resolveReferenceInRealm:(RLMRealm *)realm;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/iOS/Pods/Realm/include/RLMUpdateChecker.hpp b/iOS/Pods/Realm/include/RLMUpdateChecker.hpp
new file mode 100644 (file)
index 0000000..7f01ac7
--- /dev/null
@@ -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/iOS/Pods/Realm/include/RLMUtil.hpp b/iOS/Pods/Realm/include/RLMUtil.hpp
new file mode 100644 (file)
index 0000000..3a11bbc
--- /dev/null
@@ -0,0 +1,173 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <Realm/RLMConstants.h>
+#import <Realm/RLMOptionalBase.h>
+#import <objc/runtime.h>
+
+#import <realm/array.hpp>
+#import <realm/binary_data.hpp>
+#import <realm/string_data.hpp>
+#import <realm/timestamp.hpp>
+#import <realm/util/file.hpp>
+
+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);
+
+void RLMSetErrorOrThrow(NSError *error, NSError **outError);
+
+// returns if the object can be inserted as the given type
+BOOL RLMIsObjectValidForProperty(id obj, RLMProperty *prop);
+// throw an exception if the object is not a valid value for the property
+void RLMValidateValueForProperty(id obj, RLMObjectSchema *objectSchema,
+                                 RLMProperty *prop, bool validateObjects=false);
+BOOL RLMValidateValue(id value, RLMPropertyType type, bool optional, bool array,
+                      NSString *objectClassName);
+
+void RLMThrowTypeError(id obj, RLMObjectSchema *objectSchema, 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;
+}
+
+template<typename T>
+static inline T *RLMDynamicCast(__unsafe_unretained id obj) {
+    if ([obj isKindOfClass:[T class]]) {
+        return obj;
+    }
+    return nil;
+}
+
+template<typename T>
+static inline T RLMCoerceToNil(__unsafe_unretained T obj) {
+    if (static_cast<id>(obj) == NSNull.null) {
+        return nil;
+    }
+    else if (__unsafe_unretained auto optional = RLMDynamicCast<RLMOptionalBase>(obj)) {
+        return RLMCoerceToNil(optional.underlyingValue);
+    }
+    return obj;
+}
+
+// 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<const char *>(data.bytes) ?: static_cast<char *>((__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) {
+    if (!date)
+        return {};
+    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<int64_t>::max() - NSTimeIntervalSince1970)
+        return {std::numeric_limits<int64_t>::max(), 1'000'000'000 - 1};
+    if (timeInterval - NSTimeIntervalSince1970 < std::numeric_limits<int64_t>::min())
+        return {std::numeric_limits<int64_t>::min(), -1'000'000'000 + 1};
+
+    auto seconds = static_cast<int64_t>(timeInterval);
+    auto nanoseconds = static_cast<int32_t>((timeInterval - seconds) * 1'000'000'000.0);
+    seconds += static_cast<int64_t>(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);
+
+// 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/iOS/Pods/Realm/include/Realm.h b/iOS/Pods/Realm/include/Realm.h
new file mode 100644 (file)
index 0000000..daa86c0
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+#import <Realm/RLMArray.h>
+#import <Realm/RLMMigration.h>
+#import <Realm/RLMObject.h>
+#import <Realm/RLMObjectSchema.h>
+#import <Realm/RLMPlatform.h>
+#import <Realm/RLMProperty.h>
+#import <Realm/RLMRealm.h>
+#import <Realm/RLMRealm+Sync.h>
+#import <Realm/RLMRealmConfiguration.h>
+#import <Realm/RLMRealmConfiguration+Sync.h>
+#import <Realm/RLMResults.h>
+#import <Realm/RLMSchema.h>
+#import <Realm/RLMSyncConfiguration.h>
+#import <Realm/RLMSyncCredentials.h>
+#import <Realm/RLMSyncManager.h>
+#import <Realm/RLMSyncPermission.h>
+#import <Realm/RLMSyncSession.h>
+#import <Realm/RLMSyncUser.h>
+#import <Realm/RLMSyncUtil.h>
+#import <Realm/NSError+RLMSync.h>
diff --git a/iOS/Pods/Realm/include/binding_callback_thread_observer.hpp b/iOS/Pods/Realm/include/binding_callback_thread_observer.hpp
new file mode 100644 (file)
index 0000000..e956349
--- /dev/null
@@ -0,0 +1,42 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP
+#define REALM_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP
+
+#include <exception>
+
+namespace realm {
+// Interface for bindings interested in registering callbacks before/after the ObjectStore thread runs.
+// This is for example helpful to attach/detach the pthread to the JavaVM in order to be able to perform JNI calls.
+class BindingCallbackThreadObserver {
+public:
+    // This method is called just before the thread is started
+    virtual void did_create_thread() = 0;
+
+    // This method is called just before the thread is being destroyed
+    virtual void will_destroy_thread() = 0;
+
+    // This method is called with any exception throws by client.run().
+    virtual void handle_error(std::exception const& e) = 0;
+};
+
+extern BindingCallbackThreadObserver* g_binding_callback_thread_observer;
+}
+
+#endif // REALM_OS_BINDING_CALLBACK_THREAD_OBSERVER_HPP
diff --git a/iOS/Pods/Realm/include/binding_context.hpp b/iOS/Pods/Realm/include/binding_context.hpp
new file mode 100644 (file)
index 0000000..042307d
--- /dev/null
@@ -0,0 +1,176 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <memory>
+#include <tuple>
+#include <vector>
+
+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<std::function<void ()>>::iterator {
+//         token(std::list<std::function<void ()>>::iterator it) : std::list<std::function<void ()>>::iterator(it) { }
+//         friend class DelegateImplementation;
+//     };
+//
+//     token add_notification(std::function<void ()> 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<ObserverState> const&, std::vector<void*> const&, bool) 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<std::function<void ()>> m_registered_notifications;
+// };
+class Realm;
+class Schema;
+class BindingContext {
+public:
+    virtual ~BindingContext() = default;
+
+    std::weak_ptr<Realm> realm;
+
+    // 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 refresh called or a notification arrives which
+    // is triggered through write transaction committed by itself or a different
+    // Realm instance.
+    virtual void before_notify() { }
+
+    // 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<ObserverState> 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<ObserverState> const& observers,
+                             std::vector<void*> 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<ObserverState> const& observers,
+                            std::vector<void*> const& invalidated,
+                            bool version_changed=true);
+
+    // Called immediately after the corresponding Realm's schema is changed through
+    // update_schema()/set_schema_subset() or the schema is changed by another Realm
+    // instance. The parameter is a schema reference which is the same as the return
+    // value of Realm::schema().
+    virtual void schema_did_change(Schema const&) {}
+
+    // 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<ColumnInfo> 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<ObserverState> const&, std::vector<void*> const&) { }
+inline void BindingContext::did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) { }
+} // namespace realm
+
+#endif /* BINDING_CONTEXT_HPP */
diff --git a/iOS/Pods/Realm/include/collection_notifications.hpp b/iOS/Pods/Realm/include/collection_notifications.hpp
new file mode 100644 (file)
index 0000000..919e8b4
--- /dev/null
@@ -0,0 +1,181 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <exception>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+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, uint64_t token);
+    ~NotificationToken();
+
+    NotificationToken(NotificationToken&&);
+    NotificationToken& operator=(NotificationToken&&);
+
+    NotificationToken(NotificationToken const&) = delete;
+    NotificationToken& operator=(NotificationToken const&) = delete;
+
+    void suppress_next();
+
+private:
+    util::AtomicSharedPtr<_impl::CollectionNotifier> m_notifier;
+    uint64_t m_token;
+};
+
+struct CollectionChangeSet {
+    struct Move {
+        size_t from;
+        size_t to;
+
+        bool operator==(Move m) const noexcept { 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<Move> moves;
+
+    // Per-column version of `modifications`
+    std::vector<IndexSet> columns;
+
+    bool empty() const noexcept
+    {
+        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<typename Callback>
+    CollectionChangeCallback(Callback cb) : m_impl(make_impl(std::move(cb))) { }
+    template<typename Callback>
+    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<typename Callback, typename = decltype(std::declval<Callback>()(CollectionChangeSet(), std::exception_ptr()))>
+    std::shared_ptr<Base> make_impl(Callback cb)
+    {
+        return std::make_shared<Impl<Callback>>(std::move(cb));
+    }
+
+    template<typename Callback, typename = decltype(std::declval<Callback>().after(CollectionChangeSet())), typename = void>
+    std::shared_ptr<Base> make_impl(Callback cb)
+    {
+        return std::make_shared<Impl2<Callback>>(std::move(cb));
+    }
+
+    template<typename Callback, typename = decltype(std::declval<Callback>().after(CollectionChangeSet())), typename = void>
+    std::shared_ptr<Base> make_impl(Callback* cb)
+    {
+        return std::make_shared<Impl3<Callback>>(cb);
+    }
+
+    template<typename T>
+    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<typename T>
+    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<typename T>
+    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<Base> m_impl;
+};
+} // namespace realm
+
+#endif // REALM_COLLECTION_NOTIFICATIONS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm.hpp b/iOS/Pods/Realm/include/core/realm.hpp
new file mode 100644 (file)
index 0000000..7ede53f
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_HPP
+#define REALM_HPP
+
+#include <realm/group_shared.hpp>
+#include <realm/descriptor.hpp>
+#include <realm/link_view.hpp>
+#include <realm/table_view.hpp>
+#include <realm/query.hpp>
+#include <realm/query_engine.hpp>
+#include <realm/query_expression.hpp>
+
+#endif // REALM_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/alloc.hpp b/iOS/Pods/Realm/include/core/realm/alloc.hpp
new file mode 100644 (file)
index 0000000..6e25168
--- /dev/null
@@ -0,0 +1,387 @@
+/*************************************************************************
+ *
+ * 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 <cstdint>
+#include <cstddef>
+#include <atomic>
+
+#include <realm/util/features.h>
+#include <realm/util/terminate.hpp>
+#include <realm/util/assert.hpp>
+
+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:
+    /// 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;
+
+    // Disable copying. Copying an allocator can produce double frees.
+    Allocator(const Allocator&) = delete;
+    Allocator& operator=(const Allocator&) = delete;
+
+    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;
+
+protected:
+    size_t m_baseline = 0; // Separation line between immutable and mutable refs.
+
+    Replication* m_replication = nullptr;
+
+    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(const 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<uint_fast64_t> m_table_versioning_counter;
+    std::atomic<uint_fast64_t> m_latest_observed_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.
+    void 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;
+
+    /// Note the current global version has been observed.
+    void observe_version() noexcept;
+
+    friend class Table;
+    friend class Group;
+};
+
+inline void Allocator::bump_global_version() noexcept
+{
+    if (m_latest_observed_counter == m_table_versioning_counter)
+        m_table_versioning_counter += 1;
+}
+
+
+inline void Allocator::observe_version() noexcept
+{
+    if (m_latest_observed_counter != m_table_versioning_counter)
+        m_latest_observed_counter.store(m_table_versioning_counter, std::memory_order_relaxed);
+}
+
+
+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);
+
+    static_assert(std::is_same<ref_type, size_t>::value,
+                  "If ref_type changes, from_ref and to_ref should probably be updated");
+
+    // Make sure that we preserve the bit pattern of the ref_type (without sign extension).
+    return util::from_twos_compl<int_fast64_t>(uint_fast64_t(v));
+}
+
+inline ref_type to_ref(int_fast64_t v) noexcept
+{
+    // Check that v is divisible by 8 (64-bit aligned).
+    REALM_ASSERT_DEBUG(v % 8 == 0);
+
+    // C++11 standard, paragraph 4.7.2 [conv.integral]:
+    // If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source
+    // integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two's
+    // complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is
+    // no truncation). - end note ]
+    static_assert(std::is_unsigned<ref_type>::value,
+                  "If ref_type changes, from_ref and to_ref should probably be updated");
+    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<int64_t>::max());
+    return static_cast<int64_t>(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<void>(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<void>(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;
+    m_latest_observed_counter = 0;
+}
+
+inline Allocator::~Allocator() noexcept
+{
+}
+
+inline Replication* Allocator::get_replication() noexcept
+{
+    return m_replication;
+}
+
+} // namespace realm
+
+#endif // REALM_ALLOC_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/alloc_slab.hpp b/iOS/Pods/Realm/include/core/realm/alloc_slab.hpp
new file mode 100644 (file)
index 0000000..6d2762b
--- /dev/null
@@ -0,0 +1,568 @@
+/*************************************************************************
+ *
+ * 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 <cstdint> // unint8_t etc
+#include <vector>
+#include <string>
+#include <atomic>
+
+#include <realm/util/features.h>
+#include <realm/util/file.hpp>
+#include <realm/alloc.hpp>
+#include <realm/disable_sync_to_disk.hpp>
+
+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();
+
+    // Disable copying. Copying an allocator can produce double frees.
+    SlabAlloc(const SlabAlloc&) = delete;
+    SlabAlloc& operator=(const SlabAlloc&) = delete;
+
+    /// \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.
+    ///
+    /// Except for \a file_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& file_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.
+    ///
+    /// 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.
+    ///
+    /// 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 update_reader_view().
+    ///
+    /// 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();
+
+    /// Update the readers view of the file:
+    ///
+    /// 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.
+    ///
+    /// Clears any allocator specicific caching of address translations
+    /// and force any later address translations to trigger decryption if required.
+    void update_reader_view(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;
+
+    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;
+
+    /// 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;
+
+private:
+    void internal_invalidate_cache() noexcept;
+    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<MappedFile> 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<std::shared_ptr<const util::File::Map<char>>[]> 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<size_t[]> m_section_bases;
+    size_t m_num_section_bases = 0;
+    AttachMode m_attach_mode = attach_None;
+    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<Slab> slabs;
+    typedef std::vector<Chunk> 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.
+    void consolidate_free_read_only();
+    /// 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);
+
+    static bool is_file_on_streaming_form(const Header& header);
+    /// Read the top_ref from the given buffer and set m_file_on_streaming_form
+    /// if the buffer contains a file in streaming form
+    static 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;
+    }
+
+    friend class Group;
+    friend class SharedGroup;
+    friend class GroupWriter;
+};
+
+inline void SlabAlloc::internal_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/iOS/Pods/Realm/include/core/realm/array.hpp b/iOS/Pods/Realm/include/core/realm/array.hpp
new file mode 100644 (file)
index 0000000..cb3d7d9
--- /dev/null
@@ -0,0 +1,3100 @@
+/*************************************************************************
+ *
+ * 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 <class cond, Action action, size_t bitwidth, class Callback>
+    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 <cmath>
+#include <cstdlib> // size_t
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <ostream>
+
+#include <cstdint> // unint8_t etc
+
+#include <realm/util/assert.hpp>
+#include <realm/util/file_mapper.hpp>
+#include <realm/utilities.hpp>
+#include <realm/alloc.hpp>
+#include <realm/string_data.hpp>
+#include <realm/query_conditions.hpp>
+#include <realm/column_fwd.hpp>
+#include <realm/array_direct.hpp>
+
+/*
+    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 <emmintrin.h>             // SSE2
+#include <realm/realm_nmmintrin.h> // 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 <class T>
+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;
+const size_t max_array_payload_aligned = 0x00fffff8L;
+
+/// Alias for realm::npos.
+const size_t not_found = npos;
+
+// Pre-definitions
+class Array;
+class StringColumn;
+class GroupWriter;
+template <class T>
+class QueryState;
+namespace _impl {
+class ArrayWriterBase;
+}
+
+
+struct MemStats {
+    size_t allocated = 0;
+    size_t used = 0;
+    size_t array_count = 0;
+};
+
+#ifdef REALM_DEBUG
+template <class C, class T>
+std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& 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<ref_type, size_t> get_to_dot_parent(size_t ndx_in_parent) const = 0;
+
+    friend class Array;
+};
+
+struct TreeInsertBase {
+    size_t m_split_offset;
+    size_t m_split_size;
+};
+
+/// 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 <size_t w>
+    void set(size_t ndx, int64_t value);
+
+    int64_t get(size_t ndx) const noexcept;
+
+    template <size_t w>
+    int64_t get(size_t ndx) const noexcept;
+
+    void get_chunk(size_t ndx, int64_t res[8]) const noexcept;
+
+    template <size_t w>
+    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);
+
+    /// 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
+    /// `<algorithm>`. \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) &ge; v`, and upper_bound_int(v)
+    /// returns the index '`u`' of the first element such that `get(u) &gt;
+    /// 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;
+    void set_has_refs(bool) 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<int64_t>* state, bool nullable_array = false, bool find_null = false) const;
+
+    // Templated find function to avoid conversion to and from integer represenation of condition
+    template <class cond>
+    bool find(Action action, int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* 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<int64_t>* state) const;
+    */
+
+    template <class cond, Action action, size_t bitwidth, class Callback>
+    bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+              Callback callback, bool nullable_array = false, bool find_null = false) const;
+
+    // This is the one installed into the m_vtable->finder slots.
+    template <class cond, Action action, size_t bitwidth>
+    bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state) const;
+
+    template <class cond, Action action, class Callback>
+    bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+              Callback callback, bool nullable_array = false, bool find_null = false) const;
+
+    /*
+    template <class cond, Action action, class Callback>
+    bool find(null, size_t start, size_t end, size_t baseindex,
+              QueryState<int64_t>* state, Callback callback) const;
+    */
+
+    // Optimized implementation for release mode
+    template <class cond, Action action, size_t bitwidth, class Callback>
+    bool find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                        Callback callback, bool nullable_array = false, bool find_null = false) const;
+
+    // Called for each search result
+    template <Action action, class Callback>
+    bool find_action(size_t index, util::Optional<int64_t> value, QueryState<int64_t>* state,
+                     Callback callback) const;
+
+    template <Action action, class Callback>
+    bool find_action_pattern(size_t index, uint64_t pattern, QueryState<int64_t>* state, Callback callback) const;
+
+    // Wrappers for backwards compatibility and for simple use without
+    // setting up state initialization etc
+    template <class cond>
+    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 <class cond, Action action, size_t bitwidth, class Callback>
+    bool compare(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                 Callback callback) const;
+
+    // Non-SSE find for Equal/NotEqual
+    template <bool eq, Action action, size_t width, class Callback>
+    inline bool compare_equality(int64_t value, size_t start, size_t end, size_t baseindex,
+                                 QueryState<int64_t>* state, Callback callback) const;
+
+    // Non-SSE find for Less/Greater
+    template <bool gt, Action action, size_t bitwidth, class Callback>
+    bool compare_relation(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                          Callback callback) const;
+
+    template <class cond, Action action, size_t foreign_width, class Callback, size_t width>
+    bool compare_leafs_4(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                         Callback callback) const;
+
+    template <class cond, Action action, class Callback, size_t bitwidth, size_t foreign_bitwidth>
+    bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                       Callback callback) const;
+
+    template <class cond, Action action, class Callback>
+    bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                       Callback callback) const;
+
+    template <class cond, Action action, size_t width, class Callback>
+    bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                       Callback callback) const;
+
+// SSE find for the four functions Equal/NotEqual/Less/Greater
+#ifdef REALM_COMPILER_SSE
+    template <class cond, Action action, size_t width, class Callback>
+    bool find_sse(int64_t value, __m128i* data, size_t items, QueryState<int64_t>* state, size_t baseindex,
+                  Callback callback) const;
+
+    template <class cond, Action action, size_t width, class Callback>
+    REALM_FORCEINLINE bool find_sse_intern(__m128i* action_data, __m128i* data, size_t items,
+                                           QueryState<int64_t>* state, size_t baseindex, Callback callback) const;
+
+#endif
+
+    template <size_t width>
+    inline bool test_zero(uint64_t value) const; // Tests value for 0-elements
+
+    template <bool eq, size_t width>
+    size_t find_zero(uint64_t v) const; // Finds position of 0/non-zero element
+
+    template <size_t width, bool zero>
+    uint64_t cascade(uint64_t a) const; // Sets lowermost bits of zero or non-zero elements
+
+    template <bool gt, size_t width>
+    int64_t
+    find_gtlt_magic(int64_t v) const; // Compute magic constant needed for searching for value 'v' using bit hacks
+
+    template <size_t width>
+    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 <size_t w>
+    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 gt, Action action, size_t width, class Callback>
+    bool find_gtlt_fast(uint64_t chunk, uint64_t magic, QueryState<int64_t>* state, size_t baseindex,
+                        Callback callback) const;
+
+    // Find value greater/less in 64-bit chunk - no constraints
+    template <bool gt, Action action, size_t width, class Callback>
+    bool find_gtlt(int64_t v, uint64_t chunk, QueryState<int64_t>* state, size_t baseindex, Callback callback) const;
+
+    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<int64_t, int64_t> 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);
+
+    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 noexcept;
+
+#ifdef REALM_DEBUG
+    void print() const;
+    void verify() const;
+    typedef size_t (*LeafVerifier)(MemRef, Allocator&);
+    void verify_bptree(LeafVerifier) 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");
+
+    Array& operator=(const Array&) = delete; // not allowed
+    Array(const Array&) = delete; // not allowed
+protected:
+    typedef bool (*CallbackDummy)(int64_t);
+
+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 <size_t width>
+    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 <size_t width>
+    static int_fast64_t ubound_for_width() noexcept;
+
+    static int_fast64_t ubound_for_width(size_t width) noexcept;
+
+    template <size_t width>
+    void set_width() noexcept;
+    void set_width(size_t) noexcept;
+    void alloc(size_t init_size, size_t width);
+    void copy_on_write();
+
+private:
+    void do_copy_on_write(size_t minimum_size = 0);
+    void do_ensure_minimum_width(int_fast64_t);
+
+    template <size_t w>
+    int64_t sum(size_t start, size_t end) const;
+
+    template <bool max, size_t w>
+    bool minmax(int64_t& result, size_t start, size_t end, size_t* return_ndx) const;
+
+    template <size_t w>
+    size_t find_gte(const int64_t target, size_t start, size_t end) const;
+
+    template <size_t w>
+    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<ref_type, size_t> get_to_dot_parent(size_t ndx_in_parent) const override;
+
+    bool is_read_only() const noexcept;
+
+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<int64_t>*) 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 <size_t w>
+    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);
+
+    void report_memory_usage_2(MemUsageHandler&) const;
+
+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;
+};
+
+
+// Implementation:
+
+class QueryStateBase {
+    virtual void dyncast()
+    {
+    }
+};
+
+template <>
+class QueryState<int64_t> : 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 <Action action>
+    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<int64_t>(akku);
+        else if (action == act_CallbackIdx) {
+        }
+        else {
+            REALM_ASSERT_DEBUG(false);
+        }
+    }
+
+    template <Action action, bool pattern>
+    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<IntegerColumn*>(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 <Action action, bool pattern>
+    inline bool match(size_t index, uint64_t indexpattern, util::Optional<int64_t> value)
+    {
+        // FIXME: This is a temporary hack for nullable integers.
+        if (value) {
+            return match<action, pattern>(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<IntegerColumn*>(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 R>
+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 <Action action>
+    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<R, float>::value || std::is_same<R, double>::value));
+        m_match_count = 0;
+        m_limit = limit;
+        m_minmax_index = not_found;
+
+        if (action == act_Max)
+            m_state = -std::numeric_limits<R>::infinity();
+        else if (action == act_Min)
+            m_state = std::numeric_limits<R>::infinity();
+        else if (action == act_Sum)
+            m_state = 0.0;
+        else {
+            REALM_ASSERT_DEBUG(false);
+        }
+    }
+
+    template <Action action, bool pattern, typename resulttype>
+    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 <alloc.hpp>
+    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 <alloc.hpp>
+    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<int_fast64_t>((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 void Array::set_has_refs(bool value) noexcept
+{
+    if (m_has_refs != value) {
+        REALM_ASSERT(!is_read_only());
+        m_has_refs = value;
+        set_header_hasrefs(value);
+    }
+}
+
+inline bool Array::get_context_flag() const noexcept
+{
+    return m_context_flag;
+}
+
+inline void Array::set_context_flag(bool value) noexcept
+{
+    if (m_context_flag != value) {
+        REALM_ASSERT(!is_read_only());
+        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)
+{
+    REALM_ASSERT_3(ndx, <=, m_size);
+    if (diff != 0) {
+        // FIXME: Should be optimized
+        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)
+{
+    if (diff != 0) {
+        // 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<const uchar*>(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<const uchar*>(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<const uchar*>(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<const uchar*>(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<const uchar*>(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<const uchar*>(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<const uchar*>(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<char*>(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<uchar*>(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<uchar*>(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<uchar*>(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<uchar*>(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<uchar*>(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<uchar*>(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<uchar*>(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 bool Array::is_read_only() const noexcept
+{
+    REALM_ASSERT_DEBUG(is_attached());
+    return m_alloc.is_read_only(m_ref);
+}
+
+inline void Array::copy_on_write()
+{
+#if REALM_ENABLE_MEMDEBUG
+    // We want to relocate this array regardless if there is a need or not, in order to catch use-after-free bugs.
+    // Only exception is inside GroupWriter::write_group() (see explanation at the definition of the m_no_relocation
+    // member)
+    if (!m_no_relocation) {
+#else
+    if (is_read_only()) {
+#endif
+        do_copy_on_write();
+    }
+}
+
+inline void Array::ensure_minimum_width(int_fast64_t value)
+{
+    if (value >= m_lbound && value <= m_ubound)
+        return;
+    do_ensure_minimum_width(value);
+}
+
+
+//*************************************************************************************
+// Finding code                                                                       *
+//*************************************************************************************
+
+template <size_t w>
+int64_t Array::get(size_t ndx) const noexcept
+{
+    return get_universal<w>(m_data, ndx);
+}
+
+template <size_t w>
+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<const signed char*>(data + ndx);
+    }
+    else if (w == 16) {
+        size_t offset = ndx * 2;
+        return *reinterpret_cast<const int16_t*>(data + offset);
+    }
+    else if (w == 32) {
+        size_t offset = ndx * 4;
+        return *reinterpret_cast<const int32_t*>(data + offset);
+    }
+    else if (w == 64) {
+        size_t offset = ndx * 8;
+        return *reinterpret_cast<const int64_t*>(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 <Action action, class Callback>
+bool Array::find_action(size_t index, util::Optional<int64_t> value, QueryState<int64_t>* state,
+                        Callback callback) const
+{
+    if (action == act_CallbackIdx)
+        return callback(index);
+    else
+        return state->match<action, false>(index, 0, value);
+}
+template <Action action, class Callback>
+bool Array::find_action_pattern(size_t index, uint64_t pattern, QueryState<int64_t>* state, Callback callback) const
+{
+    static_cast<void>(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<action, true>(index, pattern, 0);
+}
+
+
+template <size_t width, bool zero>
+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 <class cond, Action action, size_t bitwidth, class Callback>
+bool Array::find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* 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<bitwidth>(start2 + 1);
+            if (c(v, value, v == get(0), find_null)) {
+                util::Optional<int64_t> v2(v == get(0) ? util::none : util::make_optional(v));
+                if (!find_action<action, Callback>(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<bitwidth>(start2), value) && start2 < end) {
+            if (!find_action<action, Callback>(start2 + baseindex, get<bitwidth>(start2), state, callback))
+                return false;
+        }
+
+        ++start2;
+
+        if (m_size > start2 && c(get<bitwidth>(start2), value) && start2 < end) {
+            if (!find_action<action, Callback>(start2 + baseindex, get<bitwidth>(start2), state, callback))
+                return false;
+        }
+
+        ++start2;
+
+        if (m_size > start2 && c(get<bitwidth>(start2), value) && start2 < end) {
+            if (!find_action<action, Callback>(start2 + baseindex, get<bitwidth>(start2), state, callback))
+                return false;
+        }
+
+        ++start2;
+
+        if (m_size > start2 && c(get<bitwidth>(start2), value) && start2 < end) {
+            if (!find_action<action, Callback>(start2 + baseindex, get<bitwidth>(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<action, Callback>(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<action, Callback>(start2 + baseindex, get<bitwidth>(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<cond, Less>::value && m_width == 64)) && end - start2 >= sizeof(__m128i) && m_width >= 8 &&
+        (sseavx<42>() || (sseavx<30>() && std::is_same<cond, Equal>::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<cond, action, bitwidth, Callback>(
+                value, start2, (reinterpret_cast<char*>(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<cond, action, bitwidth, Callback>(
+                        value, a, b - a, state,
+                        baseindex + ((reinterpret_cast<char*>(a) - m_data) * 8 / no0(bitwidth)), callback))
+                    return false;
+            }
+            else if (sseavx<30>()) {
+
+                if (!find_sse<Equal, action, bitwidth, Callback>(
+                        value, a, b - a, state,
+                        baseindex + ((reinterpret_cast<char*>(a) - m_data) * 8 / no0(bitwidth)), callback))
+                    return false;
+            }
+        }
+
+        // Search remainder with compare_equality()
+        if (!compare<cond, action, bitwidth, Callback>(
+                value, (reinterpret_cast<char*>(b) - m_data) * 8 / no0(bitwidth), end, baseindex, state, callback))
+            return false;
+
+        return true;
+    }
+    else {
+        return compare<cond, action, bitwidth, Callback>(value, start2, end, baseindex, state, callback);
+    }
+#else
+    return compare<cond, action, bitwidth, Callback>(value, start2, end, baseindex, state, callback);
+#endif
+}
+
+template <size_t width>
+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 <size_t width>
+inline bool Array::test_zero(uint64_t value) const
+{
+    uint64_t hasZeroByte;
+    uint64_t lower = lower_bits<width>();
+    uint64_t upper = lower_bits<width>() * 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 <bool eq, size_t width>
+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<width>(v | 0xffffffff00000000ULL);
+        if (eq ? !hasZeroByte : (v & 0x00000000ffffffffULL) == 0) {
+            // 00?? -> increasing
+            start += 64 / no0(width) / 2;
+            if (width <= 4) {
+                hasZeroByte = test_zero<width>(v | 0xffff000000000000ULL);
+                if (eq ? !hasZeroByte : (v & 0x0000ffffffffffffULL) == 0) {
+                    // 000?
+                    start += 64 / no0(width) / 4;
+                }
+            }
+        }
+        else {
+            if (width <= 4) {
+                // ??00
+                hasZeroByte = test_zero<width>(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 <bool gt, size_t width>
+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 gt, Action action, size_t width, class Callback>
+bool Array::find_gtlt_fast(uint64_t chunk, uint64_t magic, QueryState<int64_t>* 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<action, Callback>(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<action, Callback>(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 gt, Action action, size_t width, class Callback>
+bool Array::find_gtlt(int64_t v, uint64_t chunk, QueryState<int64_t>* 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<int64_t>(chunk & 0x1) > v : static_cast<int64_t>(chunk & 0x1) < v) {if (!find_action<action, Callback>( t + baseindex, static_cast<int64_t>(chunk & 0x1), state, callback)) return false;}
+            chunk >>= 1;
+        }
+    }
+    else if (width == 2) {
+        // Alot (50% +) faster than loop/compiler-unrolled loop
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 1 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 2 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 3 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 4 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 5 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 6 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 7 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 8 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 9 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 10 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 11 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 12 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 13 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 14 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 15 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 16 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 17 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 18 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 19 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 20 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 21 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 22 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 23 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 24 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 25 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 26 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 27 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 28 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 29 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 30 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+        if (gt ? static_cast<int64_t>(chunk & 0x3) > v : static_cast<int64_t>(chunk & 0x3) < v) {if (!find_action<action, Callback>( 31 + baseindex, static_cast<int64_t>(chunk & 0x3), state, callback)) return false;}
+        chunk >>= 2;
+    }
+    else if (width == 4) {
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 1 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 2 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 3 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 4 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 5 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 6 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 7 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 8 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 9 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 10 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 11 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 12 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 13 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 14 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+        if (gt ? static_cast<int64_t>(chunk & 0xf) > v : static_cast<int64_t>(chunk & 0xf) < v) {if (!find_action<action, Callback>( 15 + baseindex, static_cast<int64_t>(chunk & 0xf), state, callback)) return false;}
+        chunk >>= 4;
+    }
+    else if (width == 8) {
+        if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+        chunk >>= 8;
+        if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 1 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+        chunk >>= 8;
+        if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 2 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+        chunk >>= 8;
+        if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 3 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+        chunk >>= 8;
+        if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 4 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+        chunk >>= 8;
+        if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 5 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+        chunk >>= 8;
+        if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 6 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+        chunk >>= 8;
+        if (gt ? static_cast<int8_t>(chunk) > v : static_cast<int8_t>(chunk) < v) {if (!find_action<action, Callback>( 7 + baseindex, static_cast<int8_t>(chunk), state, callback)) return false;}
+        chunk >>= 8;
+    }
+    else if (width == 16) {
+
+        if (gt ? static_cast<short int>(chunk >> 0 * 16) > v : static_cast<short int>(chunk >> 0 * 16) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<short int>(chunk >> 0 * 16), state, callback)) return false;};
+        if (gt ? static_cast<short int>(chunk >> 1 * 16) > v : static_cast<short int>(chunk >> 1 * 16) < v) {if (!find_action<action, Callback>( 1 + baseindex, static_cast<short int>(chunk >> 1 * 16), state, callback)) return false;};
+        if (gt ? static_cast<short int>(chunk >> 2 * 16) > v : static_cast<short int>(chunk >> 2 * 16) < v) {if (!find_action<action, Callback>( 2 + baseindex, static_cast<short int>(chunk >> 2 * 16), state, callback)) return false;};
+        if (gt ? static_cast<short int>(chunk >> 3 * 16) > v : static_cast<short int>(chunk >> 3 * 16) < v) {if (!find_action<action, Callback>( 3 + baseindex, static_cast<short int>(chunk >> 3 * 16), state, callback)) return false;};
+    }
+    else if (width == 32) {
+        if (gt ? static_cast<int>(chunk) > v : static_cast<int>(chunk) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<int>(chunk), state, callback)) return false;}
+        chunk >>= 32;
+        if (gt ? static_cast<int>(chunk) > v : static_cast<int>(chunk) < v) {if (!find_action<action, Callback>( 1 + baseindex, static_cast<int>(chunk), state, callback)) return false;}
+        chunk >>= 32;
+    }
+    else if (width == 64) {
+        if (gt ? static_cast<int64_t>(v) > v : static_cast<int64_t>(v) < v) {if (!find_action<action, Callback>( 0 + baseindex, static_cast<int64_t>(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 <bool eq, Action action, size_t width, class Callback>
+inline bool Array::compare_equality(int64_t value, size_t start, size_t end, size_t baseindex,
+                                    QueryState<int64_t>* 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<width>(start) == value) : (get<width>(start) != value)) {
+            if (!find_action<action, Callback>(start + baseindex, get<width>(start), state, callback))
+                return false;
+        }
+
+    if (start >= end)
+        return true;
+
+    if (width != 32 && width != 64) {
+        const int64_t* p = reinterpret_cast<const int64_t*>(m_data + (start * width / 8));
+        const int64_t* const e = reinterpret_cast<int64_t*>(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<int64_t*>(m_data)) * 8 * 8 / no0(width);
+            size_t a = 0;
+
+            while (eq ? test_zero<width>(v2) : v2) {
+
+                if (find_action_pattern<action, Callback>(start + baseindex, cascade<width, eq>(v2), state, callback))
+                    break; // consumed
+
+                size_t t = find_zero<eq, width>(v2);
+                a += t;
+
+                if (a >= 64 / no0(width))
+                    break;
+
+                if (!find_action<action, Callback>(a + start + baseindex, get<width>(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<int64_t*>(m_data)) * 8 * 8 / no0(width);
+    }
+
+    while (start < end) {
+        if (eq ? get<width>(start) == value : get<width>(start) != value) {
+            if (!find_action<action, Callback>(start + baseindex, get<width>(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 <class cond, Action action, size_t bitwidth>
+bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state) const
+{
+    return find<cond, action, bitwidth>(value, start, end, baseindex, state, CallbackDummy());
+}
+
+template <class cond, Action action, class Callback>
+bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* 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 <class cond, Action action, size_t bitwidth, class Callback>
+bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                 Callback callback, bool nullable_array, bool find_null) const
+{
+    return find_optimized<cond, action, bitwidth, Callback>(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 <class cond, Action action, size_t width, class Callback>
+bool Array::find_sse(int64_t value, __m128i* data, size_t items, QueryState<int64_t>* state, size_t baseindex,
+                     Callback callback) const
+{
+    __m128i search = {0};
+
+    if (width == 8)
+        search = _mm_set1_epi8(static_cast<char>(value));
+    else if (width == 16)
+        search = _mm_set1_epi16(static_cast<short int>(value));
+    else if (width == 32)
+        search = _mm_set1_epi32(static_cast<int>(value));
+    else if (width == 64) {
+        if (std::is_same<cond, Less>::value)
+            REALM_ASSERT(false);
+        else
+            search = _mm_set_epi64x(value, value);
+    }
+
+    return find_sse_intern<cond, action, width, Callback>(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 <class cond, Action action, size_t width, class Callback>
+REALM_FORCEINLINE bool Array::find_sse_intern(__m128i* action_data, __m128i* data, size_t items,
+                                              QueryState<int64_t>* 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<cond, Equal>::value || std::is_same<cond, NotEqual>::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<cond, Greater>::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<cond, Less>::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<cond, NotEqual>::value)
+            resmask = ~resmask & 0x0000ffff;
+
+        size_t s = i * sizeof(__m128i) * 8 / no0(width);
+
+        while (resmask != 0) {
+
+            uint64_t upper = lower_bits<width / 8>() << (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<action, Callback>(s + baseindex, pattern, state, callback))
+                break;
+
+            size_t idx = first_set_bit(resmask) * 8 / no0(width);
+            s += idx;
+            if (!find_action<action, Callback>(
+                    s + baseindex, get_universal<width>(reinterpret_cast<char*>(action_data), s), state, callback))
+                return false;
+            resmask >>= (idx + 1) * no0(width) / 8;
+            ++s;
+        }
+    }
+
+    return true;
+}
+#endif // REALM_COMPILER_SSE
+
+template <class cond, Action action, class Callback>
+bool Array::compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex,
+                          QueryState<int64_t>* 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<action, Callback>(start + baseindex, v, state, callback))
+            return false;
+    }
+
+    start++;
+
+    if (start + 3 < end) {
+        v = get(start);
+        if (c(v, foreign->get(start)))
+            if (!find_action<action, Callback>(start + baseindex, v, state, callback))
+                return false;
+
+        v = get(start + 1);
+        if (c(v, foreign->get(start + 1)))
+            if (!find_action<action, Callback>(start + 1 + baseindex, v, state, callback))
+                return false;
+
+        v = get(start + 2);
+        if (c(v, foreign->get(start + 2)))
+            if (!find_action<action, Callback>(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 <class cond, Action action, size_t width, class Callback>
+bool Array::compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex,
+                          QueryState<int64_t>* 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 <class cond, Action action, size_t width, class Callback, size_t foreign_width>
+bool Array::compare_leafs_4(const Array* foreign, size_t start, size_t end, size_t baseindex,
+                            QueryState<int64_t>* 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<action, Callback>(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<size_t>(m_data) & 0xf) * 8 + start * width) % (128) != 0)) {
+            int64_t v = get_universal<width>(m_data, start);
+            int64_t fv = get_universal<foreign_width>(foreign_m_data, start);
+            if (c(v, fv)) {
+                if (!find_action<action, Callback>(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<cond, action, width, Callback>(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<width>(m_data, start);
+        int64_t fv = get_universal<foreign_width>(foreign_m_data, start);
+
+        if (c(v, fv)) {
+            if (!find_action<action, Callback>(start + baseindex, v, state, callback))
+                return false;
+        }
+
+        start++;
+    }
+
+    return true;
+}
+
+
+template <class cond, Action action, size_t bitwidth, class Callback>
+bool Array::compare(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                    Callback callback) const
+{
+    bool ret = false;
+
+    if (std::is_same<cond, Equal>::value)
+        ret = compare_equality<true, action, bitwidth, Callback>(value, start, end, baseindex, state, callback);
+    else if (std::is_same<cond, NotEqual>::value)
+        ret = compare_equality<false, action, bitwidth, Callback>(value, start, end, baseindex, state, callback);
+    else if (std::is_same<cond, Greater>::value)
+        ret = compare_relation<true, action, bitwidth, Callback>(value, start, end, baseindex, state, callback);
+    else if (std::is_same<cond, Less>::value)
+        ret = compare_relation<false, action, bitwidth, Callback>(value, start, end, baseindex, state, callback);
+    else
+        REALM_ASSERT_DEBUG(false);
+
+    return ret;
+}
+
+template <bool gt, Action action, size_t bitwidth, class Callback>
+bool Array::compare_relation(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* 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<bitwidth>(start) > value) : (get<bitwidth>(start) < value)) {
+            if (!find_action<action, Callback>(start + baseindex, get<bitwidth>(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<const int64_t*>(m_data + (start * bitwidth / 8));
+    const int64_t* const e = reinterpret_cast<int64_t*>(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<gt, bitwidth>(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<int64_t>((mask >> 1) - (gt ? 1 : 0))) {
+            // 15 ms
+            while (p < e) {
+                uint64_t upper = lower_bits<bitwidth>() << (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<gt, action, bitwidth, Callback>(
+                        v, magic, state, (p - reinterpret_cast<int64_t*>(m_data)) * 8 * 8 / no0(bitwidth) + baseindex,
+                        callback);
+                }
+                else
+                    idx = find_gtlt<gt, action, bitwidth, Callback>(
+                        value, v, state, (p - reinterpret_cast<int64_t*>(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<gt, action, bitwidth, Callback>(
+                        value, v, state, (p - reinterpret_cast<int64_t*>(m_data)) * 8 * 8 / no0(bitwidth) + baseindex,
+                        callback))
+                    return false;
+                ++p;
+            }
+        }
+        start = (p - reinterpret_cast<int64_t*>(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<bitwidth>(start) > value : get<bitwidth>(start) < value) {
+            if (!find_action<action, Callback>(start + baseindex, get<bitwidth>(start), state, callback))
+                return false;
+        }
+        ++start;
+    }
+    return true;
+}
+
+template <class cond>
+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<int64_t> 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<size_t>(state.m_state);
+}
+
+//*************************************************************************************
+// Finding code ends                                                                  *
+//*************************************************************************************
+
+
+} // namespace realm
+
+#endif // REALM_ARRAY_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/array_basic.hpp b/iOS/Pods/Realm/include/core/realm/array_basic.hpp
new file mode 100644 (file)
index 0000000..6b9c212
--- /dev/null
@@ -0,0 +1,120 @@
+/*************************************************************************
+ *
+ * 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 <realm/array.hpp>
+
+namespace realm {
+
+/// A BasicArray can currently only be used for simple unstructured
+/// types like float, double.
+template <class T>
+class BasicArray : public Array {
+public:
+    explicit BasicArray(Allocator&) noexcept;
+    ~BasicArray() noexcept override
+    {
+    }
+
+    // Disable copying, this is not allowed.
+    BasicArray& operator=(const BasicArray&) = delete;
+    BasicArray(const BasicArray&) = delete;
+
+    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<T>&) 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 find_max>
+    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<float> ArrayFloat;
+typedef BasicArray<double> ArrayDouble;
+
+} // namespace realm
+
+#include <realm/array_basic_tpl.hpp>
+
+#endif // REALM_ARRAY_BASIC_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/array_basic_tpl.hpp b/iOS/Pods/Realm/include/core/realm/array_basic_tpl.hpp
new file mode 100644 (file)
index 0000000..dae629c
--- /dev/null
@@ -0,0 +1,460 @@
+/*************************************************************************
+ *
+ * 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 <algorithm>
+#include <limits>
+#include <stdexcept>
+#include <iomanip>
+
+#include <realm/impl/destroy_guard.hpp>
+
+namespace realm {
+
+template <class T>
+inline BasicArray<T>::BasicArray(Allocator& allocator) noexcept
+    : Array(allocator)
+{
+}
+
+template <class T>
+inline MemRef BasicArray<T>::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 <class T>
+inline MemRef BasicArray<T>::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<T> tmp(allocator);
+        tmp.init_from_mem(mem);
+        T* p = reinterpret_cast<T*>(tmp.m_data);
+        T* end = p + init_size;
+        while (p < end) {
+            *p++ = value;
+        }
+    }
+    return mem;
+}
+
+
+template <class T>
+inline void BasicArray<T>::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 <class T>
+MemRef BasicArray<T>::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 <class T>
+MemRef BasicArray<T>::slice_and_clone_children(size_t offset, size_t slice_size, Allocator& target_alloc) const
+{
+    // BasicArray<T> never contains refs, so never has children.
+    return slice(offset, slice_size, target_alloc);
+}
+
+
+template <class T>
+inline void BasicArray<T>::add(T value)
+{
+    insert(m_size, value);
+}
+
+
+template <class T>
+inline T BasicArray<T>::get(size_t ndx) const noexcept
+{
+    return *(reinterpret_cast<const T*>(m_data) + ndx);
+}
+
+
+template <class T>
+inline bool BasicArray<T>::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<T, float, double>::value, "T can only be float or double");
+    auto x = get(ndx);
+    return null::is_null_float(x);
+}
+
+
+template <class T>
+inline T BasicArray<T>::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<const T*>(data) + ndx);
+}
+
+
+template <class T>
+inline void BasicArray<T>::set(size_t ndx, T value)
+{
+    REALM_ASSERT_3(ndx, <, m_size);
+    if (get(ndx) == value)
+        return;
+
+    // Check if we need to copy before modifying
+    copy_on_write(); // Throws
+
+    // Set the value
+    T* data = reinterpret_cast<T*>(m_data) + ndx;
+    *data = value;
+}
+
+template <class T>
+inline void BasicArray<T>::set_null(size_t ndx)
+{
+    // FIXME: This assumes BasicArray will only ever be instantiated for float-like T.
+    set(ndx, null::get_null_float<T>());
+}
+
+template <class T>
+void BasicArray<T>::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<T*>(m_data) + ndx;
+    *data = value;
+
+    ++m_size;
+}
+
+template <class T>
+void BasicArray<T>::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;
+        realm::safe_copy_n(src_begin, src_end - src_begin, dst_begin);
+    }
+
+    // Update size (also in header)
+    --m_size;
+    set_header_size(m_size);
+}
+
+template <class T>
+void BasicArray<T>::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 <class T>
+inline void BasicArray<T>::clear()
+{
+    truncate(0); // Throws
+}
+
+template <class T>
+bool BasicArray<T>::compare(const BasicArray<T>& a) const
+{
+    size_t n = size();
+    if (a.size() != n)
+        return false;
+    const T* data_1 = reinterpret_cast<const T*>(m_data);
+    const T* data_2 = reinterpret_cast<const T*>(a.m_data);
+    return realm::safe_equal(data_1, data_1 + n, data_2);
+}
+
+
+template <class T>
+size_t BasicArray<T>::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 <class T>
+size_t BasicArray<T>::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 <class T>
+size_t BasicArray<T>::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<const T*>(m_data);
+    const T* i = std::find(data + begin, data + end, value);
+    return i == data + end ? not_found : size_t(i - data);
+}
+
+template <class T>
+inline size_t BasicArray<T>::find_first(T value, size_t begin, size_t end) const
+{
+    return this->find(value, begin, end);
+}
+
+template <class T>
+void BasicArray<T>::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 <class T>
+size_t BasicArray<T>::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<const T*>(m_data);
+    return std::count(data + begin, data + end, value);
+}
+
+#if 0
+// currently unused
+template <class T>
+double BasicArray<T>::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<const T*>(m_data);
+    return std::accumulate(data + begin, data + end, double(0));
+}
+#endif
+
+template <class T>
+template <bool find_max>
+bool BasicArray<T>::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 <class T>
+bool BasicArray<T>::maximum(T& result, size_t begin, size_t end) const
+{
+    return minmax<true>(result, begin, end);
+}
+
+template <class T>
+bool BasicArray<T>::minimum(T& result, size_t begin, size_t end) const
+{
+    return minmax<false>(result, begin, end);
+}
+
+
+template <class T>
+ref_type BasicArray<T>::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<T> 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 <class T>
+inline size_t BasicArray<T>::lower_bound(T value) const noexcept
+{
+    const T* begin = reinterpret_cast<const T*>(m_data);
+    const T* end = begin + size();
+    return std::lower_bound(begin, end, value) - begin;
+}
+
+template <class T>
+inline size_t BasicArray<T>::upper_bound(T value) const noexcept
+{
+    const T* begin = reinterpret_cast<const T*>(m_data);
+    const T* end = begin + size();
+    return std::upper_bound(begin, end, value) - begin;
+}
+
+template <class T>
+inline size_t BasicArray<T>::calc_aligned_byte_size(size_t size)
+{
+    size_t max = std::numeric_limits<size_t>::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 <class T>
+void BasicArray<T>::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 << "<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\"><TR>\n";
+
+    // Header
+    out << "<TD BGCOLOR=\"lightgrey\"><FONT POINT-SIZE=\"7\"> ";
+    out << "0x" << std::hex << ref << std::dec << "<BR/>";
+    out << "</FONT></TD>\n";
+
+    // Values
+    size_t n = m_size;
+    for (size_t i = 0; i != n; ++i)
+        out << "<TD>" << get(i) << "</TD>\n";
+
+    out << "</TR></TABLE>>];\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/iOS/Pods/Realm/include/core/realm/array_binary.hpp b/iOS/Pods/Realm/include/core/realm/array_binary.hpp
new file mode 100644 (file)
index 0000000..41b4626
--- /dev/null
@@ -0,0 +1,259 @@
+/*************************************************************************
+ *
+ * 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 <realm/binary_data.hpp>
+#include <realm/array_blob.hpp>
+#include <realm/array_integer.hpp>
+#include <realm/exceptions.hpp>
+
+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
+    {
+    }
+
+    // Disable copying, this is not allowed.
+    ArrayBinary& operator=(const ArrayBinary&) = delete;
+    ArrayBinary(const ArrayBinary&) = delete;
+
+    /// 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/iOS/Pods/Realm/include/core/realm/array_blob.hpp b/iOS/Pods/Realm/include/core/realm/array_blob.hpp
new file mode 100644 (file)
index 0000000..3ee6e54
--- /dev/null
@@ -0,0 +1,147 @@
+/*************************************************************************
+ *
+ * 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 <realm/array.hpp>
+
+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
+    {
+    }
+
+    // Disable copying, this is not allowed.
+    ArrayBlob& operator=(const ArrayBlob&) = delete;
+    ArrayBlob(const ArrayBlob&) = delete;
+
+    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&);
+
+    size_t blob_size() const noexcept;
+#ifdef REALM_DEBUG
+    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/iOS/Pods/Realm/include/core/realm/array_blobs_big.hpp b/iOS/Pods/Realm/include/core/realm/array_blobs_big.hpp
new file mode 100644 (file)
index 0000000..5e0d520
--- /dev/null
@@ -0,0 +1,222 @@
+/*************************************************************************
+ *
+ * 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 <realm/array_blob.hpp>
+
+namespace realm {
+
+
+class ArrayBigBlobs : public Array {
+public:
+    typedef BinaryData value_type;
+
+    explicit ArrayBigBlobs(Allocator&, bool nullable) noexcept;
+
+    // Disable copying, this is not allowed.
+    ArrayBigBlobs& operator=(const ArrayBigBlobs&) = delete;
+    ArrayBigBlobs(const ArrayBigBlobs&) = delete;
+
+    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_deep(blob_ref, get_alloc()); // Deep
+    }
+    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<void>(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/iOS/Pods/Realm/include/core/realm/array_direct.hpp b/iOS/Pods/Realm/include/core/realm/array_direct.hpp
new file mode 100644 (file)
index 0000000..e337392
--- /dev/null
@@ -0,0 +1,372 @@
+/*************************************************************************
+ *
+ * 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_DIRECT_HPP
+#define REALM_ARRAY_DIRECT_HPP
+
+#include <realm/utilities.hpp>
+#include <realm/alloc.hpp>
+
+using namespace realm::util;
+
+// 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<targ, 16> arg;} \
+    else if (wid == 32) {fun<targ, 32> arg;} \
+    else if (wid == 0) {fun<targ, 0> arg;} \
+    else if (wid == 1) {fun<targ, 1> arg;} \
+    else if (wid == 2) {fun<targ, 2> arg;} \
+    else if (wid == 4) {fun<targ, 4> arg;} \
+    else if (wid == 8) {fun<targ, 8> arg;} \
+    else if (wid == 64) {fun<targ, 64> arg;} \
+    else {REALM_ASSERT_DEBUG(false); fun<targ, 0> arg;}
+
+#define REALM_TEMPEX3(fun, targ1, targ2, wid, arg) \
+    if (wid == 16) {fun<targ1, targ2, 16> arg;} \
+    else if (wid == 32) {fun<targ1, targ2, 32> arg;} \
+    else if (wid == 0) {fun<targ1, targ2, 0> arg;} \
+    else if (wid == 1) {fun<targ1, targ2, 1> arg;} \
+    else if (wid == 2) {fun<targ1, targ2, 2> arg;} \
+    else if (wid == 4) {fun<targ1, targ2, 4> arg;} \
+    else if (wid == 8) {fun<targ1, targ2, 8> arg;} \
+    else if (wid == 64) {fun<targ1, targ2, 64> arg;} \
+    else {REALM_ASSERT_DEBUG(false); fun<targ1, targ2, 0> arg;}
+
+#define REALM_TEMPEX4(fun, targ1, targ2, wid, targ3, arg) \
+    if (wid == 16) {fun<targ1, targ2, 16, targ3> arg;} \
+    else if (wid == 32) {fun<targ1, targ2, 32, targ3> arg;} \
+    else if (wid == 0) {fun<targ1, targ2, 0, targ3> arg;} \
+    else if (wid == 1) {fun<targ1, targ2, 1, targ3> arg;} \
+    else if (wid == 2) {fun<targ1, targ2, 2, targ3> arg;} \
+    else if (wid == 4) {fun<targ1, targ2, 4, targ3> arg;} \
+    else if (wid == 8) {fun<targ1, targ2, 8, targ3> arg;} \
+    else if (wid == 64) {fun<targ1, targ2, 64, targ3> arg;} \
+    else {REALM_ASSERT_DEBUG(false); fun<targ1, targ2, 0, targ3> arg;}
+
+#define REALM_TEMPEX5(fun, targ1, targ2, targ3, targ4, wid, arg) \
+    if (wid == 16) {fun<targ1, targ2, targ3, targ4, 16> arg;} \
+    else if (wid == 32) {fun<targ1, targ2, targ3, targ4, 32> arg;} \
+    else if (wid == 0) {fun<targ1, targ2, targ3, targ4, 0> arg;} \
+    else if (wid == 1) {fun<targ1, targ2, targ3, targ4, 1> arg;} \
+    else if (wid == 2) {fun<targ1, targ2, targ3, targ4, 2> arg;} \
+    else if (wid == 4) {fun<targ1, targ2, targ3, targ4, 4> arg;} \
+    else if (wid == 8) {fun<targ1, targ2, targ3, targ4, 8> arg;} \
+    else if (wid == 64) {fun<targ1, targ2, targ3, targ4, 64> arg;} \
+    else {REALM_ASSERT_DEBUG(false); fun<targ1, targ2, targ3, targ4, 0> arg;}
+// clang-format on
+
+namespace realm {
+
+// Direct access methods
+
+template <size_t width>
+void set_direct(char* data, size_t ndx, int_fast64_t value) noexcept
+{
+    if (width == 0) {
+        REALM_ASSERT_DEBUG(value == 0);
+        return;
+    }
+    else if (width == 1) {
+        REALM_ASSERT_DEBUG(0 <= value && value <= 0x01);
+        size_t byte_ndx = ndx / 8;
+        size_t bit_ndx = ndx % 8;
+        typedef unsigned char uchar;
+        uchar* p = reinterpret_cast<uchar*>(data) + byte_ndx;
+        *p = uchar((*p & ~(0x01 << bit_ndx)) | (int(value) & 0x01) << bit_ndx);
+    }
+    else if (width == 2) {
+        REALM_ASSERT_DEBUG(0 <= value && value <= 0x03);
+        size_t byte_ndx = ndx / 4;
+        size_t bit_ndx = ndx % 4 * 2;
+        typedef unsigned char uchar;
+        uchar* p = reinterpret_cast<uchar*>(data) + byte_ndx;
+        *p = uchar((*p & ~(0x03 << bit_ndx)) | (int(value) & 0x03) << bit_ndx);
+    }
+    else if (width == 4) {
+        REALM_ASSERT_DEBUG(0 <= value && value <= 0x0F);
+        size_t byte_ndx = ndx / 2;
+        size_t bit_ndx = ndx % 2 * 4;
+        typedef unsigned char uchar;
+        uchar* p = reinterpret_cast<uchar*>(data) + byte_ndx;
+        *p = uchar((*p & ~(0x0F << bit_ndx)) | (int(value) & 0x0F) << bit_ndx);
+    }
+    else if (width == 8) {
+        REALM_ASSERT_DEBUG(std::numeric_limits<int8_t>::min() <= value &&
+                           value <= std::numeric_limits<int8_t>::max());
+        *(reinterpret_cast<int8_t*>(data) + ndx) = int8_t(value);
+    }
+    else if (width == 16) {
+        REALM_ASSERT_DEBUG(std::numeric_limits<int16_t>::min() <= value &&
+                           value <= std::numeric_limits<int16_t>::max());
+        *(reinterpret_cast<int16_t*>(data) + ndx) = int16_t(value);
+    }
+    else if (width == 32) {
+        REALM_ASSERT_DEBUG(std::numeric_limits<int32_t>::min() <= value &&
+                           value <= std::numeric_limits<int32_t>::max());
+        *(reinterpret_cast<int32_t*>(data) + ndx) = int32_t(value);
+    }
+    else if (width == 64) {
+        REALM_ASSERT_DEBUG(std::numeric_limits<int64_t>::min() <= value &&
+                           value <= std::numeric_limits<int64_t>::max());
+        *(reinterpret_cast<int64_t*>(data) + ndx) = int64_t(value);
+    }
+    else {
+        REALM_ASSERT_DEBUG(false);
+    }
+}
+
+template <size_t width>
+void fill_direct(char* data, size_t begin, size_t end, int_fast64_t value) noexcept
+{
+    for (size_t i = begin; i != end; ++i)
+        set_direct<width>(data, i, value);
+}
+
+template <int w>
+int64_t get_direct(const char* data, size_t ndx) noexcept
+{
+    if (w == 0) {
+        return 0;
+    }
+    if (w == 1) {
+        size_t offset = ndx >> 3;
+        return (data[offset] >> (ndx & 7)) & 0x01;
+    }
+    if (w == 2) {
+        size_t offset = ndx >> 2;
+        return (data[offset] >> ((ndx & 3) << 1)) & 0x03;
+    }
+    if (w == 4) {
+        size_t offset = ndx >> 1;
+        return (data[offset] >> ((ndx & 1) << 2)) & 0x0F;
+    }
+    if (w == 8) {
+        return *reinterpret_cast<const signed char*>(data + ndx);
+    }
+    if (w == 16) {
+        size_t offset = ndx * 2;
+        return *reinterpret_cast<const int16_t*>(data + offset);
+    }
+    if (w == 32) {
+        size_t offset = ndx * 4;
+        return *reinterpret_cast<const int32_t*>(data + offset);
+    }
+    if (w == 64) {
+        size_t offset = ndx * 8;
+        return *reinterpret_cast<const int64_t*>(data + offset);
+    }
+    REALM_ASSERT_DEBUG(false);
+    return int64_t(-1);
+}
+
+inline int64_t get_direct(const char* data, size_t width, size_t ndx) noexcept
+{
+    REALM_TEMPEX(return get_direct, width, (data, ndx));
+}
+
+
+template <int width>
+inline std::pair<int64_t, int64_t> get_two(const char* data, size_t ndx) noexcept
+{
+    return std::make_pair(to_size_t(get_direct<width>(data, ndx + 0)), to_size_t(get_direct<width>(data, ndx + 1)));
+}
+
+inline std::pair<int64_t, int64_t> get_two(const char* data, size_t width, size_t ndx) noexcept
+{
+    REALM_TEMPEX(return get_two, width, (data, ndx));
+}
+
+
+template <int width>
+inline void get_three(const char* data, size_t ndx, ref_type& v0, ref_type& v1, ref_type& v2) noexcept
+{
+    v0 = to_ref(get_direct<width>(data, ndx + 0));
+    v1 = to_ref(get_direct<width>(data, ndx + 1));
+    v2 = to_ref(get_direct<width>(data, ndx + 2));
+}
+
+inline void get_three(const char* data, size_t width, size_t ndx, ref_type& v0, ref_type& v1, ref_type& v2) noexcept
+{
+    REALM_TEMPEX(get_three, width, (data, ndx, v0, v1, v2));
+}
+
+
+// Lower/upper bound in sorted sequence
+// ------------------------------------
+//
+//   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 semantically identical 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.
+template <int width>
+inline size_t lower_bound(const char* data, size_t size, int64_t value) noexcept
+{
+    // The binary search used here is carefully optimized. Key trick is to use a single
+    // loop controlling variable (size) instead of high/low pair, and to keep updates
+    // to size done inside the loop independent of comparisons. Further key to speed
+    // is to avoid branching inside the loop, using conditional moves instead. This
+    // provides robust performance for random searches, though predictable searches
+    // might be slightly faster if we used branches instead. The loop unrolling yields
+    // a final 5-20% speedup depending on circumstances.
+
+    size_t low = 0;
+
+    while (size >= 8) {
+        // The following code (at X, Y and Z) is 3 times manually unrolled instances of (A) below.
+        // These code blocks must be kept in sync. Meassurements indicate 3 times unrolling to give
+        // the best performance. See (A) for comments on the loop body.
+        // (X)
+        size_t half = size / 2;
+        size_t other_half = size - half;
+        size_t probe = low + half;
+        size_t other_low = low + other_half;
+        int64_t v = get_direct<width>(data, probe);
+        size = half;
+        low = (v < value) ? other_low : low;
+
+        // (Y)
+        half = size / 2;
+        other_half = size - half;
+        probe = low + half;
+        other_low = low + other_half;
+        v = get_direct<width>(data, probe);
+        size = half;
+        low = (v < value) ? other_low : low;
+
+        // (Z)
+        half = size / 2;
+        other_half = size - half;
+        probe = low + half;
+        other_low = low + other_half;
+        v = get_direct<width>(data, probe);
+        size = half;
+        low = (v < value) ? other_low : low;
+    }
+    while (size > 0) {
+        // (A)
+        // To understand the idea in this code, please note that
+        // for performance, computation of size for the next iteration
+        // MUST be INDEPENDENT of the conditional. This allows the
+        // processor to unroll the loop as fast as possible, and it
+        // minimizes the length of dependence chains leading up to branches.
+        // Making the unfolding of the loop independent of the data being
+        // searched, also minimizes the delays incurred by branch
+        // mispredictions, because they can be determined earlier
+        // and the speculation corrected earlier.
+
+        // Counterintuitive:
+        // To make size independent of data, we cannot always split the
+        // range at the theoretical optimal point. When we determine that
+        // the key is larger than the probe at some index K, and prepare
+        // to search the upper part of the range, you would normally start
+        // the search at the next index, K+1, to get the shortest range.
+        // We can only do this when splitting a range with odd number of entries.
+        // If there is an even number of entries we search from K instead of K+1.
+        // This potentially leads to redundant comparisons, but in practice we
+        // gain more performance by making the changes to size predictable.
+
+        // if size is even, half and other_half are the same.
+        // if size is odd, half is one less than other_half.
+        size_t half = size / 2;
+        size_t other_half = size - half;
+        size_t probe = low + half;
+        size_t other_low = low + other_half;
+        int64_t v = get_direct<width>(data, probe);
+        size = half;
+        // for max performance, the line below should compile into a conditional
+        // move instruction. Not all compilers do this. To maximize chance
+        // of succes, no computation should be done in the branches of the
+        // conditional.
+        low = (v < value) ? other_low : low;
+    };
+
+    return low;
+}
+
+// See lower_bound()
+template <int width>
+inline size_t upper_bound(const char* data, size_t size, int64_t value) noexcept
+{
+    size_t low = 0;
+    while (size >= 8) {
+        size_t half = size / 2;
+        size_t other_half = size - half;
+        size_t probe = low + half;
+        size_t other_low = low + other_half;
+        int64_t v = get_direct<width>(data, probe);
+        size = half;
+        low = (value >= v) ? other_low : low;
+
+        half = size / 2;
+        other_half = size - half;
+        probe = low + half;
+        other_low = low + other_half;
+        v = get_direct<width>(data, probe);
+        size = half;
+        low = (value >= v) ? other_low : low;
+
+        half = size / 2;
+        other_half = size - half;
+        probe = low + half;
+        other_low = low + other_half;
+        v = get_direct<width>(data, probe);
+        size = half;
+        low = (value >= v) ? other_low : low;
+    }
+
+    while (size > 0) {
+        size_t half = size / 2;
+        size_t other_half = size - half;
+        size_t probe = low + half;
+        size_t other_low = low + other_half;
+        int64_t v = get_direct<width>(data, probe);
+        size = half;
+        low = (value >= v) ? other_low : low;
+    };
+
+    return low;
+}
+}
+
+#endif /* ARRAY_TPL_HPP_ */
diff --git a/iOS/Pods/Realm/include/core/realm/array_integer.hpp b/iOS/Pods/Realm/include/core/realm/array_integer.hpp
new file mode 100644 (file)
index 0000000..e460c53
--- /dev/null
@@ -0,0 +1,629 @@
+/*************************************************************************
+ *
+ * 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 <realm/array.hpp>
+#include <realm/util/safe_int_ops.hpp>
+#include <realm/util/optional.hpp>
+
+namespace realm {
+
+class ArrayInteger : public Array {
+public:
+    typedef int64_t value_type;
+
+    explicit ArrayInteger(Allocator&) noexcept;
+    ~ArrayInteger() noexcept override
+    {
+    }
+
+    // Disable copying, this is not allowed.
+    ArrayInteger& operator=(const ArrayInteger&) = delete;
+    ArrayInteger(const ArrayInteger&) = delete;
+
+    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<int64_t> to_vector() const;
+
+private:
+    template <size_t w>
+    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<int64_t>;
+
+    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<int64_t>* state) const;
+
+    template <class cond, Action action, size_t bitwidth, class Callback>
+    bool find(value_type value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+              Callback callback) const;
+
+    // This is the one installed into the m_finder slots.
+    template <class cond, Action action, size_t bitwidth>
+    bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state) const;
+
+    template <class cond, Action action, class Callback>
+    bool find(value_type value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+              Callback callback) const;
+
+    // Optimized implementation for release mode
+    template <class cond, Action action, size_t bitwidth, class Callback>
+    bool find_optimized(value_type value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                        Callback callback) const;
+
+    // Called for each search result
+    template <Action action, class Callback>
+    bool find_action(size_t index, value_type value, QueryState<int64_t>* state, Callback callback) const;
+
+    template <Action action, class Callback>
+    bool find_action_pattern(size_t index, uint64_t pattern, QueryState<int64_t>* state, Callback callback) const;
+
+    // Wrappers for backwards compatibility and for simple use without
+    // setting up state initialization etc
+    template <class cond>
+    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 find_max>
+    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<int_fast64_t>(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<int64_t>(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<int64_t>(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 <bool find_max>
+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<true>(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<false>(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<int64_t>* 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 <class cond, Action action, size_t bitwidth, class Callback>
+bool ArrayIntNull::find(value_type value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                        Callback callback) const
+{
+    if (value) {
+        return Array::find<cond, action>(*value, start, end, baseindex, state, std::forward<Callback>(callback),
+                                         true /*treat as nullable array*/,
+                                         false /*search parameter given in 'value' argument*/);
+    }
+    else {
+        return Array::find<cond, action>(0 /*ignored*/, start, end, baseindex, state,
+                                         std::forward<Callback>(callback), true /*treat as nullable array*/,
+                                         true /*search for null, ignore value argument*/);
+    }
+}
+
+
+template <class cond, Action action, size_t bitwidth>
+bool ArrayIntNull::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state) const
+{
+    return Array::find<cond, action>(value, start, end, baseindex, state, true /*treat as nullable array*/,
+                                     false /*search parameter given in 'value' argument*/);
+}
+
+
+template <class cond, Action action, class Callback>
+bool ArrayIntNull::find(value_type value, size_t start, size_t end, size_t baseindex, QueryState<int64_t>* state,
+                        Callback callback) const
+{
+    if (value) {
+        return Array::find<cond, action>(*value, start, end, baseindex, state, std::forward<Callback>(callback),
+                                         true /*treat as nullable array*/,
+                                         false /*search parameter given in 'value' argument*/);
+    }
+    else {
+        return Array::find<cond, action>(0 /*ignored*/, start, end, baseindex, state,
+                                         std::forward<Callback>(callback), true /*treat as nullable array*/,
+                                         true /*search for null, ignore value argument*/);
+    }
+}
+
+
+template <Action action, class Callback>
+bool ArrayIntNull::find_action(size_t index, value_type value, QueryState<int64_t>* state, Callback callback) const
+{
+    if (value) {
+        return Array::find_action<action, Callback>(index, *value, state, callback, true /*treat as nullable array*/,
+                                                    false /*search parameter given in 'value' argument*/);
+    }
+    else {
+        return Array::find_action<action, Callback>(index, 0 /* ignored */, state, callback,
+                                                    true /*treat as nullable array*/,
+                                                    true /*search for null, ignore value argument*/);
+    }
+}
+
+
+template <Action action, class Callback>
+bool ArrayIntNull::find_action_pattern(size_t index, uint64_t pattern, QueryState<int64_t>* state,
+                                       Callback callback) const
+{
+    return Array::find_action_pattern<action, Callback>(index, pattern, state, callback,
+                                                        true /*treat as nullable array*/,
+                                                        false /*search parameter given in 'value' argument*/);
+}
+
+
+template <class cond>
+size_t ArrayIntNull::find_first(value_type value, size_t start, size_t end) const
+{
+    QueryState<int64_t> state;
+    state.init(act_ReturnFirst, nullptr, 1);
+    if (value) {
+        Array::find<cond, act_ReturnFirst>(*value, start, end, 0, &state, Array::CallbackDummy(),
+                                           true /*treat as nullable array*/,
+                                           false /*search parameter given in 'value' argument*/);
+    }
+    else {
+        Array::find<cond, act_ReturnFirst>(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<Equal>(value, begin, end);
+}
+}
+
+#endif // REALM_ARRAY_INTEGER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/array_string.hpp b/iOS/Pods/Realm/include/core/realm/array_string.hpp
new file mode 100644 (file)
index 0000000..bbf3203
--- /dev/null
@@ -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 <realm/array.hpp>
+
+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<size_t>(-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<size_t>(-1))
+        return nullable ? realm::null() : StringData("");
+
+    return StringData(data, size);
+}
+
+
+} // namespace realm
+
+#endif // REALM_ARRAY_STRING_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/array_string_long.hpp b/iOS/Pods/Realm/include/core/realm/array_string_long.hpp
new file mode 100644 (file)
index 0000000..5f8fcb8
--- /dev/null
@@ -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_ARRAY_STRING_LONG_HPP
+#define REALM_ARRAY_STRING_LONG_HPP
+
+#include <realm/array_blob.hpp>
+#include <realm/array_integer.hpp>
+
+namespace realm {
+
+
+class ArrayStringLong : public Array {
+public:
+    typedef StringData value_type;
+
+    explicit ArrayStringLong(Allocator&, bool nullable) noexcept;
+    ~ArrayStringLong() noexcept override
+    {
+    }
+
+    // Disable copying, this is not allowed.
+    ArrayStringLong& operator=(const ArrayStringLong&) = delete;
+    ArrayStringLong(const ArrayStringLong&) = delete;
+
+    /// 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/iOS/Pods/Realm/include/core/realm/binary_data.hpp b/iOS/Pods/Realm/include/core/realm/binary_data.hpp
new file mode 100644 (file)
index 0000000..a184f0c
--- /dev/null
@@ -0,0 +1,239 @@
+/*************************************************************************
+ *
+ * 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 <realm/owned_data.hpp>
+#include <realm/util/features.h>
+#include <realm/utilities.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <ostream>
+#include <string>
+
+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 <size_t N>
+    explicit BinaryData(const char (&external_data)[N])
+        : m_data(external_data)
+        , m_size(N)
+    {
+    }
+    template <class T, class A>
+    explicit BinaryData(const std::basic_string<char, T, A>&);
+
+    // BinaryData does not store data, callers must manage their own strings.
+    template <class T, class A>
+    BinaryData(const std::basic_string<char, T, A>&&) = delete;
+
+    template <class T, class A>
+    explicit operator std::basic_string<char, T, A>() 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 <class C, class T>
+    friend std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>&, 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 <class T, class A>
+inline BinaryData::BinaryData(const std::basic_string<char, T, A>& s)
+    : m_data(s.data())
+    , m_size(s.size())
+{
+}
+
+template <class T, class A>
+inline BinaryData::operator std::basic_string<char, T, A>() const
+{
+    return std::basic_string<char, T, A>(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 <class C, class T>
+inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, const BinaryData& d)
+{
+    out << "BinaryData(" << static_cast<const void*>(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/iOS/Pods/Realm/include/core/realm/bptree.hpp b/iOS/Pods/Realm/include/core/realm/bptree.hpp
new file mode 100644 (file)
index 0000000..aae64de
--- /dev/null
@@ -0,0 +1,1271 @@
+/*************************************************************************
+ *
+ * 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 <memory> // std::unique_ptr
+#include <realm/array.hpp>
+#include <realm/array_basic.hpp>
+#include <realm/column_type_traits.hpp>
+#include <realm/impl/destroy_guard.hpp>
+#include <realm/impl/output_stream.hpp>
+
+namespace realm {
+
+/// Specialize BpTree to implement column types.
+template <class T>
+class BpTree;
+
+class ArrayInteger;
+class ArrayIntNull;
+
+class BpTreeNode : public Array {
+public:
+    using Array::Array;
+    /// 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<MemRef, size_t> 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(BpTreeNode* root, size_t elem_ndx, EraseHandler&);
+
+    template <class TreeTraits>
+    struct TreeInsert : TreeInsertBase {
+        typename TreeTraits::value_type m_value;
+        bool m_nullable;
+    };
+
+    /// Same as bptree_insert() but insert after the last element.
+    template <class TreeTraits>
+    ref_type bptree_append(TreeInsert<TreeTraits>& 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 <class TreeTraits>
+    ref_type bptree_insert(size_t elem_ndx, TreeInsert<TreeTraits>& state);
+
+protected:
+    /// 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&);
+};
+
+class BpTreeBase {
+public:
+    struct unattached_tag {
+    };
+
+    // Disable copying, this is not allowed.
+    BpTreeBase& operator=(const BpTreeBase&) = delete;
+    BpTreeBase(const BpTreeBase&) = delete;
+
+    // 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;
+    BpTreeNode& root_as_node();
+    const BpTreeNode& root_as_node() const;
+    void introduce_new_root(ref_type new_sibling_ref, TreeInsertBase& state, bool is_append);
+    void replace_root(std::unique_ptr<Array> leaf);
+
+protected:
+    explicit BpTreeBase(std::unique_ptr<Array> root);
+    explicit BpTreeBase(BpTreeBase&&) = default;
+    BpTreeBase& operator=(BpTreeBase&&) = default;
+    std::unique_ptr<Array> 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 BpTreeNode& 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 T>
+class BpTree : public BpTreeBase {
+public:
+    using value_type = T;
+    using LeafType = typename ColumnTypeTraits<T>::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);
+    REALM_DEPRECATED("Initialize with MemRef instead")
+    explicit BpTree(std::unique_ptr<Array> init_root)
+        : BpTreeBase(std::move(init_root))
+    {
+    }
+    explicit BpTree(Allocator& alloc, MemRef mem)
+        : BpTreeBase(std::unique_ptr<Array>(new LeafType(alloc)))
+    {
+        init_from_mem(alloc, mem);
+    }
+    BpTree(BpTree&&) = default;
+    BpTree& operator=(BpTree&&) = default;
+
+    // Disable copying, this is not allowed.
+    BpTree& operator=(const BpTree&) = delete;
+    BpTree(const BpTree&) = delete;
+
+    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 leaf_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(BpTreeNode::UpdateHandler&);
+    void update_elem(size_t, BpTreeNode::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<Array> create_root_from_ref(Allocator& alloc, ref_type ref);
+    std::unique_ptr<Array> 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 <class TreeTraits>
+    void bptree_insert(size_t row_ndx, BpTreeNode::TreeInsert<TreeTraits>& state, size_t num_rows);
+};
+
+
+class BpTreeNode::NodeInfo {
+public:
+    MemRef m_mem;
+    Array* m_parent;
+    size_t m_ndx_in_parent;
+    size_t m_offset, m_size;
+};
+
+class BpTreeNode::VisitHandler {
+public:
+    virtual bool visit(const NodeInfo& leaf_info) = 0;
+    virtual ~VisitHandler() noexcept
+    {
+    }
+};
+
+
+class BpTreeNode::UpdateHandler {
+public:
+    virtual void update(MemRef, ArrayParent*, size_t leaf_ndx_in_parent, size_t elem_ndx_in_leaf) = 0;
+    virtual ~UpdateHandler() noexcept
+    {
+    }
+};
+
+
+class BpTreeNode::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:
+
+inline BpTreeBase::BpTreeBase(std::unique_ptr<Array> 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 BpTreeNode& BpTreeBase::root_as_node()
+{
+    REALM_ASSERT_DEBUG(!root_is_leaf());
+    REALM_ASSERT_DEBUG(dynamic_cast<BpTreeNode*>(m_root.get()) != nullptr);
+    return static_cast<BpTreeNode&>(root());
+}
+
+inline const BpTreeNode& BpTreeBase::root_as_node() const
+{
+    Array* arr = m_root.get();
+    REALM_ASSERT_DEBUG(!root_is_leaf());
+    REALM_ASSERT_DEBUG(dynamic_cast<const BpTreeNode*>(arr) != nullptr);
+    return static_cast<const BpTreeNode&>(*arr);
+}
+
+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;
+}
+
+inline size_t BpTreeNode::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 BpTreeNode::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 BpTreeNode::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 <class TreeTraits>
+ref_type BpTreeNode::bptree_append(TreeInsert<TreeTraits>& 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<char*>(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 {
+        BpTreeNode 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 <class TreeTraits>
+ref_type BpTreeNode::bptree_insert(size_t elem_ndx, TreeInsert<TreeTraits>& 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<char*>(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 {
+        BpTreeNode 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
+}
+
+template <class T>
+BpTree<T>::BpTree()
+    : BpTree(Allocator::get_default())
+{
+}
+
+template <class T>
+BpTree<T>::BpTree(Allocator& alloc)
+    : BpTreeBase(std::unique_ptr<Array>(new LeafType(alloc)))
+{
+}
+
+template <class T>
+BpTree<T>::BpTree(BpTreeBase::unattached_tag)
+    : BpTreeBase(nullptr)
+{
+}
+
+template <class T>
+std::unique_ptr<Array> BpTree<T>::create_root_from_mem(Allocator& alloc, MemRef mem)
+{
+    const char* header = mem.get_addr();
+    std::unique_ptr<Array> 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<LeafType&>(*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 BpTreeNode{alloc});
+        new_root->init_from_mem(mem);
+    }
+    else {
+        std::unique_ptr<LeafType> leaf{new LeafType{alloc}};
+        leaf->init_from_mem(mem);
+        new_root = std::move(leaf);
+    }
+    return new_root;
+}
+
+template <class T>
+std::unique_ptr<Array> BpTree<T>::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 <class T>
+void BpTree<T>::init_from_ref(Allocator& alloc, ref_type ref)
+{
+    auto new_root = create_root_from_ref(alloc, ref);
+    replace_root(std::move(new_root));
+}
+
+template <class T>
+void BpTree<T>::init_from_mem(Allocator& alloc, MemRef mem)
+{
+    auto new_root = create_root_from_mem(alloc, mem);
+    replace_root(std::move(new_root));
+}
+
+template <class T>
+void BpTree<T>::init_from_parent()
+{
+    ref_type ref = root().get_ref_from_parent();
+    if (ref) {
+        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);
+    }
+    else {
+        m_root->detach();
+    }
+}
+
+template <class T>
+typename BpTree<T>::LeafType& BpTree<T>::root_as_leaf()
+{
+    REALM_ASSERT_DEBUG(root_is_leaf());
+    REALM_ASSERT_DEBUG(dynamic_cast<LeafType*>(m_root.get()) != nullptr);
+    return static_cast<LeafType&>(root());
+}
+
+template <class T>
+const typename BpTree<T>::LeafType& BpTree<T>::root_as_leaf() const
+{
+    REALM_ASSERT_DEBUG(root_is_leaf());
+    REALM_ASSERT_DEBUG(dynamic_cast<const LeafType*>(m_root.get()) != nullptr);
+    return static_cast<const LeafType&>(root());
+}
+
+template <class T>
+size_t BpTree<T>::size() const noexcept
+{
+    if (root_is_leaf()) {
+        return root_as_leaf().size();
+    }
+    return root_as_node().get_bptree_size();
+}
+
+template <class T>
+T BpTree<T>::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 <class Leaf>
+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<ArrayInteger> {
+    static bool is_null(const ArrayInteger&, size_t)
+    {
+        return false;
+    }
+    static void set_null(ArrayInteger&, size_t)
+    {
+        REALM_ASSERT_RELEASE(false);
+    }
+};
+}
+
+template <class T>
+bool BpTree<T>::is_null(size_t ndx) const noexcept
+{
+    if (root_is_leaf()) {
+        return _impl::NullableOrNothing<LeafType>::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<LeafType>::is_null(*leaf, ndx_in_leaf);
+}
+
+template <class T>
+T BpTree<T>::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<MemRef, size_t> p = root_as_node().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 <class T>
+template <class TreeTraits>
+void BpTree<T>::bptree_insert(size_t row_ndx, BpTreeNode::TreeInsert<TreeTraits>& 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_as_node().bptree_append(state); // Throws
+            }
+            else {
+                new_sibling_ref = root_as_node().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 <class T>
+struct BpTree<T>::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, BpTreeNode::TreeInsert<LeafValueInserter>& 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 <class T>
+struct BpTree<T>::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, BpTreeNode::TreeInsert<LeafNullInserter>& 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 <class T>
+void BpTree<T>::insert(size_t row_ndx, T value, size_t num_rows)
+{
+    REALM_ASSERT_DEBUG(row_ndx == npos || row_ndx < size());
+    BpTreeNode::TreeInsert<LeafValueInserter> inserter;
+    inserter.m_value = std::move(value);
+    inserter.m_nullable = std::is_same<T, util::Optional<int64_t>>::value; // FIXME
+    bptree_insert(row_ndx, inserter, num_rows);                            // Throws
+}
+
+template <class T>
+struct BpTree<T>::UpdateHandler : BpTreeNode::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 <class T>
+struct BpTree<T>::SetNullHandler : BpTreeNode::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<LeafType>::set_null(m_leaf, elem_ndx_in_leaf); // Throws
+    }
+};
+
+template <class T>
+void BpTree<T>::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));
+        static_cast<BpTreeNode*>(m_root.get())->update_bptree_elem(ndx, set_leaf_elem); // Throws
+    }
+}
+
+template <class T>
+void BpTree<T>::set_null(size_t ndx)
+{
+    if (root_is_leaf()) {
+        _impl::NullableOrNothing<LeafType>::set_null(root_as_leaf(), ndx);
+    }
+    else {
+        SetNullHandler set_leaf_elem(*this);
+        static_cast<BpTreeNode*>(m_root.get())->update_bptree_elem(ndx, set_leaf_elem); // Throws;
+    }
+}
+
+template <class T>
+struct BpTree<T>::EraseHandler : BpTreeNode::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<LeafType> 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<LeafType> 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 <class T>
+void BpTree<T>::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);
+        BpTreeNode::erase_bptree_elem(&root_as_node(), ndx_2, handler);
+    }
+}
+
+template <class T>
+void BpTree<T>::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 <class T>
+void BpTree<T>::clear()
+{
+    if (root_is_leaf()) {
+        if (std::is_same<T, int64_t>::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<LeafType> new_root(new LeafType(alloc));
+        new_root->create();
+        replace_root(std::move(new_root));
+    }
+}
+
+
+template <class T>
+struct BpTree<T>::AdjustHandler : BpTreeNode::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 <class T>
+void BpTree<T>::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));
+        root_as_node().update_bptree_leaves(adjust_leaf_elem); // Throws
+    }
+}
+
+template <class T>
+void BpTree<T>::adjust(size_t ndx, T diff)
+{
+    static_assert(std::is_arithmetic<T>::value, "adjust is undefined for non-arithmetic trees");
+    set(ndx, get(ndx) + diff);
+}
+
+template <class T>
+struct BpTree<T>::AdjustGEHandler : BpTreeNode::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 <class T>
+void BpTree<T>::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));
+        root_as_node().update_bptree_leaves(adjust_leaf_elem); // Throws
+    }
+}
+
+template <class T>
+struct BpTree<T>::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 <class T>
+ref_type BpTree<T>::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_as_node(), slice_offset, slice_size, table_size, handler, out); // Throws
+    }
+    return ref;
+}
+
+template <class T>
+MemRef BpTree<T>::create_leaf(Array::Type leaf_type, size_t size, T value, Allocator& alloc)
+{
+    bool context_flag = false;
+    MemRef mem = LeafType::create_array(leaf_type, context_flag, size, std::move(value), alloc);
+    return mem;
+}
+
+template <class T>
+void BpTree<T>::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<MemRef, size_t> p = root_as_node().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 <class T>
+size_t BpTree<T>::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 <class T>
+void BpTree<T>::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 <class T>
+size_t BpTree<T>::verify_leaf(MemRef mem, Allocator& alloc)
+{
+    LeafType leaf(alloc);
+    leaf.init_from_mem(mem);
+    leaf.verify();
+    return leaf.size();
+}
+
+template <class T>
+void BpTree<T>::verify() const
+{
+    if (root_is_leaf()) {
+        root_as_leaf().verify();
+    }
+    else {
+        root().verify_bptree(&verify_leaf);
+    }
+}
+#endif // REALM_DEBUG
+
+template <class T>
+void BpTree<T>::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/iOS/Pods/Realm/include/core/realm/chunked_binary.hpp b/iOS/Pods/Realm/include/core/realm/chunked_binary.hpp
new file mode 100644 (file)
index 0000000..c893b69
--- /dev/null
@@ -0,0 +1,125 @@
+/*************************************************************************
+ *
+ * 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_NOINST_CHUNKED_BINARY_HPP
+#define REALM_NOINST_CHUNKED_BINARY_HPP
+
+#include <realm/binary_data.hpp>
+#include <realm/column_binary.hpp>
+#include <realm/table.hpp>
+
+#include <realm/util/buffer_stream.hpp>
+#include <realm/impl/input_stream.hpp>
+
+
+namespace realm {
+
+/// ChunkedBinaryData manages a vector of BinaryData. It is used to facilitate
+/// extracting large binaries from binary columns and tables.
+class ChunkedBinaryData {
+public:
+
+    ChunkedBinaryData();
+    ChunkedBinaryData(const BinaryData& bd);
+    ChunkedBinaryData(const BinaryIterator& bd);
+    ChunkedBinaryData(const BinaryColumn& col, size_t index);
+
+    /// size() returns the number of bytes in the chunked binary.
+    /// FIXME: This operation is O(n).
+    size_t size() const;
+
+    /// is_null returns true if the chunked binary has zero chunks or if
+    /// the first chunk points to the nullptr.
+    bool is_null() const;
+
+    /// FIXME: O(n)
+    char operator[](size_t index) const;
+
+    std::string hex_dump(const char* separator = " ", int min_digits = -1) const;
+
+    void write_to(util::ResettableExpandableBufferOutputStream& out) const;
+
+    /// copy_to() copies the chunked binary data to \a buffer of size
+    /// \a buffer_size starting at \a offset in the ChunkedBinary.
+    /// copy_to() copies until the end of \a buffer or the end of
+    /// the ChunkedBinary whichever comes first.
+    /// copy_to() returns the number of copied bytes.
+    size_t copy_to(char* buffer, size_t buffer_size, size_t offset) const;
+
+    /// copy_to() allocates a buffer of size() in \a dest and
+    /// copies the chunked binary data to \a dest.
+    size_t copy_to(std::unique_ptr<char[]>& dest) const;
+
+    /// get_first_chunk() is used in situations
+    /// where it is known that there is exactly one
+    /// chunk. This is the case if the ChunkedBinary
+    /// has been constructed from BinaryData.
+    BinaryData get_first_chunk() const;
+
+private:
+    BinaryIterator m_begin;
+    friend class ChunkedBinaryInputStream;
+};
+
+// FIXME: When ChunkedBinaryData is moved into Core, this should be moved as well.
+class ChunkedBinaryInputStream : public _impl::NoCopyInputStream {
+public:
+    explicit ChunkedBinaryInputStream(const ChunkedBinaryData& chunks)
+        : m_it(chunks.m_begin)
+    {
+    }
+
+    bool next_block(const char*& begin, const char*& end) override
+    {
+        BinaryData block = m_it.get_next();
+        begin = block.data();
+        end = begin + block.size();
+        return begin != end;
+    }
+
+private:
+    BinaryIterator m_it;
+};
+
+
+/// Implementation:
+
+
+inline ChunkedBinaryData::ChunkedBinaryData()
+{
+}
+
+inline ChunkedBinaryData::ChunkedBinaryData(const BinaryData& bd) : m_begin{bd}
+{
+}
+
+inline ChunkedBinaryData::ChunkedBinaryData(const BinaryIterator& bd) : m_begin{bd}
+{
+}
+
+inline ChunkedBinaryData::ChunkedBinaryData(const BinaryColumn& col, size_t index)
+    : m_begin{&col, index}
+{
+}
+
+
+} // namespace realm
+
+#endif // REALM_NOINST_CHUNKED_BINARY_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/column.hpp b/iOS/Pods/Realm/include/core/realm/column.hpp
new file mode 100644 (file)
index 0000000..928fd93
--- /dev/null
@@ -0,0 +1,1854 @@
+/*************************************************************************
+ *
+ * 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 <cstdint> // unint8_t etc
+#include <cstdlib> // size_t
+#include <vector>
+#include <memory>
+
+#include <realm/array_integer.hpp>
+#include <realm/column_type.hpp>
+#include <realm/column_fwd.hpp>
+#include <realm/spec.hpp>
+#include <realm/impl/output_stream.hpp>
+#include <realm/query_conditions.hpp>
+#include <realm/bptree.hpp>
+#include <realm/index_string.hpp>
+#include <realm/impl/destroy_guard.hpp>
+#include <realm/exceptions.hpp>
+#include <realm/table_ref.hpp>
+
+namespace realm {
+
+
+// Pre-definitions
+struct CascadeState;
+class StringIndex;
+
+template <class T>
+struct ImplicitNull;
+
+template <class T>
+struct ImplicitNull<util::Optional<T>> {
+    static constexpr bool value = true;
+};
+
+template <>
+struct ImplicitNull<int64_t> {
+    static constexpr bool value = false;
+};
+
+template <>
+struct ImplicitNull<float> {
+    static constexpr bool value = true;
+};
+
+template <>
+struct ImplicitNull<double> {
+    static constexpr bool value = true;
+};
+
+// FIXME: Add specialization for ImplicitNull for float, double, StringData, BinaryData.
+
+template <class T, class R, Action action, class Condition, class ColType>
+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 <typename ColumnDataType>
+class ColumnRandIterator : public std::iterator<std::random_access_iterator_tag, ColumnDataType, ptrdiff_t, size_t> {
+public:
+    ColumnRandIterator(const Column<ColumnDataType>* src_col, size_t ndx = 0);
+    bool operator==(const ColumnRandIterator<ColumnDataType>& rhs) const;
+    bool operator!=(const ColumnRandIterator<ColumnDataType>& rhs) const;
+    bool operator<(const ColumnRandIterator<ColumnDataType>& rhs) const;
+    bool operator>(const ColumnRandIterator<ColumnDataType>& rhs) const;
+    bool operator<=(const ColumnRandIterator<ColumnDataType>& rhs) const;
+    bool operator>=(const ColumnRandIterator<ColumnDataType>& rhs) const;
+    ColumnRandIterator<ColumnDataType>& operator+=(ptrdiff_t movement);
+    ColumnRandIterator<ColumnDataType>& operator-=(ptrdiff_t movement);
+    ColumnRandIterator<ColumnDataType>& operator++();
+    ColumnRandIterator<ColumnDataType>& operator--();
+    ColumnRandIterator<ColumnDataType> operator++(int);
+    ColumnRandIterator<ColumnDataType> operator--(int);
+    ColumnRandIterator<ColumnDataType> operator+(ptrdiff_t movement);
+    ColumnRandIterator<ColumnDataType> operator-(ptrdiff_t movement);
+    ptrdiff_t operator-(const ColumnRandIterator<ColumnDataType>& right) const;
+    const ColumnDataType operator*() const;
+    const ColumnDataType operator->() const;
+    const ColumnDataType operator[](ptrdiff_t offset) const;
+    size_t get_col_ndx() const;
+
+protected:
+    size_t m_col_ndx;
+    const Column<ColumnDataType>* m_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
+    {
+    }
+
+    // Disable copying, this is not supported.
+    ColumnBase& operator=(const ColumnBase&) = delete;
+    ColumnBase(const ColumnBase&) = delete;
+
+    // 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);
+
+    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<Array> 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 TableRef 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_move_row(size_t from_ndx, size_t to_ndx) 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 <class L, class T>
+    size_t lower_bound(const L& list, T value) const noexcept;
+
+    template <class L, class T>
+    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 <class Column>
+    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<Array> m_array;
+
+    void replace_root_array(std::unique_ptr<Array> 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, 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) final;
+    StringIndex* create_search_index() override = 0;
+
+protected:
+    using ColumnBase::ColumnBase;
+    ColumnBaseWithIndex(ColumnBaseWithIndex&&) = default;
+    std::unique_ptr<StringIndex> 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 T>
+class Column : public ColumnBaseWithIndex {
+public:
+    using value_type = T;
+    using LeafInfo = typename BpTree<T>::LeafInfo;
+    using LeafType = typename BpTree<T>::LeafType;
+
+    static constexpr bool nullable = ImplicitNull<T>::value;
+
+    struct unattached_root_tag {
+    };
+
+    explicit Column() noexcept
+        : ColumnBaseWithIndex(npos)
+        , m_tree(Allocator::get_default())
+    {
+    }
+    REALM_DEPRECATED("Initialize with ref instead") explicit Column(std::unique_ptr<Array> 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<T>
+    /// 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 <class U>
+    void adjust(size_t ndx, U diff);
+
+    template <class U>
+    void adjust(U diff);
+
+    template <class U>
+    void adjust_ge(T limit, U diff);
+
+    size_t count(T target) const;
+
+    typename ColumnTypeTraits<T>::sum_type sum(size_t start = 0, size_t end = npos, size_t limit = npos,
+                                               size_t* return_ndx = nullptr) const;
+
+    typename ColumnTypeTraits<T>::minmax_type maximum(size_t start = 0, size_t end = npos, size_t limit = npos,
+                                                      size_t* return_ndx = nullptr) const;
+
+    typename ColumnTypeTraits<T>::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<int64_t>& 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<T, float, double>::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<T>;
+
+    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<Array> 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
+    std::pair<ref_type, size_t> get_to_dot_parent(size_t ndx_in_parent) const;
+
+private:
+    class EraseLeafElem;
+    class CreateHandler;
+    class SliceHandler;
+
+    friend class Array;
+    friend class ColumnBase;
+    friend class StringIndex;
+
+    BpTree<T> 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)
+{
+}
+
+inline void ColumnBase::discard_child_accessors() noexcept
+{
+    do_discard_child_accessors();
+}
+
+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_move_row(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 <class Column>
+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 <class T>
+void Column<T>::set_without_updating_index(size_t ndx, T value)
+{
+    m_tree.set(ndx, std::move(value));
+}
+
+template <class T>
+void Column<T>::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 <class T>
+void Column<T>::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 <class T>
+void Column<T>::set_uint(size_t ndx, uint64_t value)
+{
+    set(ndx, util::from_twos_compl<int_fast64_t>(value));
+}
+
+template <class T>
+void Column<T>::set_as_ref(size_t ndx, ref_type ref)
+{
+    set(ndx, from_ref(ref));
+}
+
+template <class T>
+template <class U>
+void Column<T>::adjust(size_t ndx, U diff)
+{
+    REALM_ASSERT_3(ndx, <, size());
+    m_tree.adjust(ndx, diff);
+}
+
+template <class T>
+template <class U>
+void Column<T>::adjust(U diff)
+{
+    m_tree.adjust(diff);
+}
+
+template <class T>
+template <class U>
+void Column<T>::adjust_ge(T limit, U diff)
+{
+    m_tree.adjust_ge(limit, diff);
+}
+
+template <class T>
+size_t Column<T>::count(T target) const
+{
+    if (has_search_index()) {
+        return m_search_index->count(target);
+    }
+    return to_size_t(aggregate<T, int64_t, act_Count, Equal>(*this, target, 0, size(), npos, nullptr));
+}
+
+template <class T>
+typename ColumnTypeTraits<T>::sum_type Column<T>::sum(size_t start, size_t end, size_t limit,
+                                                      size_t* return_ndx) const
+{
+    using sum_type = typename ColumnTypeTraits<T>::sum_type;
+    if (nullable)
+        return aggregate<T, sum_type, act_Sum, NotNull>(*this, 0, start, end, limit, return_ndx);
+    else
+        return aggregate<T, sum_type, act_Sum, None>(*this, 0, start, end, limit, return_ndx);
+}
+
+template <class T>
+double Column<T>::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<T, int64_t, act_Count, NotNull>(*this, 0, start, end, limit, nullptr));
+    if (return_ndx)
+        *return_ndx = cnt;
+    double avg = double(s) / (cnt == 0 ? 1 : cnt);
+    return avg;
+}
+
+template <class T>
+typename ColumnTypeTraits<T>::minmax_type Column<T>::minimum(size_t start, size_t end, size_t limit,
+                                                             size_t* return_ndx) const
+{
+    using R = typename ColumnTypeTraits<T>::minmax_type;
+    return aggregate<T, R, act_Min, NotNull>(*this, 0, start, end, limit, return_ndx);
+}
+
+template <class T>
+typename ColumnTypeTraits<T>::minmax_type Column<T>::maximum(size_t start, size_t end, size_t limit,
+                                                             size_t* return_ndx) const
+{
+    using R = typename ColumnTypeTraits<T>::minmax_type;
+    return aggregate<T, R, act_Max, NotNull>(*this, 0, start, end, limit, return_ndx);
+}
+
+template <class T>
+void Column<T>::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 <class T>
+StringData Column<T>::get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept
+{
+    T x = get(ndx);
+    return to_str(x, buffer);
+}
+
+template <class T>
+void Column<T>::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 <class T>
+StringIndex* Column<T>::create_search_index()
+{
+    if (realm::is_any<T, float, double>::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 <class T>
+size_t Column<T>::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 <class T>
+void Column<T>::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 BpTreeNode::get_bptree_size_from_header(root_header);
+}
+
+template <class L, class T>
+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 <class L, class T>
+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 <class T>
+Column<T>::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 <class T>
+Column<T>::Column(unattached_root_tag, Allocator& alloc)
+    : ColumnBaseWithIndex(npos)
+    , m_tree(alloc)
+{
+}
+
+template <class T>
+Column<T>::Column(std::unique_ptr<Array> root) noexcept
+    : m_tree(std::move(root))
+{
+}
+
+template <class T>
+Column<T>::~Column() noexcept
+{
+}
+
+template <class T>
+void Column<T>::init_from_parent()
+{
+    m_tree.init_from_parent();
+}
+
+template <class T>
+void Column<T>::init_from_ref(Allocator& alloc, ref_type ref)
+{
+    m_tree.init_from_ref(alloc, ref);
+}
+
+template <class T>
+void Column<T>::init_from_mem(Allocator& alloc, MemRef mem)
+{
+    m_tree.init_from_mem(alloc, mem);
+}
+
+template <class T>
+void Column<T>::destroy() noexcept
+{
+    ColumnBaseWithIndex::destroy();
+    m_tree.destroy();
+}
+
+template <class T>
+void Column<T>::move_assign(Column<T>& col)
+{
+    ColumnBaseWithIndex::move_assign(col);
+    m_tree = std::move(col.m_tree);
+}
+
+template <class T>
+Allocator& Column<T>::get_alloc() const noexcept
+{
+    return m_tree.get_alloc();
+}
+
+template <class T>
+void Column<T>::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept
+{
+    m_tree.set_parent(parent, ndx_in_parent);
+}
+
+template <class T>
+size_t Column<T>::get_ndx_in_parent() const noexcept
+{
+    return m_tree.get_ndx_in_parent();
+}
+
+template <class T>
+void Column<T>::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 <class T>
+void Column<T>::detach() noexcept
+{
+    m_tree.detach();
+}
+
+template <class T>
+bool Column<T>::is_attached() const noexcept
+{
+    return m_tree.is_attached();
+}
+
+template <class T>
+ref_type Column<T>::get_ref() const noexcept
+{
+    return get_root_array()->get_ref();
+}
+
+template <class T>
+MemRef Column<T>::get_mem() const noexcept
+{
+    return get_root_array()->get_mem();
+}
+
+template <class T>
+void Column<T>::update_from_parent(size_t old_baseline) noexcept
+{
+    ColumnBaseWithIndex::update_from_parent(old_baseline);
+    m_tree.update_from_parent(old_baseline);
+}
+
+template <class T>
+MemRef Column<T>::clone_deep(Allocator& alloc) const
+{
+    return m_tree.clone_deep(alloc);
+}
+
+template <class T>
+size_t Column<T>::size() const noexcept
+{
+    return m_tree.size();
+}
+
+template <class T>
+bool Column<T>::is_nullable() const noexcept
+{
+    return nullable;
+}
+
+template <class T>
+T Column<T>::get(size_t ndx) const noexcept
+{
+    return m_tree.get(ndx);
+}
+
+template <class T>
+bool Column<T>::is_null(size_t ndx) const noexcept
+{
+    return nullable && m_tree.is_null(ndx);
+}
+
+template <class T>
+T Column<T>::back() const noexcept
+{
+    return m_tree.back();
+}
+
+template <class T>
+ref_type Column<T>::get_as_ref(size_t ndx) const noexcept
+{
+    return to_ref(get(ndx));
+}
+
+template <class T>
+uint64_t Column<T>::get_uint(size_t ndx) const noexcept
+{
+    static_assert(std::is_convertible<T, uint64_t>::value, "T is not convertible to uint.");
+    return static_cast<uint64_t>(get(ndx));
+}
+
+template <class T>
+void Column<T>::add(T value)
+{
+    insert(npos, std::move(value));
+}
+
+template <class T>
+void Column<T>::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 <class T>
+void Column<T>::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 <class T>
+void Column<T>::erase_without_updating_index(size_t row_ndx, bool is_last)
+{
+    m_tree.erase(row_ndx, is_last);
+}
+
+template <class T>
+void Column<T>::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 <class T>
+void Column<T>::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 <class T>
+void Column<T>::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 <class T>
+void Column<T>::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<StringData>(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 <class T>
+void Column<T>::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<StringData>(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<StringData>(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 <class T>
+void Column<T>::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 <class T>
+void Column<T>::clear_without_updating_index()
+{
+    m_tree.clear(); // Throws
+}
+
+template <class T>
+void Column<T>::clear()
+{
+    if (has_search_index()) {
+        m_search_index->clear();
+    }
+    clear_without_updating_index();
+}
+
+template <class T, class Enable = void>
+struct NullOrDefaultValue;
+template <class T>
+struct NullOrDefaultValue<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
+    static T null_or_default_value(bool is_null)
+    {
+        if (is_null) {
+            return null::get_null_float<T>();
+        }
+        else {
+            return T{};
+        }
+    }
+};
+template <class T>
+struct NullOrDefaultValue<util::Optional<T>, void> {
+    static util::Optional<T> null_or_default_value(bool is_null)
+    {
+        if (is_null) {
+            return util::none;
+        }
+        else {
+            return util::some<T>(T{});
+        }
+    }
+};
+template <class T>
+struct NullOrDefaultValue<T, typename std::enable_if<!ImplicitNull<T>::value>::type> {
+    static T null_or_default_value(bool is_null)
+    {
+        REALM_ASSERT(!is_null);
+        return T{};
+    }
+};
+
+// Implementing pure virtual method of ColumnBase.
+template <class T>
+void Column<T>::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<T>::null_or_default_value(insert_nulls);
+    insert(row_ndx_2, value, num_rows_to_insert); // Throws
+}
+
+// Implementing pure virtual method of ColumnBase.
+template <class T>
+void Column<T>::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 <class T>
+void Column<T>::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 <class T>
+void Column<T>::clear(size_t, bool)
+{
+    clear(); // Throws
+}
+
+
+template <class T>
+size_t Column<T>::lower_bound(T value) const noexcept
+{
+    if (root_is_leaf()) {
+        auto root = static_cast<const LeafType*>(get_root_array());
+        return root->lower_bound(value);
+    }
+    return ColumnBase::lower_bound(*this, value);
+}
+
+template <class T>
+size_t Column<T>::upper_bound(T value) const noexcept
+{
+    if (root_is_leaf()) {
+        auto root = static_cast<const LeafType*>(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 <class T>
+size_t Column<T>::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 <class T>
+bool Column<T>::compare(const Column<T>& 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 <class T>
+int Column<T>::compare_values(size_t row1, size_t row2) const noexcept
+{
+    return ColumnBase::compare_values(this, row1, row2);
+}
+
+template <class T>
+class Column<T>::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<T>::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 <class T>
+ref_type Column<T>::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 <class T>
+ref_type Column<T>::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 <class T>
+void Column<T>::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 <class T>
+void Column<T>::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<T>(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 <class T>
+void Column<T>::verify() const
+{
+#ifdef REALM_DEBUG
+    m_tree.verify();
+#endif
+}
+
+// LCOV_EXCL_START
+
+template <class T>
+void Column<T>::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<void>(out);
+    static_cast<void>(title);
+#endif
+}
+
+template <class T>
+void Column<T>::leaf_to_dot(MemRef leaf_mem, ArrayParent* parent, size_t ndx_in_parent, std::ostream& out) const
+{
+#ifdef REALM_DEBUG
+    BpTree<T>::leaf_to_dot(leaf_mem, parent, ndx_in_parent, out, get_alloc());
+#else
+    static_cast<void>(leaf_mem);
+    static_cast<void>(parent);
+    static_cast<void>(ndx_in_parent);
+    static_cast<void>(out);
+#endif
+}
+
+template <class T>
+void Column<T>::do_dump_node_structure(std::ostream& out, int level) const
+{
+#ifdef REALM_DEBUG
+    dump_node_structure(*get_root_array(), out, level);
+#else
+    static_cast<void>(out);
+    static_cast<void>(level);
+#endif
+}
+
+#ifdef REALM_DEBUG
+
+template <class T>
+void Column<T>::tree_to_dot(std::ostream& out) const
+{
+    ColumnBase::bptree_to_dot(get_root_array(), out);
+}
+
+
+template <class T>
+MemStats Column<T>::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 <class T>
+void Column<T>::dump_node_structure(const Array& root, std::ostream& out, int level)
+{
+    root.dump_bptree_structure(out, level, &_impl::leaf_dumper);
+}
+
+#endif
+
+template <class T>
+std::pair<ref_type, size_t> Column<T>::get_to_dot_parent(size_t ndx_in_parent) const
+{
+    auto root = get_root_array();
+    if (root->is_inner_bptree_node()) {
+        std::pair<MemRef, size_t> p = static_cast<const BpTreeNode*>(root)->get_bptree_leaf(ndx_in_parent);
+        return std::make_pair(p.first.get_ref(), p.second);
+    }
+    else {
+        return std::make_pair(root->get_ref(), ndx_in_parent);
+    }
+}
+
+// LCOV_EXCL_STOP ignore debug functions
+
+
+template <class ColumnDataType>
+ColumnRandIterator<ColumnDataType>::ColumnRandIterator(const Column<ColumnDataType>* src_col, size_t ndx)
+    : m_col_ndx(ndx)
+    , m_col(src_col)
+{
+}
+
+template <class ColumnDataType>
+bool ColumnRandIterator<ColumnDataType>::operator==(const ColumnRandIterator<ColumnDataType>& rhs) const
+{
+    return (m_col_ndx == rhs.m_col_ndx);
+}
+
+template <class ColumnDataType>
+bool ColumnRandIterator<ColumnDataType>::operator!=(const ColumnRandIterator<ColumnDataType>& rhs) const
+{
+    return !(*this == rhs);
+}
+
+template <class ColumnDataType>
+bool ColumnRandIterator<ColumnDataType>::operator<(const ColumnRandIterator<ColumnDataType>& rhs) const
+{
+    return m_col_ndx < rhs.m_col_ndx;
+}
+
+template <class ColumnDataType>
+bool ColumnRandIterator<ColumnDataType>::operator>(const ColumnRandIterator<ColumnDataType>& rhs) const
+{
+    return rhs < *this;
+}
+
+template <class ColumnDataType>
+bool ColumnRandIterator<ColumnDataType>::operator<=(const ColumnRandIterator<ColumnDataType>& rhs) const
+{
+    return !(rhs < *this);
+}
+
+template <class ColumnDataType>
+bool ColumnRandIterator<ColumnDataType>::operator>=(const ColumnRandIterator<ColumnDataType>& rhs) const
+{
+    return !(*this < rhs);
+}
+
+template <class ColumnDataType>
+ColumnRandIterator<ColumnDataType>& ColumnRandIterator<ColumnDataType>::operator+=(ptrdiff_t movement)
+{
+    m_col_ndx += movement;
+    return (*this);
+}
+
+template <class ColumnDataType>
+ColumnRandIterator<ColumnDataType>& ColumnRandIterator<ColumnDataType>::operator-=(ptrdiff_t movement)
+{
+    m_col_ndx -= movement;
+    return (*this);
+}
+
+template <class ColumnDataType>
+ColumnRandIterator<ColumnDataType>& ColumnRandIterator<ColumnDataType>::operator++()
+{
+    ++m_col_ndx;
+    return (*this);
+}
+
+template <class ColumnDataType>
+ColumnRandIterator<ColumnDataType>& ColumnRandIterator<ColumnDataType>::operator--()
+{
+    --m_col_ndx;
+    return (*this);
+}
+
+template <class ColumnDataType>
+ColumnRandIterator<ColumnDataType> ColumnRandIterator<ColumnDataType>::operator++(int)
+{
+    auto temp(*this);
+    ++m_col_ndx;
+    return temp;
+}
+
+template <class ColumnDataType>
+ColumnRandIterator<ColumnDataType> ColumnRandIterator<ColumnDataType>::operator--(int)
+{
+    auto temp(*this);
+    --m_col_ndx;
+    return temp;
+}
+
+template <class ColumnDataType>
+ColumnRandIterator<ColumnDataType> ColumnRandIterator<ColumnDataType>::operator+(ptrdiff_t movement)
+{
+    return ColumnRandIterator(m_col, m_col_ndx + movement);
+}
+
+template <class ColumnDataType>
+ColumnRandIterator<ColumnDataType> ColumnRandIterator<ColumnDataType>::operator-(ptrdiff_t movement)
+{
+    return ColumnRandIterator(m_col, m_col_ndx - movement);
+}
+
+template <class ColumnDataType>
+ptrdiff_t ColumnRandIterator<ColumnDataType>::operator-(const ColumnRandIterator<ColumnDataType>& right) const
+{
+    return m_col_ndx - right.m_col_ndx;
+}
+
+template <class ColumnDataType>
+const ColumnDataType ColumnRandIterator<ColumnDataType>::operator*() const
+{
+    return m_col->get(m_col_ndx);
+}
+
+template <class ColumnDataType>
+const ColumnDataType ColumnRandIterator<ColumnDataType>::operator->() const
+{
+    return m_col->get(m_col_ndx);
+}
+
+template <class ColumnDataType>
+const ColumnDataType ColumnRandIterator<ColumnDataType>::operator[](ptrdiff_t offset) const
+{
+    return m_col->get(m_col_ndx + offset);
+}
+
+template <class ColumnDataType>
+size_t ColumnRandIterator<ColumnDataType>::get_col_ndx() const
+{
+    return m_col_ndx;
+}
+
+template <class ColumnDataType>
+std::ostream& operator<<(std::ostream& out, const ColumnRandIterator<ColumnDataType>& it)
+{
+    out << "ColumnRandIterator at index: " << it.get_col_ndx();
+    return out;
+}
+
+} // namespace realm
+
+#endif // REALM_COLUMN_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/column_backlink.hpp b/iOS/Pods/Realm/include/core/realm/column_backlink.hpp
new file mode 100644 (file)
index 0000000..cbbe9e6
--- /dev/null
@@ -0,0 +1,246 @@
+/*************************************************************************
+ *
+ * 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 <vector>
+
+#include <realm/column.hpp>
+#include <realm/column_linkbase.hpp>
+#include <realm/table.hpp>
+
+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_move_row(size_t, size_t) noexcept override;
+    void adj_acc_merge_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<VerifyPair>&); // 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<ref_type, size_t> get_to_dot_parent(size_t) const override;
+
+private:
+    TableRef m_origin_table;
+    LinkColumnBase* m_origin_column = nullptr;
+
+    template <typename Func>
+    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_move_row(size_t from_ndx, size_t to_ndx) noexcept
+{
+    Column::adj_acc_move_row(from_ndx, to_ndx);
+
+    using tf = _impl::TableFriend;
+    tf::mark(*m_origin_table);
+}
+
+inline void BacklinkColumn::adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept
+{
+    Column::adj_acc_merge_rows(old_row_ndx, new_row_ndx);
+
+    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/iOS/Pods/Realm/include/core/realm/column_binary.hpp b/iOS/Pods/Realm/include/core/realm/column_binary.hpp
new file mode 100644 (file)
index 0000000..7802e03
--- /dev/null
@@ -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 <realm/column.hpp>
+#include <realm/array_binary.hpp>
+#include <realm/array_blobs_big.hpp>
+
+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,
+                                BpTreeNode::TreeInsert<BinaryColumn>& state);
+
+    struct InsertState : BpTreeNode::TreeInsert<BinaryColumn> {
+        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 BpTreeNode;
+    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(const 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;
+    const 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<ArrayBinary*>(m_array.get());
+            return leaf->size();
+        }
+        // Big blobs root leaf
+        ArrayBigBlobs* leaf = static_cast<ArrayBigBlobs*>(m_array.get());
+        return leaf->size();
+    }
+    // Non-leaf root
+    return static_cast<BpTreeNode*>(m_array.get())->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<ArrayBinary*>(m_array.get()));
+            ArrayBinary* leaf = static_cast<ArrayBinary*>(m_array.get());
+            leaf->update_from_parent(old_baseline);
+            return;
+        }
+        // Big blobs root leaf
+        REALM_ASSERT(dynamic_cast<ArrayBigBlobs*>(m_array.get()));
+        ArrayBigBlobs* leaf = static_cast<ArrayBigBlobs*>(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<ArrayBinary*>(m_array.get());
+            ret = leaf->get(ndx);
+        }
+        else {
+            // Big blobs root leaf
+            ArrayBigBlobs* leaf = static_cast<ArrayBigBlobs*>(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<MemRef, size_t> p = static_cast<BpTreeNode*>(m_array.get())->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 BpTreeNode::get_bptree_size_from_header(root_header);
+}
+
+
+} // namespace realm
+
+#endif // REALM_COLUMN_BINARY_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/column_fwd.hpp b/iOS/Pods/Realm/include/core/realm/column_fwd.hpp
new file mode 100644 (file)
index 0000000..5b93cdc
--- /dev/null
@@ -0,0 +1,58 @@
+/*************************************************************************
+ *
+ * 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 <cstdint>
+
+namespace realm {
+
+// Regular classes
+class ColumnBase;
+class StringColumn;
+class StringEnumColumn;
+class BinaryColumn;
+class SubtableColumn;
+class MixedColumn;
+class LinkColumn;
+class LinkListColumn;
+class TimestampColumn;
+
+// Templated classes
+template <class T>
+class Column;
+template <class T>
+class BasicColumn;
+template <class T>
+class ColumnRandIterator;
+
+namespace util {
+template <class>
+class Optional;
+}
+
+// Shortcuts, aka typedefs.
+using IntegerColumn = Column<int64_t>;
+using IntNullColumn = Column<util::Optional<int64_t>>;
+using DoubleColumn = Column<double>;
+using FloatColumn = Column<float>;
+using IntegerColumnIterator = ColumnRandIterator<int64_t>;
+} // namespace realm
+
+#endif // REALM_COLUMN_FWD_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/column_link.hpp b/iOS/Pods/Realm/include/core/realm/column_link.hpp
new file mode 100644 (file)
index 0000000..18471bb
--- /dev/null
@@ -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 <realm/column.hpp>
+#include <realm/column_linkbase.hpp>
+#include <realm/column_backlink.hpp>
+
+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/iOS/Pods/Realm/include/core/realm/column_linkbase.hpp b/iOS/Pods/Realm/include/core/realm/column_linkbase.hpp
new file mode 100644 (file)
index 0000000..fa67fab
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_COLUMN_LINKBASE_HPP
+#define REALM_COLUMN_LINKBASE_HPP
+
+#include <realm/table.hpp>
+
+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_move_row(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_move_row(size_t from_ndx, size_t to_ndx) noexcept
+{
+    IntegerColumn::adj_acc_move_row(from_ndx, to_ndx);
+
+    using tf = _impl::TableFriend;
+    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/iOS/Pods/Realm/include/core/realm/column_linklist.hpp b/iOS/Pods/Realm/include/core/realm/column_linklist.hpp
new file mode 100644 (file)
index 0000000..3dab1b3
--- /dev/null
@@ -0,0 +1,248 @@
+/*************************************************************************
+ *
+ * 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 <algorithm>
+#include <vector>
+
+#include <realm/column.hpp>
+#include <realm/column_linkbase.hpp>
+#include <realm/table.hpp>
+#include <realm/column_backlink.hpp>
+#include <realm/link_view_fwd.hpp>
+
+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;
+    using value_type = ConstLinkViewRef;
+    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_move_row(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<LinkView> 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<list_entry> m_list_accessors;
+    mutable std::atomic<bool> m_list_accessors_contains_tombstones;
+
+    std::shared_ptr<LinkView> 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 <bool fix_ndx_in_parent>
+    void adj_insert_rows(size_t row_ndx, size_t num_rows_inserted) noexcept;
+
+    template <bool fix_ndx_in_parent>
+    void adj_erase_rows(size_t row_ndx, size_t num_rows_erased) noexcept;
+
+    template <bool fix_ndx_in_parent>
+    void adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept;
+
+    template <bool fix_ndx_in_parent>
+    void adj_move(size_t from_ndx, size_t to_ndx) noexcept;
+
+    template <bool fix_ndx_in_parent>
+    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<ref_type, size_t> 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/iOS/Pods/Realm/include/core/realm/column_mixed.hpp b/iOS/Pods/Realm/include/core/realm/column_mixed.hpp
new file mode 100644 (file)
index 0000000..5f74c41
--- /dev/null
@@ -0,0 +1,265 @@
+/*************************************************************************
+ *
+ * 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 <limits>
+
+#include <realm/column.hpp>
+#include <realm/column_type.hpp>
+#include <realm/column_table.hpp>
+#include <realm/table.hpp>
+#include <realm/utilities.hpp>
+
+
+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;
+
+    TableRef 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
+    /// TableRef to that accessor for that subtable. Otherwise return
+    /// null. The accessor will be created if it does not already
+    /// exist.
+    TableRef get_subtable_tableref(size_t row_ndx);
+
+    ConstTableRef get_subtable_tableref(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_move_row(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<IntegerColumn> 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<RefsColumn> m_data;
+
+    /// For string and binary data types, the bytes are stored here.
+    std::unique_ptr<BinaryColumn> m_binary_data;
+
+    /// Timestamps are stored here.
+    std::unique_ptr<TimestampColumn> 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_tableref;
+
+    void refresh_accessor_tree(size_t, const Spec&) override;
+
+    friend class MixedColumn;
+};
+
+
+} // namespace realm
+
+
+// Implementation
+#include <realm/column_mixed_tpl.hpp>
+
+
+#endif // REALM_COLUMN_MIXED_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/column_mixed_tpl.hpp b/iOS/Pods/Realm/include/core/realm/column_mixed_tpl.hpp
new file mode 100644 (file)
index 0000000..5afdddc
--- /dev/null
@@ -0,0 +1,514 @@
+/*************************************************************************
+ *
+ * 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 <realm/column_binary.hpp>
+#include <realm/column_timestamp.hpp>
+
+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_row(size_t from_ndx, size_t to_ndx) noexcept
+{
+    m_data->adj_acc_move_row(from_ndx, to_ndx);
+}
+
+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 TableRef 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 TableRef MixedColumn::get_subtable_tableref(size_t row_ndx)
+{
+    REALM_ASSERT_3(row_ndx, <, m_types->size());
+    if (m_types->get(row_ndx) != type_Table)
+        return {};
+    return m_data->get_subtable_tableref(row_ndx); // Throws
+}
+
+inline ConstTableRef MixedColumn::get_subtable_tableref(size_t subtable_ndx) const
+{
+    return const_cast<MixedColumn*>(this)->get_subtable_tableref(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<float>::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<float>(get_value(ndx));
+}
+
+inline double MixedColumn::get_double(size_t ndx) const noexcept
+{
+    static_assert(std::numeric_limits<double>::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<double>(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<int64_t>(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<int64_t>(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<int64_t>(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<int32_t>(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
+
+    m_binary_data.reset();
+    // 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);
+    }
+
+    m_timestamp_data.reset();
+    // 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);
+    }
+
+    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
+    }
+}
+
+inline void MixedColumn::RefsColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec)
+{
+    SubtableColumnBase::refresh_accessor_tree(col_ndx, spec); // Throws
+    m_subtable_map.refresh_accessor_tree();                   // Throws
+}
+
+} // namespace realm
diff --git a/iOS/Pods/Realm/include/core/realm/column_string.hpp b/iOS/Pods/Realm/include/core/realm/column_string.hpp
new file mode 100644 (file)
index 0000000..e7693ee
--- /dev/null
@@ -0,0 +1,377 @@
+/*************************************************************************
+ *
+ * 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 <memory>
+#include <realm/array_string.hpp>
+#include <realm/array_string_long.hpp>
+#include <realm/array_blobs_big.hpp>
+#include <realm/column.hpp>
+#include <realm/column_tpl.hpp>
+
+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) override;
+    StringIndex* get_search_index() noexcept override;
+    const StringIndex* get_search_index() const noexcept override;
+    std::unique_ptr<StringIndex> 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<const ArrayParent> 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<StringIndex> 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,
+                                BpTreeNode::TreeInsert<StringColumn>& 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 BpTreeNode;
+    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<ArrayString*>(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<ArrayStringLong*>(m_array.get());
+            return leaf->size();
+        }
+        // Big strings root leaf
+        ArrayBigBlobs* leaf = static_cast<ArrayBigBlobs*>(m_array.get());
+        return leaf->size();
+    }
+    // Non-leaf root
+    BpTreeNode* node = static_cast<BpTreeNode*>(m_array.get());
+    return node->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 BpTreeNode::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/iOS/Pods/Realm/include/core/realm/column_string_enum.hpp b/iOS/Pods/Realm/include/core/realm/column_string_enum.hpp
new file mode 100644 (file)
index 0000000..a73fe0c
--- /dev/null
@@ -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 <realm/column_string.hpp>
+
+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 swap_rows(size_t row_ndx_1, size_t row_ndx_2) override;
+    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;
+    bool supports_search_index() const noexcept final
+    {
+        return true;
+    }
+    StringIndex* create_search_index() override;
+    void install_search_index(std::unique_ptr<StringIndex>) 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/iOS/Pods/Realm/include/core/realm/column_table.hpp b/iOS/Pods/Realm/include/core/realm/column_table.hpp
new file mode 100644 (file)
index 0000000..0d424c2
--- /dev/null
@@ -0,0 +1,693 @@
+/*************************************************************************
+ *
+ * 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 <vector>
+#include <mutex>
+
+#include <realm/util/features.h>
+#include <memory>
+#include <realm/column.hpp>
+#include <realm/table.hpp>
+
+namespace realm {
+
+
+/// Base class for any type of column that can contain subtables.
+// FIXME: Don't derive from IntegerColumn, but define a BpTree<ref_type> 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);
+
+    TableRef 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 adj_acc_move_row(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;
+    }
+    bool is_null(size_t ndx) const noexcept override
+    {
+        return get_as_ref(ndx) == 0;
+    }
+
+    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 {
+        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 <bool fix_ndx_in_parent>
+        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 fix_ndx_in_parent>
+        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 fix_ndx_in_parent>
+        bool adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept;
+        template <bool fix_ndx_in_parent>
+        void adj_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept;
+        template <bool fix_ndx_in_parent>
+        void adj_move_row(size_t from_ndx, size_t to_ndx) noexcept;
+        void adj_set_null(size_t row_ndx) 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();
+        void verify(const SubtableColumn& parent);
+
+    private:
+        struct SubtableEntry {
+            size_t m_subtable_ndx;
+            Table* m_table;
+        };
+        typedef std::vector<SubtableEntry> 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;
+    mutable std::recursive_mutex m_subtable_map_lock;
+
+    SubtableColumnBase(Allocator&, ref_type, Table*, size_t column_ndx);
+
+    /// Get a TableRef to the accessor of the specified subtable. The
+    /// accessor will be created if it does not already exist.
+    ///
+    /// NOTE: This method must be used only for subtables with
+    /// independent specs, i.e. for elements of a MixedColumn.
+    TableRef get_subtable_tableref(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;
+
+    // Overriding method in Table::Parent
+    std::recursive_mutex* get_accessor_management_lock() noexcept override
+    { return &m_subtable_map_lock; }
+
+    /// 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<ref_type, size_t> get_to_dot_parent(size_t ndx_in_parent) const override;
+
+    friend class Table;
+};
+
+
+class SubtableColumn : public SubtableColumnBase {
+public:
+    using value_type = ConstTableRef;
+    /// 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
+    {
+    }
+
+    // Overriding method in Table::Parent
+    Spec* get_subtable_spec() noexcept override;
+
+    size_t get_subtable_size(size_t ndx) const noexcept;
+
+    /// Get a TableRef to the accessor of the specified subtable. The
+    /// accessor will be created if it does not already exist.
+    TableRef get_subtable_tableref(size_t subtable_ndx);
+
+    ConstTableRef get_subtable_tableref(size_t subtable_ndx) const;
+
+    /// This is to be used by the query system that does not need to
+    /// modify the subtable. Will return a ref object containing a
+    /// nullptr if there is no table object yet.
+    ConstTableRef get(size_t subtable_ndx) const
+    {
+        int64_t ref = IntegerColumn::get(subtable_ndx);
+        if (ref)
+            return get_subtable_tableref(subtable_ndx);
+        else
+            return {};
+    }
+
+    // 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);
+    void set_null(size_t ndx) override;
+
+    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;
+    void refresh_subtable_map();
+
+#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)
+{
+    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);
+    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
+
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    const bool fix_ndx_in_parent = true;
+    bool last_entry_removed = m_subtable_map.adj_erase_rows<fix_ndx_in_parent>(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
+
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    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<fix_ndx_in_parent>(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
+
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    const bool fix_ndx_in_parent = true;
+    m_subtable_map.adj_swap_rows<fix_ndx_in_parent>(row_ndx_1, row_ndx_2);
+}
+
+inline void SubtableColumnBase::mark(int type) noexcept
+{
+    if (type & mark_Recursive) {
+        std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+        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.
+
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    const bool fix_ndx_in_parent = false;
+    m_subtable_map.adj_insert_rows<fix_ndx_in_parent>(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.
+
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    const bool fix_ndx_in_parent = false;
+    size_t num_rows_erased = 1;
+    bool last_entry_removed = m_subtable_map.adj_erase_rows<fix_ndx_in_parent>(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.
+
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    const bool fix_ndx_in_parent = false;
+    bool last_entry_removed = m_subtable_map.adj_move_over<fix_ndx_in_parent>(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
+{
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    const bool fix_ndx_in_parent = false;
+    m_subtable_map.adj_swap_rows<fix_ndx_in_parent>(row_ndx_1, row_ndx_2);
+}
+
+inline void SubtableColumnBase::adj_acc_move_row(size_t from_ndx, size_t to_ndx) noexcept
+{
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    const bool fix_ndx_in_parent = false;
+    m_subtable_map.adj_move_row<fix_ndx_in_parent>(from_ndx, to_ndx);
+}
+
+inline TableRef 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.
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    TableRef 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.
+
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    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 <bool fix_ndx_in_parent>
+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 fix_ndx_in_parent>
+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 fix_ndx_in_parent>
+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 <bool fix_ndx_in_parent>
+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);
+        }
+    }
+}
+
+
+template <bool fix_ndx_in_parent>
+void SubtableColumnBase::SubtableMap::adj_move_row(size_t from_ndx, size_t to_ndx) noexcept
+{
+    using tf = _impl::TableFriend;
+    for (auto& entry : m_entries) {
+        if (entry.m_subtable_ndx == from_ndx) {
+            entry.m_subtable_ndx = to_ndx;
+            if (fix_ndx_in_parent)
+                tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx);
+        }
+        else {
+            if (from_ndx < to_ndx) {
+                // shift the range (from, to] down one
+                if (entry.m_subtable_ndx <= to_ndx && entry.m_subtable_ndx > from_ndx) {
+                    entry.m_subtable_ndx--;
+                    if (fix_ndx_in_parent) {
+                        tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx);
+                    }
+                }
+            } else if (from_ndx > to_ndx) {
+                // shift the range (from, to] up one
+                if (entry.m_subtable_ndx >= to_ndx && entry.m_subtable_ndx < from_ndx) {
+                    entry.m_subtable_ndx++;
+                    if (fix_ndx_in_parent) {
+                        tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx);
+                    }
+                }
+            }
+        }
+    }
+}
+
+inline void SubtableColumnBase::SubtableMap::adj_set_null(size_t row_ndx) noexcept
+{
+    Table* table = find(row_ndx);
+    if (table)
+        _impl::TableFriend::refresh_accessor_tree(*table);
+}
+
+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_as_ref(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
+{
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    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<fix_ndx_in_parent>(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 ConstTableRef SubtableColumn::get_subtable_tableref(size_t subtable_ndx) const
+{
+    return const_cast<SubtableColumn*>(this)->get_subtable_tableref(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);
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    m_subtable_map.refresh_accessor_tree(); // Throws
+}
+
+inline void SubtableColumn::refresh_subtable_map()
+{
+    std::lock_guard<std::recursive_mutex> lg(m_subtable_map_lock);
+    m_subtable_map.refresh_accessor_tree(); // Throws
+}
+
+inline size_t SubtableColumn::get_subspec_ndx() const noexcept
+{
+    if (REALM_UNLIKELY(m_subspec_ndx == realm::npos)) {
+        typedef _impl::TableFriend tf;
+        m_subspec_ndx = tf::get_spec(*m_table).get_subspec_ndx(get_column_index());
+    }
+    return m_subspec_ndx;
+}
+
+inline Spec* SubtableColumn::get_subtable_spec() noexcept
+{
+    typedef _impl::TableFriend tf;
+    return tf::get_spec(*m_table).get_subtable_spec(get_column_index());
+}
+
+
+} // namespace realm
+
+#endif // REALM_COLUMN_TABLE_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/column_timestamp.hpp b/iOS/Pods/Realm/include/core/realm/column_timestamp.hpp
new file mode 100644 (file)
index 0000000..d5c4859
--- /dev/null
@@ -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 <realm/column.hpp>
+#include <realm/timestamp.hpp>
+
+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) 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 <class Condition>
+    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<BpTree<util::Optional<int64_t>>> m_seconds;
+    std::unique_ptr<BpTree<int64_t>> m_nanoseconds;
+
+    std::unique_ptr<StringIndex> m_search_index;
+    bool m_nullable;
+
+    template <class BT>
+    class CreateHandler;
+
+    template <class Condition>
+    Timestamp minmax(size_t* result_index) const noexcept
+    {
+        // Condition is realm::Greater for maximum and realm::Less for minimum. Any non-null value is both larger
+        // and smaller than a null value.
+        if (size() == 0) {
+            if (result_index)
+                *result_index = npos;
+            return Timestamp{};
+        }
+
+        Timestamp best = get(0);
+        size_t best_index = best.is_null() ? npos : 0;
+
+        for (size_t i = 1; i < size(); ++i) {
+            Timestamp candidate = get(i);
+            // Condition() will return false if any of the two values are null.
+            if ((best.is_null() && !candidate.is_null()) || 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/iOS/Pods/Realm/include/core/realm/column_tpl.hpp b/iOS/Pods/Realm/include/core/realm/column_tpl.hpp
new file mode 100644 (file)
index 0000000..2411007
--- /dev/null
@@ -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 <cstdlib>
+
+#include <realm/util/features.h>
+#include <realm/array.hpp>
+#include <realm/array_basic.hpp>
+
+namespace realm {
+
+template <class T, class cond>
+class FloatDoubleNode;
+template <class ColType, class Cond>
+class IntegerNode;
+template <class T>
+class SequentialGetter;
+
+template <class cond, class T>
+struct ColumnTypeTraits2;
+
+template <class cond>
+struct ColumnTypeTraits2<cond, int64_t> {
+    typedef IntegerColumn column_type;
+    typedef ArrayInteger array_type;
+};
+template <class cond>
+struct ColumnTypeTraits2<cond, bool> {
+    typedef IntegerColumn column_type;
+    typedef ArrayInteger array_type;
+};
+template <class cond>
+struct ColumnTypeTraits2<cond, float> {
+    typedef FloatColumn column_type;
+    typedef ArrayFloat array_type;
+};
+template <class cond>
+struct ColumnTypeTraits2<cond, double> {
+    typedef DoubleColumn column_type;
+    typedef ArrayDouble array_type;
+};
+
+
+namespace _impl {
+
+template <class ColType>
+struct FindInLeaf {
+    using LeafType = typename ColType::LeafType;
+
+    template <Action action, class Condition, class T, class R>
+    static bool find(const LeafType& leaf, T target, size_t local_start, size_t local_end, size_t leaf_start,
+                     QueryState<R>& 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<action, false>(leaf_start + local_index, 0, static_cast<R>(v));
+            }
+        }
+        return cont;
+    }
+};
+
+template <>
+struct FindInLeaf<IntegerColumn> {
+    using LeafType = IntegerColumn::LeafType;
+
+    template <Action action, class Condition, class T, class R>
+    static bool find(const LeafType& leaf, T target, size_t local_start, size_t local_end, size_t leaf_start,
+                     QueryState<R>& state)
+    {
+        const int c = Condition::condition;
+        return leaf.find(c, action, target, local_start, local_end, leaf_start, &state);
+    }
+};
+
+template <>
+struct FindInLeaf<IntNullColumn> {
+    using LeafType = IntNullColumn::LeafType;
+
+    template <Action action, class Condition, class T, class R>
+    static bool find(const LeafType& leaf, T target, size_t local_start, size_t local_end, size_t leaf_start,
+                     QueryState<R>& state)
+    {
+        constexpr int cond = Condition::condition;
+        return leaf.find(cond, action, target, local_start, local_end, leaf_start, &state);
+    }
+};
+
+} // namespace _impl
+
+template <class T, class R, Action action, class Condition, class ColType>
+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<R> state;
+    state.init(action, nullptr, limit);
+    SequentialGetter<ColType> 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<ColType>::template find<action, Condition>(*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/iOS/Pods/Realm/include/core/realm/column_type.hpp b/iOS/Pods/Realm/include/core/realm/column_type.hpp
new file mode 100644 (file)
index 0000000..5a6e21c
--- /dev/null
@@ -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
+// <realm/data_type.hpp>.
+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/iOS/Pods/Realm/include/core/realm/column_type_traits.hpp b/iOS/Pods/Realm/include/core/realm/column_type_traits.hpp
new file mode 100644 (file)
index 0000000..39a3208
--- /dev/null
@@ -0,0 +1,160 @@
+/*************************************************************************
+ *
+ * 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 <realm/column_fwd.hpp>
+#include <realm/column_type.hpp>
+#include <realm/data_type.hpp>
+#include <realm/array.hpp>
+
+namespace realm {
+
+class OldDateTime;
+class ArrayBinary;
+class ArrayInteger;
+class ArrayIntNull;
+template <class>
+class BasicArray;
+
+template <class T>
+struct ColumnTypeTraits;
+
+template <>
+struct ColumnTypeTraits<int64_t> {
+    using column_type = Column<int64_t>;
+    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<util::Optional<int64_t>> {
+    using column_type = Column<util::Optional<int64_t>>;
+    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<bool> : ColumnTypeTraits<int64_t> {
+    static const DataType id = type_Bool;
+    static const ColumnType column_id = col_type_Bool;
+};
+
+template <>
+struct ColumnTypeTraits<util::Optional<bool>> : ColumnTypeTraits<util::Optional<int64_t>> {
+    static const DataType id = type_Bool;
+    static const ColumnType column_id = col_type_Bool;
+};
+
+template <>
+struct ColumnTypeTraits<float> {
+    using column_type = FloatColumn;
+    using leaf_type = BasicArray<float>;
+    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<double> {
+    using column_type = DoubleColumn;
+    using leaf_type = BasicArray<double>;
+    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<OldDateTime> : ColumnTypeTraits<int64_t> {
+    static const DataType id = type_OldDateTime;
+    static const ColumnType column_id = col_type_OldDateTime;
+};
+
+template <>
+struct ColumnTypeTraits<util::Optional<OldDateTime>> : ColumnTypeTraits<util::Optional<int64_t>> {
+    static const DataType id = type_OldDateTime;
+    static const ColumnType column_id = col_type_OldDateTime;
+};
+
+template <>
+struct ColumnTypeTraits<StringData> {
+    static const DataType id = type_String;
+    static const ColumnType column_id = col_type_String;
+};
+
+template <>
+struct ColumnTypeTraits<BinaryData> {
+    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 <DataType, bool Nullable>
+struct GetColumnType;
+template <>
+struct GetColumnType<type_Int, false> {
+    using type = IntegerColumn;
+};
+template <>
+struct GetColumnType<type_Int, true> {
+    using type = IntNullColumn;
+};
+template <bool N>
+struct GetColumnType<type_Float, N> {
+    // FIXME: Null definition
+    using type = FloatColumn;
+};
+template <bool N>
+struct GetColumnType<type_Double, N> {
+    // 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 <class T, Action A>
+struct ColumnTypeTraitsSum {
+    typedef T sum_type;
+};
+
+template <>
+struct ColumnTypeTraitsSum<float, act_Sum> {
+    typedef double sum_type;
+};
+
+template <Action A>
+struct ColumnTypeTraitsSum<util::Optional<int64_t>, A> {
+    using sum_type = int64_t;
+};
+}
+
+#endif // REALM_COLUMN_TYPE_TRAITS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/data_type.hpp b/iOS/Pods/Realm/include/core/realm/data_type.hpp
new file mode 100644 (file)
index 0000000..0ba78c8
--- /dev/null
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *
+ * 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 {
+
+class StringData;
+class BinaryData;
+
+typedef int64_t Int;
+typedef bool Bool;
+typedef float Float;
+typedef double Double;
+typedef realm::StringData String;
+typedef realm::BinaryData Binary;
+
+
+// Note: Value assignments must be kept in sync with <realm/column_type.h>
+// Note: Value assignments must be kept in sync with <realm/c/data_type.h>
+// Note: Value assignments must be kept in sync with <realm/objc/type.h>
+// 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/iOS/Pods/Realm/include/core/realm/descriptor.hpp b/iOS/Pods/Realm/include/core/realm/descriptor.hpp
new file mode 100644 (file)
index 0000000..9d37de5
--- /dev/null
@@ -0,0 +1,812 @@
+/*************************************************************************
+ *
+ * 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 <cstddef>
+
+#include <realm/util/assert.hpp>
+#include <realm/descriptor_fwd.hpp>
+#include <realm/table.hpp>
+
+
+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<Descriptor> {
+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 table 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);
+
+    /// If the descriptor is describing a subtable column, the add_search_index()
+    /// and remove_search_index() will add or remove search indexes of *all*
+    /// subtables of the subtable column. This may take a while if there are many
+    /// subtables with many rows each.
+    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);
+
+    /// 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<Descriptor> m_subdesc;
+        subdesc_entry(size_t column_ndx, DescriptorRef);
+    };
+    typedef std::vector<subdesc_entry> 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 adj_insert_column(size_t col_ndx) noexcept;
+    void adj_erase_column(size_t col_ndx) noexcept;
+
+    friend class util::bind_ptr<Descriptor>;
+    friend class util::bind_ptr<const Descriptor>;
+    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::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<Descriptor*>(this)->get_subdescriptor(column_ndx);
+}
+
+inline DescriptorRef Descriptor::get_parent() noexcept
+{
+    return m_parent;
+}
+
+inline ConstDescriptorRef Descriptor::get_parent() const noexcept
+{
+    return const_cast<Descriptor*>(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<Descriptor*>(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>(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 void detach_subdesc_accessors(Descriptor& desc) noexcept
+    {
+        desc.detach_subdesc_accessors();
+    }
+
+    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 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);
+    }
+};
+
+} // namespace realm
+
+#endif // REALM_DESCRIPTOR_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/descriptor_fwd.hpp b/iOS/Pods/Realm/include/core/realm/descriptor_fwd.hpp
new file mode 100644 (file)
index 0000000..2937724
--- /dev/null
@@ -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 <memory>
+
+
+namespace realm {
+
+class Descriptor;
+typedef std::shared_ptr<Descriptor> DescriptorRef;
+typedef std::shared_ptr<const Descriptor> ConstDescriptorRef;
+
+} // namespace realm
+
+#endif // REALM_DESCRIPTOR_FWD_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/disable_sync_to_disk.hpp b/iOS/Pods/Realm/include/core/realm/disable_sync_to_disk.hpp
new file mode 100644 (file)
index 0000000..f642d6f
--- /dev/null
@@ -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 <realm/util/features.h>
+
+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/iOS/Pods/Realm/include/core/realm/exceptions.hpp b/iOS/Pods/Realm/include/core/realm/exceptions.hpp
new file mode 100644 (file)
index 0000000..320e5c7
--- /dev/null
@@ -0,0 +1,305 @@
+/*************************************************************************
+ *
+ * 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 <stdexcept>
+
+#include <realm/util/features.h>
+
+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 FileFormatUpgradeRequired exception can be thrown by the SharedGroup
+/// constructor when opening a database that uses a deprecated file format
+/// and/or a deprecated history schema, 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 a sync agent attempts to join a session in which there is
+/// already a sync agent. A session may only contain one sync agent at any given
+/// time.
+class MultipleSyncAgents : 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.
+};
+
+/// Thrown when creating references that are too large to be contained in our ref_type (size_t)
+class MaximumFileSizeExceeded : public std::runtime_error {
+public:
+    MaximumFileSizeExceeded(const std::string& msg);
+    /// runtime_error::what() returns the msg provided in the constructor.
+};
+
+/// Thrown when writing fails because the disk is full.
+class OutOfDiskSpace : public std::runtime_error {
+public:
+    OutOfDiskSpace(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,
+
+        /// History schema version (as specified by the Replication
+        /// implementation passed to the SharedGroup constructor) was not
+        /// consistent across the session.
+        mixed_history_schema_version,
+
+        /// Adding rows to a table with no columns is not supported.
+        table_has_no_columns,
+
+        /// Referring to a column that has been deleted.
+        column_does_not_exist,
+
+        /// You can not add index on a subtable of a subtable
+        subtable_of_subtable_index
+    };
+
+    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";
+}
+
+inline const char* MultipleSyncAgents::what() const noexcept
+{
+    return "Multiple sync agents attempted to join the same session";
+}
+
+// LCOV_EXCL_STOP
+
+inline AddressSpaceExhausted::AddressSpaceExhausted(const std::string& msg)
+    : std::runtime_error(msg)
+{
+}
+
+inline MaximumFileSizeExceeded::MaximumFileSizeExceeded(const std::string& msg)
+    : std::runtime_error(msg)
+{
+}
+
+inline OutOfDiskSpace::OutOfDiskSpace(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/iOS/Pods/Realm/include/core/realm/group.hpp b/iOS/Pods/Realm/include/core/realm/group.hpp
new file mode 100644 (file)
index 0000000..3e79e19
--- /dev/null
@@ -0,0 +1,1415 @@
+/*************************************************************************
+ *
+ * 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 <functional>
+#include <string>
+#include <vector>
+#include <map>
+#include <stdexcept>
+
+#include <realm/util/features.h>
+#include <realm/exceptions.hpp>
+#include <realm/impl/input_stream.hpp>
+#include <realm/impl/output_stream.hpp>
+#include <realm/impl/cont_transact_hist.hpp>
+#include <realm/metrics/metrics.hpp>
+#include <realm/table.hpp>
+#include <realm/alloc_slab.hpp>
+
+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& operator=(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 Realm file that contains a history (see Replication::HistoryType) may
+    /// be opened via Group::open(), as long as the application can ensure that
+    /// there is no concurrent access to the file (see below for more on
+    /// concurrency), but if the file is modified via Group::commit() the
+    /// history will be discarded. To retain the history, the application must
+    /// instead access the file in shared mode, i.e., via SharedGroup, and
+    /// supply the right kind of replication plugin (see
+    /// Replication::get_history_type()).
+    ///
+    /// 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.
+    ///
+    /// \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);
+
+    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);
+
+    //@}
+
+    // 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<size_t>::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<row> rows;
+
+        /// An unordered list of links which will be nullified by the current
+        /// operation.
+        std::vector<link> links;
+    };
+
+    bool has_cascade_notification_handler() const noexcept;
+    void set_cascade_notification_handler(std::function<void(const CascadeNotification&)> 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<void()> new_handler) noexcept;
+
+    //@}
+
+    // Conversion
+    template <class S>
+    void to_json(S& out, size_t link_depth = 0, std::map<std::string, std::string>* 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);
+    }
+
+    /// Compute the sum of the sizes in number of bytes of all the array nodes
+    /// that currently make up this group. When this group represents a snapshot
+    /// in a Realm file (such as during a read transaction via a SharedGroup
+    /// instance), this function computes the footprint of that snapshot within
+    /// the Realm file.
+    ///
+    /// If this group accessor is the detached state, this function returns
+    /// zero.
+    size_t compute_aggregated_byte_size() const noexcept;
+
+    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;
+
+    int m_file_format_version;
+    /// `m_top` is the root node (or top array) of the Realm, and has the
+    /// following layout:
+    ///
+    /// <pre>
+    ///
+    ///                                                     Introduced in file
+    ///   Slot  Value                                       format version
+    ///   ---------------------------------------------------------------------
+    ///    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   History type         (optional)             4
+    ///    9th   History ref          (optional)             4
+    ///   10th   History version      (optional)             7
+    ///
+    /// </pre>
+    ///
+    /// The 'History type' slot stores a value of type
+    /// Replication::HistoryType. The 'History version' slot stores a history
+    /// schema version as returned by Replication::get_history_schema_version().
+    ///
+    /// 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
+    /// are 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
+    /// are present, and the size of `m_top` is 7. In files that contain a
+    /// changeset history, the 8th, 9th, and 10th entry are present, except that
+    /// if the file was opened in nonshared mode (via Group::open()), and the
+    /// file format remains at 6 (not previously upgraded to 7 or later), then
+    /// the 10th entry will be absent.
+    ///
+    /// 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` will 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*> table_accessors;
+    mutable table_accessors m_table_accessors;
+
+    bool m_attached = false;
+    const bool m_is_shared;
+
+    std::function<void(const CascadeNotification&)> m_notify_handler;
+    std::function<void()> m_schema_change_handler;
+    std::shared_ptr<metrics::Metrics> m_metrics;
+    size_t m_total_rows;
+
+    struct shared_tag {
+    };
+    Group(shared_tag) noexcept;
+
+    void init_array_parents() noexcept;
+
+    void open(ref_type top_ref, const std::string& file_path);
+
+    /// 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
+    std::recursive_mutex* get_accessor_management_lock() noexcept override
+    { return nullptr; } // we don't need locking for group!
+
+    // Overriding method in Table::Parent
+    Group* get_parent_group() noexcept override;
+
+    class TableWriter;
+    class DefaultTableWriter;
+
+    static void write(std::ostream&, int file_format_version, 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;
+    std::shared_ptr<metrics::Metrics> get_metrics() const noexcept;
+    void set_metrics(std::shared_ptr<metrics::Metrics> other) noexcept;
+    void update_num_objects();
+    class TransactAdvancer;
+    void advance_transact(ref_type new_top_ref, size_t new_file_size, _impl::NoCopyInputStream&);
+    void refresh_dirty_accessors();
+    template <class F>
+    void update_table_indices(F&& map_function);
+
+    /// \brief The version of the format of the node structure (in file or in
+    /// memory) in use by Realm objects associated with this group.
+    ///
+    /// Every group contains a file format version field, which is returned
+    /// by this function. 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 and the value is used to determine if a file format
+    /// upgrade is required.
+    ///
+    /// A value of zero means that the file format is not yet decided. This is
+    /// only possible for empty Realms where top-ref is zero. (When group is created
+    /// with the unattached_tag). The version number will then be determined in the
+    /// subsequent call to Group::open.
+    ///
+    /// 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 can get out of date, but only for a short while. It is always
+    /// guaranteed 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 the file format version must be changed to a proper
+    /// before the opening process completes (Group::open() or SharedGroup::open()).
+    ///
+    /// 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.
+    ///
+    ///   7 Introduced "history schema version" as 10th entry in top array.
+    ///
+    ///   8 Subtables can now have search index.
+    ///
+    ///   9 Replication instruction values shuffled, instr_MoveRow added.
+    ///
+    /// IMPORTANT: When introducing a new file format version, be sure to review
+    /// the file validity checks in Group::open() and SharedGroup::do_open, 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;
+    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<ref_type, size_t> 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_info(const Array& top, _impl::History::version_type& version,
+                                             int& history_type, int& history_schema_version) noexcept;
+    static ref_type get_history_ref(const Array& top) noexcept;
+    static int get_history_schema_version(const Array& top) noexcept;
+    void set_history_schema_version(int version);
+    void set_history_parent(Array& history_root) noexcept;
+    void prepare_history_parent(Array& history_root, int history_type, int history_schema_version);
+
+    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;
+    friend class metrics::QueryInfo;
+    friend class metrics::Metrics;
+};
+
+
+// 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)
+    , m_total_rows(0)
+{
+    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)
+    , m_total_rows(0)
+{
+    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)
+    , m_total_rows(0)
+{
+    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)
+    , m_total_rows(0)
+{
+    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 <class S>
+void Group::to_json(S& out, size_t link_depth, std::map<std::string, std::string>* renames) const
+{
+    if (!is_attached())
+        throw LogicError(LogicError::detached_accessor);
+
+    std::map<std::string, std::string> 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<std::string, std::string>& 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<void(const CascadeNotification&)> 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<void()> 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_info(const Array& top, _impl::History::version_type& version,
+                                                int& history_type, int& history_schema_version) noexcept
+{
+    using version_type = _impl::History::version_type;
+    version_type version_2 = 0;
+    int history_type_2 = 0;
+    int history_schema_version_2 = 0;
+    if (top.is_attached()) {
+        if (top.size() >= 6) {
+            REALM_ASSERT(top.size() >= 7);
+            version_2 = version_type(top.get_as_ref_or_tagged(6).get_as_int());
+        }
+        if (top.size() >= 8) {
+            REALM_ASSERT(top.size() >= 9);
+            history_type_2           = int(top.get_as_ref_or_tagged(7).get_as_int());
+        }
+        if (top.size() >= 10) {
+            history_schema_version_2 = int(top.get_as_ref_or_tagged(9).get_as_int());
+        }
+    }
+    // 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;
+    history_schema_version = history_schema_version_2;
+}
+
+inline ref_type Group::get_history_ref(const Array& top) noexcept
+{
+    bool has_history = (top.is_attached() && top.size() >= 8);
+    if (has_history) {
+        // This function is only used is shared mode (from SharedGroup)
+        REALM_ASSERT(top.size() >= 10);
+        return top.get_as_ref(8);
+    }
+    return 0;
+}
+
+inline int Group::get_history_schema_version(const Array& top) noexcept
+{
+    bool has_history = (top.is_attached() && top.size() >= 8);
+    if (has_history) {
+        // This function is only used is shared mode (from SharedGroup)
+        REALM_ASSERT(top.size() >= 10);
+        return int(top.get_as_ref_or_tagged(9).get_as_int());
+    }
+    return 0;
+}
+
+inline void Group::set_history_schema_version(int version)
+{
+    // This function is only used is shared mode (from SharedGroup)
+    REALM_ASSERT(m_top.size() >= 10);
+    m_top.set(9, RefOrTagged::make_tagged(unsigned(version))); // Throws
+}
+
+inline void Group::set_history_parent(Array& history_root) noexcept
+{
+    history_root.set_parent(&m_top, 8);
+}
+
+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<Group*>(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<Group*>(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);
+}
+
+inline std::shared_ptr<metrics::Metrics> Group::get_metrics() const noexcept
+{
+    return m_metrics;
+}
+
+inline void Group::set_metrics(std::shared_ptr<metrics::Metrics> shared) noexcept
+{
+    m_metrics = shared;
+}
+
+// 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 const Allocator& get_alloc(const Group& group) noexcept
+    {
+        return group.m_alloc;
+    }
+
+    static ref_type get_top_ref(const Group& group) noexcept
+    {
+        return group.m_top.get_ref();
+    }
+
+    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_info(const Allocator& alloc, ref_type top_ref,
+                                             _impl::History::version_type& version,
+                                             int& history_type,
+                                             int& history_schema_version) noexcept
+    {
+        Array top{const_cast<Allocator&>(alloc)};
+        if (top_ref != 0)
+            top.init_from_ref(top_ref);
+        Group::get_version_and_history_info(top, version, history_type, history_schema_version);
+    }
+
+    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 int get_history_schema_version(const Group& group) noexcept
+    {
+        return Group::get_history_schema_version(group.m_top);
+    }
+
+    static int get_history_schema_version(Allocator& alloc, ref_type top_ref) noexcept
+    {
+        Array top{alloc};
+        if (top_ref != 0)
+            top.init_from_ref(top_ref);
+        return Group::get_history_schema_version(top);
+    }
+
+    static void set_history_schema_version(Group& group, int version)
+    {
+        group.set_history_schema_version(version); // Throws
+    }
+
+    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,
+                                       int history_schema_version)
+    {
+        group.prepare_history_parent(history_root, history_type, history_schema_version); // 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;
+
+    /// If false, weak links are followed too
+    bool only_strong_links = true;
+};
+
+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/iOS/Pods/Realm/include/core/realm/group_shared.hpp b/iOS/Pods/Realm/include/core/realm/group_shared.hpp
new file mode 100644 (file)
index 0000000..3da877b
--- /dev/null
@@ -0,0 +1,1208 @@
+/*************************************************************************
+ *
+ * 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
+
+#include <functional>
+#include <limits>
+#include <realm/util/features.h>
+#include <realm/util/thread.hpp>
+#include <realm/util/interprocess_condvar.hpp>
+#include <realm/util/interprocess_mutex.hpp>
+#include <realm/group.hpp>
+#include <realm/group_shared_options.hpp>
+#include <realm/handover_defs.hpp>
+#include <realm/impl/transact_log.hpp>
+#include <realm/metrics/metrics.hpp>
+#include <realm/replication.hpp>
+#include <realm/version_id.hpp>
+
+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)
+    {
+    }
+};
+
+/// Thrown by SharedGroup::open() if the type of history
+/// (Replication::HistoryType) in the opened Realm file is incompatible with the
+/// mode in which the Realm file is opened. For example, if there is a mismatch
+/// between the history type in the file, and the history type associated with
+/// the replication plugin passed to SharedGroup::open().
+///
+/// This exception will also be thrown if the history schema version is lower
+/// than required, and no migration is possible
+/// (Replication::is_upgradable_history_schema()).
+struct IncompatibleHistories : util::File::AccessError {
+    IncompatibleHistories(const std::string& msg, const std::string& path)
+        : util::File::AccessError("Incompatible histories. " + msg, path)
+    {
+    }
+};
+
+/// 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;
+
+    // Disable copying to prevent accessor errors. If you really want another
+    // instance, open another SharedGroup object on the same file.
+    SharedGroup(const SharedGroup&) = delete;
+    SharedGroup& operator=(const SharedGroup&) = delete;
+
+    /// 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();
+    // Return true (and take the write lock) if there is no other write
+    // in progress. In case of contention return false immediately.
+    // If the write lock is obtained, also provide the Group associated
+    // with the SharedGroup for further operations.
+    bool try_begin_write(Group*& group);
+    version_type commit();
+    void rollback() noexcept;
+    // report statistics of last commit done on THIS shared group.
+    // The free space reported is what can be expected to be freed
+    // by compact(). This may not correspond to the space which is free
+    // at the point where get_stats() is called, since that will include
+    // memory required to hold older versions of data, which still
+    // needs to be available.
+    void get_stats(size_t& free_space, size_t& used_space);
+    //@}
+
+    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<T> 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 <typename T>
+    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 <typename T>
+    std::unique_ptr<Handover<T>> export_for_handover(const T& accessor, ConstSourcePayload mode);
+
+    // specialization for handover of Rows
+    template <typename T>
+    std::unique_ptr<Handover<BasicRow<T>>> export_for_handover(const BasicRow<T>& accessor);
+
+    // destructive export (mode is Move)
+    template <typename T>
+    std::unique_ptr<Handover<T>> 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 <typename T>
+    std::unique_ptr<T> import_from_handover(std::unique_ptr<Handover<T>> handover);
+
+    // We need two cases for handling of LinkViews, because they are ref counted.
+    std::unique_ptr<Handover<LinkView>> export_linkview_for_handover(const LinkViewRef& accessor);
+    LinkViewRef import_linkview_from_handover(std::unique_ptr<Handover<LinkView>> handover);
+
+    // likewise for Tables.
+    std::unique_ptr<Handover<Table>> export_table_for_handover(const TableRef& accessor);
+    TableRef import_table_from_handover(std::unique_ptr<Handover<Table>> 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);
+
+#if REALM_METRICS
+    std::shared_ptr<metrics::Metrics> get_metrics();
+#endif // REALM_METRICS
+
+    // Try to grab a exclusive lock of the given realm path's lock file. If the lock
+    // can be acquired, the callback will be executed with the lock and then return true.
+    // Otherwise false will be returned directly.
+    // The lock taken precludes races with other threads or processes accessing the
+    // files through a SharedGroup.
+    // It is safe to delete/replace realm files inside the callback.
+    // WARNING: It is not safe to delete the lock file in the callback.
+    using CallbackWithLock = std::function<void(const std::string& realm_path)>;
+    static bool call_with_lock(const std::string& realm_path, CallbackWithLock callback);
+
+    // Return a list of files/directories core may use of the given realm file path.
+    // The first element of the pair in the returned list is the path string, the
+    // second one is to indicate the path is a directory or not.
+    // The temporary files are not returned by this function.
+    // It is safe to delete those returned files/directories in the call_with_lock's callback.
+    static std::vector<std::pair<std::string, bool>> get_core_files(const std::string& realm_path);
+
+private:
+    struct SharedInfo;
+    struct ReadCount;
+    struct ReadLockInfo {
+        uint_fast64_t m_version = std::numeric_limits<version_type>::max();
+        uint_fast32_t m_reader_idx = 0;
+        ref_type m_top_ref = 0;
+        size_t m_file_size = 0;
+    };
+    class ReadLockUnlockGuard;
+
+    // Member variables
+    size_t m_free_space = 0;
+    size_t m_used_space = 0;
+    Group m_group;
+    ReadLockInfo m_read_lock;
+    uint_fast32_t m_local_max_entry;
+    util::File m_file;
+    util::File::Map<SharedInfo> m_file_map; // Never remapped
+    util::File::Map<SharedInfo> 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;
+#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;
+    util::InterprocessCondVar m_pick_next_writer;
+    std::function<void(int, int)> m_upgrade_callback;
+
+#if REALM_METRICS
+    std::shared_ptr<metrics::Metrics> m_metrics;
+#endif // REALM_METRICS
+
+    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;
+    /// return true if write transaction can commence, false otherwise.
+    bool do_try_begin_write();
+    void do_begin_write();
+    version_type do_commit();
+    void do_end_write() noexcept;
+    void set_transact_stage(TransactStage stage) 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();
+
+    /// Upgrade file format and/or history schema
+    void upgrade_file_format(bool allow_file_format_upgrade, int target_file_format_version,
+                             int current_hist_schema_version, int target_hist_schema_version);
+
+    //@{
+    /// See LangBindHelper.
+    template <class O>
+    void advance_read(O* observer, VersionID);
+    template <class O>
+    void promote_to_write(O* observer);
+    version_type commit_and_continue_as_read();
+    template <class O>
+    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 <class O>
+    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;
+
+    /// finish up the process of starting a write transaction. Internal use only.
+    void finish_begin_write();
+
+    void close_internal(std::unique_lock<InterprocessMutex>) noexcept;
+    friend class _impl::SharedGroupFriend;
+};
+
+
+inline void SharedGroup::get_stats(size_t& free_space, size_t& used_space) {
+    free_space = m_free_space;
+    used_space = m_used_space;
+}
+
+
+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
+    }
+
+    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
+    }
+
+    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 <typename T>
+struct SharedGroup::Handover {
+    std::unique_ptr<typename T::HandoverPatch> patch;
+    std::unique_ptr<T> clone;
+    VersionID version;
+};
+
+template <typename T>
+std::unique_ptr<SharedGroup::Handover<T>> SharedGroup::export_for_handover(const T& accessor, ConstSourcePayload mode)
+{
+    if (m_transact_stage != transact_Reading)
+        throw LogicError(LogicError::wrong_transact_state);
+    std::unique_ptr<Handover<T>> result(new Handover<T>());
+    // 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<T*>(accessor.clone_for_handover(result->patch, mode).release()));
+    result->version = get_version_of_current_transaction();
+    return move(result);
+}
+
+
+template <typename T>
+std::unique_ptr<SharedGroup::Handover<BasicRow<T>>> SharedGroup::export_for_handover(const BasicRow<T>& accessor)
+{
+    if (m_transact_stage != transact_Reading)
+        throw LogicError(LogicError::wrong_transact_state);
+    std::unique_ptr<Handover<BasicRow<T>>> result(new Handover<BasicRow<T>>());
+    // See implementation note above.
+    result->clone.reset(dynamic_cast<BasicRow<T>*>(accessor.clone_for_handover(result->patch).release()));
+    result->version = get_version_of_current_transaction();
+    return move(result);
+}
+
+
+template <typename T>
+std::unique_ptr<SharedGroup::Handover<T>> SharedGroup::export_for_handover(T& accessor, MutableSourcePayload mode)
+{
+    if (m_transact_stage != transact_Reading)
+        throw LogicError(LogicError::wrong_transact_state);
+    std::unique_ptr<Handover<T>> result(new Handover<T>());
+    // see implementation note above.
+    result->clone.reset(dynamic_cast<T*>(accessor.clone_for_handover(result->patch, mode).release()));
+    result->version = get_version_of_current_transaction();
+    return move(result);
+}
+
+
+template <typename T>
+std::unique_ptr<T> SharedGroup::import_from_handover(std::unique_ptr<SharedGroup::Handover<T>> handover)
+{
+    if (handover->version != get_version_of_current_transaction()) {
+        throw BadVersion();
+    }
+    std::unique_ptr<T> result = move(handover->clone);
+    result->apply_and_consume_patch(handover->patch, m_group);
+    return result;
+}
+
+template <class O>
+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 <class O>
+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;
+    }
+
+    set_transact_stage(transact_Writing);
+}
+
+template <class O>
+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();
+
+    set_transact_stage(transact_Reading);
+}
+
+template <class O>
+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;
+
+        // Synchronize readers view of the file
+        SlabAlloc& alloc = m_group.m_alloc;
+        alloc.update_reader_view(new_file_size);
+
+        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 <class O>
+    static void advance_read(SharedGroup& sg, O* obs, SharedGroup::VersionID ver)
+    {
+        sg.advance_read(obs, ver); // Throws
+    }
+
+    template <class O>
+    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 <class O>
+    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/iOS/Pods/Realm/include/core/realm/group_shared_options.hpp b/iOS/Pods/Realm/include/core/realm/group_shared_options.hpp
new file mode 100644 (file)
index 0000000..75d5bda
--- /dev/null
@@ -0,0 +1,111 @@
+/*************************************************************************
+ *
+ * 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 <functional>
+#include <string>
+
+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<void(int, int)> file_upgrade_callback = std::function<void(int, int)>(),
+                                std::string temp_directory = sys_tmp_dir,
+                                bool track_metrics = false)
+        : durability(level)
+        , encryption_key(key)
+        , allow_file_format_upgrade(allow_upgrade)
+        , upgrade_callback(file_upgrade_callback)
+        , temp_dir(temp_directory)
+        , enable_metrics(track_metrics)
+
+    {
+    }
+
+    explicit SharedGroupOptions(const char* key)
+        : durability(Durability::Full)
+        , encryption_key(key)
+        , allow_file_format_upgrade(true)
+        , upgrade_callback(std::function<void(int, int)>())
+        , temp_dir(sys_tmp_dir)
+        , enable_metrics(false)
+    {
+    }
+
+    /// 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<void(int, int)> 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;
+
+    /// Controls the feature of collecting various metrics to the SharedGroup.
+    /// A prerequisite is compiling with REALM_METRICS=ON.
+    bool enable_metrics;
+
+    /// sys_tmp_dir will be used if the temp_dir is empty when creating SharedGroupOptions.
+    /// It must be writable and allowed to create pipe/fifo file on it.
+    /// set_sys_tmp_dir is not a thread-safe call and it is only supposed to be called once
+    //  when process starts.
+    static void set_sys_tmp_dir(const std::string& dir) noexcept { sys_tmp_dir = dir; }
+    static std::string get_sys_tmp_dir() noexcept { return sys_tmp_dir; }
+
+private:
+    static std::string sys_tmp_dir;
+};
+
+} // end namespace realm
+
+#endif // REALM_GROUP_SHARED_OPTIONS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/group_writer.hpp b/iOS/Pods/Realm/include/core/realm/group_writer.hpp
new file mode 100644 (file)
index 0000000..87352f5
--- /dev/null
@@ -0,0 +1,166 @@
+/*************************************************************************
+ *
+ * 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 <cstdint> // unint8_t etc
+#include <utility>
+
+#include <realm/util/file.hpp>
+#include <realm/alloc.hpp>
+#include <realm/impl/array_writer.hpp>
+#include <realm/array_integer.hpp>
+
+
+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
+
+    size_t get_free_space();
+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;
+    size_t m_alloc_position;
+
+    // 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<std::unique_ptr<MapWindow>> 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<size_t, size_t> 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<size_t, size_t> 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<size_t, size_t> 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/iOS/Pods/Realm/include/core/realm/handover_defs.hpp b/iOS/Pods/Realm/include/core/realm/handover_defs.hpp
new file mode 100644 (file)
index 0000000..ee554ff
--- /dev/null
@@ -0,0 +1,84 @@
+/*************************************************************************
+ *
+ * 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 <memory>
+#include <vector>
+
+namespace realm {
+
+enum class ConstSourcePayload { Copy, Stay };
+enum class MutableSourcePayload { Move };
+
+struct RowBaseHandoverPatch;
+struct TableViewHandoverPatch;
+
+struct TableHandoverPatch {
+    bool m_is_sub_table;
+    size_t m_table_num;
+    size_t m_col_ndx;
+    size_t m_row_ndx;
+};
+
+struct LinkViewHandoverPatch {
+    std::unique_ptr<TableHandoverPatch> 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<std::unique_ptr<QueryNodeHandoverPatch>>;
+
+struct QueryHandoverPatch {
+    std::unique_ptr<TableHandoverPatch> m_table;
+    std::unique_ptr<TableViewHandoverPatch> table_view_data;
+    std::unique_ptr<LinkViewHandoverPatch> link_view_data;
+    QueryNodeHandoverPatches m_node_data;
+};
+
+struct DescriptorOrderingHandoverPatch {
+    std::vector<std::vector<std::vector<size_t>>> columns;
+    std::vector<std::vector<bool>> ascending;
+};
+
+struct TableViewHandoverPatch {
+    std::unique_ptr<TableHandoverPatch> m_table;
+    std::unique_ptr<RowBaseHandoverPatch> linked_row;
+    size_t linked_col;
+    bool was_in_sync;
+    QueryHandoverPatch query_patch;
+    std::unique_ptr<LinkViewHandoverPatch> linkview_patch;
+    std::unique_ptr<DescriptorOrderingHandoverPatch> descriptors_patch;
+};
+
+
+struct RowBaseHandoverPatch {
+    std::unique_ptr<TableHandoverPatch> m_table;
+    size_t row_ndx;
+};
+
+
+} // end namespace Realm
+
+#endif
diff --git a/iOS/Pods/Realm/include/core/realm/history.hpp b/iOS/Pods/Realm/include/core/realm/history.hpp
new file mode 100644 (file)
index 0000000..9710d0b
--- /dev/null
@@ -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 <memory>
+#include <string>
+
+#include <realm/replication.hpp>
+
+
+namespace realm {
+
+std::unique_ptr<Replication> make_in_realm_history(const std::string& realm_path);
+
+} // namespace realm
+
+
+#endif // REALM_HISTORY_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/impl/array_writer.hpp b/iOS/Pods/Realm/include/core/realm/impl/array_writer.hpp
new file mode 100644 (file)
index 0000000..f039ad5
--- /dev/null
@@ -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 <realm/alloc.hpp>
+
+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/iOS/Pods/Realm/include/core/realm/impl/clamped_hex_dump.hpp b/iOS/Pods/Realm/include/core/realm/impl/clamped_hex_dump.hpp
new file mode 100644 (file)
index 0000000..06335d1
--- /dev/null
@@ -0,0 +1,50 @@
+/*************************************************************************
+ *
+ * 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_CLAMPED_HEX_DUMP_HPP
+#define REALM_IMPL_CLAMPED_HEX_DUMP_HPP
+
+#include <realm/util/hex_dump.hpp>
+#include <realm/binary_data.hpp>
+
+namespace realm {
+namespace _impl {
+
+/// Limit the amount of dumped data to 1024 bytes. For use in connection with
+/// logging.
+inline std::string clamped_hex_dump(BinaryData blob)
+{
+    bool was_clipped = false;
+    std::size_t max_size = 1024;
+    std::size_t size_2 = blob.size();
+    if (size_2 > max_size) {
+        size_2 = max_size;
+        was_clipped = true;
+    }
+    std::string str = util::hex_dump(blob.data(), size_2); // Throws
+    if (was_clipped)
+        str += "..."; // Throws
+    return str;
+}
+
+} // namespace _impl
+} // namespace realm
+
+#endif // REALM_IMPL_CLAMPED_HEX_DUMP_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/impl/cont_transact_hist.hpp b/iOS/Pods/Realm/include/core/realm/impl/cont_transact_hist.hpp
new file mode 100644 (file)
index 0000000..54e0c84
--- /dev/null
@@ -0,0 +1,153 @@
+/*************************************************************************
+ *
+ * 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_CONT_TRANSACT_HIST_HPP
+#define REALM_IMPL_CONT_TRANSACT_HIST_HPP
+
+#include <cstdint>
+#include <memory>
+
+#include <realm/column_binary.hpp>
+#include <realm/version_id.hpp>
+
+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
+    {
+    }
+};
+
+} // namespace _impl
+} // namespace realm
+
+#endif // REALM_IMPL_CONTINUOUS_TRANSACTIONS_HISTORY_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/impl/destroy_guard.hpp b/iOS/Pods/Realm/include/core/realm/impl/destroy_guard.hpp
new file mode 100644 (file)
index 0000000..f5b5e37
--- /dev/null
@@ -0,0 +1,237 @@
+/*************************************************************************
+ *
+ * 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 <realm/util/features.h>
+#include <realm/array.hpp>
+
+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 T>
+class DestroyGuard {
+public:
+    DestroyGuard() noexcept;
+
+    DestroyGuard(T*) noexcept;
+
+    ~DestroyGuard() noexcept;
+
+    // Default implementations of copy/assign can trigger multiple destructions
+    DestroyGuard(const DestroyGuard&) = delete;
+    DestroyGuard& operator=(const DestroyGuard&) = delete;
+
+    void reset(T*) noexcept;
+
+    T* get() const noexcept;
+
+    T* release() noexcept;
+
+private:
+    T* m_ptr;
+};
+
+using ShallowArrayDestroyGuard = DestroyGuard<Array>;
+
+
+/// 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;
+
+    // Default implementations of copy/assign can trigger multiple destructions
+    DeepArrayDestroyGuard(const DeepArrayDestroyGuard&) = delete;
+    DeepArrayDestroyGuard& operator=(const DeepArrayDestroyGuard&) = delete;
+
+    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;
+
+    // Default implementations of copy/assign can trigger multiple destructions
+    DeepArrayRefDestroyGuard(const DeepArrayRefDestroyGuard&) = delete;
+    DeepArrayRefDestroyGuard& operator=(const DeepArrayRefDestroyGuard&) = delete;
+
+    void reset(ref_type) noexcept;
+
+    ref_type get() const noexcept;
+
+    ref_type release() noexcept;
+
+private:
+    ref_type m_ref;
+    Allocator& m_alloc;
+};
+
+
+// Implementation:
+
+// DestroyGuard<T>
+
+template <class T>
+inline DestroyGuard<T>::DestroyGuard() noexcept
+    : m_ptr(nullptr)
+{
+}
+
+template <class T>
+inline DestroyGuard<T>::DestroyGuard(T* ptr) noexcept
+    : m_ptr(ptr)
+{
+}
+
+template <class T>
+inline DestroyGuard<T>::~DestroyGuard() noexcept
+{
+    if (m_ptr)
+        m_ptr->destroy();
+}
+
+template <class T>
+inline void DestroyGuard<T>::reset(T* ptr) noexcept
+{
+    if (m_ptr)
+        m_ptr->destroy();
+    m_ptr = ptr;
+}
+
+template <class T>
+inline T* DestroyGuard<T>::get() const noexcept
+{
+    return m_ptr;
+}
+
+template <class T>
+inline T* DestroyGuard<T>::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/iOS/Pods/Realm/include/core/realm/impl/input_stream.hpp b/iOS/Pods/Realm/include/core/realm/impl/input_stream.hpp
new file mode 100644 (file)
index 0000000..80db30a
--- /dev/null
@@ -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_IMPL_INPUT_STREAM_HPP
+#define REALM_IMPL_INPUT_STREAM_HPP
+
+#include <algorithm>
+
+#include <realm/binary_data.hpp>
+#include <realm/impl/cont_transact_hist.hpp>
+#include <realm/util/buffer.hpp>
+
+
+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;
+        realm::safe_copy_n(begin, n, 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.
+                realm::safe_copy_n(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/iOS/Pods/Realm/include/core/realm/impl/output_stream.hpp b/iOS/Pods/Realm/include/core/realm/impl/output_stream.hpp
new file mode 100644 (file)
index 0000000..1d022cd
--- /dev/null
@@ -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 <cstddef>
+#include <ostream>
+
+#include <cstdint>
+
+#include <realm/util/features.h>
+
+#include <realm/impl/array_writer.hpp>
+
+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/iOS/Pods/Realm/include/core/realm/impl/sequential_getter.hpp b/iOS/Pods/Realm/include/core/realm/impl/sequential_getter.hpp
new file mode 100644 (file)
index 0000000..c7dd866
--- /dev/null
@@ -0,0 +1,130 @@
+/*************************************************************************
+ *
+ * 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 ColType>
+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)
+    {
+        init(static_cast<const ColType*>(&table.get_column_base(column_ndx)));
+    }
+
+    SequentialGetter(const ColType* column)
+    {
+        init(column);
+    }
+
+    ~SequentialGetter() noexcept override
+    {
+    }
+
+    void init(const ColType* column)
+    {
+        REALM_ASSERT(column != nullptr);
+        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)
+    {
+        // Set m_leaf_ptr to point at the leaf that contains the value at column row `index`. Return whether or not
+        // the leaf has changed (could be useful to know for caller).
+
+        // FIXME: Below line has been commented away because array leafs might relocate during the lifetime of the
+        // object that owns this SequentialGetter. Enable again when we have proper support for that.
+        //        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
+        return m_column->get(index);
+
+        // FIXME: Below optimization is skipped because array leafs might relocate during the lifetime of the
+        // object that owns this SequentialGetter. Enable again when we have proper support for that.
+//
+//      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 = 0;
+    size_t m_leaf_end = 0;
+    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<sizeof(ArrayType), alignof(ArrayType)>::type m_leaf_accessor_storage;
+    std::unique_ptr<ArrayType, PlacementDelete> m_array_ptr;
+};
+
+} // namespace realm
+
+#endif // REALM_IMPL_SEQUENTIAL_GETTER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/impl/simulated_failure.hpp b/iOS/Pods/Realm/include/core/realm/impl/simulated_failure.hpp
new file mode 100644 (file)
index 0000000..4958151
--- /dev/null
@@ -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_IMPL_SIMULATED_FAILURE_HPP
+#define REALM_IMPL_SIMULATED_FAILURE_HPP
+
+#include <cstdint>
+#include <system_error>
+
+#include <realm/util/features.h>
+
+#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;
+};
+
+std::error_code make_error_code(SimulatedFailure::FailureType) noexcept;
+
+} // namespace _impl
+} // namespace realm
+
+namespace std {
+
+template<> struct is_error_code_enum<realm::_impl::SimulatedFailure::FailureType> {
+    static const bool value = true;
+};
+
+} // namespace std
+
+namespace realm {
+namespace _impl {
+
+
+// Implementation
+
+inline void SimulatedFailure::prime_one_shot(FailureType failure_type)
+{
+#ifdef REALM_ENABLE_SIMULATED_FAILURE
+    do_prime_one_shot(failure_type);
+#else
+    static_cast<void>(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<void>(failure_type);
+    static_cast<void>(n);
+    static_cast<void>(m);
+    static_cast<void>(seed);
+#endif
+}
+
+inline void SimulatedFailure::unprime(FailureType failure_type) noexcept
+{
+#ifdef REALM_ENABLE_SIMULATED_FAILURE
+    do_unprime(failure_type);
+#else
+    static_cast<void>(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<void>(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/iOS/Pods/Realm/include/core/realm/impl/transact_log.hpp b/iOS/Pods/Realm/include/core/realm/impl/transact_log.hpp
new file mode 100644 (file)
index 0000000..ee3c710
--- /dev/null
@@ -0,0 +1,2808 @@
+/*************************************************************************
+ *
+ * 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 <stdexcept>
+
+#include <realm/string_data.hpp>
+#include <realm/data_type.hpp>
+#include <realm/binary_data.hpp>
+#include <realm/olddatetime.hpp>
+#include <realm/mixed.hpp>
+#include <realm/util/buffer.hpp>
+#include <realm/util/string_buffer.hpp>
+#include <realm/impl/input_stream.hpp>
+
+#include <realm/group.hpp>
+#include <realm/descriptor.hpp>
+#include <realm/link_view.hpp>
+
+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, // UNSUPPORTED/UNUSED. FIXME: remove in next breaking change
+    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_MoveRow = 16,
+    instr_MergeRows = 17,  // Replace links pointing to row A with links to row B
+    instr_ClearTable = 18, // Remove all rows in selected table
+    instr_OptimizeTable = 19,
+    instr_SelectDescriptor = 20, // Select descriptor from currently selected root table
+    instr_InsertColumn =
+        21, // Insert new non-nullable column into to selected descriptor (nullable is instr_InsertNullableColumn)
+    instr_InsertLinkColumn = 22,     // do, but for a link-type column
+    instr_InsertNullableColumn = 23, // Insert nullable column
+    instr_EraseColumn = 24,          // Remove column from selected descriptor
+    instr_EraseLinkColumn = 25,      // Remove link-type column from selected descriptor
+    instr_RenameColumn = 26,         // Rename column in selected descriptor
+    instr_MoveColumn = 27,           // Move column in selected descriptor (UNSUPPORTED/UNUSED) FIXME: remove
+    instr_AddSearchIndex = 28,       // Add a search index to a column
+    instr_RemoveSearchIndex = 29,    // Remove a search index from a column
+    instr_SetLinkType = 30,          // Strong/weak
+    instr_SelectLinkList = 31,
+    instr_LinkListSet = 32,     // Assign to link list entry
+    instr_LinkListInsert = 33,  // Insert entry into link list
+    instr_LinkListMove = 34,    // Move an entry within a link list
+    instr_LinkListSwap = 35,    // Swap two entries within a link list
+    instr_LinkListErase = 36,   // Remove an entry from a link list
+    instr_LinkListNullify = 37, // Remove an entry from a link list due to linked row being erased
+    instr_LinkListClear = 38,   // Ramove all entries from a link list
+    instr_LinkListSetAll = 39,  // Assign to link list entry
+    instr_AddRowWithKey = 40,   // Insert a row with a given key
+};
+
+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<char> 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;
+    }
+
+    // Must have table selected:
+    bool insert_empty_rows(size_t, size_t, size_t, bool)
+    {
+        return true;
+    }
+    bool add_row_with_key(size_t, size_t, size_t, int64_t)
+    {
+        return true;
+    }
+    bool erase_rows(size_t, size_t, size_t, bool)
+    {
+        return true;
+    }
+    bool swap_rows(size_t, size_t)
+    {
+        return true;
+    }
+    bool move_row(size_t, size_t)
+    {
+        return true;
+    }
+    bool merge_rows(size_t, size_t)
+    {
+        return true;
+    }
+    bool clear_table(size_t)
+    {
+        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 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);
+
+    /// 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 add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx, int64_t key);
+    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 move_row(size_t from_ndx, size_t to_ndx);
+    bool merge_rows(size_t row_ndx, size_t new_row_ndx);
+    bool clear_table(size_t old_table_size);
+
+    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 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:
+    using IntegerList = std::tuple<IntegerColumnIterator, IntegerColumnIterator>;
+    using UnsignedList = std::tuple<const size_t*, const size_t*>;
+
+    // Make sure this is in agreement with the actual integer encoding
+    // scheme (see encode_int()).
+    static constexpr int max_enc_bytes_per_int = 10;
+    static constexpr int max_enc_bytes_per_double = sizeof(double);
+    static constexpr 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;
+// Space is reserved in chunks to avoid excessive over allocation.
+#ifdef REALM_DEBUG
+    static constexpr int max_numbers_per_chunk = 2; // Increase the chance of chunking in debug mode
+#else
+    static constexpr int max_numbers_per_chunk = 8;
+#endif
+
+    // 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 <class T>
+    size_t max_size(T);
+
+    size_t max_size_list()
+    {
+        return 0;
+    }
+
+    template <class T, class... Args>
+    size_t max_size_list(T val, Args... args)
+    {
+        return max_size(val) + max_size_list(args...);
+    }
+
+    template <class T>
+    char* encode(char* ptr, T value);
+
+    char* encode_list(char* ptr)
+    {
+        advance(ptr);
+        return ptr;
+    }
+
+    template <class T, class... Args>
+    char* encode_list(char* ptr, T value, Args... args)
+    {
+        return encode_list(encode(ptr, value), args...);
+    }
+
+    template <class... L>
+    void append_simple_instr(L... numbers);
+
+    template <class... L>
+    void append_mixed_instr(Instruction instr, const Mixed& value, L... numbers);
+
+    template <class T>
+    static char* encode_int(char*, T value);
+    friend class TransactLogParser;
+};
+
+class TransactLogConvenientEncoder {
+public:
+    virtual void insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name);
+    virtual void erase_group_level_table(size_t table_ndx, size_t num_tables);
+    virtual void rename_group_level_table(size_t table_ndx, StringData new_name);
+    virtual void insert_column(const Descriptor&, size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link,
+                               bool nullable = false);
+    virtual void erase_column(const Descriptor&, size_t col_ndx);
+    virtual void rename_column(const Descriptor&, size_t col_ndx, StringData name);
+
+    virtual void set_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant = instr_Set);
+    virtual void add_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value);
+    virtual void set_bool(const Table*, size_t col_ndx, size_t ndx, bool value, Instruction variant = instr_Set);
+    virtual void set_float(const Table*, size_t col_ndx, size_t ndx, float value, Instruction variant = instr_Set);
+    virtual void set_double(const Table*, size_t col_ndx, size_t ndx, double value, Instruction variant = instr_Set);
+    virtual void set_string(const Table*, size_t col_ndx, size_t ndx, StringData value, Instruction variant = instr_Set);
+    virtual void set_binary(const Table*, size_t col_ndx, size_t ndx, BinaryData value, Instruction variant = instr_Set);
+    virtual void set_olddatetime(const Table*, size_t col_ndx, size_t ndx, OldDateTime value,
+                                 Instruction variant = instr_Set);
+    virtual void set_timestamp(const Table*, size_t col_ndx, size_t ndx, Timestamp value, Instruction variant = instr_Set);
+    virtual void set_table(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set);
+    virtual void set_mixed(const Table*, size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant = instr_Set);
+    virtual void set_link(const Table*, size_t col_ndx, size_t ndx, size_t value, Instruction variant = instr_Set);
+    virtual void set_null(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set);
+    virtual void set_link_list(const LinkView&, const IntegerColumn& values);
+    virtual void insert_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, StringData);
+    virtual 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.
+    virtual void insert_empty_rows(const Table*, size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows);
+    virtual void add_row_with_key(const Table* t, size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx,
+                                  int64_t key);
+
+    /// \param prior_num_rows The number of rows in the table prior to the
+    /// modification.
+    virtual 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);
+
+    virtual void swap_rows(const Table*, size_t row_ndx_1, size_t row_ndx_2);
+    virtual void move_row(const Table*, size_t from_ndx, size_t to_ndx);
+    virtual void merge_rows(const Table*, size_t row_ndx, size_t new_row_ndx);
+    virtual void add_search_index(const Descriptor&, size_t col_ndx);
+    virtual void remove_search_index(const Descriptor&, size_t col_ndx);
+    virtual void set_link_type(const Table*, size_t col_ndx, LinkType);
+    virtual void clear_table(const Table*, size_t prior_num_rows);
+    virtual void optimize_table(const Table*);
+
+    virtual void link_list_set(const LinkView&, size_t link_ndx, size_t value);
+    virtual void link_list_insert(const LinkView&, size_t link_ndx, size_t value);
+    virtual void link_list_move(const LinkView&, size_t from_link_ndx, size_t to_link_ndx);
+    virtual void link_list_swap(const LinkView&, size_t link_ndx_1, size_t link_ndx_2);
+    virtual void link_list_erase(const LinkView&, size_t link_ndx);
+    virtual 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.
+
+    virtual void nullify_link(const Table*, size_t col_ndx, size_t ndx);
+    virtual 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<size_t> 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<const LinkView*> 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 <class InstructionHandler>
+    void parse(InputStream&, InstructionHandler&);
+
+    template <class InstructionHandler>
+    void parse(NoCopyInputStream&, InstructionHandler&);
+
+private:
+    util::Buffer<char> 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<size_t> m_path;
+
+    REALM_NORETURN void parser_error() const;
+
+    template <class InstructionHandler>
+    void parse_one(InstructionHandler&);
+    bool has_next() noexcept;
+
+    template <class T>
+    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 = realm::safe_copy_n(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 <class T>
+char* TransactLogEncoder::encode_int(char* ptr, T value)
+{
+    static_assert(std::numeric_limits<T>::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<T>::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<uchar*>(ptr) = uchar((1U << bits_per_byte) | unsigned(value & ((1U << bits_per_byte) - 1)));
+        ++ptr;
+        value >>= bits_per_byte;
+    }
+    *reinterpret_cast<uchar*>(ptr) = uchar(negative ? (1U << (bits_per_byte - 1)) | unsigned(value) : value);
+    return ++ptr;
+}
+
+template <class T>
+char* TransactLogEncoder::encode(char* ptr, T value)
+{
+    auto value_2 = value + 0; // Perform integral promotion
+    return encode_int(ptr, value_2);
+}
+
+template <>
+inline char* TransactLogEncoder::encode<char>(char* ptr, char value)
+{
+    // Write the char as-is without encoding.
+    *ptr++ = value;
+    return ptr;
+}
+
+template <>
+inline char* TransactLogEncoder::encode<Instruction>(char* ptr, Instruction inst)
+{
+    return encode<char>(ptr, inst);
+}
+
+template <>
+inline char* TransactLogEncoder::encode<bool>(char* ptr, bool value)
+{
+    return encode<char>(ptr, value);
+}
+
+template <>
+inline char* TransactLogEncoder::encode<float>(char* ptr, float value)
+{
+    static_assert(std::numeric_limits<float>::is_iec559 &&
+                      sizeof(float) * std::numeric_limits<unsigned char>::digits == 32,
+                  "Unsupported 'float' representation");
+    const char* val_ptr = reinterpret_cast<char*>(&value);
+    return realm::safe_copy_n(val_ptr, sizeof value, ptr);
+}
+
+template <>
+inline char* TransactLogEncoder::encode<double>(char* ptr, double value)
+{
+    static_assert(std::numeric_limits<double>::is_iec559 &&
+                      sizeof(double) * std::numeric_limits<unsigned char>::digits == 64,
+                  "Unsupported 'double' representation");
+    const char* val_ptr = reinterpret_cast<char*>(&value);
+    return realm::safe_copy_n(val_ptr, sizeof value, ptr);
+}
+
+template <>
+inline char* TransactLogEncoder::encode<DataType>(char* ptr, DataType type)
+{
+    return encode<char>(ptr, type);
+}
+
+template <>
+inline char* TransactLogEncoder::encode<StringData>(char* ptr, StringData s)
+{
+    ptr = encode_int(ptr, s.size());
+    return std::copy_n(s.data(), s.size(), ptr);
+}
+
+template <>
+inline char* TransactLogEncoder::encode<TransactLogEncoder::IntegerList>(char* ptr,
+                                                                         TransactLogEncoder::IntegerList list)
+{
+    IntegerColumnIterator i = std::get<0>(list);
+    IntegerColumnIterator end = std::get<1>(list);
+
+    while (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 != end)
+        ptr = encode_int(ptr, *i++);
+
+    return ptr;
+}
+
+template <>
+inline char* TransactLogEncoder::encode<TransactLogEncoder::UnsignedList>(char* ptr,
+                                                                          TransactLogEncoder::UnsignedList list)
+{
+    const size_t* i = std::get<0>(list);
+    const size_t* end = std::get<1>(list);
+
+    while (i != end)
+        ptr = encode_int(ptr, *i++);
+
+    return ptr;
+}
+
+template <class T>
+size_t TransactLogEncoder::max_size(T)
+{
+    return max_enc_bytes_per_num;
+}
+
+template <>
+inline size_t TransactLogEncoder::max_size(char)
+{
+    return 1;
+}
+
+template <>
+inline size_t TransactLogEncoder::max_size(bool)
+{
+    return 1;
+}
+
+template <>
+inline size_t TransactLogEncoder::max_size(Instruction)
+{
+    return 1;
+}
+
+template <>
+inline size_t TransactLogEncoder::max_size(DataType)
+{
+    return 1;
+}
+
+template <>
+inline size_t TransactLogEncoder::max_size(StringData s)
+{
+    return max_enc_bytes_per_num + s.size();
+}
+
+template <>
+inline size_t TransactLogEncoder::max_size<TransactLogEncoder::IntegerList>(IntegerList)
+{
+    // We only allocate space for 'max_numbers_per_chunk' at a time
+    return max_enc_bytes_per_num * max_numbers_per_chunk;
+}
+
+template <>
+inline size_t TransactLogEncoder::max_size<TransactLogEncoder::UnsignedList>(UnsignedList list)
+{
+    const size_t* begin = std::get<0>(list);
+    const size_t* end = std::get<1>(list);
+    // list contains (end - begin) elements
+    return max_enc_bytes_per_num * (end - begin);
+}
+
+template <class... L>
+void TransactLogEncoder::append_simple_instr(L... numbers)
+{
+    size_t max_required_bytes = max_size_list(numbers...);
+    char* ptr = reserve(max_required_bytes); // Throws
+    encode_list(ptr, numbers...);
+}
+
+template <class... L>
+void TransactLogEncoder::append_mixed_instr(Instruction instr, const Mixed& value, L... numbers)
+{
+    DataType type = value.get_type();
+    switch (type) {
+        case type_Int:
+            append_simple_instr(instr, numbers..., type, value.get_int()); // Throws
+            return;
+        case type_Bool:
+            append_simple_instr(instr, numbers..., type, value.get_bool()); // Throws
+            return;
+        case type_Float:
+            append_simple_instr(instr, numbers..., type, value.get_float()); // Throws
+            return;
+        case type_Double:
+            append_simple_instr(instr, numbers..., type, value.get_double()); // Throws
+            return;
+        case type_OldDateTime: {
+            auto value_2 = value.get_olddatetime().get_olddatetime();
+            append_simple_instr(instr, numbers..., type, value_2); // Throws
+            return;
+        }
+        case type_String: {
+            append_simple_instr(instr, numbers..., type, value.get_string()); // Throws
+            return;
+        }
+        case type_Binary: {
+            BinaryData value_2 = value.get_binary();
+            StringData value_3(value_2.data(), value_2.size());
+            append_simple_instr(instr, numbers..., type, 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();
+            append_simple_instr(instr, numbers..., type, seconds, nano_seconds); // Throws
+            return;
+        }
+        case type_Table:
+            append_simple_instr(instr, numbers..., type); // Throws
+            return;
+        case type_Mixed:
+            // Mixed in mixed is not possible
+            REALM_TERMINATE("Mixed in Mixed not possible");
+        case type_Link:
+        case type_LinkList:
+            // FIXME: Need to handle new link types here.
+            REALM_TERMINATE("Link types in Mixed not supported.");
+    }
+    REALM_TERMINATE("Invalid Mixed.");
+}
+
+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_simple_instr(instr_InsertGroupLevelTable, 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, 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_simple_instr(instr_RenameGroupLevelTable, 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::insert_column(size_t col_ndx, DataType type, StringData name, bool nullable)
+{
+    Instruction instr = (nullable ? instr_InsertNullableColumn : instr_InsertColumn);
+    append_simple_instr(instr, 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_simple_instr(instr_InsertLinkColumn, 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, 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, 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_simple_instr(instr_RenameColumn, 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::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, type_Int, col_ndx, ndx, prior_num_rows, value); // Throws
+    else
+        append_simple_instr(variant, 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, 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, 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, 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, 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_simple_instr(variant, type_String, col_ndx, ndx, prior_num_rows, value); // Throws
+        else
+            append_simple_instr(variant, 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_simple_instr(variant, 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, 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, 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, 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, value, type_Mixed, col_ndx, ndx); // 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, 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, set_null_sentinel(), col_ndx, ndx, prior_num_rows); // Throws
+    }
+    else {
+        append_simple_instr(variant, 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, 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_simple_instr(instr_InsertSubstring, 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, 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, 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::add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx,
+                                                 int64_t key)
+{
+    append_simple_instr(instr_AddRowWithKey, row_ndx, prior_num_rows, key_col_ndx, key); // Throws
+    return true;
+}
+
+inline void TransactLogConvenientEncoder::add_row_with_key(const Table* t, size_t row_ndx, size_t prior_num_rows,
+                                                           size_t key_col_ndx, int64_t key)
+{
+    select_table(t);                                          // Throws
+    m_encoder.add_row_with_key(row_ndx, prior_num_rows, key_col_ndx, key); // 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, 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, 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::move_row(size_t from_ndx, size_t to_ndx)
+{
+    append_simple_instr(instr_MoveRow, from_ndx, to_ndx); // Throws
+    return true;
+}
+
+inline void TransactLogConvenientEncoder::move_row(const Table* t, size_t from_ndx, size_t to_ndx)
+{
+    REALM_ASSERT(from_ndx != to_ndx);
+    select_table(t); // Throws
+    m_encoder.move_row(from_ndx, to_ndx);
+}
+
+inline bool TransactLogEncoder::merge_rows(size_t row_ndx, size_t new_row_ndx)
+{
+    append_simple_instr(instr_MergeRows, 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, col_ndx); // Throws
+    return true;
+}
+
+inline void TransactLogConvenientEncoder::add_search_index(const Descriptor& desc, size_t col_ndx)
+{
+    select_desc(desc);                   // Throws
+    m_encoder.add_search_index(col_ndx); // Throws
+}
+
+
+inline bool TransactLogEncoder::remove_search_index(size_t col_ndx)
+{
+    append_simple_instr(instr_RemoveSearchIndex, col_ndx); // Throws
+    return true;
+}
+
+inline void TransactLogConvenientEncoder::remove_search_index(const Descriptor& desc, size_t col_ndx)
+{
+    select_desc(desc);                      // 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, 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(size_t old_size)
+{
+    append_simple_instr(instr_ClearTable, old_size); // Throws
+    return true;
+}
+
+inline void TransactLogConvenientEncoder::clear_table(const Table* t, size_t prior_num_rows)
+{
+    select_table(t);         // Throws
+    m_encoder.clear_table(prior_num_rows); // Throws
+}
+
+inline bool TransactLogEncoder::optimize_table()
+{
+    append_simple_instr(instr_OptimizeTable); // 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, 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, 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)
+{
+    size_t num_values = values.size();
+    append_simple_instr(
+        instr_LinkListSetAll, num_values,
+        std::make_tuple(IntegerColumnIterator(&values, 0), IntegerColumnIterator(&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, 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, 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, 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, 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, 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 <class InstructionHandler>
+void TransactLogParser::parse(NoCopyInputStream& in, InstructionHandler& handler)
+{
+    m_input = &in;
+    m_input_begin = m_input_end = nullptr;
+
+    while (has_next())
+        parse_one(handler); // Throws
+}
+
+template <class InstructionHandler>
+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 <class InstructionHandler>
+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<int>();          // Throws
+            size_t col_ndx = read_int<size_t>(); // Throws
+            size_t row_ndx = read_int<size_t>(); // Throws
+            size_t prior_num_rows = 0;
+            if (REALM_UNLIKELY(instr == instr_SetUnique))
+                prior_num_rows = read_int<size_t>(); // 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<int64_t>();                             // 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<int_fast64_t>();                // Throws
+                    if (!handler.set_olddatetime(col_ndx, row_ndx, value, instr)) // Throws
+                        parser_error();
+                    return;
+                }
+                case type_Timestamp: {
+                    int64_t seconds = read_int<int64_t>();     // Throws
+                    int32_t nanoseconds = read_int<int32_t>(); // 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<size_t>(); // 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<size_t>();                                     // 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<size_t>();           // Throws
+            size_t row_ndx = read_int<size_t>();           // Throws
+            int_fast64_t value = read_int<int64_t>();      // Throws
+            if (!handler.add_int(col_ndx, row_ndx, value)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_NullifyLink: {
+            size_t col_ndx = read_int<size_t>();                                 // Throws
+            size_t row_ndx = read_int<size_t>();                                 // Throws
+            size_t target_group_level_ndx = read_int<size_t>();                  // 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<size_t>();                         // Throws
+            size_t row_ndx = read_int<size_t>();                         // Throws
+            size_t pos = read_int<size_t>();                             // 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<size_t>();                       // Throws
+            size_t row_ndx = read_int<size_t>();                       // Throws
+            size_t pos = read_int<size_t>();                           // Throws
+            size_t size = read_int<size_t>();                          // Throws
+            if (!handler.erase_substring(col_ndx, row_ndx, pos, size)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_InsertEmptyRows: {
+            size_t row_ndx = read_int<size_t>();                                                    // Throws
+            size_t num_rows_to_insert = read_int<size_t>();                                         // Throws
+            size_t prior_num_rows = read_int<size_t>();                                             // 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_AddRowWithKey: {
+            size_t row_ndx = read_int<size_t>();                         // Throws
+            size_t prior_num_rows = read_int<size_t>();                  // Throws
+            size_t key_col_ndx = read_int<size_t>();                     // Throws
+            int64_t key = read_int<int64_t>();                           // Throws
+            if (!handler.add_row_with_key(row_ndx, prior_num_rows, key_col_ndx, key)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_EraseRows: {
+            size_t row_ndx = read_int<size_t>();                                            // Throws
+            size_t num_rows_to_erase = read_int<size_t>();                                  // Throws
+            size_t prior_num_rows = read_int<size_t>();                                     // 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<size_t>();        // Throws
+            size_t row_ndx_2 = read_int<size_t>();        // Throws
+            if (!handler.swap_rows(row_ndx_1, row_ndx_2)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_MoveRow: {
+            size_t from_ndx = read_int<size_t>();    // Throws
+            size_t to_ndx = read_int<size_t>();      // Throws
+            if (!handler.move_row(from_ndx, to_ndx)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_MergeRows: {
+            size_t row_ndx = read_int<size_t>();           // Throws
+            size_t new_row_ndx = read_int<size_t>();       // Throws
+            if (!handler.merge_rows(row_ndx, new_row_ndx)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_SelectTable: {
+            int levels = read_int<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<size_t>(); // Throws
+            for (int i = 0; i != levels; ++i) {
+                size_t col_ndx = read_int<size_t>(); // Throws
+                size_t row_ndx = read_int<size_t>(); // 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: {
+            size_t old_size = read_int<size_t>();   // Throws
+            if (!handler.clear_table(old_size)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_LinkListSet: {
+            size_t link_ndx = read_int<size_t>();                    // Throws
+            size_t value = read_int<size_t>();                       // Throws
+            size_t prior_size = read_int<size_t>();                  // 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<size_t>(); // Throws
+            for (size_t i = 0; i < size; i++) {
+                size_t link = read_int<size_t>();          // Throws
+                if (!handler.link_list_set(i, link, size)) // Throws
+                    parser_error();
+            }
+            return;
+        }
+        case instr_LinkListInsert: {
+            size_t link_ndx = read_int<size_t>();                       // Throws
+            size_t value = read_int<size_t>();                          // Throws
+            size_t prior_size = read_int<size_t>();                     // 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<size_t>();               // Throws
+            size_t to_link_ndx = read_int<size_t>();                 // 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<size_t>();             // Throws
+            size_t link2_ndx = read_int<size_t>();             // Throws
+            if (!handler.link_list_swap(link1_ndx, link2_ndx)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_LinkListErase: {
+            size_t link_ndx = read_int<size_t>();               // Throws
+            size_t prior_size = read_int<size_t>();             // Throws
+            if (!handler.link_list_erase(link_ndx, prior_size)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_LinkListNullify: {
+            size_t link_ndx = read_int<size_t>();                 // Throws
+            size_t prior_size = read_int<size_t>();               // Throws
+            if (!handler.link_list_nullify(link_ndx, prior_size)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_LinkListClear: {
+            size_t old_list_size = read_int<size_t>();   // Throws
+            if (!handler.link_list_clear(old_list_size)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_SelectLinkList: {
+            size_t col_ndx = read_int<size_t>();                                     // Throws
+            size_t row_ndx = read_int<size_t>();                                     // Throws
+            size_t target_group_level_ndx = read_int<size_t>();                      // Throws
+            if (!handler.select_link_list(col_ndx, row_ndx, target_group_level_ndx)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_MoveColumn: {
+            // FIXME: remove this in the next breaking change.
+            // This instruction is no longer supported and not used by either
+            // bindings or sync, so if we see it here, there was a problem parsing.
+            parser_error();
+            return;
+        }
+        case instr_AddSearchIndex: {
+            size_t col_ndx = read_int<size_t>();    // Throws
+            if (!handler.add_search_index(col_ndx)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_RemoveSearchIndex: {
+            size_t col_ndx = read_int<size_t>();       // Throws
+            if (!handler.remove_search_index(col_ndx)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_SetLinkType: {
+            size_t col_ndx = read_int<size_t>(); // Throws
+            int link_type = read_int<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<size_t>(); // Throws
+            int type = read_int<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_Mixed))) {
+                // Nullability not supported for 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<size_t>(); // Throws
+            int type = read_int<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<size_t>(); // Throws
+            size_t backlink_col_ndx = read_int<size_t>();      // 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<size_t>(); // Throws
+            if (!handler.erase_column(col_ndx))  // Throws
+                parser_error();
+            return;
+        }
+        case instr_EraseLinkColumn: {
+            size_t col_ndx = read_int<size_t>();                                              // Throws
+            size_t link_target_table_ndx = read_int<size_t>();                                // Throws
+            size_t backlink_col_ndx = read_int<size_t>();                                     // 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<size_t>();            // Throws
+            StringData name = read_string(m_string_buffer); // Throws
+            if (!handler.rename_column(col_ndx, name))      // Throws
+                parser_error();
+            return;
+        }
+        case instr_SelectDescriptor: {
+            int levels = read_int<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<size_t>(); // Throws
+                path[i] = col_ndx;
+            }
+            if (!handler.select_descriptor(levels, path)) // Throws
+                parser_error();
+            return;
+        }
+        case instr_InsertGroupLevelTable: {
+            size_t table_ndx = read_int<size_t>();                              // Throws
+            size_t num_tables = read_int<size_t>();                             // 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<size_t>();                             // Throws
+            size_t prior_num_tables = read_int<size_t>();                      // 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<size_t>();                      // 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: {
+            // This instruction is no longer supported and not used by either
+            // bindings or sync, so if we see it here, there was a problem parsing.
+            // FIXME: remove this in the next breaking change.
+            parser_error();
+            return;
+        }
+        case instr_OptimizeTable: {
+            if (!handler.optimize_table()) // Throws
+                parser_error();
+            return;
+        }
+    }
+
+    throw BadTransactLog();
+}
+
+
+template <class T>
+T TransactLogParser::read_int()
+{
+    T value = 0;
+    int part = 0;
+    const int max_bytes = (std::numeric_limits<T>::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<unsigned char>(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;
+        realm::safe_copy_n(m_input_begin, avail, data);
+        if (!next_input_buffer())
+            throw BadTransactLog();
+        data += avail;
+        size -= avail;
+    }
+    const char* to = m_input_begin + size;
+    realm::safe_copy_n(m_input_begin, size, 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<char>();
+}
+
+
+inline float TransactLogParser::read_float()
+{
+    static_assert(std::numeric_limits<float>::is_iec559 &&
+                      sizeof(float) * std::numeric_limits<unsigned char>::digits == 32,
+                  "Unsupported 'float' representation");
+    float value;
+    read_bytes(reinterpret_cast<char*>(&value), sizeof value); // Throws
+    return value;
+}
+
+
+inline double TransactLogParser::read_double()
+{
+    static_assert(std::numeric_limits<double>::is_iec559 &&
+                      sizeof(double) * std::numeric_limits<unsigned char>::digits == 64,
+                  "Unsupported 'double' representation");
+    double value;
+    read_bytes(reinterpret_cast<char*>(&value), sizeof value); // Throws
+    return value;
+}
+
+
+inline StringData TransactLogParser::read_string(util::StringBuffer& buf)
+{
+    size_t size = read_int<size_t>(); // 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()
+{
+    int64_t seconds = read_int<int64_t>();     // Throws
+    int32_t nanoseconds = read_int<int32_t>(); // Throws
+    return Timestamp(seconds, nanoseconds);
+}
+
+
+inline BinaryData TransactLogParser::read_binary(util::StringBuffer& buf)
+{
+    size_t size = read_int<size_t>(); // Throws
+
+    return read_buffer(buf, size);
+}
+
+
+inline void TransactLogParser::read_mixed(Mixed* mixed)
+{
+    DataType type = DataType(read_int<int>()); // Throws
+    switch (type) {
+        case type_Int: {
+            int_fast64_t value = read_int<int64_t>(); // 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<int_fast64_t>(); // 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 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 add_row_with_key(size_t row_ndx, size_t prior_num_rows, size_t, int64_t)
+    {
+        bool unordered = true;
+        m_encoder.erase_rows(row_ndx, 1, prior_num_rows + 1, 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 move_row(size_t from_ndx, size_t to_ndx)
+    {
+        m_encoder.move_row(to_ndx, from_ndx);
+        append_instruction();
+        return true;
+    }
+
+    bool merge_rows(size_t row_ndx, size_t new_row_ndx)
+    {
+        // There is no instruction we can generate here to change back.
+        // However, we do need to refresh accessors for any tables
+        // connected through backlinks, so we generate updates on each
+        // affected row by merging to itself.
+        m_encoder.merge_rows(row_ndx, row_ndx);
+        append_instruction();
+        m_encoder.merge_rows(new_row_ndx, new_row_ndx);
+        append_instruction();
+        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(size_t old_size)
+    {
+        bool unordered = false;
+        m_encoder.insert_empty_rows(0, old_size, 0, unordered);
+        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 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<Instr> 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<TransactReverser::Instr>& m_instr_order;
+    size_t m_current;
+};
+
+} // namespace _impl
+} // namespace realm
+
+#endif // REALM_IMPL_TRANSACT_LOG_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/index_string.hpp b/iOS/Pods/Realm/include/core/realm/index_string.hpp
new file mode 100644 (file)
index 0000000..b357c19
--- /dev/null
@@ -0,0 +1,606 @@
+/*************************************************************************
+ *
+ * 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 <cstring>
+#include <memory>
+#include <array>
+
+#include <realm/array.hpp>
+#include <realm/column_fwd.hpp>
+
+/*
+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 IndexArray : public Array {
+public:
+    IndexArray(Allocator& allocator)
+        : Array(allocator)
+    {
+    }
+
+    size_t index_string_find_first(StringData value, ColumnBase* column) const;
+    void index_string_find_all(IntegerColumn& result, StringData value, ColumnBase* column, bool case_insensitive = false) 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;
+
+private:
+    template <IndexMethod>
+    size_t from_list(StringData value, InternalFindResult& result_ref, const IntegerColumn& rows,
+                     ColumnBase* column) const;
+
+    void from_list_all(StringData value, IntegerColumn& result, const IntegerColumn& rows, ColumnBase* column) const;
+
+    void from_list_all_ins(StringData value, std::vector<size_t>& result, const IntegerColumn& rows,
+                           ColumnBase* column) const;
+
+    template <IndexMethod method>
+    size_t index_string(StringData value, InternalFindResult& result_ref, ColumnBase* column) const;
+
+    void index_string_all(StringData value, IntegerColumn& result, ColumnBase* column) const;
+
+    void index_string_all_ins(StringData value, IntegerColumn& result, ColumnBase* column) const;
+};
+
+
+class StringIndex {
+public:
+    StringIndex(ColumnBase* target_column, Allocator&);
+    StringIndex(ref_type, ArrayParent*, size_t ndx_in_parent, ColumnBase* target_column, Allocator&);
+    ~StringIndex() noexcept
+    {
+    }
+
+    static ref_type create_empty(Allocator& alloc);
+
+    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<char, string_conversion_buffer_size>;
+
+    bool is_empty() const;
+
+    template <class T>
+    void insert(size_t row_ndx, T value, size_t num_rows, bool is_append);
+    template <class T>
+    void insert(size_t row_ndx, util::Optional<T> value, size_t num_rows, bool is_append);
+
+    template <class T>
+    void set(size_t row_ndx, T new_value);
+    template <class T>
+    void set(size_t row_ndx, util::Optional<T> new_value);
+
+    template <class T>
+    void erase(size_t row_ndx, bool is_last);
+
+    template <class T>
+    size_t find_first(T value) const;
+    template <class T>
+    void find_all(IntegerColumn& result, T value, bool case_insensitive = false) const;
+    template <class T>
+    FindRes find_all_no_copy(T value, InternalFindResult& result) const;
+    template <class T>
+    size_t count(T value) const;
+    template <class T>
+    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;
+
+    void verify() const;
+#ifdef REALM_DEBUG
+    template <typename T>
+    void verify_entries(const T& column) const;
+    void do_dump_node_structure(std::ostream&, int) const;
+    void to_dot() const;
+    void to_dot(std::ostream&, StringData title = StringData()) const;
+    void to_dot_2(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<IndexArray> m_array;
+    ColumnBase* m_target_column;
+
+    struct inner_node_tag {
+    };
+    StringIndex(inner_node_tag, Allocator&);
+
+    static IndexArray* 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);
+    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 <class T>
+struct GetIndexData;
+
+template <>
+struct GetIndexData<int64_t> {
+    static StringData get_index_data(const int64_t& value, StringIndex::StringConversionBuffer& buffer)
+    {
+        const char* c = reinterpret_cast<const char*>(&value);
+        realm::safe_copy_n(c, sizeof(int64_t), buffer.data());
+        return StringData{buffer.data(), sizeof(int64_t)};
+    }
+};
+
+template <>
+struct GetIndexData<StringData> {
+    static StringData get_index_data(StringData data, StringIndex::StringConversionBuffer&)
+    {
+        return data;
+    }
+};
+
+template <>
+struct GetIndexData<null> {
+    static StringData get_index_data(null, StringIndex::StringConversionBuffer&)
+    {
+        return null{};
+    }
+};
+
+template <>
+struct GetIndexData<Timestamp> {
+    static StringData get_index_data(const Timestamp&, StringIndex::StringConversionBuffer&);
+};
+
+template <class T>
+struct GetIndexData<util::Optional<T>> {
+    static StringData get_index_data(const util::Optional<T>& value, StringIndex::StringConversionBuffer& buffer)
+    {
+        if (value)
+            return GetIndexData<T>::get_index_data(*value, buffer);
+        return null{};
+    }
+};
+
+template <>
+struct GetIndexData<float> {
+    static StringData get_index_data(float, StringIndex::StringConversionBuffer&)
+    {
+        REALM_ASSERT_RELEASE(false); // LCOV_EXCL_LINE; Index on float not supported
+    }
+};
+
+template <>
+struct GetIndexData<double> {
+    static StringData get_index_data(double, StringIndex::StringConversionBuffer&)
+    {
+        REALM_ASSERT_RELEASE(false); // LCOV_EXCL_LINE; Index on float not supported
+    }
+};
+
+template <>
+struct GetIndexData<const char*> : GetIndexData<StringData> {
+};
+
+// 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 <class T>
+inline StringData to_str(T&& value, StringIndex::StringConversionBuffer& buffer)
+{
+    return GetIndexData<typename std::remove_reference<T>::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)
+{
+}
+
+inline StringIndex::StringIndex(ref_type ref, ArrayParent* parent, size_t ndx_in_parent, ColumnBase* target_column,
+                                Allocator& alloc)
+    : m_array(new IndexArray(alloc))
+    , m_target_column(target_column)
+{
+    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)
+{
+}
+
+// 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<unsigned char>(str[3])) << 0);
+three:
+    key |= (key_type(static_cast<unsigned char>(str[2])) << 8);
+two:
+    key |= (key_type(static_cast<unsigned char>(str[1])) << 16);
+one:
+    key |= (key_type(static_cast<unsigned char>(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 <class T>
+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 <class T>
+void StringIndex::insert(size_t row_ndx, util::Optional<T> 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 <class T>
+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<T>(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 <class T>
+void StringIndex::set(size_t row_ndx, util::Optional<T> new_value)
+{
+    if (new_value) {
+        set(row_ndx, *new_value);
+    }
+    else {
+        set(row_ndx, null{});
+    }
+}
+
+template <class T>
+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 <class T>
+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 <class T>
+void StringIndex::find_all(IntegerColumn& result, T value, bool case_insensitive) const
+{
+    // Use direct access method
+    StringConversionBuffer buffer;
+    return m_array->index_string_find_all(result, to_str(value, buffer), m_target_column, case_insensitive);
+}
+
+template <class T>
+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 <class T>
+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 <class T>
+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/iOS/Pods/Realm/include/core/realm/lang_bind_helper.hpp b/iOS/Pods/Realm/include/core/realm/lang_bind_helper.hpp
new file mode 100644 (file)
index 0000000..de287bf
--- /dev/null
@@ -0,0 +1,378 @@
+/*************************************************************************
+ *
+ * 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 <cstddef>
+
+#include <realm/table.hpp>
+#include <realm/table_view.hpp>
+#include <realm/link_view.hpp>
+#include <realm/group.hpp>
+#include <realm/group_shared.hpp>
+
+#include <realm/replication.hpp>
+
+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 <class O>
+    static void advance_read(SharedGroup&, O&& observer, VersionID = VersionID());
+    static void promote_to_write(SharedGroup&);
+    template <class O>
+    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 <class O>
+    static void rollback_and_continue_as_read(SharedGroup&, O&& observer);
+
+    //@}
+
+    /// Returns the name of the specified data type. Examples:
+    ///
+    /// <pre>
+    ///
+    ///   type_Int          ->  "int"
+    ///   type_Bool         ->  "bool"
+    ///   type_Float        ->  "float"
+    ///   ...
+    ///
+    /// </pre>
+    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)
+{
+    TableRef subtab = t->get_subtable_tableref(column_ndx, row_ndx); // Throws
+    return subtab.release();
+}
+
+inline const Table* LangBindHelper::get_subtable_ptr(const Table* t, size_t column_ndx, size_t row_ndx)
+{
+    ConstTableRef subtab = t->get_subtable_tableref(column_ndx, row_ndx); // Throws
+    return subtab.release();
+}
+
+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 <class O>
+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 <class O>
+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 <class O>
+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/iOS/Pods/Realm/include/core/realm/link_view.hpp b/iOS/Pods/Realm/include/core/realm/link_view.hpp
new file mode 100644 (file)
index 0000000..14be525
--- /dev/null
@@ -0,0 +1,397 @@
+/*************************************************************************
+ *
+ * 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 <realm/column.hpp>
+#include <realm/column_linklist.hpp>
+#include <realm/link_view_fwd.hpp>
+#include <realm/table.hpp>
+
+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<LinkView> {
+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(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<HandoverPatch>& patch);
+    static LinkViewRef create_from_and_consume_patch(std::unique_ptr<HandoverPatch>& 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<LinkView> create(Table* origin_table, LinkListColumn&, size_t row_ndx);
+    static std::shared_ptr<LinkView> create_detached();
+
+    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);
+    LinkView(const ctor_cookie&);
+};
+
+
+// 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)
+{
+    m_row_indexes.set_parent(m_origin_column, row_ndx);
+    m_row_indexes.init_from_parent();
+}
+
+// create a detached LinkView. Only partially initialized, as it will never be used for
+// anything, but indicating that it is detached.
+inline LinkView::LinkView(const ctor_cookie&)
+    : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) // Throws
+    , m_origin_table(TableRef())
+    , m_origin_column(nullptr)
+{
+}
+
+inline std::shared_ptr<LinkView> LinkView::create(Table* origin_table, LinkListColumn& column, size_t row_ndx)
+{
+    return std::make_shared<LinkView>(ctor_cookie(), origin_table, column, row_ndx);
+}
+
+inline std::shared_ptr<LinkView> LinkView::create_detached()
+{
+    return std::make_shared<LinkView>(ctor_cookie());
+}
+
+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<bool>(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<LinkView*>(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
+{
+    set_origin_row_index(new_row_ndx);
+    m_row_indexes.init_from_parent();
+}
+
+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);
+    }
+
+    static const LinkListColumn& get_origin_column(const LinkView& list)
+    {
+        REALM_ASSERT(list.is_attached());
+        return *list.m_origin_column;
+    }
+};
+
+} // namespace realm
+
+#endif // REALM_LINK_VIEW_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/link_view_fwd.hpp b/iOS/Pods/Realm/include/core/realm/link_view_fwd.hpp
new file mode 100644 (file)
index 0000000..cba8801
--- /dev/null
@@ -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 <memory>
+
+namespace realm {
+
+class LinkView;
+using LinkViewRef = std::shared_ptr<LinkView>;
+using ConstLinkViewRef = std::shared_ptr<const LinkView>;
+
+} // namespace realm
+
+#endif // REALM_LINK_VIEW_FWD_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/metrics/metric_timer.hpp b/iOS/Pods/Realm/include/core/realm/metrics/metric_timer.hpp
new file mode 100644 (file)
index 0000000..5a8523c
--- /dev/null
@@ -0,0 +1,103 @@
+/*************************************************************************
+ *
+ * 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_METRIC_TIMER_HPP
+#define REALM_METRIC_TIMER_HPP
+
+#include <realm/util/features.h>
+
+#include <chrono>
+#include <memory>
+#include <ostream>
+
+#if REALM_METRICS
+
+namespace realm {
+namespace metrics {
+
+
+class MetricTimerResult
+{
+public:
+    MetricTimerResult();
+    ~MetricTimerResult();
+    double get_elapsed_seconds() const;
+    virtual void report_seconds(double time);
+protected:
+    double m_elapsed_seconds;
+};
+
+
+class MetricTimer {
+public:
+    MetricTimer(std::shared_ptr<MetricTimerResult> destination = nullptr);
+    ~MetricTimer();
+
+    void reset();
+
+    /// Returns elapsed time in seconds since last call to reset().
+    double get_elapsed_time() const;
+    /// Same as get_elapsed_time().
+    operator double() const;
+
+    /// Format the elapsed time on the form 0h00m, 00m00s, 00.00s, or
+    /// 000.0ms depending on magnitude.
+    static void format(double seconds, std::ostream&);
+
+    static std::string format(double seconds);
+
+private:
+    using clock_type = std::chrono::high_resolution_clock;
+    using time_point = std::chrono::time_point<clock_type>;
+    time_point m_start;
+    time_point m_paused_at;
+    std::shared_ptr<MetricTimerResult> m_dest;
+
+    time_point get_timer_ticks() const;
+    double calc_elapsed_seconds(time_point begin, time_point end) const;
+};
+
+
+inline void MetricTimer::reset()
+{
+    m_start = get_timer_ticks();
+}
+
+inline double MetricTimer::get_elapsed_time() const
+{
+    return calc_elapsed_seconds(m_start, get_timer_ticks());
+}
+
+inline MetricTimer::operator double() const
+{
+    return get_elapsed_time();
+}
+
+inline std::ostream& operator<<(std::ostream& out, const MetricTimer& timer)
+{
+    MetricTimer::format(timer, out);
+    return out;
+}
+
+
+} // namespace metrics
+} // namespace realm
+
+#endif // REALM_METRICS
+
+#endif // REALM_METRIC_TIMER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/metrics/metrics.hpp b/iOS/Pods/Realm/include/core/realm/metrics/metrics.hpp
new file mode 100644 (file)
index 0000000..73e8d59
--- /dev/null
@@ -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_METRICS_HPP
+#define REALM_METRICS_HPP
+
+#include <memory>
+#include <vector>
+
+#include <realm/metrics/query_info.hpp>
+#include <realm/metrics/transaction_info.hpp>
+#include <realm/util/features.h>
+
+namespace realm {
+
+class Group;
+
+namespace metrics {
+
+#if REALM_METRICS
+
+class Metrics {
+public:
+    Metrics();
+    ~Metrics() noexcept;
+    size_t num_query_metrics() const;
+    size_t num_transaction_metrics() const;
+
+    void add_query(QueryInfo info);
+    void add_transaction(TransactionInfo info);
+
+    void start_read_transaction();
+    void start_write_transaction();
+    void end_read_transaction(size_t total_size, size_t free_space, size_t num_objects, size_t num_versions);
+    void end_write_transaction(size_t total_size, size_t free_space, size_t num_objects, size_t num_versions);
+    static std::unique_ptr<MetricTimer> report_fsync_time(const Group& g);
+    static std::unique_ptr<MetricTimer> report_write_time(const Group& g);
+
+    using QueryInfoList = std::vector<QueryInfo>;
+    using TransactionInfoList = std::vector<TransactionInfo>;
+
+    // Get the list of metric objects tracked since the last take
+    std::unique_ptr<QueryInfoList> take_queries();
+    std::unique_ptr<TransactionInfoList> take_transactions();
+private:
+    std::unique_ptr<QueryInfoList> m_query_info;
+    std::unique_ptr<TransactionInfoList> m_transaction_info;
+
+    std::unique_ptr<TransactionInfo> m_pending_read;
+    std::unique_ptr<TransactionInfo> m_pending_write;
+};
+
+
+#else
+
+class Metrics
+{
+};
+
+#endif // REALM_METRICS
+
+} // namespace metrics
+} // namespace realm
+
+
+
+#endif // REALM_METRICS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/metrics/query_info.hpp b/iOS/Pods/Realm/include/core/realm/metrics/query_info.hpp
new file mode 100644 (file)
index 0000000..020eaa5
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_QUERY_INFO_HPP
+#define REALM_QUERY_INFO_HPP
+
+#include <memory>
+#include <string>
+#include <sstream>
+
+#include <realm/array.hpp>
+#include <realm/util/features.h>
+#include <realm/metrics/metric_timer.hpp>
+
+#if REALM_METRICS
+
+namespace realm {
+
+class Query; // forward declare in namespace realm
+
+namespace metrics {
+
+class QueryInfo {
+public:
+
+    enum QueryType {
+        type_Find,
+        type_FindAll,
+        type_Count,
+        type_Sum,
+        type_Average,
+        type_Maximum,
+        type_Minimum,
+        type_Invalid
+    };
+
+    QueryInfo(const Query* query, QueryType type);
+    ~QueryInfo() noexcept;
+
+    std::string get_description() const;
+    QueryType get_type() const;
+    double get_query_time() const;
+
+    static std::unique_ptr<MetricTimer> track(const Query* query, QueryType type);
+    static QueryType type_from_action(Action action);
+
+private:
+    std::string m_description;
+    QueryType m_type;
+    std::shared_ptr<MetricTimerResult> m_query_time;
+};
+
+} // namespace metrics
+} // namespace realm
+
+#endif // REALM_METRICS
+#endif // REALM_QUERY_INFO_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/metrics/transaction_info.hpp b/iOS/Pods/Realm/include/core/realm/metrics/transaction_info.hpp
new file mode 100644 (file)
index 0000000..c647e34
--- /dev/null
@@ -0,0 +1,77 @@
+/*************************************************************************
+ *
+ * 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_TRANSACTION_INFO_HPP
+#define REALM_TRANSACTION_INFO_HPP
+
+#include <memory>
+#include <string>
+
+#include <realm/metrics/metric_timer.hpp>
+#include <realm/util/features.h>
+
+#if REALM_METRICS
+
+namespace realm {
+namespace metrics {
+
+class Metrics;
+
+class TransactionInfo {
+public:
+    enum TransactionType {
+        read_transaction,
+        write_transaction
+    };
+    TransactionInfo(TransactionType type);
+    TransactionInfo(const TransactionInfo&) = default;
+    ~TransactionInfo() noexcept;
+
+    TransactionType get_transaction_type() const;
+    // the transaction time is a total amount which includes fsync_time + write_time + user_time
+    double get_transaction_time() const;
+    double get_fsync_time() const;
+    double get_write_time() const;
+    size_t get_disk_size() const;
+    size_t get_free_space() const;
+    size_t get_total_objects() const;
+    size_t get_num_available_versions() const;
+
+private:
+    MetricTimerResult m_transaction_time;
+    std::shared_ptr<MetricTimerResult> m_fsync_time;
+    std::shared_ptr<MetricTimerResult> m_write_time;
+    MetricTimer m_transact_timer;
+
+    size_t m_realm_disk_size;
+    size_t m_realm_free_space;
+    size_t m_total_objects;
+    TransactionType m_type;
+    size_t m_num_versions;
+
+    friend class Metrics;
+    void update_stats(size_t disk_size, size_t free_space, size_t total_objects, size_t available_versions);
+    void finish_timer();
+};
+
+} // namespace metrics
+} // namespace realm
+
+#endif // REALM_METRICS
+
+#endif // REALM_TRANSACTION_INFO_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/mixed.hpp b/iOS/Pods/Realm/include/core/realm/mixed.hpp
new file mode 100644 (file)
index 0000000..f50f4a0
--- /dev/null
@@ -0,0 +1,680 @@
+/*************************************************************************
+ *
+ * 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 <cstdint> // int64_t - not part of C++03, not even required by C++11 (see C++11 section 18.4.1)
+
+#include <cstddef> // size_t
+#include <cstring>
+
+#include <realm/binary_data.hpp>
+#include <realm/data_type.hpp>
+#include <realm/olddatetime.hpp>
+#include <realm/string_data.hpp>
+#include <realm/timestamp.hpp>
+#include <realm/util/assert.hpp>
+#include <realm/utilities.hpp>
+
+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 <class Ch, class Tr>
+    friend std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>&, 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<Mixed>. 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 <class T>
+bool operator==(Wrap<Mixed>, const T&) noexcept;
+template <class T>
+bool operator!=(Wrap<Mixed>, const T&) noexcept;
+template <class T>
+bool operator==(const T&, Wrap<Mixed>) noexcept;
+template <class T>
+bool operator!=(const T&, Wrap<Mixed>) noexcept;
+
+// Compare mixed with boolean
+bool operator==(Wrap<Mixed>, bool) noexcept;
+bool operator!=(Wrap<Mixed>, bool) noexcept;
+bool operator==(bool, Wrap<Mixed>) noexcept;
+bool operator!=(bool, Wrap<Mixed>) noexcept;
+
+// Compare mixed with float
+bool operator==(Wrap<Mixed>, float);
+bool operator!=(Wrap<Mixed>, float);
+bool operator==(float, Wrap<Mixed>);
+bool operator!=(float, Wrap<Mixed>);
+
+// Compare mixed with double
+bool operator==(Wrap<Mixed>, double);
+bool operator!=(Wrap<Mixed>, double);
+bool operator==(double, Wrap<Mixed>);
+bool operator!=(double, Wrap<Mixed>);
+
+// Compare mixed with string
+bool operator==(Wrap<Mixed>, StringData) noexcept;
+bool operator!=(Wrap<Mixed>, StringData) noexcept;
+bool operator==(StringData, Wrap<Mixed>) noexcept;
+bool operator!=(StringData, Wrap<Mixed>) noexcept;
+bool operator==(Wrap<Mixed>, const char* c_str) noexcept;
+bool operator!=(Wrap<Mixed>, const char* c_str) noexcept;
+bool operator==(const char* c_str, Wrap<Mixed>) noexcept;
+bool operator!=(const char* c_str, Wrap<Mixed>) noexcept;
+bool operator==(Wrap<Mixed>, char* c_str) noexcept;
+bool operator!=(Wrap<Mixed>, char* c_str) noexcept;
+bool operator==(char* c_str, Wrap<Mixed>) noexcept;
+bool operator!=(char* c_str, Wrap<Mixed>) noexcept;
+
+// Compare mixed with binary data
+bool operator==(Wrap<Mixed>, BinaryData) noexcept;
+bool operator!=(Wrap<Mixed>, BinaryData) noexcept;
+bool operator==(BinaryData, Wrap<Mixed>) noexcept;
+bool operator!=(BinaryData, Wrap<Mixed>) noexcept;
+
+// Compare mixed with date
+bool operator==(Wrap<Mixed>, OldDateTime) noexcept;
+bool operator!=(Wrap<Mixed>, OldDateTime) noexcept;
+bool operator==(OldDateTime, Wrap<Mixed>) noexcept;
+bool operator!=(OldDateTime, Wrap<Mixed>) noexcept;
+
+// Compare mixed with Timestamp
+bool operator==(Wrap<Mixed>, Timestamp) noexcept;
+bool operator!=(Wrap<Mixed>, Timestamp) noexcept;
+bool operator==(Timestamp, Wrap<Mixed>) noexcept;
+bool operator!=(Timestamp, Wrap<Mixed>) 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();
+}
+
+inline void Mixed::set_timestamp(Timestamp v) noexcept
+{
+    m_type = type_Timestamp;
+    m_timestamp = v;
+}
+
+// LCOV_EXCL_START
+template <class Ch, class Tr>
+inline std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& 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 <class T>
+inline bool operator==(Wrap<Mixed> a, const T& b) noexcept
+{
+    return Mixed(a).get_type() == type_Int && Mixed(a).get_int() == b;
+}
+
+template <class T>
+inline bool operator!=(Wrap<Mixed> a, const T& b) noexcept
+{
+    return Mixed(a).get_type() != type_Int || Mixed(a).get_int() != b;
+}
+
+template <class T>
+inline bool operator==(const T& a, Wrap<Mixed> b) noexcept
+{
+    return type_Int == Mixed(b).get_type() && a == Mixed(b).get_int();
+}
+
+template <class T>
+inline bool operator!=(const T& a, Wrap<Mixed> b) noexcept
+{
+    return type_Int != Mixed(b).get_type() || a != Mixed(b).get_int();
+}
+
+
+// Compare mixed with boolean
+
+inline bool operator==(Wrap<Mixed> a, bool b) noexcept
+{
+    return Mixed(a).get_type() == type_Bool && Mixed(a).get_bool() == b;
+}
+
+inline bool operator!=(Wrap<Mixed> a, bool b) noexcept
+{
+    return Mixed(a).get_type() != type_Bool || Mixed(a).get_bool() != b;
+}
+
+inline bool operator==(bool a, Wrap<Mixed> b) noexcept
+{
+    return type_Bool == Mixed(b).get_type() && a == Mixed(b).get_bool();
+}
+
+inline bool operator!=(bool a, Wrap<Mixed> b) noexcept
+{
+    return type_Bool != Mixed(b).get_type() || a != Mixed(b).get_bool();
+}
+
+
+// Compare mixed with float
+
+inline bool operator==(Wrap<Mixed> a, float b)
+{
+    return Mixed(a).get_type() == type_Float && Mixed(a).get_float() == b;
+}
+
+inline bool operator!=(Wrap<Mixed> a, float b)
+{
+    return Mixed(a).get_type() != type_Float || Mixed(a).get_float() != b;
+}
+
+inline bool operator==(float a, Wrap<Mixed> b)
+{
+    return type_Float == Mixed(b).get_type() && a == Mixed(b).get_float();
+}
+
+inline bool operator!=(float a, Wrap<Mixed> b)
+{
+    return type_Float != Mixed(b).get_type() || a != Mixed(b).get_float();
+}
+
+
+// Compare mixed with double
+
+inline bool operator==(Wrap<Mixed> a, double b)
+{
+    return Mixed(a).get_type() == type_Double && Mixed(a).get_double() == b;
+}
+
+inline bool operator!=(Wrap<Mixed> a, double b)
+{
+    return Mixed(a).get_type() != type_Double || Mixed(a).get_double() != b;
+}
+
+inline bool operator==(double a, Wrap<Mixed> b)
+{
+    return type_Double == Mixed(b).get_type() && a == Mixed(b).get_double();
+}
+
+inline bool operator!=(double a, Wrap<Mixed> b)
+{
+    return type_Double != Mixed(b).get_type() || a != Mixed(b).get_double();
+}
+
+
+// Compare mixed with string
+
+inline bool operator==(Wrap<Mixed> a, StringData b) noexcept
+{
+    return Mixed(a).get_type() == type_String && Mixed(a).get_string() == b;
+}
+
+inline bool operator!=(Wrap<Mixed> a, StringData b) noexcept
+{
+    return Mixed(a).get_type() != type_String || Mixed(a).get_string() != b;
+}
+
+inline bool operator==(StringData a, Wrap<Mixed> b) noexcept
+{
+    return type_String == Mixed(b).get_type() && a == Mixed(b).get_string();
+}
+
+inline bool operator!=(StringData a, Wrap<Mixed> b) noexcept
+{
+    return type_String != Mixed(b).get_type() || a != Mixed(b).get_string();
+}
+
+inline bool operator==(Wrap<Mixed> a, const char* b) noexcept
+{
+    return a == StringData(b);
+}
+
+inline bool operator!=(Wrap<Mixed> a, const char* b) noexcept
+{
+    return a != StringData(b);
+}
+
+inline bool operator==(const char* a, Wrap<Mixed> b) noexcept
+{
+    return StringData(a) == b;
+}
+
+inline bool operator!=(const char* a, Wrap<Mixed> b) noexcept
+{
+    return StringData(a) != b;
+}
+
+inline bool operator==(Wrap<Mixed> a, char* b) noexcept
+{
+    return a == StringData(b);
+}
+
+inline bool operator!=(Wrap<Mixed> a, char* b) noexcept
+{
+    return a != StringData(b);
+}
+
+inline bool operator==(char* a, Wrap<Mixed> b) noexcept
+{
+    return StringData(a) == b;
+}
+
+inline bool operator!=(char* a, Wrap<Mixed> b) noexcept
+{
+    return StringData(a) != b;
+}
+
+
+// Compare mixed with binary data
+
+inline bool operator==(Wrap<Mixed> a, BinaryData b) noexcept
+{
+    return Mixed(a).get_type() == type_Binary && Mixed(a).get_binary() == b;
+}
+
+inline bool operator!=(Wrap<Mixed> a, BinaryData b) noexcept
+{
+    return Mixed(a).get_type() != type_Binary || Mixed(a).get_binary() != b;
+}
+
+inline bool operator==(BinaryData a, Wrap<Mixed> b) noexcept
+{
+    return type_Binary == Mixed(b).get_type() && a == Mixed(b).get_binary();
+}
+
+inline bool operator!=(BinaryData a, Wrap<Mixed> b) noexcept
+{
+    return type_Binary != Mixed(b).get_type() || a != Mixed(b).get_binary();
+}
+
+
+// Compare mixed with date
+
+inline bool operator==(Wrap<Mixed> a, OldDateTime b) noexcept
+{
+    return Mixed(a).get_type() == type_OldDateTime && OldDateTime(Mixed(a).get_olddatetime()) == b;
+}
+
+inline bool operator!=(Wrap<Mixed> a, OldDateTime b) noexcept
+{
+    return Mixed(a).get_type() != type_OldDateTime || OldDateTime(Mixed(a).get_olddatetime()) != b;
+}
+
+inline bool operator==(OldDateTime a, Wrap<Mixed> b) noexcept
+{
+    return type_OldDateTime == Mixed(b).get_type() && a == OldDateTime(Mixed(b).get_olddatetime());
+}
+
+inline bool operator!=(OldDateTime a, Wrap<Mixed> b) noexcept
+{
+    return type_OldDateTime != Mixed(b).get_type() || a != OldDateTime(Mixed(b).get_olddatetime());
+}
+
+// Compare mixed with Timestamp
+
+inline bool operator==(Wrap<Mixed> a, Timestamp b) noexcept
+{
+    return Mixed(a).get_type() == type_Timestamp && Timestamp(Mixed(a).get_timestamp()) == b;
+}
+
+inline bool operator!=(Wrap<Mixed> a, Timestamp b) noexcept
+{
+    return Mixed(a).get_type() != type_Timestamp || Timestamp(Mixed(a).get_timestamp()) != b;
+}
+
+inline bool operator==(Timestamp a, Wrap<Mixed> b) noexcept
+{
+    return type_Timestamp == Mixed(b).get_type() && a == Timestamp(Mixed(b).get_timestamp());
+}
+
+inline bool operator!=(Timestamp a, Wrap<Mixed> b) noexcept
+{
+    return type_Timestamp != Mixed(b).get_type() || a != Timestamp(Mixed(b).get_timestamp());
+}
+
+
+} // namespace realm
+
+#endif // REALM_MIXED_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/null.hpp b/iOS/Pods/Realm/include/core/realm/null.hpp
new file mode 100644 (file)
index 0000000..733e798
--- /dev/null
@@ -0,0 +1,172 @@
+/*************************************************************************
+ *
+ * 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 <cmath>
+#include <cstring>
+
+#include <realm/util/features.h>
+#include <realm/util/optional.hpp>
+#include <realm/utilities.hpp>
+#include <realm/exceptions.hpp>
+
+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 <class T>
+    operator util::Optional<T>()
+    {
+        return util::none;
+    }
+
+    template <class T>
+    bool operator==(const T&) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class T>
+    bool operator!=(const T&) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class T>
+    bool operator>(const T&) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class T>
+    bool operator>=(const T&) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class T>
+    bool operator<=(const T&) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class T>
+    bool operator<(const T&) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+
+    /// Returns whether `v` bitwise equals the null bit-pattern
+    template <class T>
+    static bool is_null_float(T v)
+    {
+        T i = null::get_null_float<T>();
+        return std::memcmp(&i, &v, sizeof(T)) == 0;
+    }
+
+    /// Returns the quiet NaNs that represent null for floats/doubles in Realm in stored payload.
+    template <class T>
+    static T get_null_float()
+    {
+        typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type i;
+        int64_t double_nan = 0x7ff80000000000aa;
+        i = std::is_same<T, float>::value ? 0x7fc000aa : static_cast<decltype(i)>(double_nan);
+        T d = type_punning<T, decltype(i)>(i);
+        REALM_ASSERT_DEBUG(std::isnan(d));
+        REALM_ASSERT_DEBUG(!is_signaling(d));
+        return d;
+    }
+
+    /// Takes a NaN as argument and returns whether or not it's signaling
+    template <class T>
+    static bool is_signaling(T v)
+    {
+        REALM_ASSERT(std::isnan(static_cast<double>(v)));
+        typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type i;
+        size_t signal_bit = std::is_same<T, float>::value ? 22 : 51; // If this bit is set, it's quiet
+        i = type_punning<decltype(i), T>(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 <class T>
+    static T to_realm(T v)
+    {
+        if (std::isnan(static_cast<double>(v))) {
+            typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type i;
+            if (std::is_same<T, float>::value) {
+                i = is_signaling(v) ? 0x7fa00000 : 0x7fc00000;
+            }
+            else {
+                i = static_cast<decltype(i)>(is_signaling(v) ? 0x7ff4000000000000 : 0x7ff8000000000000);
+            }
+            return type_punning<T, decltype(i)>(i);
+        }
+        else {
+            return v;
+        }
+    }
+};
+
+template <class OS>
+OS& operator<<(OS& os, const null&)
+{
+    os << "(null)";
+    return os;
+}
+
+} // namespace realm
+
+#endif // REALM_NULL_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/olddatetime.hpp b/iOS/Pods/Realm/include/core/realm/olddatetime.hpp
new file mode 100644 (file)
index 0000000..b662899
--- /dev/null
@@ -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 <ctime>
+#include <ostream>
+
+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 <class Ch, class Tr>
+    friend std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& 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 <typename T>
+    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 <class Ch, class Tr>
+inline std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& 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/iOS/Pods/Realm/include/core/realm/owned_data.hpp b/iOS/Pods/Realm/include/core/realm/owned_data.hpp
new file mode 100644 (file)
index 0000000..c707f9d
--- /dev/null
@@ -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_OWNED_DATA_HPP
+#define REALM_OWNED_DATA_HPP
+
+#include <realm/util/assert.hpp>
+
+#include <cstring>
+#include <memory>
+
+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<char[]>(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<char[]> 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<char[]> 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<char[]>(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/iOS/Pods/Realm/include/core/realm/parser/parser.hpp b/iOS/Pods/Realm/include/core/realm/parser/parser.hpp
new file mode 100644 (file)
index 0000000..635cff2
--- /dev/null
@@ -0,0 +1,100 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_PARSER_HPP
+#define REALM_PARSER_HPP
+
+#include <vector>
+#include <string>
+
+namespace realm {
+
+namespace parser {
+struct Expression
+{
+    enum class Type { None, Number, String, KeyPath, Argument, True, False, Null, Timestamp, Base64 } type;
+    enum class KeyPathOp { None, Min, Max, Avg, Sum, Count, Size } collection_op;
+    std::string s;
+    std::vector<std::string> time_inputs;
+    std::string op_suffix;
+    Expression(Type t = Type::None, std::string input = "") : type(t), collection_op(KeyPathOp::None), s(input) {}
+    Expression(std::vector<std::string>&& timestamp) : type(Type::Timestamp), collection_op(KeyPathOp::None), time_inputs(timestamp) {}
+    Expression(std::string prefix, KeyPathOp op, std::string suffix) : type(Type::KeyPath), collection_op(op), s(prefix), op_suffix(suffix) {}
+};
+
+struct Predicate
+{
+    enum class Type
+    {
+        Comparison,
+        Or,
+        And,
+        True,
+        False
+    } type = Type::And;
+
+    enum class Operator
+    {
+        None,
+        Equal,
+        NotEqual,
+        LessThan,
+        LessThanOrEqual,
+        GreaterThan,
+        GreaterThanOrEqual,
+        BeginsWith,
+        EndsWith,
+        Contains,
+        Like
+    };
+
+    enum class OperatorOption
+    {
+        None,
+        CaseInsensitive,
+    };
+
+    struct Comparison
+    {
+        Operator op = Operator::None;
+        OperatorOption option = OperatorOption::None;
+        Expression expr[2] = {{Expression::Type::None, ""}, {Expression::Type::None, ""}};
+    };
+
+    struct Compound
+    {
+        std::vector<Predicate> sub_predicates;
+    };
+
+    Comparison cmpr;
+    Compound   cpnd;
+
+    bool negate = false;
+
+    Predicate(Type t, bool n = false) : type(t), negate(n) {}
+};
+
+Predicate parse(const std::string &query);
+
+// run the analysis tool to check for cycles in the grammar
+// returns the number of problems found and prints some info to std::cout
+size_t analyze_grammar();
+}
+}
+
+#endif // REALM_PARSER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/parser/query_builder.hpp b/iOS/Pods/Realm/include/core/realm/parser/query_builder.hpp
new file mode 100644 (file)
index 0000000..fac3b6b
--- /dev/null
@@ -0,0 +1,124 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_QUERY_BUILDER_HPP
+#define REALM_QUERY_BUILDER_HPP
+
+#include <string>
+#include <memory>
+#include <vector>
+
+#include <realm/binary_data.hpp>
+#include <realm/null.hpp>
+#include <realm/string_data.hpp>
+#include <realm/timestamp.hpp>
+#include <realm/table.hpp>
+#include <realm/util/any.hpp>
+#include <realm/util/string_buffer.hpp>
+
+namespace realm {
+class Query;
+class Realm;
+class Table;
+template<typename> class BasicRowExpr;
+using RowExpr = BasicRowExpr<Table>;
+
+namespace parser {
+    struct Predicate;
+}
+
+namespace query_builder {
+class Arguments;
+
+void apply_predicate(Query& query, const parser::Predicate& predicate, Arguments& arguments);
+
+void apply_predicate(Query& query, const parser::Predicate& predicate); // zero out of string args version
+
+
+struct AnyContext
+{
+    template<typename T>
+    T unbox(const util::Any& wrapper) {
+        return util::any_cast<T>(wrapper);
+    }
+    bool is_null(const util::Any& wrapper) {
+        if (!wrapper.has_value()) {
+            return true;
+        }
+        if (wrapper.type() == typeid(realm::null)) {
+            return true;
+        }
+        return false;
+    }
+};
+
+class Arguments {
+public:
+    virtual bool bool_for_argument(size_t argument_index) = 0;
+    virtual long long long_for_argument(size_t argument_index) = 0;
+    virtual float float_for_argument(size_t argument_index) = 0;
+    virtual double double_for_argument(size_t argument_index) = 0;
+    virtual StringData string_for_argument(size_t argument_index) = 0;
+    virtual BinaryData binary_for_argument(size_t argument_index) = 0;
+    virtual Timestamp timestamp_for_argument(size_t argument_index) = 0;
+    virtual size_t object_index_for_argument(size_t argument_index) = 0;
+    virtual bool is_argument_null(size_t argument_index) = 0;
+    util::StringBuffer buffer_space; // dynamic conversion space with lifetime tied to this
+};
+
+template<typename ValueType, typename ContextType>
+class ArgumentConverter : public Arguments {
+public:
+    ArgumentConverter(ContextType& context, const ValueType* arguments, size_t count)
+    : m_ctx(context)
+    , m_arguments(arguments)
+    , m_count(count)
+    {}
+
+    bool bool_for_argument(size_t i) override { return get<bool>(i); }
+    long long long_for_argument(size_t i) override { return get<int64_t>(i); }
+    float float_for_argument(size_t i) override { return get<float>(i); }
+    double double_for_argument(size_t i) override { return get<double>(i); }
+    StringData string_for_argument(size_t i) override { return get<StringData>(i); }
+    BinaryData binary_for_argument(size_t i) override { return get<BinaryData>(i); }
+    Timestamp timestamp_for_argument(size_t i) override { return get<Timestamp>(i); }
+    size_t object_index_for_argument(size_t i) override { return get<RowExpr>(i).get_index(); }
+    bool is_argument_null(size_t i) override { return m_ctx.is_null(at(i)); }
+
+private:
+    ContextType& m_ctx;
+    const ValueType* m_arguments;
+    size_t m_count;
+
+    const ValueType& at(size_t index) const
+    {
+        if (index >= m_count)
+            throw std::out_of_range("vector");
+        return m_arguments[index];
+    }
+
+    template<typename T>
+    T get(size_t index) const
+    {
+        return m_ctx.template unbox<T>(at(index));
+    }
+};
+} // namespace query_builder
+} // namespace realm
+
+#endif // REALM_QUERY_BUILDER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/query.hpp b/iOS/Pods/Realm/include/core/realm/query.hpp
new file mode 100644 (file)
index 0000000..9964264
--- /dev/null
@@ -0,0 +1,475 @@
+/*************************************************************************
+ *
+ * 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 <cstdint>
+#include <cstdio>
+#include <climits>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#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 <pthread.h>
+#endif
+
+#include <realm/views.hpp>
+#include <realm/table_ref.hpp>
+#include <realm/binary_data.hpp>
+#include <realm/olddatetime.hpp>
+#include <realm/handover_defs.hpp>
+#include <realm/link_view_fwd.hpp>
+#include <realm/descriptor_fwd.hpp>
+#include <realm/row.hpp>
+
+namespace realm {
+
+
+// Pre-declarations
+class ParentNode;
+class Table;
+class TableView;
+class TableViewBase;
+class ConstTableView;
+class Array;
+class Expression;
+class SequentialGetterBase;
+class Group;
+
+namespace metrics {
+class QueryInfo;
+}
+
+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<ParentNode> 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<TableViewBase>);
+    Query(const Table& table, const LinkViewRef& lv);
+    Query();
+    Query(std::unique_ptr<Expression>);
+    ~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: size
+    Query& size_equal(size_t column_ndx, int64_t value);
+    Query& size_not_equal(size_t column_ndx, int64_t value);
+    Query& size_greater(size_t column_ndx, int64_t value);
+    Query& size_greater_equal(size_t column_ndx, int64_t value);
+    Query& size_less_equal(size_t column_ndx, int64_t value);
+    Query& size_less(size_t column_ndx, int64_t value);
+    Query& size_between(size_t column_ndx, int64_t from, int64_t to);
+
+    // 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);
+    Query& like(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<uint_fast64_t> sync_view_if_needed() const;
+
+    std::string validate();
+
+    std::string get_description() const;
+
+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);
+
+public:
+    using HandoverPatch = QueryHandoverPatch;
+
+    std::unique_ptr<Query> clone_for_handover(std::unique_ptr<HandoverPatch>& patch, ConstSourcePayload mode) const
+    {
+        patch.reset(new HandoverPatch);
+        return std::make_unique<Query>(*this, *patch, mode);
+    }
+
+    std::unique_ptr<Query> clone_for_handover(std::unique_ptr<HandoverPatch>& patch, MutableSourcePayload mode)
+    {
+        patch.reset(new HandoverPatch);
+        return std::make_unique<Query>(*this, *patch, mode);
+    }
+
+    void apply_and_consume_patch(std::unique_ptr<HandoverPatch>& 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<Expression>);
+
+    template <class ColumnType>
+    Query& equal(size_t column_ndx1, size_t column_ndx2);
+
+    template <class ColumnType>
+    Query& less(size_t column_ndx1, size_t column_ndx2);
+
+    template <class ColumnType>
+    Query& less_equal(size_t column_ndx1, size_t column_ndx2);
+
+    template <class ColumnType>
+    Query& greater(size_t column_ndx1, size_t column_ndx2);
+
+    template <class ColumnType>
+    Query& greater_equal(size_t column_ndx1, size_t column_ndx2);
+
+    template <class ColumnType>
+    Query& not_equal(size_t column_ndx1, size_t column_ndx2);
+
+    template <typename TConditionFunction, class T>
+    Query& add_condition(size_t column_ndx, T value);
+
+    template <typename TConditionFunction>
+    Query& add_size_condition(size_t column_ndx, int64_t value);
+
+    template <typename T, bool Nullable>
+    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 <Action action, typename T, typename R, class ColClass>
+    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<ParentNode>);
+
+    friend class Table;
+    friend class TableViewBase;
+    friend class metrics::QueryInfo;
+
+    std::string error_code;
+
+    std::vector<QueryGroup> m_groups;
+
+    // Used to access schema while building query:
+    std::vector<size_t> 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<TableViewBase> 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/iOS/Pods/Realm/include/core/realm/query_conditions.hpp b/iOS/Pods/Realm/include/core/realm/query_conditions.hpp
new file mode 100644 (file)
index 0000000..e4f589d
--- /dev/null
@@ -0,0 +1,801 @@
+/*************************************************************************
+ *
+ * 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 <cstdint>
+#include <string>
+
+#include <realm/unicode.hpp>
+#include <realm/binary_data.hpp>
+#include <realm/utilities.hpp>
+
+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 <class A, class B, class C>
+    bool can_match(A, B, C)
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class A, class B, class C>
+    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);
+    }
+    bool operator()(StringData v1, const std::array<uint8_t, 256> &charmap, StringData v2) const
+    {
+        return v2.contains(v1, charmap);
+    }
+
+    template <class A, class B>
+    bool operator()(A, B) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class A, class B, class C, class D>
+    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 std::string description()
+    {
+        return "CONTAINS";
+    }
+
+    static const int condition = -1;
+};
+
+// Does v2 contain something like v1 (wildcard matching)?
+struct Like : public HackClass {
+    bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const
+    {
+        return v2.like(v1);
+    }
+    bool operator()(StringData v1, StringData v2, bool = false, bool = false) const
+    {
+        return v2.like(v1);
+    }
+    bool operator()(BinaryData, BinaryData, bool = false, bool = false) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+
+    template <class A, class B>
+    bool operator()(A, B) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+
+    template <class A, class B, class C, class D>
+    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 std::string description()
+    {
+        return "LIKE";
+    }
+
+    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 <class A, class B, class C, class D>
+    bool operator()(A, B, C, D) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class A, class B>
+    bool operator()(A, B) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+
+    static std::string description()
+    {
+        return "BEGINSWITH";
+    }
+
+    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 <class A, class B>
+    bool operator()(A, B) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class A, class B, class C, class D>
+    bool operator()(A, B, C, D) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+
+    static std::string description()
+    {
+        return "ENDSWITH";
+    }
+
+    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 <class T>
+    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);
+    }
+
+    static std::string description()
+    {
+        return "==";
+    }
+};
+
+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 <class T>
+    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 <class A, class B, class C, class D>
+    bool operator()(A, B, C, D) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+
+    static std::string description()
+    {
+        return "!=";
+    }
+};
+
+// 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();
+    }
+    
+    // Case insensitive Boyer-Moore version
+    bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, const std::array<uint8_t, 256> &charmap, StringData v2) const
+    {
+        if (v2.is_null() && !v1.is_null())
+            return false;
+        
+        if (v1.size() == 0 && !v2.is_null())
+            return true;
+        
+        return contains_ins(v2, v1_upper, v1_lower, v1.size(), charmap);
+    }
+
+
+    template <class A, class B>
+    bool operator()(A, B) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class A, class B, class C, class D>
+    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 std::string description()
+    {
+        return "CONTAINS[c]";
+    }
+
+    static const int condition = -1;
+};
+
+// Does v2 contain something like v1 (wildcard matching)?
+struct LikeIns : 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 (v2.is_null() && v1.is_null());
+        }
+
+        return string_like_ins(v2, v1_lower, v1_upper);
+    }
+
+    // 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 (v2.is_null() && v1.is_null());
+        }
+
+        std::string v1_upper = case_map(v1, true, IgnoreErrors);
+        std::string v1_lower = case_map(v1, false, IgnoreErrors);
+        return string_like_ins(v2, v1_lower, v1_upper);
+    }
+
+    template <class A, class B>
+    bool operator()(A, B) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class A, class B, class C, class D>
+    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 std::string description()
+    {
+        return "LIKE[c]";
+    }
+
+    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 <class A, class B>
+    bool operator()(A, B) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class A, class B, class C, class D>
+    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 std::string description()
+    {
+        return "BEGINSWITH[c]";
+    }
+
+    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 <class A, class B>
+    bool operator()(A, B) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class A, class B, class C, class D>
+    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 std::string description()
+    {
+        return "ENDSWITH[c]";
+    }
+
+    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 <class A, class B>
+    bool operator()(A, B) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class A, class B, class C, class D>
+    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 std::string description()
+    {
+        return "==[c]";
+    }
+
+    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 <class A, class B>
+    bool operator()(A, B) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    template <class A, class B, class C, class D>
+    bool operator()(A, B, C, D) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+
+    static std::string description()
+    {
+        return "!=[c]";
+    }
+
+    static const int condition = -1;
+};
+
+struct Greater {
+    static const int avx = 0x1E; // _CMP_GT_OQ
+    template <class T>
+    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 <class A, class B, class C, class D>
+    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<void>(lbound);
+        return ubound > v;
+    }
+    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
+    {
+        static_cast<void>(ubound);
+        return lbound > v;
+    }
+
+    static std::string description()
+    {
+        return ">";
+    }
+};
+
+struct None {
+    template <class T>
+    bool operator()(const T&, const T&, bool = false, bool = false) const
+    {
+        return true;
+    }
+    static const int condition = cond_None;
+    template <class A, class B, class C, class D>
+    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<void>(lbound);
+        static_cast<void>(ubound);
+        static_cast<void>(v);
+        return true;
+    }
+    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
+    {
+        static_cast<void>(lbound);
+        static_cast<void>(ubound);
+        static_cast<void>(v);
+        return true;
+    }
+
+    static std::string description()
+    {
+        return "none";
+    }
+};
+
+struct NotNull {
+    template <class T>
+    bool operator()(const T&, const T&, bool v = false, bool = false) const
+    {
+        return !v;
+    }
+    static const int condition = cond_LeftNotNull;
+    template <class A, class B, class C, class D>
+    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<void>(lbound);
+        static_cast<void>(ubound);
+        static_cast<void>(v);
+        return true;
+    }
+    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
+    {
+        static_cast<void>(lbound);
+        static_cast<void>(ubound);
+        static_cast<void>(v);
+        return true;
+    }
+    static std::string description()
+    {
+        return "!= NULL";
+    }
+};
+
+
+struct Less {
+    static const int avx = 0x11; // _CMP_LT_OQ
+    template <class T>
+    bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const
+    {
+        if (v1null || v2null)
+            return false;
+
+        return v1 < v2;
+    }
+    template <class A, class B, class C, class D>
+    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<void>(ubound);
+        return lbound < v;
+    }
+    bool will_match(int64_t v, int64_t lbound, int64_t ubound)
+    {
+        static_cast<void>(lbound);
+        return ubound < v;
+    }
+    static std::string description()
+    {
+        return "<";
+    }
+};
+
+struct LessEqual : public HackClass {
+    static const int avx = 0x12; // _CMP_LE_OQ
+    template <class T>
+    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 <class A, class B, class C, class D>
+    bool operator()(A, B, C, D) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    static std::string description()
+    {
+        return "<=";
+    }
+    static const int condition = -1;
+};
+
+struct GreaterEqual : public HackClass {
+    static const int avx = 0x1D; // _CMP_GE_OQ
+    template <class T>
+    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 <class A, class B, class C, class D>
+    bool operator()(A, B, C, D) const
+    {
+        REALM_ASSERT(false);
+        return false;
+    }
+    static std::string description()
+    {
+        return ">=";
+    }
+    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 <class T>
+struct CompareLess {
+    static bool compare(T v1, T v2, bool = false, bool = false)
+    {
+        return v1 < v2;
+    }
+};
+template <>
+struct CompareLess<StringData> {
+    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/iOS/Pods/Realm/include/core/realm/query_engine.hpp b/iOS/Pods/Realm/include/core/realm/query_engine.hpp
new file mode 100644 (file)
index 0000000..f8d47fe
--- /dev/null
@@ -0,0 +1,2113 @@
+/*************************************************************************
+ *
+ * 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 <algorithm>
+#include <functional>
+#include <sstream>
+#include <string>
+#include <array>
+
+#include <realm/array_basic.hpp>
+#include <realm/array_string.hpp>
+#include <realm/column_binary.hpp>
+#include <realm/column_fwd.hpp>
+#include <realm/column_link.hpp>
+#include <realm/column_linklist.hpp>
+#include <realm/column_mixed.hpp>
+#include <realm/column_string.hpp>
+#include <realm/column_string_enum.hpp>
+#include <realm/column_table.hpp>
+#include <realm/column_timestamp.hpp>
+#include <realm/column_type_traits.hpp>
+#include <realm/column_type_traits.hpp>
+#include <realm/impl/sequential_getter.hpp>
+#include <realm/link_view.hpp>
+#include <realm/metrics/query_info.hpp>
+#include <realm/query_conditions.hpp>
+#include <realm/query_operators.hpp>
+#include <realm/table.hpp>
+#include <realm/unicode.hpp>
+#include <realm/util/miscellaneous.hpp>
+#include <realm/util/serializer.hpp>
+#include <realm/util/shared_ptr.hpp>
+#include <realm/utilities.hpp>
+
+#include <map>
+
+#if REALM_X86_OR_X64_TRUE && defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219
+#include <immintrin.h>
+#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<ParentNode*>& 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()
+    {
+        // Verify that the cached column accessor is still valid
+        verify_column(); // throws
+
+        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 <Action TAction, class TSourceColumn>
+    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<TSourceValue, TAction>::sum_type;
+
+        // Sum of float column must accumulate in double
+        static_assert(!(TAction == act_Sum &&
+                        (std::is_same<TSourceColumn, float>::value && !std::is_same<TResult, double>::value)),
+                      "");
+
+        TSourceValue av{};
+        // uses_val test because compiler cannot see that IntegerColumn::get has no side effect and result is
+        // discarded
+        if (static_cast<QueryState<TResult>*>(st)->template uses_val<TAction>() && source_column != nullptr) {
+            REALM_ASSERT_DEBUG(dynamic_cast<SequentialGetter<TSourceColumn>*>(source_column) != nullptr);
+            av = static_cast<SequentialGetter<TSourceColumn>*>(source_column)->get_next(r);
+        }
+        REALM_ASSERT_DEBUG(dynamic_cast<QueryState<TResult>*>(st) != nullptr);
+        bool cont = static_cast<QueryState<TResult>*>(st)->template match<TAction, 0>(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(patches ? ConstTableRef{} : from.m_table)
+    {
+    }
+
+    void add_child(std::unique_ptr<ParentNode> child)
+    {
+        if (m_child)
+            m_child->add_child(std::move(child));
+        else
+            m_child = std::move(child);
+    }
+
+    virtual std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* = nullptr) const = 0;
+
+    virtual void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group)
+    {
+        if (m_child)
+            m_child->apply_handover_patch(patches, group);
+    }
+
+    virtual void verify_column() const = 0;
+
+    virtual std::string describe_column() const
+    {
+        return describe_column(m_condition_column_idx);
+    }
+
+    virtual std::string describe_column(size_t col_ndx) const
+    {
+        if (m_table && col_ndx != npos) {
+            return std::string(m_table->get_column_name(col_ndx));
+        }
+        return "";
+    }
+
+    virtual std::string describe() const
+    {
+        return "";
+    }
+
+    virtual std::string describe_condition() const
+    {
+        return "matches";
+    }
+
+    virtual std::string describe_expression() const
+    {
+        std::string s;
+        s = describe();
+        if (m_child) {
+            s = s + " and " + m_child->describe_expression();
+        }
+        return s;
+    }
+
+    std::unique_ptr<ParentNode> m_child;
+    std::vector<ParentNode*> 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 <class ColType>
+    const ColType& get_column(size_t ndx)
+    {
+        auto& col = m_table->get_column_base(ndx);
+        REALM_ASSERT_DEBUG(dynamic_cast<const ColType*>(&col));
+        return static_cast<const ColType&>(col);
+    }
+
+    ColumnType get_real_column_type(size_t ndx)
+    {
+        return m_table->get_real_column_type(ndx);
+    }
+
+    template <class ColType>
+    void copy_getter(SequentialGetter<ColType>& dst, size_t& dst_idx, const SequentialGetter<ColType>& src,
+                     const QueryNodeHandoverPatches* patches)
+    {
+        if (src.m_column) {
+            if (patches)
+                dst_idx = src.m_column->get_column_index();
+            else
+                dst.init(src.m_column);
+        }
+    }
+
+    void do_verify_column(const ColumnBase* col, size_t col_ndx = npos) const
+    {
+        if (col_ndx == npos)
+            col_ndx = m_condition_column_idx;
+        if (m_table && col_ndx != npos) {
+            m_table->verify_column(col_ndx, col);
+        }
+    }
+
+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<ParentNode> condition)
+        : m_condition(std::move(condition))
+    {
+        m_dT = 100.0;
+        m_condition_column_idx = column;
+    }
+
+    void init() override
+    {
+        ParentNode::init();
+
+        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<ParentNode*> v;
+            m_condition->gather_children(v);
+        }
+    }
+
+    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);
+    }
+
+    void verify_column() const override
+    {
+        if (m_table)
+            m_table->verify_column(m_condition_column_idx, m_column);
+    }
+
+    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();
+    }
+    std::string describe() const override
+    {
+        return "subtable expression";
+    }
+
+
+    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) {
+            ConstTableRef subtable; // TBD: optimize this back to Table*
+            if (m_col_type == col_type_Table)
+                subtable = static_cast<const SubtableColumn*>(m_column)->get_subtable_tableref(s);
+            else {
+                subtable = static_cast<const MixedColumn*>(m_column)->get_subtable_tableref(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<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(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<ParentNode> m_condition;
+    const ColumnBase* m_column = nullptr;
+    ColumnType m_col_type;
+};
+
+namespace _impl {
+
+template <class ColType>
+struct CostHeuristic;
+
+template <>
+struct CostHeuristic<IntegerColumn> {
+    static constexpr double dD()
+    {
+        return 100.0;
+    }
+    static constexpr double dT()
+    {
+        return 1.0 / 4.0;
+    }
+};
+
+template <>
+struct CostHeuristic<IntNullColumn> {
+    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 <Action TAction, class ColType>
+    bool match_callback(int64_t v)
+    {
+        using TSourceValue = typename ColType::value_type;
+        using QueryStateType = typename ColumnTypeTraitsSum<TSourceValue, TAction>::sum_type;
+
+        size_t i = to_size_t(v);
+        m_last_local_match = i;
+        m_local_matches++;
+
+        auto state = static_cast<QueryState<QueryStateType>*>(m_state);
+        auto source_column = static_cast<SequentialGetter<ColType>*>(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<TAction>()) { // 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<TAction, false>(i, 0, av);
+        }
+        else {
+            b = state->template match<TAction, false>(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 ColType>
+class IntegerNodeBase : public ColumnNodeBase {
+    using ThisType = IntegerNodeBase<ColType>;
+
+public:
+    using TConditionValue = typename ColType::value_type;
+    static const bool nullable = ColType::nullable;
+
+    template <class TConditionFunction, Action TAction, DataType TDataType, bool Nullable>
+    bool find_callback_specialization(size_t s, size_t end_in_leaf)
+    {
+        using AggregateColumnType = typename GetColumnType<TDataType, Nullable>::type;
+        bool cont;
+        size_t start_in_leaf = s - this->m_leaf_start;
+        cont = this->m_leaf_ptr->template find<TConditionFunction, act_CallbackIdx>(
+            m_value, start_in_leaf, end_in_leaf, this->m_leaf_start, nullptr,
+            std::bind(std::mem_fn(&ThisType::template match_callback<TAction, AggregateColumnType>), 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<QueryState<int64_t>*>(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<ColType>(m_condition_column_idx);
+    }
+
+    void verify_column() const override
+    {
+        do_verify_column(m_condition_column);
+    }
+
+    void init() override
+    {
+        ColumnNodeBase::init();
+
+        m_dT = _impl::CostHeuristic<ColType>::dT();
+        m_dD = _impl::CostHeuristic<ColType>::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()));
+    }
+
+    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<SequentialGetter<ColType>*>(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<sizeof(LeafType), alignof(LeafType)>::type;
+    LeafCacheStorage m_leaf_cache_storage;
+    std::unique_ptr<LeafType, PlacementDelete> 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 ColType, class TConditionFunction>
+class IntegerNode : public IntegerNodeBase<ColType> {
+    using BaseType = IntegerNodeBase<ColType>;
+    using ThisType = IntegerNode<ColType, TConditionFunction>;
+
+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<TConditionFunction>(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;
+    }
+
+    virtual std::string describe() const override
+    {
+        return this->describe_column() + " " + describe_condition() + " " + util::serializer::print_value(IntegerNodeBase<ColType>::m_value);
+    }
+
+    virtual std::string describe_condition() const override
+    {
+        return TConditionFunction::description();
+    }
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(new IntegerNode<ColType, TConditionFunction>(*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<act_Count>(col_id, nullable);
+            case act_Sum:
+                return get_specialized_callback_2<act_Sum>(col_id, nullable);
+            case act_Max:
+                return get_specialized_callback_2<act_Max>(col_id, nullable);
+            case act_Min:
+                return get_specialized_callback_2<act_Min>(col_id, nullable);
+            case act_FindAll:
+                return get_specialized_callback_2_int<act_FindAll>(col_id, nullable);
+            case act_CallbackIdx:
+                return get_specialized_callback_2_int<act_CallbackIdx>(col_id, nullable);
+            default:
+                break;
+        }
+        REALM_ASSERT(false); // Invalid aggregate function
+        return nullptr;
+    }
+
+    template <Action TAction>
+    static TFind_callback_specialized get_specialized_callback_2(DataType col_id, bool nullable)
+    {
+        switch (col_id) {
+            case type_Int:
+                return get_specialized_callback_3<TAction, type_Int>(nullable);
+            case type_Float:
+                return get_specialized_callback_3<TAction, type_Float>(nullable);
+            case type_Double:
+                return get_specialized_callback_3<TAction, type_Double>(nullable);
+            default:
+                break;
+        }
+        REALM_ASSERT(false); // Invalid aggregate source column
+        return nullptr;
+    }
+
+    template <Action TAction>
+    static TFind_callback_specialized get_specialized_callback_2_int(DataType col_id, bool nullable)
+    {
+        if (col_id == type_Int) {
+            return get_specialized_callback_3<TAction, type_Int>(nullable);
+        }
+        REALM_ASSERT(false); // Invalid aggregate source column
+        return nullptr;
+    }
+
+    template <Action TAction, DataType TDataType>
+    static TFind_callback_specialized get_specialized_callback_3(bool nullable)
+    {
+        if (nullable) {
+            return &BaseType::template find_callback_specialization<TConditionFunction, TAction, TDataType, true>;
+        }
+        else {
+            return &BaseType::template find_callback_specialization<TConditionFunction, TAction, TDataType, false>;
+        }
+    }
+};
+
+
+// This node is currently used for floats and doubles only
+template <class ColType, class TConditionFunction>
+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<TConditionValue>())
+    {
+        m_condition_column_idx = column_ndx;
+        m_dT = 1.0;
+    }
+
+    void table_changed() override
+    {
+        m_condition_column.init(&get_column<ColType>(m_condition_column_idx));
+    }
+
+    void verify_column() const override
+    {
+        do_verify_column(m_condition_column.m_column);
+    }
+
+    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<TConditionValue>(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);
+    }
+
+    virtual std::string describe() const override
+    {
+        return this->describe_column() + " " + describe_condition() + " " + util::serializer::print_value(FloatDoubleNode::m_value);
+    }
+    virtual std::string describe_condition() const override
+    {
+        return TConditionFunction::description();
+    }
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(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<ColType> m_condition_column;
+};
+
+template <class ColType, class TConditionFunction>
+class SizeNode : public ParentNode {
+public:
+    SizeNode(int64_t v, size_t column)
+        : m_value(v)
+    {
+        m_condition_column_idx = column;
+    }
+
+    void table_changed() override
+    {
+        m_condition_column = &get_column<ColType>(m_condition_column_idx);
+    }
+
+    void verify_column() const override
+    {
+        do_verify_column(m_condition_column);
+    }
+
+    void init() override
+    {
+        ParentNode::init();
+        m_dD = 10.0;
+    }
+
+    size_t find_first_local(size_t start, size_t end) override
+    {
+        for (size_t s = start; s < end; ++s) {
+            TConditionValue v = m_condition_column->get(s);
+            if (v) {
+                int64_t sz = m_size_operator(v);
+                if (TConditionFunction()(sz, m_value))
+                    return s;
+            }
+        }
+        return not_found;
+    }
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(new SizeNode(*this, patches));
+    }
+
+    SizeNode(const SizeNode& 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:
+    using TConditionValue = typename ColType::value_type;
+
+    int64_t m_value;
+    const ColType* m_condition_column = nullptr;
+    Size<TConditionValue> m_size_operator;
+};
+
+
+template <class TConditionFunction>
+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<BinaryColumn>(m_condition_column_idx);
+    }
+
+    void verify_column() const override
+    {
+        do_verify_column(m_condition_column);
+    }
+
+    void init() override
+    {
+        ParentNode::init();
+
+        m_dD = 100.0;
+    }
+
+    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;
+    }
+
+    virtual std::string describe() const override
+    {
+        return this->describe_column() + " " + TConditionFunction::description() + " "
+            + util::serializer::print_value(BinaryNode::m_value.get());
+    }
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(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 TConditionFunction>
+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<TimestampColumn>(m_condition_column_idx);
+    }
+
+    void verify_column() const override
+    {
+        do_verify_column(m_condition_column);
+    }
+
+    void init() override
+    {
+        ParentNode::init();
+
+        m_dD = 100.0;
+    }
+
+    size_t find_first_local(size_t start, size_t end) override
+    {
+        size_t ret = m_condition_column->find<TConditionFunction>(m_value, start, end);
+        return ret;
+    }
+
+    virtual std::string describe() const override
+    {
+        return this->describe_column() + " " + TConditionFunction::description() + " " + util::serializer::print_value(TimestampNode::m_value);
+    }
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(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 verify_column() const override
+    {
+        do_verify_column(m_condition_column);
+    }
+
+    void init() override
+    {
+        ParentNode::init();
+
+        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();
+    }
+
+    virtual std::string describe() const override
+    {
+        StringData sd;
+        if (bool(StringNodeBase::m_value)) {
+            sd = StringData(StringNodeBase::m_value.value());
+        }
+        return this->describe_column() + " " + describe_condition() + " " + util::serializer::print_value(sd);
+    }
+
+protected:
+    util::Optional<std::string> m_value;
+
+    const ColumnBase* m_condition_column = nullptr;
+    ColumnType m_column_type;
+
+    // Used for linear scan through short/long-string
+    std::unique_ptr<const ArrayParent> 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;
+    
+    inline StringData get_string(size_t s)
+    {
+        StringData t;
+        
+        if (m_column_type == col_type_StringEnum) {
+            // enum
+            t = static_cast<const StringEnumColumn*>(m_condition_column)->get(s);
+        }
+        else {
+            // short or long
+            const StringColumn* asc = static_cast<const StringColumn*>(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<const ArrayString&>(*m_leaf).size();
+                else if (m_leaf_type == StringColumn::leaf_type_Medium)
+                    m_end_s = m_leaf_start + static_cast<const ArrayStringLong&>(*m_leaf).size();
+                else
+                    m_end_s = m_leaf_start + static_cast<const ArrayBigBlobs&>(*m_leaf).size();
+            }
+            
+            if (m_leaf_type == StringColumn::leaf_type_Small)
+                t = static_cast<const ArrayString&>(*m_leaf).get(s - m_leaf_start);
+            else if (m_leaf_type == StringColumn::leaf_type_Medium)
+                t = static_cast<const ArrayStringLong&>(*m_leaf).get(s - m_leaf_start);
+            else
+                t = static_cast<const ArrayBigBlobs&>(*m_leaf).get_string(s - m_leaf_start);
+        }
+        return t;
+    }
+};
+
+// Conditions for strings. Note that Equal is specialized later in this file!
+template <class TConditionFunction>
+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();
+    }
+
+
+    size_t find_first_local(size_t start, size_t end) override
+    {
+        TConditionFunction cond;
+
+        for (size_t s = start; s < end; ++s) {
+            StringData t = get_string(s);
+            
+            if (cond(StringData(m_value), m_ucase.data(), m_lcase.data(), t))
+                return s;
+        }
+        return not_found;
+    }
+
+    virtual std::string describe_condition() const override
+    {
+        return TConditionFunction::description();
+    }
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(new StringNode<TConditionFunction>(*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 Contains condition on Strings - we specialize because we can utilize Boyer-Moore
+template <>
+class StringNode<Contains> : public StringNodeBase {
+public:
+    StringNode(StringData v, size_t column)
+    : StringNodeBase(v, column), m_charmap()
+    {
+        if (v.size() == 0)
+            return;
+        
+        // Build a dictionary of char-to-last distances in the search string
+        // (zero indicates that the char is not in needle)
+        size_t last_char_pos = v.size()-1;
+        for (size_t i = 0; i < last_char_pos; ++i) {
+            // we never jump longer increments than 255 chars, even if needle is longer (to fit in one byte)
+            uint8_t jump = last_char_pos-i < 255 ? static_cast<uint8_t>(last_char_pos-i) : 255;
+            
+            unsigned char c = v[i];
+            m_charmap[c] = jump;
+        }
+    }
+    
+    void init() override
+    {
+        clear_leaf_state();
+        
+        m_dD = 100.0;
+        
+        StringNodeBase::init();
+    }
+    
+    
+    size_t find_first_local(size_t start, size_t end) override
+    {
+        Contains cond;
+        
+        for (size_t s = start; s < end; ++s) {
+            StringData t = get_string(s);
+            
+            if (cond(StringData(m_value), m_charmap, t))
+                return s;
+        }
+        return not_found;
+    }
+
+    virtual std::string describe_condition() const override
+    {
+        return Contains::description();
+    }
+
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(new StringNode<Contains>(*this, patches));
+    }
+    
+    StringNode(const StringNode& from, QueryNodeHandoverPatches* patches)
+    : StringNodeBase(from, patches)
+    , m_charmap(from.m_charmap)
+    {
+    }
+    
+protected:
+    std::array<uint8_t, 256> m_charmap;
+};
+
+// Specialization for ContainsIns condition on Strings - we specialize because we can utilize Boyer-Moore
+template <>
+class StringNode<ContainsIns> : public StringNodeBase {
+public:
+    StringNode(StringData v, size_t column)
+    : StringNodeBase(v, column), m_charmap()
+    {
+        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);
+        }
+
+        if (v.size() == 0)
+            return;
+
+        // Build a dictionary of char-to-last distances in the search string
+        // (zero indicates that the char is not in needle)
+        size_t last_char_pos = m_ucase.size()-1;
+        for (size_t i = 0; i < last_char_pos; ++i) {
+            // we never jump longer increments than 255 chars, even if needle is longer (to fit in one byte)
+            uint8_t jump = last_char_pos-i < 255 ? static_cast<uint8_t>(last_char_pos-i) : 255;
+
+            unsigned char uc = m_ucase[i];
+            unsigned char lc = m_lcase[i];
+            m_charmap[uc] = jump;
+            m_charmap[lc] = jump;
+        }
+
+    }
+
+    void init() override
+    {
+        clear_leaf_state();
+
+        m_dD = 100.0;
+
+        StringNodeBase::init();
+    }
+
+
+    size_t find_first_local(size_t start, size_t end) override
+    {
+        ContainsIns cond;
+
+        for (size_t s = start; s < end; ++s) {
+            StringData t = get_string(s);
+            // The current behaviour is to return all results when querying for a null string.
+            // See comment above Query_NextGen_StringConditions on why every string including "" contains null.
+            if (!bool(m_value)) {
+                return s;
+            }
+            if (cond(StringData(m_value), m_ucase.data(), m_lcase.data(), m_charmap, t))
+                return s;
+        }
+        return not_found;
+    }
+
+    virtual std::string describe_condition() const override
+    {
+        return ContainsIns::description();
+    }
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(new StringNode<ContainsIns>(*this, patches));
+    }
+
+    StringNode(const StringNode& from, QueryNodeHandoverPatches* patches)
+    : StringNodeBase(from, patches)
+    , m_charmap(from.m_charmap)
+    , m_ucase(from.m_ucase)
+    , m_lcase(from.m_lcase)
+    {
+    }
+
+protected:
+    std::array<uint8_t, 256> m_charmap;
+    std::string m_ucase;
+    std::string m_lcase;
+};
+
+class StringNodeEqualBase : public StringNodeBase {
+public:
+    StringNodeEqualBase(StringData v, size_t column)
+        : StringNodeBase(v, column)
+    {
+    }
+    StringNodeEqualBase(const StringNodeEqualBase& from, QueryNodeHandoverPatches* patches)
+        : StringNodeBase(from, patches)
+    {
+    }
+    ~StringNodeEqualBase() noexcept override
+    {
+        deallocate();
+    }
+
+    void deallocate() noexcept;
+    void init() override;
+    size_t find_first_local(size_t start, size_t end) override;
+
+    virtual std::string describe_condition() const override
+    {
+        return Equal::description();
+    }
+
+protected:
+    inline BinaryData str_to_bin(const StringData& s) noexcept
+    {
+        return BinaryData(s.data(), s.size());
+    }
+
+    virtual void _search_index_init() = 0;
+    virtual size_t _find_first_local(size_t start, size_t end) = 0;
+
+    size_t m_key_ndx = not_found;
+    size_t m_last_indexed;
+
+    // Used for linear scan through enum-string
+    SequentialGetter<StringEnumColumn> m_cse;
+
+    // Used for index lookup
+    std::unique_ptr<IntegerColumn> m_index_matches;
+    bool m_index_matches_destroy = false;
+    std::unique_ptr<SequentialGetter<IntegerColumn>> m_index_getter;
+    size_t m_results_start;
+    size_t m_results_end;
+    size_t m_last_start;
+};
+
+
+// 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<Equal> : public StringNodeEqualBase {
+public:
+    using StringNodeEqualBase::StringNodeEqualBase;
+
+    void _search_index_init() override;
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(new StringNode<Equal>(*this, patches));
+    }
+
+private:
+    size_t _find_first_local(size_t start, size_t end) override;
+};
+
+
+// Specialization for EqualIns condition on Strings - we specialize because we can utilize indexes (if they exist) for
+// EqualIns.
+template <>
+class StringNode<EqualIns> : public StringNodeEqualBase {
+public:
+    StringNode(StringData v, size_t column)
+        : StringNodeEqualBase(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 _search_index_init() override;
+
+    virtual std::string describe_condition() const override
+    {
+        return EqualIns::description();
+    }
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(new StringNode(*this, patches));
+    }
+
+    StringNode(const StringNode& from, QueryNodeHandoverPatches* patches)
+        : StringNodeEqualBase(from, patches)
+        , m_ucase(from.m_ucase)
+        , m_lcase(from.m_lcase)
+    {
+    }
+
+private:
+    std::string m_ucase;
+    std::string m_lcase;
+
+    size_t _find_first_local(size_t start, size_t end) override;
+};
+
+
+// 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<ParentNode> 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 verify_column() const override
+    {
+        for (auto& condition : m_conditions) {
+            condition->verify_column();
+        }
+    }
+    std::string describe() const override
+    {
+        if (m_conditions.size() >= 2) {
+
+        }
+        std::string s;
+        for (size_t i = 0; i < m_conditions.size(); ++i) {
+            if (m_conditions[i]) {
+                s += m_conditions[i]->describe_expression();
+                if (i != m_conditions.size() - 1) {
+                    s += " or ";
+                }
+            }
+        }
+        if (m_conditions.size() > 1) {
+            s = "(" + s + ")";
+        }
+        return s;
+    }
+
+
+    void init() override
+    {
+        ParentNode::init();
+
+        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<ParentNode*> v;
+        for (auto& condition : m_conditions) {
+            condition->init();
+            v.clear();
+            condition->gather_children(v);
+        }
+    }
+
+    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<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(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<std::unique_ptr<ParentNode>> m_conditions;
+
+private:
+    // start index of the last find for each cond
+    std::vector<size_t> 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<size_t> m_last;
+    std::vector<bool> m_was_match;
+};
+
+
+class NotNode : public ParentNode {
+public:
+    NotNode(std::unique_ptr<ParentNode> condition)
+        : m_condition(std::move(condition))
+    {
+        m_dT = 50.0;
+    }
+
+    void table_changed() override
+    {
+        m_condition->set_table(*m_table);
+    }
+
+    void verify_column() const override
+    {
+        m_condition->verify_column();
+    }
+
+    void init() override
+    {
+        ParentNode::init();
+
+        m_dD = 10.0;
+
+        std::vector<ParentNode*> 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;
+    }
+
+    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 "";
+    }
+
+    virtual std::string describe() const override
+    {
+        if (m_condition) {
+            return "!(" + m_condition->describe_expression() + ")";
+        }
+        return "!()";
+    }
+
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(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<ParentNode> 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 ColType, class TConditionFunction>
+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<ColType>(m_condition_column_idx1));
+        m_getter2.init(&get_column<ColType>(m_condition_column_idx2));
+    }
+
+    void verify_column() const override
+    {
+        do_verify_column(m_getter1.m_column, m_condition_column_idx1);
+        do_verify_column(m_getter2.m_column, m_condition_column_idx2);
+    }
+
+    void init() override
+    {
+        ParentNode::init();
+        m_dD = 100.0;
+    }
+
+    size_t find_first_local(size_t start, size_t end) override
+    {
+        size_t s = start;
+
+        while (s < end) {
+            if (std::is_same<TConditionValue, int64_t>::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<int64_t> qs;
+                bool resume = m_getter1.m_leaf_ptr->template compare_leafs<TConditionFunction, act_ReturnFirst>(
+                    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<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(new TwoColumnsNode<ColType, TConditionFunction>(*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)
+        , m_condition_column_idx1(from.m_condition_column_idx1)
+        , m_condition_column_idx2(from.m_condition_column_idx2)
+    {
+        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<ColType> m_getter1;
+    SequentialGetter<ColType> m_getter2;
+};
+
+
+// For Next-Generation expressions like col1 / col2 + 123 > col4 * 100.
+class ExpressionNode : public ParentNode {
+
+public:
+    ExpressionNode(std::unique_ptr<Expression>);
+
+    size_t find_first_local(size_t start, size_t end) override;
+
+    void table_changed() override;
+    void verify_column() const override;
+
+    virtual std::string describe() const override;
+
+    std::unique_ptr<ParentNode> clone(QueryNodeHandoverPatches* patches) const override;
+    void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override;
+
+private:
+    ExpressionNode(const ExpressionNode& from, QueryNodeHandoverPatches* patches);
+
+    std::unique_ptr<Expression> m_expression;
+};
+
+
+struct LinksToNodeHandoverPatch : public QueryNodeHandoverPatch {
+    std::unique_ptr<RowBaseHandoverPatch> 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<Table*>(m_table.get())->get_column_link_base(m_origin_column);
+        REALM_ASSERT(m_column_type == type_Link || m_column_type == type_LinkList);
+    }
+
+    void verify_column() const override
+    {
+        do_verify_column(m_column, m_origin_column);
+    }
+
+    virtual std::string describe() const override
+    {
+        return this->describe_column(m_origin_column) + " " + describe_condition() + " " + util::serializer::print_value(m_target_row.get_index());
+    }
+    virtual std::string describe_condition() const override
+    {
+        return "links to";
+    }
+
+
+    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<LinkColumn&>(*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<LinkListColumn&>(*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<ParentNode> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<ParentNode>(patches ? new LinksToNode(*this, patches) : new LinksToNode(*this));
+    }
+
+    void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override
+    {
+        REALM_ASSERT(patches.size());
+        std::unique_ptr<QueryNodeHandoverPatch> abstract_patch = std::move(patches.back());
+        patches.pop_back();
+
+        auto patch = dynamic_cast<LinksToNodeHandoverPatch*>(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<LinksToNodeHandoverPatch>();
+        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/iOS/Pods/Realm/include/core/realm/query_expression.hpp b/iOS/Pods/Realm/include/core/realm/query_expression.hpp
new file mode 100644 (file)
index 0000000..247baad
--- /dev/null
@@ -0,0 +1,3786 @@
+/*************************************************************************
+ *
+ * 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<T>  Compare<Cond, T>  Subexpr2<T>
+                        operator! Expression
+
+    Subexpr2<T>:        Value<T>
+                        Columns<T>
+                        Subexpr2<T>  Operator<Oper<T>  Subexpr2<T>
+                        power(Subexpr2<T>) // power(x) = x * x, as example of unary operator
+
+    Value<T>:           T
+
+    Operator<Oper<T>>:  +, -, *, /
+
+    Compare<Cond, T>:   ==, !=, >=, <=, >, <
+
+    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<Subexpr2> m_left;                               // left expression subtree
+    unique_ptr<Subexpr2> m_right;                              // right expression subtree
+
+Operator: public Subexpr2
+    void evaluate(size_t i, ValueBase* destination)
+    unique_ptr<Subexpr2> m_left;                               // left expression subtree
+    unique_ptr<Subexpr2> m_right;                              // right expression subtree
+
+Value<T>: public Subexpr2
+    void evaluate(size_t i, ValueBase* destination)
+    T m_v[8];
+
+Columns<T>: public Subexpr2
+    void evaluate(size_t i, ValueBase* destination)
+    SequentialGetter<T> sg;                         // class bound to a column, lets you read values in a fast way
+    Table* m_table;
+
+class ColumnAccessor<>: public Columns<double>
+
+
+Call diagram:
+-----------------------------------------------------------------------------------------------------------------------
+Example of 'table.first > 34.6 + table.second':
+
+size_t Compare<Greater>::find_first()-------------+
+         |                                        |
+         |                                        |
+         |                                        |
+         +--> Columns<float>::evaluate()          +--------> Operator<Plus>::evaluate()
+                                                                |               |
+                                               Value<float>::evaluate()    Columns<float>::evaluate()
+
+Operator, Value and Columns have an evaluate(size_t i, ValueBase* destination) method which returns a Value<T>
+containing 8 values representing table rows i...i + 7.
+
+So Value<T> 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 <realm/column_link.hpp>
+#include <realm/column_linklist.hpp>
+#include <realm/column_table.hpp>
+#include <realm/column_type_traits.hpp>
+#include <realm/impl/sequential_getter.hpp>
+#include <realm/link_view.hpp>
+#include <realm/metrics/query_info.hpp>
+#include <realm/query_operators.hpp>
+#include <realm/util/optional.hpp>
+#include <realm/util/serializer.hpp>
+
+#include <numeric>
+
+// 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 <class T>
+T minimum(T a, T b)
+{
+    return a < b ? a : b;
+}
+
+#ifdef REALM_OLDQUERY_FALLBACK
+// Hack to avoid template instantiation errors. See create(). Todo, see if we can simplify only_numeric somehow
+namespace _impl {
+
+template <class T, class U>
+inline T only_numeric(U in)
+{
+    return static_cast<T>(util::unwrap(in));
+}
+
+template <class T>
+inline int only_numeric(const StringData&)
+{
+    REALM_ASSERT(false);
+    return 0;
+}
+
+template <class T>
+inline int only_numeric(const BinaryData&)
+{
+    REALM_ASSERT(false);
+    return 0;
+}
+
+template <class T>
+inline StringData only_string(T in)
+{
+    REALM_ASSERT(false);
+    static_cast<void>(in);
+    return StringData();
+}
+
+inline StringData only_string(StringData in)
+{
+    return in;
+}
+
+template <class T, class U>
+inline T no_timestamp(U in)
+{
+    return static_cast<T>(util::unwrap(in));
+}
+
+template <class T>
+inline int no_timestamp(const Timestamp&)
+{
+    REALM_ASSERT(false);
+    return 0;
+}
+
+} // namespace _impl
+
+#endif // REALM_OLDQUERY_FALLBACK
+
+template <class T>
+struct Plus {
+    T operator()(T v1, T v2) const
+    {
+        return v1 + v2;
+    }
+    static std::string description()
+    {
+        return "plus";
+    }
+    typedef T type;
+};
+
+template <class T>
+struct Minus {
+    T operator()(T v1, T v2) const
+    {
+        return v1 - v2;
+    }
+    static std::string description()
+    {
+        return "minus";
+    }
+    typedef T type;
+};
+
+template <class T>
+struct Div {
+    T operator()(T v1, T v2) const
+    {
+        return v1 / v2;
+    }
+    static std::string description()
+    {
+        return "divided by";
+    }
+    typedef T type;
+};
+
+template <class T>
+struct Mul {
+    T operator()(T v1, T v2) const
+    {
+        return v1 * v2;
+    }
+    static std::string description()
+    {
+        return "multiplied by";
+    }
+    typedef T type;
+};
+
+// Unary operator
+template <class T>
+struct Pow {
+    T operator()(T v) const
+    {
+        return v * v;
+    }
+    static std::string description()
+    {
+        return "to the power of";
+    }
+    typedef T type;
+};
+
+// Finds a common type for T1 and T2 according to C++ conversion/promotion in arithmetic (float + int => float, etc)
+template <class T1, class T2, bool T1_is_int = std::numeric_limits<T1>::is_integer || std::is_same<T1, null>::value,
+          bool T2_is_int = std::numeric_limits<T2>::is_integer || std::is_same<T2, null>::value,
+          bool T1_is_widest = (sizeof(T1) > sizeof(T2) || std::is_same<T2, null>::value)>
+struct Common;
+template <class T1, class T2, bool b>
+struct Common<T1, T2, b, b, true> {
+    typedef T1 type;
+};
+template <class T1, class T2, bool b>
+struct Common<T1, T2, b, b, false> {
+    typedef T2 type;
+};
+template <class T1, class T2, bool b>
+struct Common<T1, T2, false, true, b> {
+    typedef T1 type;
+};
+template <class T1, class T2, bool b>
+struct Common<T1, T2, true, false, b> {
+    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);
+    }
+    template <class C, class T>
+    friend std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>&, const RowIndex&);
+
+private:
+    util::Optional<size_t> m_row_index;
+};
+
+template <class C, class T>
+inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, const RowIndex& r)
+{
+    if (!r.is_attached()) {
+        out << "detached row";
+    } else if (r.is_null()) {
+        out << "null row";
+    } else {
+        out << r.m_row_index;
+    }
+    return out;
+}
+
+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 void verify_column() const = 0;
+    virtual const Table* get_base_table() const = 0;
+    virtual std::string description() const = 0;
+
+    virtual std::unique_ptr<Expression> clone(QueryNodeHandoverPatches*) const = 0;
+    virtual void apply_handover_patch(QueryNodeHandoverPatches&, Group&)
+    {
+    }
+};
+
+template <typename T, typename... Args>
+std::unique_ptr<Expression> make_expression(Args&&... args)
+{
+    return std::unique_ptr<Expression>(new T(std::forward<Args>(args)...));
+}
+
+class Subexpr {
+public:
+    virtual ~Subexpr()
+    {
+    }
+
+    virtual std::unique_ptr<Subexpr> 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*)
+    {
+    }
+
+    virtual void verify_column() const = 0;
+    virtual std::string description() const = 0;
+
+    // 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 <typename T, typename... Args>
+std::unique_ptr<Subexpr> make_subexpr(Args&&... args)
+{
+    return std::unique_ptr<Subexpr>(new T(std::forward<Args>(args)...));
+}
+
+template <class T>
+class Columns;
+template <class T>
+class Value;
+class ConstantStringValue;
+template <class T>
+class Subexpr2;
+template <class oper, class TLeft = Subexpr, class TRight = Subexpr>
+class Operator;
+template <class oper, class TLeft = Subexpr>
+class UnaryOperator;
+template <class oper, class TLeft = Subexpr>
+class SizeOperator;
+template <class TCond, class T, class TLeft = Subexpr, class TRight = Subexpr>
+class Compare;
+template <bool has_links>
+class UnaryLinkCompare;
+class ColumnAccessorBase;
+
+
+// Handle cases where left side is a constant (int, float, int64_t, double, StringData)
+template <class Cond, class L, class R>
+Query create(L left, const Subexpr2<R>& 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 <cond> Subexpr2. Interception of Subexpr2 <cond> Subexpr is elsewhere.
+
+#ifdef REALM_OLDQUERY_FALLBACK // if not defined, then never fallback to query_engine.hpp; always use query_expression
+    const Columns<R>* column = dynamic_cast<const Columns<R>*>(&right);
+    // TODO: recognize size operator expressions
+    // auto size_operator = dynamic_cast<const SizeOperator<Size<StringData>, Subexpr>*>(&right);
+
+    if (column && ((std::numeric_limits<L>::is_integer && std::numeric_limits<R>::is_integer) ||
+                   (std::is_same<L, double>::value && std::is_same<R, double>::value) ||
+                   (std::is_same<L, float>::value && std::is_same<R, float>::value) ||
+                   (std::is_same<L, Timestamp>::value && std::is_same<R, Timestamp>::value) ||
+                   (std::is_same<L, StringData>::value && std::is_same<R, StringData>::value) ||
+                   (std::is_same<L, BinaryData>::value && std::is_same<R, BinaryData>::value)) &&
+        !column->links_exist()) {
+        const Table* t = column->get_base_table();
+        Query q = Query(*t);
+
+        if (std::is_same<Cond, Less>::value)
+            q.greater(column->column_ndx(), _impl::only_numeric<R>(left));
+        else if (std::is_same<Cond, Greater>::value)
+            q.less(column->column_ndx(), _impl::only_numeric<R>(left));
+        else if (std::is_same<Cond, Equal>::value)
+            q.equal(column->column_ndx(), left);
+        else if (std::is_same<Cond, NotEqual>::value)
+            q.not_equal(column->column_ndx(), left);
+        else if (std::is_same<Cond, LessEqual>::value)
+            q.greater_equal(column->column_ndx(), _impl::only_numeric<R>(left));
+        else if (std::is_same<Cond, GreaterEqual>::value)
+            q.less_equal(column->column_ndx(), _impl::only_numeric<R>(left));
+        else if (std::is_same<Cond, EqualIns>::value)
+            q.equal(column->column_ndx(), _impl::only_string(left), false);
+        else if (std::is_same<Cond, NotEqualIns>::value)
+            q.not_equal(column->column_ndx(), _impl::only_string(left), false);
+        else if (std::is_same<Cond, BeginsWith>::value)
+            q.begins_with(column->column_ndx(), _impl::only_string(left));
+        else if (std::is_same<Cond, BeginsWithIns>::value)
+            q.begins_with(column->column_ndx(), _impl::only_string(left), false);
+        else if (std::is_same<Cond, EndsWith>::value)
+            q.ends_with(column->column_ndx(), _impl::only_string(left));
+        else if (std::is_same<Cond, EndsWithIns>::value)
+            q.ends_with(column->column_ndx(), _impl::only_string(left), false);
+        else if (std::is_same<Cond, Contains>::value)
+            q.contains(column->column_ndx(), _impl::only_string(left));
+        else if (std::is_same<Cond, ContainsIns>::value)
+            q.contains(column->column_ndx(), _impl::only_string(left), false);
+        else if (std::is_same<Cond, Like>::value)
+            q.like(column->column_ndx(), _impl::only_string(left));
+        else if (std::is_same<Cond, LikeIns>::value)
+            q.like(column->column_ndx(), _impl::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<L, R>::type;
+        using ValueType =
+            typename std::conditional<std::is_same<L, StringData>::value, ConstantStringValue, Value<L>>::type;
+        return make_expression<Compare<Cond, CommonType>>(make_subexpr<ValueType>(left), right.clone());
+    }
+}
+
+
+// All overloads where left-hand-side is Subexpr2<L>:
+//
+// left-hand-side       operator                              right-hand-side
+// Subexpr2<L>          +, -, *, /, <, >, ==, !=, <=, >=      R, Subexpr2<R>
+//
+// For L = R = {int, int64_t, float, double, StringData, Timestamp}:
+template <class L, class R>
+class Overloads {
+    typedef typename Common<L, R>::type CommonType;
+
+    std::unique_ptr<Subexpr> clone_subexpr() const
+    {
+        return static_cast<const Subexpr2<L>&>(*this).clone();
+    }
+
+public:
+    // Arithmetic, right side constant
+    Operator<Plus<CommonType>> operator+(R right) const
+    {
+        return {clone_subexpr(), make_subexpr<Value<R>>(right)};
+    }
+    Operator<Minus<CommonType>> operator-(R right) const
+    {
+        return {clone_subexpr(), make_subexpr<Value<R>>(right)};
+    }
+    Operator<Mul<CommonType>> operator*(R right) const
+    {
+        return {clone_subexpr(), make_subexpr<Value<R>>(right)};
+    }
+    Operator<Div<CommonType>> operator/(R right) const
+    {
+        return {clone_subexpr(), make_subexpr<Value<R>>(right)};
+    }
+
+    // Arithmetic, right side subexpression
+    Operator<Plus<CommonType>> operator+(const Subexpr2<R>& right) const
+    {
+        return {clone_subexpr(), right.clone()};
+    }
+    Operator<Minus<CommonType>> operator-(const Subexpr2<R>& right) const
+    {
+        return {clone_subexpr(), right.clone()};
+    }
+    Operator<Mul<CommonType>> operator*(const Subexpr2<R>& right) const
+    {
+        return {clone_subexpr(), right.clone()};
+    }
+    Operator<Div<CommonType>> operator/(const Subexpr2<R>& right) const
+    {
+        return {clone_subexpr(), right.clone()};
+    }
+
+    // Compare, right side constant
+    Query operator>(R right)
+    {
+        return create<Less>(right, static_cast<Subexpr2<L>&>(*this));
+    }
+    Query operator<(R right)
+    {
+        return create<Greater>(right, static_cast<Subexpr2<L>&>(*this));
+    }
+    Query operator>=(R right)
+    {
+        return create<LessEqual>(right, static_cast<Subexpr2<L>&>(*this));
+    }
+    Query operator<=(R right)
+    {
+        return create<GreaterEqual>(right, static_cast<Subexpr2<L>&>(*this));
+    }
+    Query operator==(R right)
+    {
+        return create<Equal>(right, static_cast<Subexpr2<L>&>(*this));
+    }
+    Query operator!=(R right)
+    {
+        return create<NotEqual>(right, static_cast<Subexpr2<L>&>(*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 <cond> Subexpr2 only. Value <cond> Subexpr2 is intercepted elsewhere.
+    template <class Cond>
+    Query create2(const Subexpr2<R>& 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<R>* left_col = dynamic_cast<const Columns<R>*>(static_cast<Subexpr2<L>*>(this));
+        const Columns<R>* right_col = dynamic_cast<const Columns<R>*>(&right);
+
+        // query_engine supports 'T-column <op> <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<L, R>::value && !left_col->is_nullable() &&
+            !right_col->is_nullable() && !left_col->links_exist() && !right_col->links_exist() &&
+            !std::is_same<L, Timestamp>::value) {
+            const Table* t = left_col->get_base_table();
+            Query q = Query(*t);
+
+            if (std::numeric_limits<L>::is_integer || std::is_same<L, OldDateTime>::value) {
+                if (std::is_same<Cond, Less>::value)
+                    q.less_int(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, Greater>::value)
+                    q.greater_int(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, Equal>::value)
+                    q.equal_int(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, NotEqual>::value)
+                    q.not_equal_int(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, LessEqual>::value)
+                    q.less_equal_int(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, GreaterEqual>::value)
+                    q.greater_equal_int(left_col->column_ndx(), right_col->column_ndx());
+                else {
+                    REALM_ASSERT(false);
+                }
+            }
+            else if (std::is_same<L, float>::value) {
+                if (std::is_same<Cond, Less>::value)
+                    q.less_float(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, Greater>::value)
+                    q.greater_float(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, Equal>::value)
+                    q.equal_float(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, NotEqual>::value)
+                    q.not_equal_float(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, LessEqual>::value)
+                    q.less_equal_float(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, GreaterEqual>::value)
+                    q.greater_equal_float(left_col->column_ndx(), right_col->column_ndx());
+                else {
+                    REALM_ASSERT(false);
+                }
+            }
+            else if (std::is_same<L, double>::value) {
+                if (std::is_same<Cond, Less>::value)
+                    q.less_double(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, Greater>::value)
+                    q.greater_double(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, Equal>::value)
+                    q.equal_double(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, NotEqual>::value)
+                    q.not_equal_double(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, LessEqual>::value)
+                    q.less_equal_double(left_col->column_ndx(), right_col->column_ndx());
+                else if (std::is_same<Cond, GreaterEqual>::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<Compare<Cond, typename Common<L, R>::type>>(clone_subexpr(), right.clone());
+        }
+    }
+
+    // Compare, right side subexpression
+    Query operator==(const Subexpr2<R>& right)
+    {
+        return create2<Equal>(right);
+    }
+    Query operator!=(const Subexpr2<R>& right)
+    {
+        return create2<NotEqual>(right);
+    }
+    Query operator>(const Subexpr2<R>& right)
+    {
+        return create2<Greater>(right);
+    }
+    Query operator<(const Subexpr2<R>& right)
+    {
+        return create2<Less>(right);
+    }
+    Query operator>=(const Subexpr2<R>& right)
+    {
+        return create2<GreaterEqual>(right);
+    }
+    Query operator<=(const Subexpr2<R>& right)
+    {
+        return create2<LessEqual>(right);
+    }
+};
+
+// With this wrapper class we can define just 20 overloads inside Overloads<L, R> 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 T>
+class Subexpr2 : public Subexpr,
+                 public Overloads<T, const char*>,
+                 public Overloads<T, int>,
+                 public Overloads<T, float>,
+                 public Overloads<T, double>,
+                 public Overloads<T, int64_t>,
+                 public Overloads<T, StringData>,
+                 public Overloads<T, bool>,
+                 public Overloads<T, Timestamp>,
+                 public Overloads<T, OldDateTime>,
+                 public Overloads<T, null> {
+public:
+    virtual ~Subexpr2()
+    {
+    }
+
+#define RLM_U2(t, o) using Overloads<T, t>::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<Link> only provides equality comparisons. Their implementations can be found later in this file.
+template <>
+class Subexpr2<Link> : public Subexpr {
+};
+
+template <>
+class Subexpr2<StringData> : public Subexpr, public Overloads<StringData, StringData> {
+public:
+    Query equal(StringData sd, bool case_sensitive = true);
+    Query equal(const Subexpr2<StringData>& col, bool case_sensitive = true);
+    Query not_equal(StringData sd, bool case_sensitive = true);
+    Query not_equal(const Subexpr2<StringData>& col, bool case_sensitive = true);
+    Query begins_with(StringData sd, bool case_sensitive = true);
+    Query begins_with(const Subexpr2<StringData>& col, bool case_sensitive = true);
+    Query ends_with(StringData sd, bool case_sensitive = true);
+    Query ends_with(const Subexpr2<StringData>& col, bool case_sensitive = true);
+    Query contains(StringData sd, bool case_sensitive = true);
+    Query contains(const Subexpr2<StringData>& col, bool case_sensitive = true);
+    Query like(StringData sd, bool case_sensitive = true);
+    Query like(const Subexpr2<StringData>& col, bool case_sensitive = true);
+};
+
+/*
+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<int> = {5, 5, 5, 5, 5, 5, 5, 5} and then read values
+NullableVector<int> = {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<null>). This allows compile
+time optimizations for these cases.
+*/
+
+template <class T, size_t prealloc = 8>
+struct NullableVector {
+    using Underlying = typename util::RemoveOptional<T>::type;
+    using t_storage =
+        typename std::conditional<std::is_same<Underlying, bool>::value || std::is_same<Underlying, int>::value,
+                                  int64_t, Underlying>::type;
+
+    NullableVector()
+    {
+    }
+
+    NullableVector& operator=(const NullableVector& other)
+    {
+        if (this != &other) {
+            init(other.m_size);
+            realm::safe_copy_n(other.m_first, other.m_size, m_first);
+            m_null = other.m_null;
+        }
+        return *this;
+    }
+
+    NullableVector(const NullableVector& other)
+    {
+        init(other.m_size);
+        realm::safe_copy_n(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<T>(m_first[index]);
+    }
+
+    inline bool is_null(size_t index) const
+    {
+        REALM_ASSERT((std::is_same<t_storage, int64_t>::value));
+        return m_first[index] == m_null;
+    }
+
+    inline void set_null(size_t index)
+    {
+        REALM_ASSERT((std::is_same<t_storage, int64_t>::value));
+        m_first[index] = m_null;
+    }
+
+    template <typename Type = t_storage>
+    typename std::enable_if<std::is_same<Type, int64_t>::value, void>::type set(size_t index, t_storage value)
+    {
+        REALM_ASSERT((std::is_same<t_storage, int64_t>::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<uint64_t>(m_null) + 0xfffffffbULL;
+            while (std::find(m_first, m_first + m_size, static_cast<int64_t>(candidate)) != m_first + m_size)
+                candidate += 0xfffffffbULL;
+            std::replace(m_first, m_first + m_size, m_null, static_cast<int64_t>(candidate));
+        }
+        m_first[index] = value;
+    }
+
+    template <typename Type = T>
+    typename std::enable_if<realm::is_any<Type, float, double, OldDateTime, BinaryData, StringData, RowIndex,
+                                          Timestamp, ConstTableRef, null>::value,
+                            void>::type
+    set(size_t index, t_storage value)
+    {
+        m_first[index] = value;
+    }
+
+    inline util::Optional<T> 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<Underlying> 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<T, null>::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<t_storage*>(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<int64_t>(&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<double>::is_null(size_t index) const
+{
+    return null::is_null_float(m_first[index]);
+}
+
+template <>
+inline void NullableVector<double>::set_null(size_t index)
+{
+    m_first[index] = null::get_null_float<double>();
+}
+
+// Float
+template <>
+inline bool NullableVector<float>::is_null(size_t index) const
+{
+    return null::is_null_float(m_first[index]);
+}
+
+template <>
+inline void NullableVector<float>::set_null(size_t index)
+{
+    m_first[index] = null::get_null_float<float>();
+}
+
+
+// Null
+template <>
+inline void NullableVector<null>::set_null(size_t)
+{
+    return;
+}
+template <>
+inline bool NullableVector<null>::is_null(size_t) const
+{
+    return true;
+}
+
+
+// OldDateTime
+template <>
+inline bool NullableVector<OldDateTime>::is_null(size_t index) const
+{
+    return m_first[index].get_olddatetime() == m_null;
+}
+
+
+template <>
+inline void NullableVector<OldDateTime>::set_null(size_t index)
+{
+    m_first[index] = m_null;
+}
+
+// StringData
+
+template <>
+inline bool NullableVector<StringData>::is_null(size_t index) const
+{
+    return m_first[index].is_null();
+}
+
+template <>
+inline void NullableVector<StringData>::set_null(size_t index)
+{
+    m_first[index] = StringData();
+}
+
+// BinaryData
+
+template <>
+inline bool NullableVector<BinaryData>::is_null(size_t index) const
+{
+    return m_first[index].is_null();
+}
+
+template <>
+inline void NullableVector<BinaryData>::set_null(size_t index)
+{
+    m_first[index] = BinaryData();
+}
+
+// RowIndex
+template <>
+inline bool NullableVector<RowIndex>::is_null(size_t index) const
+{
+    return m_first[index].is_null();
+}
+template <>
+inline void NullableVector<RowIndex>::set_null(size_t index)
+{
+    m_first[index] = RowIndex();
+}
+
+
+// Timestamp
+
+template <>
+inline bool NullableVector<Timestamp>::is_null(size_t index) const
+{
+    return m_first[index].is_null();
+}
+
+template <>
+inline void NullableVector<Timestamp>::set_null(size_t index)
+{
+    m_first[index] = Timestamp{};
+}
+
+// ConstTableRef
+template <>
+inline bool NullableVector<ConstTableRef>::is_null(size_t index) const
+{
+    return !bool(m_first[index]);
+}
+template <>
+inline void NullableVector<ConstTableRef>::set_null(size_t index)
+{
+    m_first[index].reset();
+}
+
+template <typename Operator>
+struct OperatorOptionalAdapter {
+    template <typename L, typename R>
+    util::Optional<typename Operator::type> operator()(const util::Optional<L>& left, const util::Optional<R>& right)
+    {
+        if (!left || !right)
+            return util::none;
+        return Operator()(*left, *right);
+    }
+
+    template <typename T>
+    util::Optional<typename Operator::type> operator()(const util::Optional<T>& arg)
+    {
+        if (!arg)
+            return util::none;
+        return Operator()(*arg);
+    }
+};
+
+
+struct TrueExpression : Expression {
+    size_t find_first(size_t start, size_t end) const override
+    {
+        REALM_ASSERT(start <= end);
+        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;
+    }
+    void verify_column() const override
+    {
+    }
+    std::string description() const override
+    {
+        return "TRUEPREDICATE";
+    }
+    std::unique_ptr<Expression> clone(QueryNodeHandoverPatches*) const override
+    {
+        return std::unique_ptr<Expression>(new TrueExpression(*this));
+    }
+};
+
+
+struct FalseExpression : Expression {
+    size_t find_first(size_t, size_t) const override
+    {
+        return realm::not_found;
+    }
+    void set_base_table(const Table*) override
+    {
+    }
+    void verify_column() const override
+    {
+    }
+    std::string description() const override
+    {
+        return "FALSEPREDICATE";
+    }
+    const Table* get_base_table() const override
+    {
+        return nullptr;
+    }
+    std::unique_ptr<Expression> clone(QueryNodeHandoverPatches*) const override
+    {
+        return std::unique_ptr<Expression>(new FalseExpression(*this));
+    }
+};
+
+
+// Stores N values of type T. Can also exchange data with other ValueBase of different types
+template <class T>
+class Value : public ValueBase, public Subexpr2<T> {
+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 verify_column() const override
+    {
+    }
+
+    virtual std::string description() const override
+    {
+        if (ValueBase::m_from_link_list) {
+            return util::serializer::print_value(util::to_string(ValueBase::m_values)
+                                        + (ValueBase::m_values == 1 ? " value" : " values"));
+        }
+        if (m_storage.m_size > 0) {
+            return util::serializer::print_value(m_storage[0]);
+        }
+        return "";
+    }
+
+    void evaluate(size_t, ValueBase& destination) override
+    {
+        destination.import(*this);
+    }
+
+
+    template <class TOperator>
+    REALM_FORCEINLINE void fun(const Value* left, const Value* right)
+    {
+        OperatorOptionalAdapter<TOperator> 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 <class TOperator>
+    REALM_FORCEINLINE void fun(const Value* value)
+    {
+        init(value->m_from_link_list, value->m_values);
+
+        OperatorOptionalAdapter<TOperator> 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 <class D>
+    typename std::enable_if<std::is_convertible<T, D>::value>::type REALM_FORCEINLINE
+    export2(ValueBase& destination) const
+    {
+        Value<D>& d = static_cast<Value<D>&>(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<D>(m_storage[t]));
+            }
+        }
+    }
+
+    template <class D>
+    typename std::enable_if<!std::is_convertible<T, D>::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<Timestamp>(destination);
+    }
+
+    REALM_FORCEINLINE void export_bool(ValueBase& destination) const override
+    {
+        export2<bool>(destination);
+    }
+
+    REALM_FORCEINLINE void export_int64_t(ValueBase& destination) const override
+    {
+        export2<int64_t>(destination);
+    }
+
+    REALM_FORCEINLINE void export_float(ValueBase& destination) const override
+    {
+        export2<float>(destination);
+    }
+
+    REALM_FORCEINLINE void export_int(ValueBase& destination) const override
+    {
+        export2<int>(destination);
+    }
+
+    REALM_FORCEINLINE void export_double(ValueBase& destination) const override
+    {
+        export2<double>(destination);
+    }
+    REALM_FORCEINLINE void export_StringData(ValueBase& destination) const override
+    {
+        export2<StringData>(destination);
+    }
+    REALM_FORCEINLINE void export_BinaryData(ValueBase& destination) const override
+    {
+        export2<BinaryData>(destination);
+    }
+    REALM_FORCEINLINE void export_RowIndex(ValueBase& destination) const override
+    {
+        export2<RowIndex>(destination);
+    }
+    REALM_FORCEINLINE void export_null(ValueBase& destination) const override
+    {
+        Value<null>& d = static_cast<Value<null>&>(destination);
+        d.init(m_from_link_list, m_values);
+    }
+
+    REALM_FORCEINLINE void import(const ValueBase& source) override
+    {
+        if (std::is_same<T, int>::value)
+            source.export_int(*this);
+        else if (std::is_same<T, Timestamp>::value)
+            source.export_Timestamp(*this);
+        else if (std::is_same<T, bool>::value)
+            source.export_bool(*this);
+        else if (std::is_same<T, float>::value)
+            source.export_float(*this);
+        else if (std::is_same<T, double>::value)
+            source.export_double(*this);
+        else if (std::is_same<T, int64_t>::value || std::is_same<T, bool>::value ||
+                 std::is_same<T, OldDateTime>::value)
+            source.export_int64_t(*this);
+        else if (std::is_same<T, StringData>::value)
+            source.export_StringData(*this);
+        else if (std::is_same<T, BinaryData>::value)
+            source.export_BinaryData(*this);
+        else if (std::is_same<T, RowIndex>::value)
+            source.export_RowIndex(*this);
+        else if (std::is_same<T, null>::value)
+            source.export_null(*this);
+        else
+            REALM_ASSERT_DEBUG(false);
+    }
+
+    // Given a TCond (==, !=, >, <, >=, <=) and two Value<T>, return index of first match
+    template <class TCond>
+    REALM_FORCEINLINE static size_t compare(Value<T>* left, Value<T>* 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<Subexpr> clone(QueryNodeHandoverPatches*) const override
+    {
+        return make_subexpr<Value<T>>(*this);
+    }
+
+    NullableVector<T> m_storage;
+};
+
+class ConstantStringValue : public Value<StringData> {
+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<Subexpr> clone(QueryNodeHandoverPatches*) const override
+    {
+        return std::unique_ptr<Subexpr>(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<std::string> m_string;
+};
+
+// All overloads where left-hand-side is L:
+//
+// left-hand-side       operator                              right-hand-side
+// L                    +, -, *, /, <, >, ==, !=, <=, >=      Subexpr2<R>
+//
+// For L = R = {int, int64_t, float, double, Timestamp}:
+// Compare numeric values
+template <class R>
+Query operator>(double left, const Subexpr2<R>& right)
+{
+    return create<Greater>(left, right);
+}
+template <class R>
+Query operator>(float left, const Subexpr2<R>& right)
+{
+    return create<Greater>(left, right);
+}
+template <class R>
+Query operator>(int left, const Subexpr2<R>& right)
+{
+    return create<Greater>(left, right);
+}
+template <class R>
+Query operator>(int64_t left, const Subexpr2<R>& right)
+{
+    return create<Greater>(left, right);
+}
+template <class R>
+Query operator>(Timestamp left, const Subexpr2<R>& right)
+{
+    return create<Greater>(left, right);
+}
+
+template <class R>
+Query operator<(double left, const Subexpr2<R>& right)
+{
+    return create<Less>(left, right);
+}
+template <class R>
+Query operator<(float left, const Subexpr2<R>& right)
+{
+    return create<Less>(left, right);
+}
+template <class R>
+Query operator<(int left, const Subexpr2<R>& right)
+{
+    return create<Less>(left, right);
+}
+template <class R>
+Query operator<(int64_t left, const Subexpr2<R>& right)
+{
+    return create<Less>(left, right);
+}
+template <class R>
+Query operator<(Timestamp left, const Subexpr2<R>& right)
+{
+    return create<Less>(left, right);
+}
+template <class R>
+Query operator==(double left, const Subexpr2<R>& right)
+{
+    return create<Equal>(left, right);
+}
+template <class R>
+Query operator==(float left, const Subexpr2<R>& right)
+{
+    return create<Equal>(left, right);
+}
+template <class R>
+Query operator==(int left, const Subexpr2<R>& right)
+{
+    return create<Equal>(left, right);
+}
+template <class R>
+Query operator==(int64_t left, const Subexpr2<R>& right)
+{
+    return create<Equal>(left, right);
+}
+template <class R>
+Query operator==(Timestamp left, const Subexpr2<R>& right)
+{
+    return create<Equal>(left, right);
+}
+template <class R>
+Query operator>=(double left, const Subexpr2<R>& right)
+{
+    return create<GreaterEqual>(left, right);
+}
+template <class R>
+Query operator>=(float left, const Subexpr2<R>& right)
+{
+    return create<GreaterEqual>(left, right);
+}
+template <class R>
+Query operator>=(int left, const Subexpr2<R>& right)
+{
+    return create<GreaterEqual>(left, right);
+}
+template <class R>
+Query operator>=(int64_t left, const Subexpr2<R>& right)
+{
+    return create<GreaterEqual>(left, right);
+}
+template <class R>
+Query operator>=(Timestamp left, const Subexpr2<R>& right)
+{
+    return create<GreaterEqual>(left, right);
+}
+template <class R>
+Query operator<=(double left, const Subexpr2<R>& right)
+{
+    return create<LessEqual>(left, right);
+}
+template <class R>
+Query operator<=(float left, const Subexpr2<R>& right)
+{
+    return create<LessEqual>(left, right);
+}
+template <class R>
+Query operator<=(int left, const Subexpr2<R>& right)
+{
+    return create<LessEqual>(left, right);
+}
+template <class R>
+Query operator<=(int64_t left, const Subexpr2<R>& right)
+{
+    return create<LessEqual>(left, right);
+}
+template <class R>
+Query operator<=(Timestamp left, const Subexpr2<R>& right)
+{
+    return create<LessEqual>(left, right);
+}
+template <class R>
+Query operator!=(double left, const Subexpr2<R>& right)
+{
+    return create<NotEqual>(left, right);
+}
+template <class R>
+Query operator!=(float left, const Subexpr2<R>& right)
+{
+    return create<NotEqual>(left, right);
+}
+template <class R>
+Query operator!=(int left, const Subexpr2<R>& right)
+{
+    return create<NotEqual>(left, right);
+}
+template <class R>
+Query operator!=(int64_t left, const Subexpr2<R>& right)
+{
+    return create<NotEqual>(left, right);
+}
+template <class R>
+Query operator!=(Timestamp left, const Subexpr2<R>& right)
+{
+    return create<NotEqual>(left, right);
+}
+
+// Arithmetic
+template <class R>
+Operator<Plus<typename Common<R, double>::type>> operator+(double left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<double>>(left), right.clone()};
+}
+template <class R>
+Operator<Plus<typename Common<R, float>::type>> operator+(float left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<float>>(left), right.clone()};
+}
+template <class R>
+Operator<Plus<typename Common<R, int>::type>> operator+(int left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<int>>(left), right.clone()};
+}
+template <class R>
+Operator<Plus<typename Common<R, int64_t>::type>> operator+(int64_t left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<int64_t>>(left), right.clone()};
+}
+template <class R>
+Operator<Minus<typename Common<R, double>::type>> operator-(double left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<double>>(left), right.clone()};
+}
+template <class R>
+Operator<Minus<typename Common<R, float>::type>> operator-(float left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<float>>(left), right.clone()};
+}
+template <class R>
+Operator<Minus<typename Common<R, int>::type>> operator-(int left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<int>>(left), right.clone()};
+}
+template <class R>
+Operator<Minus<typename Common<R, int64_t>::type>> operator-(int64_t left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<int64_t>>(left), right.clone()};
+}
+template <class R>
+Operator<Mul<typename Common<R, double>::type>> operator*(double left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<double>>(left), right.clone()};
+}
+template <class R>
+Operator<Mul<typename Common<R, float>::type>> operator*(float left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<float>>(left), right.clone()};
+}
+template <class R>
+Operator<Mul<typename Common<R, int>::type>> operator*(int left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<int>>(left), right.clone()};
+}
+template <class R>
+Operator<Mul<typename Common<R, int64_t>::type>> operator*(int64_t left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<int64_t>>(left), right.clone()};
+}
+template <class R>
+Operator<Div<typename Common<R, double>::type>> operator/(double left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<double>>(left), right.clone()};
+}
+template <class R>
+Operator<Div<typename Common<R, float>::type>> operator/(float left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<float>>(left), right.clone()};
+}
+template <class R>
+Operator<Div<typename Common<R, int>::type>> operator/(int left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<int>>(left), right.clone()};
+}
+template <class R>
+Operator<Div<typename Common<R, int64_t>::type>> operator/(int64_t left, const Subexpr2<R>& right)
+{
+    return {make_subexpr<Value<int64_t>>(left), right.clone()};
+}
+
+// Unary operators
+template <class T>
+UnaryOperator<Pow<T>> power(const Subexpr2<T>& 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<void>(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<size_t>& result)
+        : m_links(result)
+    {
+    }
+
+    bool consume(size_t row_index) override
+    {
+        m_links.push_back(row_index);
+        return true; // continue evaluation
+    }
+    std::vector<size_t>& 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<size_t> 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<size_t> columns)
+        : m_link_column_indexes(std::move(columns))
+    {
+        set_base_table(table);
+    }
+
+    LinkMap(LinkMap const& other, QueryNodeHandoverPatches* patches)
+        : LinkMap(other)
+    {
+        if (!patches)
+            return;
+
+        m_link_column_indexes.clear();
+        const Table* table = base_table();
+        m_tables.clear();
+        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<const BacklinkColumn*>(column)->get_origin_table();
+            else
+                table = &static_cast<const LinkColumnBase*>(column)->get_target_table();
+        }
+    }
+
+    void set_base_table(const Table* table)
+    {
+        if (table == base_table())
+            return;
+
+        m_tables.clear();
+        m_tables.push_back(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
+            const Table* t = m_tables.back();
+            ColumnType type = t->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 = t->get_column_link_list(link_column_index);
+                m_link_columns.push_back(&cll);
+                m_only_unary_links = false;
+                m_tables.push_back(&cll.get_target_table());
+            }
+            else if (type == col_type_Link) {
+                const LinkColumn& cl = t->get_column_link(link_column_index);
+                m_link_columns.push_back(&cl);
+                m_tables.push_back(&cl.get_target_table());
+            }
+            else if (type == col_type_BackLink) {
+                const BacklinkColumn& bl = t->get_column_backlink(link_column_index);
+                m_link_columns.push_back(&bl);
+                m_only_unary_links = false;
+                m_tables.push_back(&bl.get_origin_table());
+            }
+        }
+    }
+
+    void verify_columns() const
+    {
+        for (size_t i = 0; i < m_link_column_indexes.size(); i++) {
+            m_tables[i]->verify_column(m_link_column_indexes[i], m_link_columns[i]);
+        }
+    }
+
+    virtual std::string description() const
+    {
+        std::string s;
+        for (size_t i = 0; i < m_link_column_indexes.size(); ++i) {
+            if (i < m_tables.size() && m_tables[i]) {
+                if (m_link_types[i] == col_type_BackLink) {
+                    s += "backlink";
+                } else if (m_link_column_indexes[i] < m_tables[i]->get_column_count()) {
+                    s += std::string(m_tables[i]->get_column_name(m_link_column_indexes[i]));
+                }
+                if (i != m_link_column_indexes.size() - 1) {
+                    s += util::serializer::value_separator;
+                }
+            }
+        }
+        return s;
+    }
+
+    std::vector<size_t> get_links(size_t index)
+    {
+        std::vector<size_t> 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_tables.empty() ? nullptr : m_tables[0];
+    }
+
+    const Table* target_table() const
+    {
+        REALM_ASSERT(!m_tables.empty());
+        return m_tables.back();
+    }
+
+    std::vector<const ColumnBase*> 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<const LinkColumn*>(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<const LinkListColumn*>(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<const BacklinkColumn*>(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<size_t>& result)
+    {
+        MakeLinkVector mlv = MakeLinkVector(result);
+        map_links(row, mlv);
+    }
+
+    std::vector<size_t> m_link_column_indexes;
+    std::vector<ColumnType> m_link_types;
+    std::vector<const Table*> m_tables;
+    bool m_only_unary_links = true;
+
+    template <class>
+    friend Query compare(const Subexpr2<Link>&, const ConstRow&);
+};
+
+template <class T, class S, class I>
+Query string_compare(const Subexpr2<StringData>& left, T right, bool case_insensitive);
+template <class S, class I>
+Query string_compare(const Subexpr2<StringData>& left, const Subexpr2<StringData>& right, bool case_insensitive);
+
+template <class T>
+Value<T> make_value_for_link(bool only_unary_links, size_t size)
+{
+    Value<T> 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<T> : public SimpleQuerySupport<T>` 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<T>.
+// To get faster Query support, either add SequentialGetter support (faster) or create a query_engine.hpp
+// node for it (super fast).
+
+template <class T>
+class SimpleQuerySupport : public Subexpr2<T> {
+public:
+    SimpleQuerySupport(size_t column, const Table* table, std::vector<size_t> 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 verify_column() const override
+    {
+        // verify links
+        m_link_map.verify_columns();
+        // verify target table
+        const Table* target_table = m_link_map.target_table();
+        if (target_table && m_column_ndx != npos) {
+            target_table->verify_column(m_column_ndx, m_column);
+        }
+    }
+
+    void evaluate(size_t index, ValueBase& destination) override
+    {
+        Value<T>& d = static_cast<Value<T>&>(destination);
+        size_t col = column_ndx();
+
+        if (links_exist()) {
+            std::vector<size_t> links = m_link_map.get_links(index);
+            Value<T> v = make_value_for_link<T>(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<T>(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<T>(col, index + t));
+            }
+        }
+    }
+
+    bool links_exist() const
+    {
+        return m_link_map.m_link_columns.size() > 0;
+    }
+
+    virtual std::string description() const override
+    {
+        std::string desc;
+        if (links_exist()) {
+            desc = m_link_map.description() + util::serializer::value_separator;
+        }
+        const Table* target_table = m_link_map.target_table();
+        if (target_table && target_table->is_attached()) {
+            desc += std::string(target_table->get_column_name(m_column_ndx));
+        }
+        return desc;
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches = nullptr) const override
+    {
+        return make_subexpr<Columns<T>>(static_cast<const Columns<T>&>(*this), patches);
+    }
+
+    SimpleQuerySupport(SimpleQuerySupport const& other, QueryNodeHandoverPatches* patches)
+        : Subexpr2<T>(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();
+    }
+
+    SizeOperator<Size<T>> size()
+    {
+        return SizeOperator<Size<T>>(this->clone(nullptr));
+    }
+
+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<Timestamp> : public SimpleQuerySupport<Timestamp> {
+    using SimpleQuerySupport::SimpleQuerySupport;
+};
+
+template <>
+class Columns<BinaryData> : public SimpleQuerySupport<BinaryData> {
+    using SimpleQuerySupport::SimpleQuerySupport;
+};
+
+template <>
+class Columns<StringData> : public SimpleQuerySupport<StringData> {
+public:
+    Columns(size_t column, const Table* table, std::vector<size_t> links = {})
+        : SimpleQuerySupport(column, table, links)
+    {
+    }
+
+    Columns(Columns const& other, QueryNodeHandoverPatches* patches = nullptr)
+        : SimpleQuerySupport(other, patches)
+    {
+    }
+
+    Columns(Columns&& other)
+        : SimpleQuerySupport(other)
+    {
+    }
+};
+
+template <class T, class S, class I>
+Query string_compare(const Subexpr2<StringData>& left, T right, bool case_sensitive)
+{
+    StringData sd(right);
+    if (case_sensitive)
+        return create<S>(sd, left);
+    else
+        return create<I>(sd, left);
+}
+
+template <class S, class I>
+Query string_compare(const Subexpr2<StringData>& left, const Subexpr2<StringData>& right, bool case_sensitive)
+{
+    if (case_sensitive)
+        return make_expression<Compare<S, StringData>>(right.clone(), left.clone());
+    else
+        return make_expression<Compare<I, StringData>>(right.clone(), left.clone());
+}
+
+// Columns<String> == Columns<String>
+inline Query operator==(const Columns<StringData>& left, const Columns<StringData>& right)
+{
+    return string_compare<Equal, EqualIns>(left, right, true);
+}
+
+// Columns<String> != Columns<String>
+inline Query operator!=(const Columns<StringData>& left, const Columns<StringData>& right)
+{
+    return string_compare<NotEqual, NotEqualIns>(left, right, true);
+}
+
+// String == Columns<String>
+template <class T>
+Query operator==(T left, const Columns<StringData>& right)
+{
+    return operator==(right, left);
+}
+
+// String != Columns<String>
+template <class T>
+Query operator!=(T left, const Columns<StringData>& right)
+{
+    return operator!=(right, left);
+}
+
+// Columns<String> == String
+template <class T>
+Query operator==(const Columns<StringData>& left, T right)
+{
+    return string_compare<T, Equal, EqualIns>(left, right, true);
+}
+
+// Columns<String> != String
+template <class T>
+Query operator!=(const Columns<StringData>& left, T right)
+{
+    return string_compare<T, NotEqual, NotEqualIns>(left, right, true);
+}
+
+
+inline Query operator==(const Columns<BinaryData>& left, BinaryData right)
+{
+    return create<Equal>(right, left);
+}
+
+inline Query operator==(BinaryData left, const Columns<BinaryData>& right)
+{
+    return create<Equal>(left, right);
+}
+
+inline Query operator!=(const Columns<BinaryData>& left, BinaryData right)
+{
+    return create<NotEqual>(right, left);
+}
+
+inline Query operator!=(BinaryData left, const Columns<BinaryData>& right)
+{
+    return create<NotEqual>(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 <bool has_links> 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 <bool has_links>
+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);
+    }
+
+    void verify_column() const override
+    {
+        m_link_map.verify_columns();
+    }
+
+    // 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;) {
+            FindNullLinks fnl;
+            m_link_map.map_links(start, fnl);
+            if (fnl.m_has_link == has_links)
+                return start;
+
+            start++;
+        }
+
+        return not_found;
+    }
+
+    virtual std::string description() const override
+    {
+        return m_link_map.description() + (has_links ? " != NULL" : " == NULL");
+    }
+
+    std::unique_ptr<Expression> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<Expression>(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<Int> {
+public:
+    LinkCount(LinkMap link_map)
+        : m_link_map(std::move(link_map))
+    {
+    }
+    LinkCount(LinkCount const& other, QueryNodeHandoverPatches* patches)
+        : Subexpr2<Int>(other)
+        , m_link_map(other.m_link_map, patches)
+    {
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return make_subexpr<LinkCount>(*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 verify_column() const override
+    {
+        m_link_map.verify_columns();
+    }
+
+    void evaluate(size_t index, ValueBase& destination) override
+    {
+        size_t count = m_link_map.count_links(index);
+        destination.import(Value<Int>(false, 1, count));
+    }
+
+    virtual std::string description() const override
+    {
+        return m_link_map.description() + util::serializer::value_separator + "@count";
+    }
+
+private:
+    LinkMap m_link_map;
+};
+
+template <class oper, class TExpr>
+class SizeOperator : public Subexpr2<Int> {
+public:
+    SizeOperator(std::unique_ptr<TExpr> left)
+        : m_expr(std::move(left))
+    {
+    }
+
+    // See comment in base class
+    void set_base_table(const Table* table) override
+    {
+        m_expr->set_base_table(table);
+    }
+
+    void verify_column() const override
+    {
+        m_expr->verify_column();
+    }
+
+    // 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_expr->get_base_table();
+    }
+
+    // destination = operator(left)
+    void evaluate(size_t index, ValueBase& destination) override
+    {
+        REALM_ASSERT_DEBUG(dynamic_cast<Value<Int>*>(&destination) != nullptr);
+        Value<Int>* d = static_cast<Value<Int>*>(&destination);
+        REALM_ASSERT(d);
+
+        Value<T> v;
+        m_expr->evaluate(index, v);
+
+        size_t sz = v.m_values;
+        d->init(v.m_from_link_list, sz);
+
+        for (size_t i = 0; i < sz; i++) {
+            auto elem = v.m_storage.get(i);
+            if (!elem) {
+                d->m_storage.set_null(i);
+            }
+            else {
+                d->m_storage.set(i, oper()(*elem));
+            }
+        }
+    }
+
+    std::string description() const override
+    {
+        if (m_expr) {
+            return m_expr->description() + util::serializer::value_separator + "@size";
+        }
+        return "@size";
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<Subexpr>(new SizeOperator(*this, patches));
+    }
+
+    void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override
+    {
+        m_expr->apply_handover_patch(patches, group);
+    }
+
+private:
+    SizeOperator(const SizeOperator& other, QueryNodeHandoverPatches* patches)
+        : m_expr(other.m_expr->clone(patches))
+    {
+    }
+
+    typedef typename oper::type T;
+    std::unique_ptr<TExpr> m_expr;
+};
+
+struct ConstantRowValueHandoverPatch : public QueryNodeHandoverPatch {
+    std::unique_ptr<RowBaseHandoverPatch> row_patch;
+};
+
+class ConstantRowValue : public Subexpr2<Link> {
+public:
+    ConstantRowValue(const ConstRow& row)
+        : m_row(row)
+    {
+    }
+
+    void set_base_table(const Table*) override
+    {
+    }
+
+    void verify_column() const override
+    {
+    }
+
+    const Table* get_base_table() const override
+    {
+        return nullptr;
+    }
+
+    void evaluate(size_t, ValueBase& destination) override
+    {
+        if (m_row.is_attached()) {
+            Value<RowIndex> v(RowIndex(m_row.get_index()));
+            destination.import(v);
+        }
+        else {
+            Value<RowIndex> v(RowIndex::Detached);
+            destination.import(v);
+        }
+    }
+
+    virtual std::string description() const override
+    {
+        if (!m_row.is_attached()) {
+            return util::serializer::print_value("detached object");
+        }
+        return util::serializer::print_value(m_row.get_index());
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<Subexpr>(new ConstantRowValue(*this, patches));
+    }
+
+    void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override
+    {
+        REALM_ASSERT(patches.size());
+        std::unique_ptr<QueryNodeHandoverPatch> abstract_patch = std::move(patches.back());
+        patches.pop_back();
+
+        auto patch = dynamic_cast<ConstantRowValueHandoverPatch*>(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<ConstantRowValueHandoverPatch> patch(new ConstantRowValueHandoverPatch);
+        ConstRow::generate_patch(source.m_row, patch->row_patch);
+        patches->emplace_back(patch.release());
+    }
+
+    ConstRow m_row;
+};
+
+template <typename T>
+class SubColumns;
+
+// This is for LinkList and BackLink too since they're declared as typedefs of Link.
+template <>
+class Columns<Link> : public Subexpr2<Link> {
+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<UnaryLinkCompare<false>>(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<UnaryLinkCompare<true>>(m_link_map);
+    }
+
+    LinkCount count() const
+    {
+        return LinkCount(m_link_map);
+    }
+
+    template <typename C>
+    SubColumns<C> column(size_t column_ndx) const
+    {
+        return SubColumns<C>(Columns<C>(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);
+    }
+
+    void verify_column() const override
+    {
+        m_link_map.verify_columns();
+    }
+
+    std::string description() const override
+    {
+        return m_link_map.description();
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<Subexpr>(new Columns<Link>(*this, patches));
+    }
+
+    void evaluate(size_t index, ValueBase& destination) override;
+
+
+private:
+    LinkMap m_link_map;
+    friend class Table;
+
+    Columns(size_t column_ndx, const Table* table, const std::vector<size_t>& links = {})
+        : m_link_map(table, links)
+    {
+        static_cast<void>(column_ndx);
+    }
+    Columns(const Columns& other, QueryNodeHandoverPatches* patches)
+        : Subexpr2<Link>(other)
+        , m_link_map(other.m_link_map, patches)
+    {
+    }
+};
+
+template <typename T>
+class ListColumns;
+template <typename T, typename Operation>
+class ListColumnAggregate;
+namespace aggregate_operations {
+template <typename T>
+class Minimum;
+template <typename T>
+class Maximum;
+template <typename T>
+class Sum;
+template <typename T>
+class Average;
+}
+
+template <>
+class Columns<SubTable> : public Subexpr2<SubTable> {
+public:
+    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 = &m_link_map.target_table()->get_column_table(m_column_ndx);
+    }
+
+    void verify_column() const override
+    {
+        m_link_map.verify_columns();
+        m_link_map.target_table()->verify_column(m_column_ndx, m_column);
+    }
+
+    std::string description() const override
+    {
+        return m_link_map.description();
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<Subexpr>(new Columns<SubTable>(*this, patches));
+    }
+
+    void evaluate(size_t index, ValueBase& destination) override
+    {
+        evaluate_internal(index, destination, ValueBase::default_size);
+    }
+
+    void evaluate_internal(size_t index, ValueBase& destination, size_t nb_elements);
+
+    template <typename T>
+    ListColumns<T> column(size_t ndx) const
+    {
+        return ListColumns<T>(ndx, Columns<SubTable>(*this, nullptr));
+    }
+
+    template <typename T>
+    ListColumns<T> list() const
+    {
+        return column<T>(0);
+    }
+
+    SizeOperator<Size<ConstTableRef>> size()
+    {
+        return SizeOperator<Size<ConstTableRef>>(this->clone(nullptr));
+    }
+
+private:
+    LinkMap m_link_map;
+    size_t m_column_ndx;
+    const SubtableColumn* m_column = nullptr;
+    friend class Table;
+    template <class T>
+    friend class ListColumnsBase;
+    template <class T, class U>
+    friend class ListColumnAggregate;
+
+    Columns(size_t column_ndx, const Table* table, const std::vector<size_t>& links = {})
+        : m_link_map(table, links)
+        , m_column_ndx(column_ndx)
+        , m_column(&m_link_map.target_table()->get_column_table(column_ndx))
+    {
+    }
+
+    Columns(const Columns<SubTable>& other, QueryNodeHandoverPatches* patches)
+        : Subexpr2<SubTable>(other)
+        , m_link_map(other.m_link_map, patches)
+        , m_column_ndx(other.m_column_ndx)
+        , m_column(other.m_column)
+    {
+        if (m_column && patches)
+            m_column_ndx = m_column->get_column_index();
+    }
+};
+
+template <typename T>
+class ListColumnsBase : public Subexpr2<T> {
+public:
+    ListColumnsBase(size_t column_ndx, Columns<SubTable> column)
+        : m_column_ndx(column_ndx)
+        , m_subtable_column(std::move(column))
+    {
+    }
+
+    ListColumnsBase(const ListColumnsBase& other, QueryNodeHandoverPatches* patches)
+        : m_column_ndx(other.m_column_ndx)
+        , m_subtable_column(other.m_subtable_column, patches)
+    {
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return make_subexpr<ListColumns<T>>(*this, patches);
+    }
+
+    const Table* get_base_table() const override
+    {
+        return m_subtable_column.get_base_table();
+    }
+
+    void set_base_table(const Table* table) override
+    {
+        m_subtable_column.set_base_table(table);
+    }
+
+    void verify_column() const override
+    {
+        m_subtable_column.verify_column();
+    }
+
+    void evaluate(size_t index, ValueBase& destination) override
+    {
+        Value<ConstTableRef> subtables;
+        m_subtable_column.evaluate_internal(index, subtables, 1);
+        size_t sz = 0;
+        for (size_t i = 0; i < subtables.m_values; i++) {
+            auto val = subtables.m_storage[i];
+            if (val)
+                sz += val->size();
+        }
+        auto v = make_value_for_link<typename util::RemoveOptional<T>::type>(false, sz);
+        size_t k = 0;
+        for (size_t i = 0; i < subtables.m_values; i++) {
+            auto table = subtables.m_storage[i];
+            if (table) {
+                size_t s = table->size();
+                for (size_t j = 0; j < s; j++) {
+                    if (!table->is_null(m_column_ndx, j)) {
+                        v.m_storage.set(k++, table->get<T>(m_column_ndx, j));
+                    }
+                }
+            }
+        }
+        destination.import(v);
+    }
+
+    virtual std::string description() const override
+    {
+        const Table* table = get_base_table();
+        if (table && table->is_attached()) {
+            if (m_subtable_column.m_column) {
+                return std::string(table->get_column_name(m_subtable_column.m_column_ndx));
+
+            }
+            else {
+                return std::string(table->get_column_name(m_column_ndx));
+            }
+        }
+        return "";
+    }
+
+    ListColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
+    {
+        return {m_column_ndx, m_subtable_column};
+    }
+
+    ListColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
+    {
+        return {m_column_ndx, m_subtable_column};
+    }
+
+    ListColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
+    {
+        return {m_column_ndx, m_subtable_column};
+    }
+
+    ListColumnAggregate<T, aggregate_operations::Average<T>> average() const
+    {
+        return {m_column_ndx, m_subtable_column};
+    }
+
+
+private:
+    // Storing the column index here could be a potential problem if the column
+    // changes id due to insertion/deletion.
+    size_t m_column_ndx;
+    Columns<SubTable> m_subtable_column;
+};
+
+template <class T>
+class ListColumns : public ListColumnsBase<T> {
+public:
+    using ListColumnsBase<T>::ListColumnsBase;
+};
+
+template <>
+class ListColumns<StringData> : public ListColumnsBase<StringData> {
+public:
+    ListColumns(size_t column_ndx, Columns<SubTable> column)
+        : ListColumnsBase(column_ndx, column)
+    {
+    }
+
+    ListColumns(const ListColumnsBase& other, QueryNodeHandoverPatches* patches)
+        : ListColumnsBase(other, patches)
+    {
+    }
+
+    ListColumns(ListColumns&& other)
+        : ListColumnsBase(other)
+    {
+    }
+};
+
+template <typename T, typename Operation>
+class ListColumnAggregate : public Subexpr2<typename Operation::ResultType> {
+public:
+    using R = typename Operation::ResultType;
+
+    ListColumnAggregate(size_t column_ndx, Columns<SubTable> column)
+        : m_column_ndx(column_ndx)
+        , m_subtable_column(std::move(column))
+    {
+    }
+
+    ListColumnAggregate(const ListColumnAggregate& other, QueryNodeHandoverPatches* patches)
+        : m_column_ndx(other.m_column_ndx)
+        , m_subtable_column(other.m_subtable_column, patches)
+    {
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return make_subexpr<ListColumnAggregate>(*this, patches);
+    }
+
+    const Table* get_base_table() const override
+    {
+        return m_subtable_column.get_base_table();
+    }
+
+    void set_base_table(const Table* table) override
+    {
+        m_subtable_column.set_base_table(table);
+    }
+
+    void verify_column() const override
+    {
+        m_subtable_column.verify_column();
+    }
+
+    void evaluate(size_t index, ValueBase& destination) override
+    {
+        Value<ConstTableRef> subtables;
+        m_subtable_column.evaluate_internal(index, subtables, 1);
+        REALM_ASSERT_DEBUG(subtables.m_values > 0 || subtables.m_from_link_list);
+        size_t sz = subtables.m_values;
+        // The result is an aggregate value for each table
+        auto v = make_value_for_link<R>(!subtables.m_from_link_list, sz);
+        for (unsigned i = 0; i < sz; i++) {
+            auto table = subtables.m_storage[i];
+            Operation op;
+            if (table) {
+                size_t s = table->size();
+                for (unsigned j = 0; j < s; j++) {
+                    op.accumulate(table->get<T>(m_column_ndx, j));
+                }
+            }
+            if (op.is_null()) {
+                v.m_storage.set_null(i);
+            }
+            else {
+                v.m_storage.set(i, op.result());
+            }
+        }
+        destination.import(v);
+    }
+
+    virtual std::string description() const override
+    {
+        const Table* table = get_base_table();
+        if (table && table->is_attached()) {
+            return std::string(table->get_column_name(m_column_ndx)) + util::serializer::value_separator + Operation::description() + "()";
+        }
+        return "";
+    }
+
+private:
+    size_t m_column_ndx;
+    Columns<SubTable> m_subtable_column;
+};
+
+template <class Operator>
+Query compare(const Subexpr2<Link>& left, const ConstRow& row)
+{
+    static_assert(std::is_same<Operator, Equal>::value || std::is_same<Operator, NotEqual>::value,
+                  "Links can only be compared for equality.");
+    const Columns<Link>* column = dynamic_cast<const Columns<Link>*>(&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<Operator, Equal>::value)) {
+                const Table* t = column->get_base_table();
+                Query query(*t);
+
+                if (std::is_same<Operator, NotEqual>::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<Compare<Operator, RowIndex>>(left.clone(), make_subexpr<ConstantRowValue>(row));
+}
+
+inline Query operator==(const Subexpr2<Link>& left, const ConstRow& row)
+{
+    return compare<Equal>(left, row);
+}
+inline Query operator!=(const Subexpr2<Link>& left, const ConstRow& row)
+{
+    return compare<NotEqual>(left, row);
+}
+inline Query operator==(const ConstRow& row, const Subexpr2<Link>& right)
+{
+    return compare<Equal>(right, row);
+}
+inline Query operator!=(const ConstRow& row, const Subexpr2<Link>& right)
+{
+    return compare<NotEqual>(right, row);
+}
+
+template <class Operator>
+Query compare(const Subexpr2<Link>& left, null)
+{
+    static_assert(std::is_same<Operator, Equal>::value || std::is_same<Operator, NotEqual>::value,
+                  "Links can only be compared for equality.");
+    return make_expression<Compare<Operator, RowIndex>>(left.clone(), make_subexpr<Value<RowIndex>>());
+}
+
+inline Query operator==(const Subexpr2<Link>& left, null)
+{
+    return compare<Equal>(left, null());
+}
+inline Query operator!=(const Subexpr2<Link>& left, null)
+{
+    return compare<NotEqual>(left, null());
+}
+inline Query operator==(null, const Subexpr2<Link>& right)
+{
+    return compare<Equal>(right, null());
+}
+inline Query operator!=(null, const Subexpr2<Link>& right)
+{
+    return compare<NotEqual>(right, null());
+}
+
+
+template <class T>
+class Columns : public Subexpr2<T> {
+public:
+    using ColType = typename ColumnTypeTraits<T>::column_type;
+
+    Columns(size_t column, const Table* table, std::vector<size_t> links = {})
+        : m_link_map(table, std::move(links))
+        , m_column_ndx(column)
+        , m_nullable(m_link_map.target_table()->is_nullable(m_column_ndx))
+    {
+    }
+
+    Columns(const Columns& other, QueryNodeHandoverPatches* patches = nullptr)
+        : m_link_map(other.m_link_map, patches)
+        , m_column_ndx(other.m_column_ndx)
+        , m_nullable(other.m_nullable)
+    {
+        if (!other.m_sg)
+            return;
+
+        if (patches) {
+            m_column_ndx = other.get_column_base().get_column_index();
+        }
+        else {
+            if (m_nullable && std::is_same<typename ColType::value_type, int64_t>::value) {
+                init<IntNullColumn>(&other.get_column_base());
+            }
+            else {
+                init<ColType>(&other.get_column_base());
+            }
+        }
+    }
+
+    Columns& operator=(const Columns& other)
+    {
+        if (this != &other) {
+            m_link_map = other.m_link_map;
+            m_sg.reset();
+            m_column_ndx = other.m_column_ndx;
+            m_nullable = other.m_nullable;
+        }
+        return *this;
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return make_subexpr<Columns<T>>(*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_ndx);
+
+        const ColumnBase* c = &m_link_map.target_table()->get_column_base(m_column_ndx);
+        if (m_nullable && std::is_same<typename ColType::value_type, int64_t>::value) {
+            init<IntNullColumn>(c);
+        }
+        else {
+            init<ColType>(c);
+        }
+    }
+
+    void verify_column() const override
+    {
+        // verify links
+        m_link_map.verify_columns();
+        // verify target table
+        const Table* target_table = m_link_map.target_table();
+        if (target_table && m_column_ndx != npos) {
+            target_table->verify_column(m_column_ndx, &get_column_base());
+        }
+    }
+
+    template <class ActualColType>
+    void init(const ColumnBase* c)
+    {
+        REALM_ASSERT_DEBUG(dynamic_cast<const ActualColType*>(c));
+        if (m_sg == nullptr) {
+            m_sg.reset(new SequentialGetter<ActualColType>());
+        }
+        static_cast<SequentialGetter<ActualColType>&>(*m_sg).init(static_cast<const ActualColType*>(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 <class ColType2 = ColType>
+    void evaluate_internal(size_t index, ValueBase& destination)
+    {
+        REALM_ASSERT_DEBUG(m_sg.get());
+        REALM_ASSERT_DEBUG(dynamic_cast<SequentialGetter<ColType2>*>(m_sg.get()));
+
+        using U = typename ColType2::value_type;
+        auto sgc = static_cast<SequentialGetter<ColType2>*>(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<size_t> links = m_link_map.get_links(index);
+            auto v = make_value_for_link<typename util::RemoveOptional<U>::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<U, int64_t>::value && index + ValueBase::default_size <= sgc->m_leaf_end) {
+                Value<int64_t> 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<SequentialGetter<ColType>*>(m_sg.get());
+                sgc_2->m_leaf_ptr->get_chunk(index - sgc->m_leaf_start, v.m_storage.m_first);
+
+                destination.import(v);
+            }
+            else {
+                size_t rows = colsize - index;
+                if (rows > ValueBase::default_size)
+                    rows = ValueBase::default_size;
+                Value<typename util::RemoveOptional<U>::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);
+            }
+        }
+    }
+
+    virtual std::string description() const override
+    {
+        std::string desc = "";
+        if (links_exist()) {
+            desc = m_link_map.description() + util::serializer::value_separator;
+        }
+        const Table* target_table = m_link_map.target_table();
+        if (target_table && target_table->is_attached() && m_column_ndx != npos) {
+            desc += std::string(target_table->get_column_name(m_column_ndx));
+            return desc;
+        }
+        return "";
+    }
+
+    // Load values from Column into destination
+    void evaluate(size_t index, ValueBase& destination) override
+    {
+        if (m_nullable && std::is_same<typename ColType::value_type, int64_t>::value) {
+            evaluate_internal<IntNullColumn>(index, destination);
+        }
+        else {
+            evaluate_internal<ColType>(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_ndx;
+    }
+
+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<SequentialGetterBase> m_sg;
+
+    // Column index of payload column of m_table
+    size_t m_column_ndx;
+
+    // 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<int64_t, T>::value)
+            return *static_cast<SequentialGetter<IntNullColumn>&>(*m_sg).m_column;
+        else
+            return *static_cast<SequentialGetter<ColType>&>(*m_sg).m_column;
+    }
+};
+
+template <typename T, typename Operation>
+class SubColumnAggregate;
+
+template <typename T>
+class SubColumns : public Subexpr {
+public:
+    SubColumns(Columns<T> column, LinkMap link_map)
+        : m_column(std::move(column))
+        , m_link_map(std::move(link_map))
+    {
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches*) const override
+    {
+        return make_subexpr<SubColumns<T>>(*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 verify_column() const override
+    {
+        m_link_map.verify_columns();
+        m_column.verify_column();
+    }
+
+    void evaluate(size_t, ValueBase&) override
+    {
+        // SubColumns can only be used in an expression in conjunction with its aggregate methods.
+        REALM_ASSERT(false);
+    }
+
+    virtual std::string description() const override
+    {
+        return ""; // by itself there are no conditions, see SubColumnAggregate
+    }
+
+    SubColumnAggregate<T, aggregate_operations::Minimum<T>> min() const
+    {
+        return {m_column, m_link_map};
+    }
+
+    SubColumnAggregate<T, aggregate_operations::Maximum<T>> max() const
+    {
+        return {m_column, m_link_map};
+    }
+
+    SubColumnAggregate<T, aggregate_operations::Sum<T>> sum() const
+    {
+        return {m_column, m_link_map};
+    }
+
+    SubColumnAggregate<T, aggregate_operations::Average<T>> average() const
+    {
+        return {m_column, m_link_map};
+    }
+
+private:
+    Columns<T> m_column;
+    LinkMap m_link_map;
+};
+
+template <typename T, typename Operation>
+class SubColumnAggregate : public Subexpr2<typename Operation::ResultType> {
+public:
+    SubColumnAggregate(Columns<T> 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<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return make_subexpr<SubColumnAggregate>(*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 verify_column() const override
+    {
+        m_link_map.verify_columns();
+        m_column.verify_column();
+    }
+
+    void evaluate(size_t index, ValueBase& destination) override
+    {
+        std::vector<size_t> 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<T> value;
+            size_t link = links[link_index];
+            m_column.evaluate(link, value);
+
+            // Columns<T>::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<null>(false, 1, null()));
+        }
+        else {
+            destination.import(Value<typename Operation::ResultType>(false, 1, op.result()));
+        }
+    }
+
+    virtual std::string description() const override
+    {
+        return m_link_map.description() + util::serializer::value_separator + Operation::description() + util::serializer::value_separator + m_column.description();
+    }
+
+private:
+    Columns<T> m_column;
+    LinkMap m_link_map;
+};
+
+struct SubQueryCountHandoverPatch : QueryNodeHandoverPatch {
+    QueryHandoverPatch m_query;
+};
+
+class SubQueryCount : public Subexpr2<Int> {
+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 verify_column() const override
+    {
+        m_link_map.verify_columns();
+    }
+
+    void evaluate(size_t index, ValueBase& destination) override
+    {
+        std::vector<size_t> links = m_link_map.get_links(index);
+        std::sort(links.begin(), links.end());
+
+        size_t count = std::accumulate(links.begin(), links.end(), size_t(0), [this](size_t running_count, size_t link) {
+            return running_count + m_query.count(link, link + 1, 1);
+        });
+
+        destination.import(Value<Int>(false, 1, size_t(count)));
+    }
+
+    virtual std::string description() const override
+    {
+        return m_link_map.description() + util::serializer::value_separator + "SUBQUERY(" + m_query.get_description() + ")"
+            + util::serializer::value_separator + "@count";
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        if (patches)
+            return std::unique_ptr<Subexpr>(new SubQueryCount(*this, patches));
+
+        return make_subexpr<SubQueryCount>(*this);
+    }
+
+    void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override
+    {
+        REALM_ASSERT(patches.size());
+        std::unique_ptr<QueryNodeHandoverPatch> abstract_patch = std::move(patches.back());
+        patches.pop_back();
+
+        auto patch = dynamic_cast<SubQueryCountHandoverPatch*>(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<SubQueryCountHandoverPatch> 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>
+class SubQuery {
+public:
+    SubQuery(Columns<Link> 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 <typename T, typename Derived, typename R = T>
+class BaseAggregateOperation {
+    static_assert(std::is_same<T, Int>::value || std::is_same<T, Float>::value || std::is_same<T, Double>::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 <typename T>
+class Minimum : public BaseAggregateOperation<T, Minimum<T>> {
+public:
+    static T initial_value()
+    {
+        return std::numeric_limits<T>::max();
+    }
+    static T apply(T a, T b)
+    {
+        return std::min(a, b);
+    }
+    static std::string description()
+    {
+        return "@min";
+    }
+};
+
+template <typename T>
+class Maximum : public BaseAggregateOperation<T, Maximum<T>> {
+public:
+    static T initial_value()
+    {
+        return std::numeric_limits<T>::min();
+    }
+    static T apply(T a, T b)
+    {
+        return std::max(a, b);
+    }
+    static std::string description()
+    {
+        return "@max";
+    }
+};
+
+template <typename T>
+class Sum : public BaseAggregateOperation<T, Sum<T>> {
+public:
+    static T initial_value()
+    {
+        return T();
+    }
+    static T apply(T a, T b)
+    {
+        return a + b;
+    }
+    bool is_null() const
+    {
+        return false;
+    }
+    static std::string description()
+    {
+        return "@sum";
+    }
+};
+
+template <typename T>
+class Average : public BaseAggregateOperation<T, Average<T>, double> {
+    using Base = BaseAggregateOperation<T, Average<T>, 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;
+    }
+    static std::string description()
+    {
+        return "@avg";
+    }
+
+};
+}
+
+template <class oper, class TLeft>
+class UnaryOperator : public Subexpr2<typename oper::type> {
+public:
+    UnaryOperator(std::unique_ptr<TLeft> 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);
+    }
+
+    void verify_column() const override
+    {
+        m_left->verify_column();
+    }
+
+    // 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<T> result;
+        Value<T> left;
+        m_left->evaluate(index, left);
+        result.template fun<oper>(&left);
+        destination.import(result);
+    }
+
+    virtual std::string description() const override
+    {
+        if (m_left) {
+            return m_left->description();
+        }
+        return "";
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return make_subexpr<UnaryOperator>(*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<TLeft> m_left;
+};
+
+
+template <class oper, class TLeft, class TRight>
+class Operator : public Subexpr2<typename oper::type> {
+public:
+    Operator(std::unique_ptr<TLeft> left, std::unique_ptr<TRight> 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);
+    }
+
+    void verify_column() const override
+    {
+        m_left->verify_column();
+        m_right->verify_column();
+    }
+
+    // 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<T>
+        return l ? l : r;
+    }
+
+    // destination = operator(left, right)
+    void evaluate(size_t index, ValueBase& destination) override
+    {
+        Value<T> result;
+        Value<T> left;
+        Value<T> right;
+        m_left->evaluate(index, left);
+        m_right->evaluate(index, right);
+        result.template fun<oper>(&left, &right);
+        destination.import(result);
+    }
+
+    virtual std::string description() const override
+    {
+        std::string s;
+        if (m_left) {
+            s += m_left->description();
+        }
+        s += oper::description();
+        if (m_right) {
+            s += m_right->description();
+        }
+        return s;
+    }
+
+    std::unique_ptr<Subexpr> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return make_subexpr<Operator>(*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<TLeft> m_left;
+    std::unique_ptr<TRight> m_right;
+};
+
+
+template <class TCond, class T, class TLeft, class TRight>
+class Compare : public Expression {
+public:
+    Compare(std::unique_ptr<TLeft> left, std::unique_ptr<TRight> 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);
+    }
+
+    void verify_column() const override
+    {
+        m_left->verify_column();
+        m_right->verify_column();
+    }
+
+    // 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<T>
+        return l ? l : r;
+    }
+
+    size_t find_first(size_t start, size_t end) const override
+    {
+        size_t match;
+        Value<T> right;
+        Value<T> left;
+
+        for (; start < end;) {
+            m_left->evaluate(start, left);
+            m_right->evaluate(start, right);
+            match = Value<T>::template compare<TCond>(&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
+    }
+
+    virtual std::string description() const override
+    {
+        if (std::is_same<TCond, BeginsWith>::value
+            || std::is_same<TCond, BeginsWithIns>::value
+            || std::is_same<TCond, EndsWith>::value
+            || std::is_same<TCond, EndsWithIns>::value
+            || std::is_same<TCond, Contains>::value
+            || std::is_same<TCond, ContainsIns>::value
+            || std::is_same<TCond, Like>::value
+            || std::is_same<TCond, LikeIns>::value) {
+            // these string conditions have the arguments reversed but the order is important
+            // operations ==, and != can be reversed because the produce the same results both ways
+            return util::serializer::print_value(m_right->description() + " " + TCond::description()
+                                                 + " " + m_left->description());
+        }
+        return util::serializer::print_value(m_left->description() + " " + TCond::description()
+                                             + " " + m_right->description());
+    }
+
+    std::unique_ptr<Expression> clone(QueryNodeHandoverPatches* patches) const override
+    {
+        return std::unique_ptr<Expression>(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<TLeft> m_left;
+    std::unique_ptr<TRight> m_right;
+};
+}
+#endif // REALM_QUERY_EXPRESSION_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/query_operators.hpp b/iOS/Pods/Realm/include/core/realm/query_operators.hpp
new file mode 100644 (file)
index 0000000..203af84
--- /dev/null
@@ -0,0 +1,71 @@
+/*************************************************************************
+ *
+ * Copyright 2017 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_OPERATORS_HPP
+#define REALM_QUERY_OPERATORS_HPP
+
+#include <realm/binary_data.hpp>
+#include <realm/link_view.hpp>
+#include <realm/string_data.hpp>
+#include <realm/table.hpp>
+
+namespace realm {
+
+// This is not supported in the general case
+template <class T>
+struct Size;
+
+template <>
+struct Size<StringData> {
+    int64_t operator()(StringData v) const
+    {
+        return v.size();
+    }
+    typedef StringData type;
+};
+
+template <>
+struct Size<BinaryData> {
+    int64_t operator()(BinaryData v) const
+    {
+        return v.size();
+    }
+    typedef BinaryData type;
+};
+
+template <>
+struct Size<ConstTableRef> {
+    int64_t operator()(ConstTableRef v) const
+    {
+        return v->size();
+    }
+    typedef ConstTableRef type;
+};
+
+template <>
+struct Size<ConstLinkViewRef> {
+    int64_t operator()(ConstLinkViewRef v) const
+    {
+        return v->size();
+    }
+    typedef ConstLinkViewRef type;
+};
+
+} // namespace realm
+
+#endif // REALM_QUERY_OPERATORS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/realm_nmmintrin.h b/iOS/Pods/Realm/include/core/realm/realm_nmmintrin.h
new file mode 100644 (file)
index 0000000..8144da6
--- /dev/null
@@ -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 <emmintrin.h> // 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<int op>
+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<int op>
+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/iOS/Pods/Realm/include/core/realm/replication.hpp b/iOS/Pods/Realm/include/core/realm/replication.hpp
new file mode 100644 (file)
index 0000000..f68a9af
--- /dev/null
@@ -0,0 +1,514 @@
+/*************************************************************************
+ *
+ * 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 <algorithm>
+#include <limits>
+#include <memory>
+#include <exception>
+#include <string>
+
+#include <realm/util/assert.hpp>
+#include <realm/util/safe_int_ops.hpp>
+#include <realm/util/buffer.hpp>
+#include <realm/util/string_buffer.hpp>
+#include <realm/impl/cont_transact_hist.hpp>
+#include <realm/impl/transact_log.hpp>
+
+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);
+
+    /// CAUTION: These values are stored in Realm files, so value reassignment
+    /// is not allowed.
+    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.
+        ///
+        /// NOTE: This history type is no longer in use. The value needs to stay
+        /// reserved in case someone tries to open an old Realm file.
+        hist_OutOfRealm = 1,
+
+        /// In-Realm history supporting continuous transactions
+        /// (make_in_realm_history()).
+        hist_InRealm = 2,
+
+        /// In-Realm history supporting continuous transactions and client-side
+        /// synchronization protocol (realm::sync::ClientHistory).
+        hist_SyncClient = 3,
+
+        /// In-Realm history supporting continuous transactions and server-side
+        /// synchronization protocol (realm::_impl::ServerHistory).
+        hist_SyncServer = 4
+    };
+
+    /// 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 the history type is still undecided and
+    /// all history types (as returned by get_history_type()) are threfore
+    /// allowed for the session initiator. Note that this case only arises if
+    /// there was no preceding session, or if no transaction was sucessfully
+    /// committed 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
+    ///     implicitly 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.
+    ///
+    /// It shall be allowed to switch to a \ref hist_InRealm history if the
+    /// stored history type is \ref hist_None. This can be done simply by adding
+    /// a new history to the Realm file. This is possible because histories of
+    /// this type a transient in nature, and need not survive from one session
+    /// to the next.
+    ///
+    /// 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 would be possible because of the transient
+    /// nature of it, 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_SyncClient history type can only be used if the stored
+    /// history type is also \ref hist_SyncClient, or when there is no top array
+    /// yet. Likewise, the \ref hist_SyncServer history type can only be used if
+    /// the stored history type is also \ref hist_SyncServer, or when there is
+    /// no top array yet. Additionally, when the stored history type is \ref
+    /// hist_SyncClient or \ref hist_SyncServer, then all subsequent sessions
+    /// must have the same type. These restrictions apply because such a history
+    /// needs to be maintained persistently across 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 in that
+    /// session. 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.
+    ///
+    /// This function must return \ref hist_None when, and only when
+    /// get_history() returns null.
+    virtual HistoryType get_history_type() const noexcept = 0;
+
+    /// Returns the schema version of the history maintained by this Replication
+    /// implementation, or 0 if no history is maintained by it. All session
+    /// participants must agree on history schema version.
+    ///
+    /// Must return 0 if get_history_type() returns \ref hist_None.
+    virtual int get_history_schema_version() const noexcept = 0;
+
+    /// Implementation may assume that this function is only ever called with a
+    /// stored schema version that is less than what was returned by
+    /// get_history_schema_version().
+    virtual bool is_upgradable_history_schema(int stored_schema_version) const noexcept = 0;
+
+    /// The implementation may assume that this function is only ever called if
+    /// is_upgradable_history_schema() was called with the same stored schema
+    /// version, and returned true. This implies that the specified stored
+    /// schema version is always strictly less than what was returned by
+    /// get_history_schema_version().
+    virtual void upgrade_history_schema(int stored_schema_version) = 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;
+
+    /// Returns false by default, but must return true if, and only if this
+    /// history object represents a session participant that is a sync
+    /// agent. This is used to enforce the "maximum one sync agent per session"
+    /// constraint.
+    virtual bool is_sync_agent() const noexcept;
+
+    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<char> 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 bool Replication::is_sync_agent() const noexcept
+{
+    return false;
+}
+
+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/iOS/Pods/Realm/include/core/realm/row.hpp b/iOS/Pods/Realm/include/core/realm/row.hpp
new file mode 100644 (file)
index 0000000..c1439b7
--- /dev/null
@@ -0,0 +1,862 @@
+/*************************************************************************
+ *
+ * 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 <cstdint>
+
+#include <realm/util/type_traits.hpp>
+#include <realm/mixed.hpp>
+#include <realm/table_ref.hpp>
+#include <realm/link_view_fwd.hpp>
+#include <realm/handover_defs.hpp>
+
+namespace realm {
+
+template <class>
+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 T, class R>
+class RowFuncs {
+public:
+    typedef T table_type;
+
+    typedef BasicTableRef<const T> ConstTableRef;
+    typedef BasicTableRef<T> TableRef; // Same as ConstTableRef if `T` is 'const'
+
+    typedef typename util::CopyConst<T, LinkView>::type L;
+    using ConstLinkViewRef = std::shared_ptr<const L>;
+    using LinkViewRef = std::shared_ptr<L>; // 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 <typename U>
+    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 add_int(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);
+
+    template <typename U>
+    void set(size_t col_ndx, U&& value, bool is_default = false);
+
+    template <typename U>
+    void set_unique(size_t col_ndx, U&& value);
+
+    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 noexcept;
+    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 T>
+class BasicRowExpr : public RowFuncs<T, BasicRowExpr<T>> {
+public:
+    BasicRowExpr() noexcept = default;
+
+    template <class U>
+    BasicRowExpr(const BasicRowExpr<U>&) noexcept;
+
+    template <class U>
+    BasicRowExpr(const BasicRow<U>&) noexcept;
+
+private:
+    T* m_table = nullptr;       // nullptr if detached.
+    size_t m_row_ndx = 0; // 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<T, BasicRowExpr<T>>;
+
+    // Make m_table and m_row_ndx accessible from BasicRowExpr(const
+    // BasicRowExpr<U>&) for any U.
+    template <class>
+    friend class BasicRowExpr;
+
+    // Make m_table and m_row_ndx accessible from
+    // BasicRow::BaicRow(BasicRowExpr<U>) for any U.
+    template <class>
+    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 T>
+class BasicRow : private RowBase, public RowFuncs<T, BasicRow<T>> {
+public:
+    BasicRow() noexcept;
+
+    template <class U>
+    BasicRow(BasicRowExpr<U>) noexcept;
+
+    BasicRow(const BasicRow<T>&) noexcept;
+
+    template <class U>
+    BasicRow(const BasicRow<U>&) noexcept;
+
+    template <class U>
+    BasicRow& operator=(BasicRowExpr<U>) noexcept;
+
+    template <class U>
+    BasicRow& operator=(BasicRow<U>) noexcept;
+
+    BasicRow& operator=(const BasicRow<T>&) 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<T, BasicRow<T>>;
+
+    // Make m_table and m_row_ndx accessible from BasicRow(const BasicRow<U>&)
+    // for any U.
+    template <class>
+    friend class BasicRow;
+
+    // Make m_table and m_row_ndx accessible from BasicRowExpr(const
+    // BasicRow<U>&) for any U.
+    template <class>
+    friend class BasicRowExpr;
+
+public:
+    std::unique_ptr<BasicRow<T>> clone_for_handover(std::unique_ptr<HandoverPatch>& patch) const
+    {
+        patch.reset(new HandoverPatch);
+        std::unique_ptr<BasicRow<T>> retval(new BasicRow<T>(*this, *patch));
+        return retval;
+    }
+
+    static void generate_patch(const BasicRow& row, std::unique_ptr<HandoverPatch>& patch)
+    {
+        patch.reset(new HandoverPatch);
+        RowBase::generate_patch(row, *patch);
+    }
+
+    void apply_and_consume_patch(std::unique_ptr<HandoverPatch>& 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<T>& source, HandoverPatch& patch)
+        : RowBase(source, patch)
+    {
+    }
+    friend class SharedGroup;
+};
+
+typedef BasicRow<Table> Row;
+typedef BasicRow<const Table> ConstRow;
+
+
+// Implementation
+
+template <class T, class R>
+inline int_fast64_t RowFuncs<T, R>::get_int(size_t col_ndx) const noexcept
+{
+    return table()->get_int(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline bool RowFuncs<T, R>::get_bool(size_t col_ndx) const noexcept
+{
+    return table()->get_bool(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline float RowFuncs<T, R>::get_float(size_t col_ndx) const noexcept
+{
+    return table()->get_float(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline double RowFuncs<T, R>::get_double(size_t col_ndx) const noexcept
+{
+    return table()->get_double(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline StringData RowFuncs<T, R>::get_string(size_t col_ndx) const noexcept
+{
+    return table()->get_string(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline BinaryData RowFuncs<T, R>::get_binary(size_t col_ndx) const noexcept
+{
+    return table()->get_binary(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline OldDateTime RowFuncs<T, R>::get_olddatetime(size_t col_ndx) const noexcept
+{
+    return table()->get_olddatetime(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline Timestamp RowFuncs<T, R>::get_timestamp(size_t col_ndx) const noexcept
+{
+    return table()->get_timestamp(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline typename RowFuncs<T, R>::ConstTableRef RowFuncs<T, R>::get_subtable(size_t col_ndx) const
+{
+    return table()->get_subtable(col_ndx, row_ndx()); // Throws
+}
+
+template <class T, class R>
+inline typename RowFuncs<T, R>::TableRef RowFuncs<T, R>::get_subtable(size_t col_ndx)
+{
+    return table()->get_subtable(col_ndx, row_ndx()); // Throws
+}
+
+template <class T, class R>
+inline size_t RowFuncs<T, R>::get_subtable_size(size_t col_ndx) const noexcept
+{
+    return table()->get_subtable_size(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline size_t RowFuncs<T, R>::get_link(size_t col_ndx) const noexcept
+{
+    return table()->get_link(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline bool RowFuncs<T, R>::is_null_link(size_t col_ndx) const noexcept
+{
+    return table()->is_null_link(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline bool RowFuncs<T, R>::is_null(size_t col_ndx) const noexcept
+{
+    return table()->is_null(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline typename RowFuncs<T, R>::ConstLinkViewRef RowFuncs<T, R>::get_linklist(size_t col_ndx) const
+{
+    return table()->get_linklist(col_ndx, row_ndx()); // Throws
+}
+
+template <class T, class R>
+inline typename RowFuncs<T, R>::LinkViewRef RowFuncs<T, R>::get_linklist(size_t col_ndx)
+{
+    return table()->get_linklist(col_ndx, row_ndx()); // Throws
+}
+
+template <class T, class R>
+inline bool RowFuncs<T, R>::linklist_is_empty(size_t col_ndx) const noexcept
+{
+    return table()->linklist_is_empty(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline size_t RowFuncs<T, R>::get_link_count(size_t col_ndx) const noexcept
+{
+    return table()->get_link_count(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline Mixed RowFuncs<T, R>::get_mixed(size_t col_ndx) const noexcept
+{
+    return table()->get_mixed(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline DataType RowFuncs<T, R>::get_mixed_type(size_t col_ndx) const noexcept
+{
+    return table()->get_mixed_type(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+template <class U>
+inline U RowFuncs<T, R>::get(size_t col_ndx) const noexcept
+{
+    return table()->template get<U>(col_ndx, row_ndx());
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_int(size_t col_ndx, int_fast64_t value)
+{
+    table()->set_int(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_int_unique(size_t col_ndx, int_fast64_t value)
+{
+    table()->set_int_unique(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::add_int(size_t col_ndx, int_fast64_t value)
+{
+    table()->add_int(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_bool(size_t col_ndx, bool value)
+{
+    table()->set_bool(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_float(size_t col_ndx, float value)
+{
+    table()->set_float(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_double(size_t col_ndx, double value)
+{
+    table()->set_double(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_string(size_t col_ndx, StringData value)
+{
+    table()->set_string(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_string_unique(size_t col_ndx, StringData value)
+{
+    table()->set_string_unique(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_binary(size_t col_ndx, BinaryData value)
+{
+    table()->set_binary(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_olddatetime(size_t col_ndx, OldDateTime value)
+{
+    table()->set_olddatetime(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_timestamp(size_t col_ndx, Timestamp value)
+{
+    table()->set_timestamp(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_subtable(size_t col_ndx, const Table* value)
+{
+    table()->set_subtable(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_link(size_t col_ndx, size_t value)
+{
+    table()->set_link(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::nullify_link(size_t col_ndx)
+{
+    table()->nullify_link(col_ndx, row_ndx()); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_mixed(size_t col_ndx, Mixed value)
+{
+    table()->set_mixed(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_mixed_subtable(size_t col_ndx, const Table* value)
+{
+    table()->set_mixed_subtable(col_ndx, row_ndx(), value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_null(size_t col_ndx)
+{
+    table()->set_null(col_ndx, row_ndx()); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::set_null_unique(size_t col_ndx)
+{
+    table()->set_null_unique(col_ndx, row_ndx()); // Throws
+}
+
+template <class T, class R>
+template <class U>
+inline void RowFuncs<T, R>::set(size_t col_ndx, U&& value, bool is_default)
+{
+    table()->set(col_ndx, row_ndx(), std::forward<U>(value), is_default); // Throws
+}
+
+template <class T, class R>
+template <class U>
+inline void RowFuncs<T, R>::set_unique(size_t col_ndx, U&& value)
+{
+    table()->set_unique(col_ndx, row_ndx(), std::forward<U>(value)); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::insert_substring(size_t col_ndx, size_t pos, StringData value)
+{
+    table()->insert_substring(col_ndx, row_ndx(), pos, value); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::remove_substring(size_t col_ndx, size_t pos, size_t size)
+{
+    table()->remove_substring(col_ndx, row_ndx(), pos, size); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::remove()
+{
+    table()->remove(row_ndx()); // Throws
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::move_last_over()
+{
+    table()->move_last_over(row_ndx()); // Throws
+}
+
+template <class T, class R>
+inline size_t RowFuncs<T, R>::get_backlink_count() const noexcept
+{
+    return table()->get_backlink_count(row_ndx());
+}
+
+template <class T, class R>
+inline size_t RowFuncs<T, R>::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 <class T, class R>
+inline size_t RowFuncs<T, R>::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 <class T, class R>
+inline size_t RowFuncs<T, R>::get_column_count() const noexcept
+{
+    return table()->get_column_count();
+}
+
+template <class T, class R>
+inline DataType RowFuncs<T, R>::get_column_type(size_t col_ndx) const noexcept
+{
+    return table()->get_column_type(col_ndx);
+}
+
+template <class T, class R>
+inline StringData RowFuncs<T, R>::get_column_name(size_t col_ndx) const noexcept
+{
+    return table()->get_column_name(col_ndx);
+}
+
+template <class T, class R>
+inline size_t RowFuncs<T, R>::get_column_index(StringData name) const noexcept
+{
+    return table()->get_column_index(name);
+}
+
+template <class T, class R>
+inline bool RowFuncs<T, R>::is_attached() const noexcept
+{
+    return static_cast<const R*>(this)->impl_get_table();
+}
+
+template <class T, class R>
+inline void RowFuncs<T, R>::detach() noexcept
+{
+    static_cast<R*>(this)->impl_detach();
+}
+
+template <class T, class R>
+inline const T* RowFuncs<T, R>::get_table() const noexcept
+{
+    return table();
+}
+
+template <class T, class R>
+inline T* RowFuncs<T, R>::get_table() noexcept
+{
+    return table();
+}
+
+template <class T, class R>
+inline size_t RowFuncs<T, R>::get_index() const noexcept
+{
+    return row_ndx();
+}
+
+template <class T, class R>
+inline RowFuncs<T, R>::operator bool() const noexcept
+{
+    return is_attached();
+}
+
+template <class T, class R>
+inline const T* RowFuncs<T, R>::table() const noexcept
+{
+    return static_cast<const R*>(this)->impl_get_table();
+}
+
+template <class T, class R>
+inline T* RowFuncs<T, R>::table() noexcept
+{
+    return static_cast<R*>(this)->impl_get_table();
+}
+
+template <class T, class R>
+inline size_t RowFuncs<T, R>::row_ndx() const noexcept
+{
+    return static_cast<const R*>(this)->impl_get_row_ndx();
+}
+
+
+template <class T>
+template <class U>
+inline BasicRowExpr<T>::BasicRowExpr(const BasicRowExpr<U>& expr) noexcept
+    : m_table(expr.m_table)
+    , m_row_ndx(expr.m_row_ndx)
+{
+}
+
+template <class T>
+template <class U>
+inline BasicRowExpr<T>::BasicRowExpr(const BasicRow<U>& row) noexcept
+    : m_table(row.m_table.get())
+    , m_row_ndx(row.m_row_ndx)
+{
+}
+
+template <class T>
+inline BasicRowExpr<T>::BasicRowExpr(T* init_table, size_t init_row_ndx) noexcept
+    : m_table(init_table)
+    , m_row_ndx(init_row_ndx)
+{
+}
+
+template <class T>
+inline T* BasicRowExpr<T>::impl_get_table() const noexcept
+{
+    return m_table;
+}
+
+template <class T>
+inline size_t BasicRowExpr<T>::impl_get_row_ndx() const noexcept
+{
+    return m_row_ndx;
+}
+
+template <class T>
+inline void BasicRowExpr<T>::impl_detach() noexcept
+{
+    m_table = nullptr;
+}
+
+
+template <class T>
+inline BasicRow<T>::BasicRow() noexcept
+{
+}
+
+template <class T>
+inline BasicRow<T>::BasicRow(const BasicRow<T>& row) noexcept
+    : RowBase()
+{
+    attach(const_cast<Table*>(row.m_table.get()), row.m_row_ndx);
+}
+
+template <class T>
+template <class U>
+inline BasicRow<T>::BasicRow(BasicRowExpr<U> expr) noexcept
+{
+    T* expr_table = expr.m_table; // Check that pointer types are compatible
+    attach(const_cast<Table*>(expr_table), expr.m_row_ndx);
+}
+
+template <class T>
+template <class U>
+inline BasicRow<T>::BasicRow(const BasicRow<U>& row) noexcept
+{
+    T* row_table = row.m_table.get(); // Check that pointer types are compatible
+    attach(const_cast<Table*>(row_table), row.m_row_ndx);
+}
+
+template <class T>
+template <class U>
+inline BasicRow<T>& BasicRow<T>::operator=(BasicRowExpr<U> expr) noexcept
+{
+    T* expr_table = expr.m_table; // Check that pointer types are compatible
+    reattach(const_cast<Table*>(expr_table), expr.m_row_ndx);
+    return *this;
+}
+
+template <class T>
+template <class U>
+inline BasicRow<T>& BasicRow<T>::operator=(BasicRow<U> row) noexcept
+{
+    T* row_table = row.m_table.get(); // Check that pointer types are compatible
+    reattach(const_cast<Table*>(row_table), row.m_row_ndx);
+    return *this;
+}
+
+template <class T>
+inline BasicRow<T>& BasicRow<T>::operator=(const BasicRow<T>& row) noexcept
+{
+    reattach(const_cast<Table*>(row.m_table.get()), row.m_row_ndx);
+    return *this;
+}
+
+template <class T>
+inline BasicRow<T>::~BasicRow() noexcept
+{
+    RowBase::impl_detach();
+}
+
+template <class T>
+inline T* BasicRow<T>::impl_get_table() const noexcept
+{
+    return m_table.get();
+}
+
+template <class T>
+inline size_t BasicRow<T>::impl_get_row_ndx() const noexcept
+{
+    return m_row_ndx;
+}
+
+} // namespace realm
+
+#endif // REALM_ROW_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/spec.hpp b/iOS/Pods/Realm/include/core/realm/spec.hpp
new file mode 100644 (file)
index 0000000..6b793f0
--- /dev/null
@@ -0,0 +1,388 @@
+/*************************************************************************
+ *
+ * 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 <realm/util/features.h>
+#include <realm/array.hpp>
+#include <realm/array_string.hpp>
+#include <realm/array_integer.hpp>
+#include <realm/data_type.hpp>
+#include <realm/column_type.hpp>
+
+namespace realm {
+
+class Table;
+
+class Spec {
+public:
+    ~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);
+
+    /// 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.
+    Spec* get_subtable_spec(size_t column_ndx) 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;
+    Spec* get_subspec_by_ndx(size_t subspec_ndx) noexcept;
+    const Spec* 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);
+
+    // Backlinks
+    bool has_backlinks() const noexcept;
+    size_t first_backlink_column_index() const noexcept;
+    size_t backlink_column_count() 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 detach() 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)
+    struct SubspecPtr {
+        SubspecPtr(bool is_spec_ptr = false)
+            : m_is_spec_ptr(is_spec_ptr)
+        {
+        }
+        std::unique_ptr<Spec> m_spec;
+        bool m_is_spec_ptr;
+    };
+    using SubspecPtrs = std::vector<SubspecPtr>;
+    SubspecPtrs m_subspec_ptrs;
+    bool m_has_strong_link_columns;
+
+    Spec(Allocator&) noexcept; // Unattached
+
+    bool init(ref_type) noexcept;
+    void init(MemRef) noexcept;
+    void update_has_strong_link_columns() noexcept;
+    void reset_subspec_ptrs();
+    void adj_subspec_ptrs();
+
+    // Returns true in case the ref has changed.
+    bool 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.
+    bool 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;
+};
+
+// 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);
+}
+
+// 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 Spec* 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 get_subspec_by_ndx(subspec_ndx);
+}
+
+inline const Spec* Spec::get_subspec_by_ndx(size_t subspec_ndx) const noexcept
+{
+    return const_cast<Spec*>(this)->get_subspec_by_ndx(subspec_ndx);
+}
+
+inline bool Spec::init_from_parent() noexcept
+{
+    ref_type ref = m_top.get_ref_from_parent();
+    return 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.
+}
+
+inline size_t Spec::first_backlink_column_index() const noexcept
+{
+    return m_names.size();
+}
+
+inline size_t Spec::backlink_column_count() const noexcept
+{
+    return m_types.size() - m_names.size();
+}
+
+// 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);
+}
+
+} // namespace realm
+
+#endif // REALM_SPEC_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/string_data.hpp b/iOS/Pods/Realm/include/core/realm/string_data.hpp
new file mode 100644 (file)
index 0000000..a617ebe
--- /dev/null
@@ -0,0 +1,379 @@
+/*************************************************************************
+ *
+ * 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 <realm/null.hpp>
+#include <realm/util/features.h>
+#include <realm/util/optional.hpp>
+
+#include <algorithm>
+#include <array>
+#include <cfloat>
+#include <cmath>
+#include <cstddef>
+#include <cstring>
+#include <ostream>
+#include <string>
+
+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 <class T, class A>
+    StringData(const std::basic_string<char, T, A>&);
+
+    template <class T, class A>
+    operator std::basic_string<char, T, A>() const;
+
+    // StringData does not store data, callers must manage their own strings.
+    template <class T, class A>
+    StringData(const std::basic_string<char, T, A>&&) = delete;
+
+    template <class T, class A>
+    StringData(const util::Optional<std::basic_string<char, T, A>>&);
+
+    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;
+    bool contains(StringData d, const std::array<uint8_t, 256> &charmap) const noexcept;
+    
+    // Wildcard matching ('?' for single char, '*' for zero or more chars)
+    // case insensitive version in unicode.hpp
+    bool like(StringData) const noexcept;
+
+    //@{
+    /// Undefined behavior if \a n, \a i, or <tt>i+n</tt> 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 <class C, class T>
+    friend std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>&, const StringData&);
+
+    explicit operator bool() const noexcept;
+
+private:
+    const char* m_data;
+    size_t m_size;
+
+    static bool matchlike(const StringData& text, const StringData& pattern) noexcept;
+    static bool matchlike_ins(const StringData& text, const StringData& pattern_upper,
+                              const StringData& pattern_lower) noexcept;
+
+    friend bool string_like_ins(StringData, StringData) noexcept;
+    friend bool string_like_ins(StringData, StringData, StringData) noexcept;
+};
+
+
+// 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 <class T, class A>
+inline StringData::StringData(const std::basic_string<char, T, A>& s)
+    : m_data(s.data())
+    , m_size(s.size())
+{
+}
+
+template <class T, class A>
+inline StringData::operator std::basic_string<char, T, A>() const
+{
+    return std::basic_string<char, T, A>(m_data, m_size);
+}
+
+template <class T, class A>
+inline StringData::StringData(const util::Optional<std::basic_string<char, T, A>>& 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<char>::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;
+}
+
+/// This method takes an array that maps chars to distance that can be moved (and zero for chars not in needle),
+/// allowing the method to apply Boyer-Moore for quick substring search
+/// The map is calculated in the StringNode<Contains> class (so it can be reused across searches)
+inline bool StringData::contains(StringData d, const std::array<uint8_t, 256> &charmap) const noexcept
+{
+    if (is_null() && !d.is_null())
+        return false;
+    
+    size_t needle_size = d.size();
+    if (needle_size == 0)
+        return true;
+    
+    // Prepare vars to avoid lookups in loop
+    size_t last_char_pos = d.size()-1;
+    unsigned char lastChar = d[last_char_pos];
+    
+    // Do Boyer-Moore search
+    size_t p = last_char_pos;
+    while (p < m_size) {
+        unsigned char c = m_data[p]; // Get candidate for last char
+        
+        if (c == lastChar) {
+            StringData candidate = substr(p-needle_size+1, needle_size);
+            if (candidate == d)
+                return true; // text found!
+        }
+        
+        // If we don't have a match, see how far we can move char_pos
+        if (charmap[c] == 0)
+            p += needle_size; // char was not present in search string
+        else
+            p += charmap[c];
+    }
+    
+    return false;
+}
+    
+inline bool StringData::like(StringData d) const noexcept
+{
+    if (is_null() || d.is_null()) {
+        return (is_null() && d.is_null());
+    }
+
+    return matchlike(*this, d);
+}
+
+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 <class C, class T>
+inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& 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/iOS/Pods/Realm/include/core/realm/sync/changeset.hpp b/iOS/Pods/Realm/include/core/realm/sync/changeset.hpp
new file mode 100644 (file)
index 0000000..1efa4ac
--- /dev/null
@@ -0,0 +1,630 @@
+/*************************************************************************
+ *
+ * REALM CONFIDENTIAL
+ * __________________
+ *
+ *  [2011] - [2017] 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_CHANGESET_HPP
+#define REALM_SYNC_CHANGESET_HPP
+
+#include <realm/sync/instructions.hpp>
+#include <realm/util/optional.hpp>
+
+#include <type_traits>
+
+namespace realm {
+namespace sync {
+
+using InternStrings = std::unordered_map<uint32_t, StringBufferRange>;
+
+struct BadChangesetError : std::exception {
+    const char* message;
+    BadChangesetError() : BadChangesetError("Bad bad changeset") {}
+    BadChangesetError(const char* msg) : message(msg) {}
+    const char* what() const noexcept override
+    {
+        return message;
+    }
+};
+
+struct Changeset {
+    struct Range;
+    using timestamp_type = uint_fast64_t;
+    using file_ident_type = uint_fast64_t;
+    using version_type = uint_fast64_t; // FIXME: Get from `History`.
+
+    Changeset();
+    struct share_buffers_tag {};
+    Changeset(const Changeset&, share_buffers_tag);
+    Changeset(Changeset&&) = default;
+    Changeset& operator=(Changeset&&) = default;
+    Changeset(const Changeset&) = delete;
+    Changeset& operator=(const Changeset&) = delete;
+
+    InternString intern_string(StringData); // Slow!
+    StringData string_data() const noexcept;
+
+    util::StringBuffer& string_buffer() noexcept;
+    const util::StringBuffer& string_buffer() const noexcept;
+    const InternStrings& interned_strings() const noexcept;
+    InternStrings& interned_strings() noexcept;
+
+    StringBufferRange get_intern_string(InternString) const noexcept;
+    util::Optional<StringBufferRange> try_get_intern_string(InternString) const noexcept;
+    util::Optional<StringData> try_get_string(StringBufferRange) const noexcept;
+    StringData get_string(StringBufferRange) const noexcept;
+    StringData get_string(InternString) const noexcept;
+    StringBufferRange append_string(StringData);
+
+    // Interface to imitate std::vector:
+    template <bool is_const> struct IteratorImpl;
+    using iterator = IteratorImpl<false>;
+    using const_iterator = IteratorImpl<true>;
+    using value_type = Instruction;
+    iterator begin() noexcept;
+    iterator end() noexcept;
+    const_iterator begin() const noexcept;
+    const_iterator end() const noexcept;
+    const_iterator cbegin() const noexcept;
+    const_iterator cend() const noexcept;
+    bool empty() const noexcept;
+
+    /// Size of the Changeset, not counting tombstones.
+    ///
+    /// FIXME: This is an O(n) operation.
+    size_t size() const noexcept;
+
+    void clear() noexcept;
+
+    //@{
+    /// Insert instructions, invalidating all iterators.
+    iterator insert(iterator pos, Instruction);
+    template <class InputIt>
+    iterator insert(iterator pos, InputIt begin, InputIt end);
+    //@}
+
+    /// Erase an instruction, invalidating all iterators.
+    iterator erase(iterator);
+
+    /// Insert an instruction at the end, invalidating all iterators.
+    void push_back(Instruction);
+
+    //@{
+    /// Insert instructions at \a position without invalidating other
+    /// iterators.
+    ///
+    /// Only iterators created before any call to `insert_stable()` may be
+    /// considered stable across calls to `insert_stable()`. In addition,
+    /// "iterator stability" has a very specific meaning here: Other copies of
+    /// \a position in the program will point to the newly inserted elements
+    /// after calling `insert_stable()`, rather than point to the value at the
+    /// position prior to insertion. This is different from, say, a tree
+    /// structure, where iterator stability signifies the property that
+    /// iterators keep pointing to the same element after insertion before or
+    /// after that position.
+    ///
+    /// For the purpose of supporting `ChangesetIndex`, and the OT merge
+    /// algorithm, these semantics are acceptable, since prepended instructions
+    /// can never create new object or table references.
+    iterator insert_stable(iterator position, Instruction);
+    template <class InputIt>
+    iterator insert_stable(iterator position, InputIt begin, InputIt end);
+    //@}
+
+    /// Erase instruction at \a position without invalidating other iterators.
+    /// If erasing the object would invalidate other iterators, it is turned
+    /// into a tombstone instead, and subsequent derefencing of the iterator
+    /// will return `nullptr`. An iterator pointing to a tombstone remains valid
+    /// and can be incremented.
+    ///
+    /// Only iterators created before any call to `insert_stable()` may be
+    /// considered stable across calls to `erase_stable()`. If other copies of
+    /// \a position exist in the program, they will either point to the
+    /// subsequent element if that element was previously inserted with
+    /// `insert_stable()`, or otherwise it will be turned into a tombstone.
+    iterator erase_stable(iterator position);
+
+#if REALM_DEBUG
+    struct Reflector;
+    struct Printer;
+    void verify() const;
+    void print(std::ostream&) const;
+    void print() const; // prints to std::err
+#endif
+
+    /// The version that this changeset produced. Note: This may not be the
+    /// version produced by this changeset on the client on which this changeset
+    /// originated, but may for instance be the version produced on the server
+    /// after receiving and re-sending this changeset to another client.
+    version_type version = 0;
+
+    /// On clients, the last integrated server version. On the server, this is
+    /// the last integrated client version.
+    version_type last_integrated_remote_version = 0;
+
+    /// Timestamp at origin when producting this changeset.
+    timestamp_type origin_timestamp = 0;
+
+    /// Client file identifier that produced this changeset.
+    file_ident_type origin_client_file_ident = 0;
+
+private:
+    struct MultiInstruction {
+        std::vector<Instruction> instructions;
+    };
+
+    // In order to achieve iterator semi-stability (just enough to be able to
+    // run the merge algorithm while maintaining a ChangesetIndex), a Changeset
+    // is really a list of lists. A Changeset is a vector of
+    // `InstructionContainer`s, and each `InstructionContainer` represents 0-N
+    // "real" instructions.
+    //
+    // As an optimization, there is a special case for when the
+    // `InstructionContainer` represents exactly 1 instruction, in which case it
+    // is represented inside the `InstructionContainer` without any additional
+    // allocations or indirections. The `InstructionContainer` derived from
+    // the `Instruction` struct, and co-opts the `type` field such that if the
+    // (invalid) value of `type` is 0xff, the contents of the `Instruction` are
+    // instead interpreted as an instance of `MultiInstruction`, which holds
+    // a vector of `Instruction`s.
+    //
+    // The size of the `MultiInstruction` may also be zero, in which case it is
+    // considered a "tombstone" - always as a result of a call to
+    // `Changeset::erase_stable()`. The potential existence of these tombstones
+    // is the reason that the value type of `Changeset::iterator` is
+    // `Instruction*`, rather than `Instruction&`.
+    //
+    // FIXME: It would be better if `Changeset::iterator::value_type` could be
+    // `util::Optional<Instruction&>`, but this is prevented by a bug in
+    // `util::Optional`.
+    struct InstructionContainer : Instruction {
+        InstructionContainer();
+        InstructionContainer(Instruction instr);
+        InstructionContainer(InstructionContainer&&);
+        InstructionContainer(const InstructionContainer&);
+        ~InstructionContainer();
+        InstructionContainer& operator=(InstructionContainer&&);
+        InstructionContainer& operator=(const InstructionContainer&);
+
+        bool is_multi() const noexcept;
+        void convert_to_multi();
+        void insert(size_t position, Instruction instr);
+        void erase(size_t position);
+        size_t size() const noexcept;
+
+        Instruction& at(size_t pos) noexcept;
+        const Instruction& at(size_t pos) const noexcept;
+
+        MultiInstruction& get_multi() noexcept;
+        const MultiInstruction& get_multi() const noexcept;
+    };
+
+    std::vector<InstructionContainer> m_instructions;
+    std::shared_ptr<util::StringBuffer> m_string_buffer;
+    std::shared_ptr<InternStrings> m_strings;
+};
+
+/// An iterator type that hides the implementation details of the support for
+/// iterator stability.
+///
+/// A `Changeset::iterator` is composed of an
+/// `std::vector<InstructionContainer>::iterator` and a `size_t` representing
+/// the index into the current `InstructionContainer`. If that container is
+/// empty, and the position is zero, the iterator is pointing to a tombstone.
+template <bool is_const>
+struct Changeset::IteratorImpl {
+    using list_type = std::vector<InstructionContainer>;
+    using inner_iterator_type = std::conditional_t<is_const, list_type::const_iterator, list_type::iterator>;
+
+    // reference_type is a pointer because we have no way to create a reference
+    // to a tombstone instruction. Alternatively, it could have been
+    // `util::Optional<Instruction&>`, but that runs into other issues.
+    using reference_type = std::conditional_t<is_const, const Instruction*, Instruction*>;
+
+    using pointer_type   = std::conditional_t<is_const, const Instruction*, Instruction*>;
+    using difference_type = std::ptrdiff_t;
+
+    IteratorImpl() : m_pos(0) {}
+    IteratorImpl(const IteratorImpl& other) : m_inner(other.m_inner), m_pos(other.m_pos) {}
+    template <bool is_const_ = is_const>
+    IteratorImpl(const IteratorImpl<false>& other, std::enable_if_t<is_const_>* = nullptr)
+        : m_inner(other.m_inner), m_pos(other.m_pos) {}
+    IteratorImpl(inner_iterator_type inner) : m_inner(inner), m_pos(0) {}
+
+    IteratorImpl& operator++()
+    {
+        ++m_pos;
+        if (m_pos >= m_inner->size()) {
+            ++m_inner;
+            m_pos = 0;
+        }
+        return *this;
+    }
+
+    IteratorImpl operator++(int)
+    {
+        auto copy = *this;
+        ++(*this);
+        return copy;
+    }
+
+    IteratorImpl& operator--()
+    {
+        if (m_pos == 0) {
+            --m_inner;
+            m_pos = m_inner->size();
+            if (m_pos != 0)
+                --m_pos;
+        }
+        else {
+            --m_pos;
+        }
+        return *this;
+    }
+
+    IteratorImpl operator--(int)
+    {
+        auto copy = *this;
+        --(*this);
+        return copy;
+    }
+
+    reference_type operator*() const
+    {
+        if (m_inner->size()) {
+            return &m_inner->at(m_pos);
+        }
+        // It was a tombstone.
+        return nullptr;
+    }
+
+    pointer_type operator->() const
+    {
+        if (m_inner->size()) {
+            return &m_inner->at(m_pos);
+        }
+        // It was a tombstone.
+        return nullptr;
+    }
+
+    bool operator==(const IteratorImpl& other) const
+    {
+        return m_inner == other.m_inner && m_pos == other.m_pos;
+    }
+
+    bool operator!=(const IteratorImpl& other) const
+    {
+        return !(*this == other);
+    }
+
+    bool operator<(const IteratorImpl& other) const
+    {
+        if (m_inner == other.m_inner)
+            return m_pos < other.m_pos;
+        return m_inner < other.m_inner;
+    }
+
+    bool operator<=(const IteratorImpl& other) const
+    {
+        if (m_inner == other.m_inner)
+            return m_pos <= other.m_pos;
+        return m_inner < other.m_inner;
+    }
+
+    bool operator>(const IteratorImpl& other) const
+    {
+        if (m_inner == other.m_inner)
+            return m_pos > other.m_pos;
+        return m_inner > other.m_inner;
+    }
+
+    bool operator>=(const IteratorImpl& other) const
+    {
+        if (m_inner == other.m_inner)
+            return m_pos >= other.m_pos;
+        return m_inner > other.m_inner;
+    }
+
+    inner_iterator_type m_inner;
+    size_t m_pos;
+};
+
+struct Changeset::Range {
+    iterator begin;
+    iterator end;
+};
+
+#if REALM_DEBUG
+struct Changeset::Reflector {
+    struct Tracer {
+        virtual void name(StringData) = 0;
+        virtual void field(StringData, StringData) = 0;
+        virtual void field(StringData, ObjectID) = 0;
+        virtual void field(StringData, int64_t) = 0;
+        virtual void field(StringData, double) = 0;
+        virtual void after_each() {}
+        virtual void before_each() {}
+    };
+
+    Reflector(Tracer& tracer, const Changeset& log) :
+        m_tracer(tracer), m_log(log)
+    {}
+
+    void visit_all() const;
+private:
+    Tracer& m_tracer;
+    const Changeset& m_log;
+
+    friend struct Instruction; // permit access for visit()
+#define REALM_DEFINE_REFLECTOR_VISITOR(X) void operator()(const Instruction::X&) const;
+    REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_REFLECTOR_VISITOR)
+#undef REALM_DEFINE_REFLECTOR_VISITOR
+};
+
+struct Changeset::Printer : Changeset::Reflector::Tracer {
+    explicit Printer(std::ostream& os) : m_out(os)
+    {}
+
+    // ChangesetReflector::Tracer interface:
+    void name(StringData) final;
+    void field(StringData, StringData) final;
+    void field(StringData, ObjectID) final;
+    void field(StringData, int64_t) final;
+    void field(StringData, double) final;
+    void after_each() final;
+
+private:
+    std::ostream& m_out;
+    void pad_or_ellipsis(StringData, int width) const;
+};
+#endif // REALM_DEBUG
+
+
+
+/// Implementation:
+
+inline Changeset::iterator Changeset::begin() noexcept
+{
+    return m_instructions.begin();
+}
+
+inline Changeset::iterator Changeset::end() noexcept
+{
+    return m_instructions.end();
+}
+
+inline Changeset::const_iterator Changeset::begin() const noexcept
+{
+    return m_instructions.begin();
+}
+
+inline Changeset::const_iterator Changeset::end() const noexcept
+{
+    return m_instructions.end();
+}
+
+inline Changeset::const_iterator Changeset::cbegin() const noexcept
+{
+    return m_instructions.cbegin();
+}
+
+inline Changeset::const_iterator Changeset::cend() const noexcept
+{
+    return m_instructions.end();
+}
+
+inline bool Changeset::empty() const noexcept
+{
+    return size() == 0;
+}
+
+inline size_t Changeset::size() const noexcept
+{
+    size_t sum = 0;
+    for (auto& x: m_instructions)
+        sum += x.size();
+    return sum;
+}
+
+inline void Changeset::clear() noexcept
+{
+    m_instructions.clear();
+}
+
+inline util::Optional<StringBufferRange> Changeset::try_get_intern_string(InternString string) const noexcept
+{
+    auto it = m_strings->find(string.value);
+    if (it == m_strings->end())
+        return util::none;
+    return it->second;
+}
+
+inline StringBufferRange Changeset::get_intern_string(InternString string) const noexcept
+{
+    auto str = try_get_intern_string(string);
+    REALM_ASSERT(str);
+    return *str;
+}
+
+inline InternStrings& Changeset::interned_strings() noexcept
+{
+    return *m_strings;
+}
+
+inline const InternStrings& Changeset::interned_strings() const noexcept
+{
+    return *m_strings;
+}
+
+inline util::StringBuffer& Changeset::string_buffer() noexcept
+{
+    return *m_string_buffer;
+}
+
+inline const util::StringBuffer& Changeset::string_buffer() const noexcept
+{
+    return *m_string_buffer;
+}
+
+inline util::Optional<StringData> Changeset::try_get_string(StringBufferRange range) const noexcept
+{
+    if (range.offset > m_string_buffer->size())
+        return util::none;
+    if (range.offset + range.size > m_string_buffer->size())
+        return util::none;
+    return StringData{m_string_buffer->data() + range.offset, range.size};
+}
+
+inline StringData Changeset::get_string(StringBufferRange range) const noexcept
+{
+    auto string = try_get_string(range);
+    REALM_ASSERT(string);
+    return *string;
+}
+
+inline StringData Changeset::get_string(InternString string) const noexcept
+{
+    return get_string(get_intern_string(string));
+}
+
+inline StringData Changeset::string_data() const noexcept
+{
+    return StringData{m_string_buffer->data(), m_string_buffer->size()};
+}
+
+inline StringBufferRange Changeset::append_string(StringData string)
+{
+    m_string_buffer->reserve(1024); // we expect more strings
+    size_t offset = m_string_buffer->size();
+    m_string_buffer->append(string.data(), string.size());
+    return StringBufferRange{uint32_t(offset), uint32_t(string.size())};
+}
+
+inline Changeset::iterator Changeset::insert(iterator pos, Instruction instr)
+{
+    Instruction* p = &instr;
+    return insert(pos, p, p + 1);
+}
+
+template <class InputIt>
+inline Changeset::iterator Changeset::insert(iterator pos, InputIt begin, InputIt end)
+{
+    if (pos.m_pos == 0)
+        return m_instructions.insert(pos.m_inner, begin, end);
+    return insert_stable(pos, begin, end);
+}
+
+inline Changeset::iterator Changeset::erase(iterator pos)
+{
+    if (pos.m_inner->size() <= 1)
+        return m_instructions.erase(pos.m_inner);
+    return erase_stable(pos);
+}
+
+inline Changeset::iterator Changeset::insert_stable(iterator pos, Instruction instr)
+{
+    Instruction* p = &instr;
+    return insert_stable(pos, p, p + 1);
+}
+
+template <class InputIt>
+inline Changeset::iterator Changeset::insert_stable(iterator pos, InputIt begin, InputIt end)
+{
+    size_t i = 0;
+    for (auto it = begin; it != end; ++it, ++i) {
+        pos.m_inner->insert(pos.m_pos + i, *it);
+    }
+    return pos;
+}
+
+inline Changeset::iterator Changeset::erase_stable(iterator pos)
+{
+    pos.m_inner->erase(pos.m_pos);
+    ++pos;
+    return pos;
+}
+
+inline void Changeset::push_back(Instruction instr)
+{
+    m_instructions.push_back(std::move(instr));
+}
+
+inline Changeset::InstructionContainer::~InstructionContainer()
+{
+    if (is_multi()) {
+        get_multi().~MultiInstruction();
+    }
+    // Instruction subtypes are required to be POD-types (trivially
+    // destructible), and this is checked by a static_assert in
+    // instructions.hpp. Therefore, it is safe to do nothing if this is not a
+    // multi-instruction.
+}
+
+inline bool Changeset::InstructionContainer::is_multi() const noexcept
+{
+    return type == Type(InstrTypeMultiInstruction);
+}
+
+inline size_t Changeset::InstructionContainer::size() const noexcept
+{
+    if (is_multi())
+        return get_multi().instructions.size();
+    return 1;
+}
+
+inline Instruction& Changeset::InstructionContainer::at(size_t pos) noexcept
+{
+    REALM_ASSERT(pos < size());
+    if (is_multi())
+        return get_multi().instructions[pos];
+    return *this;
+}
+
+inline const Instruction& Changeset::InstructionContainer::at(size_t pos) const noexcept
+{
+    REALM_ASSERT(pos < size());
+    if (is_multi())
+        return get_multi().instructions[pos];
+    return *this;
+}
+
+inline Changeset::MultiInstruction& Changeset::InstructionContainer::get_multi() noexcept
+{
+    REALM_ASSERT(is_multi());
+    return *reinterpret_cast<MultiInstruction*>(&m_storage);
+}
+
+inline const Changeset::MultiInstruction& Changeset::InstructionContainer::get_multi() const noexcept
+{
+    REALM_ASSERT(is_multi());
+    return *reinterpret_cast<const MultiInstruction*>(&m_storage);
+}
+
+} // namespace sync
+} // namespace realm
+
+namespace std {
+
+template <bool is_const>
+struct iterator_traits<realm::sync::Changeset::IteratorImpl<is_const>> {
+    using difference_type = std::ptrdiff_t;
+    using iterator_category = std::bidirectional_iterator_tag;
+};
+
+} // namespace std
+
+#endif // REALM_SYNC_CHANGESET_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/changeset_cooker.hpp b/iOS/Pods/Realm/include/core/realm/sync/changeset_cooker.hpp
new file mode 100644 (file)
index 0000000..3d003be
--- /dev/null
@@ -0,0 +1,40 @@
+/*************************************************************************
+ *
+ * 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 <realm/sync/history.hpp>
+
+#ifndef REALM_SYNC_CHANGESET_COOKER_HPP
+#define REALM_SYNC_CHANGESET_COOKER_HPP
+
+namespace realm {
+namespace sync {
+
+/// Copy raw changesets unmodified.
+class TrivialChangesetCooker: public ClientHistory::ChangesetCooker {
+public:
+    bool cook_changeset(const Group&, const char* changeset,
+                        std::size_t changeset_size,
+                        util::AppendBuffer<char>&) override;
+};
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_CHANGESET_COOKER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/changeset_encoder.hpp b/iOS/Pods/Realm/include/core/realm/sync/changeset_encoder.hpp
new file mode 100644 (file)
index 0000000..d443862
--- /dev/null
@@ -0,0 +1,109 @@
+/*************************************************************************
+ *
+ * REALM CONFIDENTIAL
+ * __________________
+ *
+ *  [2011] - [2017] 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_CHANGESET_ENCODER_HPP
+#define REALM_SYNC_CHANGESET_ENCODER_HPP
+
+#include <realm/sync/changeset.hpp>
+
+namespace realm {
+namespace sync {
+
+struct ChangesetEncoder: InstructionHandler {
+    util::AppendBuffer<char> release() noexcept;
+    void reset() noexcept;
+    const util::AppendBuffer<char>& buffer() const noexcept;
+    InternString intern_string(StringData);
+
+    void set_intern_string(uint32_t index, StringBufferRange) override;
+    // FIXME: This doesn't copy the input, but the drawback is that there can
+    // only be a single StringBufferRange per instruction. Luckily, no
+    // instructions exist that require two or more.
+    StringBufferRange add_string_range(StringData) override;
+    void operator()(const Instruction&) override;
+
+#define REALM_DEFINE_INSTRUCTION_HANDLER(X) void operator()(const Instruction::X&);
+    REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_HANDLER)
+#undef REALM_DEFINE_INSTRUCTION_HANDLER
+
+protected:
+    template<class E> static void encode(E& encoder, const Instruction&);
+
+    StringData get_string(StringBufferRange) const noexcept;
+
+private:
+    template<class... Args>
+    void append(Instruction::Type t, Args&&...);
+    void append_string(StringBufferRange); // does not intern the string
+    void append_bytes(const void*, size_t);
+
+    template<class T> void append_int(T);
+    template<class T> char* encode_int(char* buffer, T value);
+    void append_payload(const Instruction::Payload&);
+    void append_value(DataType);
+    void append_value(bool);
+    void append_value(uint8_t);
+    void append_value(int64_t);
+    void append_value(uint32_t);
+    void append_value(uint64_t);
+    void append_value(float);
+    void append_value(double);
+    void append_value(InternString);
+    void append_value(sync::ObjectID);
+    void append_value(Timestamp);
+
+    util::AppendBuffer<char> m_buffer;
+    std::unordered_map<std::string, uint32_t> m_intern_strings_rev;
+    StringData m_string_range;
+};
+
+void encode_changeset(const Changeset&, util::AppendBuffer<char>& out_buffer);
+
+
+
+
+// Implementation
+
+inline const util::AppendBuffer<char>& ChangesetEncoder::buffer() const noexcept
+{
+    return m_buffer;
+}
+
+inline void ChangesetEncoder::operator()(const Instruction& instr)
+{
+    encode(*this, instr); // Throws
+}
+
+template<class E> inline void ChangesetEncoder::encode(E& encoder, const Instruction& instr)
+{
+    instr.visit(encoder); // Throws
+}
+
+inline StringData ChangesetEncoder::get_string(StringBufferRange range) const noexcept
+{
+    const char* data = m_string_range.data() + range.offset;
+    std::size_t size = std::size_t(range.size);
+    return StringData{data, size};
+}
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_CHANGESET_ENCODER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/changeset_parser.hpp b/iOS/Pods/Realm/include/core/realm/sync/changeset_parser.hpp
new file mode 100644 (file)
index 0000000..29cb512
--- /dev/null
@@ -0,0 +1,43 @@
+/*************************************************************************
+ *
+ * REALM CONFIDENTIAL
+ * __________________
+ *
+ *  [2011] - [2017] 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_CHANGESET_PARSER_HPP
+#define REALM_SYNC_CHANGESET_PARSER_HPP
+
+#include <realm/sync/changeset.hpp>
+#include <realm/impl/input_stream.hpp>
+
+namespace realm {
+namespace sync {
+
+struct ChangesetParser {
+    void parse(_impl::NoCopyInputStream&, InstructionHandler&);
+private:
+    struct State;
+};
+
+void parse_changeset(_impl::NoCopyInputStream&, Changeset& out_log);
+void parse_changeset(_impl::InputStream&, Changeset& out_log);
+
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_CHANGESET_PARSER_HPP
\ No newline at end of file
diff --git a/iOS/Pods/Realm/include/core/realm/sync/client.hpp b/iOS/Pods/Realm/include/core/realm/sync/client.hpp
new file mode 100644 (file)
index 0000000..c08ff96
--- /dev/null
@@ -0,0 +1,1006 @@
+/*************************************************************************
+ *
+ * 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 <stdint.h>
+#include <memory>
+#include <utility>
+#include <functional>
+#include <exception>
+#include <string>
+
+#include <realm/util/logger.hpp>
+#include <realm/util/network.hpp>
+#include <realm/impl/cont_transact_hist.hpp>
+#include <realm/sync/history.hpp>
+
+namespace realm {
+namespace sync {
+
+class Client {
+public:
+    enum class Error;
+
+    enum class ReconnectMode {
+        /// 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 (i.e., a server hammering protection mechanism).
+        normal,
+
+        /// Delay reconnect attempts indefinitely. For testing purposes only.
+        ///
+        /// A reconnect attempt can be manually scheduled by calling
+        /// cancel_reconnect_delay(). In particular, when a connection breaks,
+        /// or when an attempt at establishing the connection fails, the
+        /// connection state change listener is called. If one calls
+        /// cancel_reconnect_delay() from that invocation of the listener, the
+        /// effect is to allow another reconnect attempt to occur.
+        never,
+
+        /// Never delay reconnect attempts. Perform them immediately. For
+        /// testing purposes only.
+        immediate
+    };
+
+    static constexpr std::uint_fast64_t default_ping_keepalive_period_ms  = 600000; // 10 minutes
+    static constexpr std::uint_fast64_t default_pong_keepalive_timeout_ms = 300000; //  5 minutes
+    static constexpr std::uint_fast64_t default_pong_urgent_timeout_ms    =   5000; //  5 seconds
+
+    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 either on behalf of the constructor or on behalf
+        /// of the invocation of run().
+        util::Logger* logger = nullptr;
+
+        /// 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.
+        ReconnectMode reconnect_mode = ReconnectMode::normal;
+
+        /// Create a separate connection for each session. For testing purposes
+        /// only.
+        ///
+        /// FIXME: This setting needs to be true for now, due to limitations in
+        /// the load balancer.
+        bool one_connection_per_session = true;
+
+        /// 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;
+
+        /// The default changeset cooker to be used by new sessions. Can be
+        /// overridden by Session::Config::changeset_cooker.
+        ///
+        /// \sa make_client_history(), TrivialChangesetCooker.
+        std::shared_ptr<ClientHistory::ChangesetCooker> changeset_cooker;
+
+        /// The number of ms between periodic keep-alive pings.
+        std::uint_fast64_t ping_keepalive_period_ms = default_ping_keepalive_period_ms;
+
+        /// The number of ms to wait for keep-alive pongs.
+        std::uint_fast64_t pong_keepalive_timeout_ms = default_pong_keepalive_timeout_ms;
+
+        /// The number of ms to wait for urgent pongs.
+        std::uint_fast64_t pong_urgent_timeout_ms = default_pong_urgent_timeout_ms;
+
+        /// If enable_upload_log_compaction is true, every changeset will be
+        /// compacted before it is uploaded to the server. Compaction will
+        /// reduce the size of a changeset if the same field is set multiple
+        /// times or if newly created objects are deleted within the same
+        /// transaction. Log compaction increeses CPU usage and memory
+        /// consumption.
+        bool enable_upload_log_compaction = true;
+
+        /// Set the `TCP_NODELAY` option on all TCP/IP sockets. This disables
+        /// the Nagle algorithm. Disabling it, can in some cases be used to
+        /// decrease latencies, but possibly at the expense of scalability. Be
+        /// sure to research the subject before you enable this option.
+        bool tcp_no_delay = false;
+    };
+
+    /// \throw util::EventLoop::Implementation::NotAvailable if no event loop
+    /// implementation was specified, and
+    /// util::EventLoop::Implementation::get_default() throws it.
+    Client(Config = {});
+    Client(Client&&) noexcept;
+    ~Client() noexcept;
+
+    /// 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();
+
+    /// See run().
+    ///
+    /// Thread-safe.
+    void stop() noexcept;
+
+    /// \brief Cancel current or next reconnect delay for all servers.
+    ///
+    /// This corresponds to calling Session::cancel_reconnect_delay() on all
+    /// bound sessions, but will also cancel reconnect delays applying to
+    /// servers for which there are currently no bound sessions.
+    ///
+    /// Thread-safe.
+    void cancel_reconnect_delay();
+
+    /// \brief Wait for session termination to complete.
+    ///
+    /// Wait for termination of all sessions whose termination was initiated
+    /// prior this call (the completion condition), or until the client's event
+    /// loop thread exits from Client::run(), whichever happens
+    /// first. Termination of a session can be initiated implicitly (e.g., via
+    /// destruction of the session object), or explicitly by Session::detach().
+    ///
+    /// Note: After session termination (when this function returns true) no
+    /// session specific callback function can be called or continue to execute,
+    /// and the client is guaranteed to no longer have a Realm file open on
+    /// behalf of the terminated session.
+    ///
+    /// CAUTION: If run() returns while a wait operation is in progress, this
+    /// waiting function will return immediately, even if the completion
+    /// condition is not yet satisfied. The completion condition is guaranteed
+    /// to be satisfied only when these functions return true. If it returns
+    /// false, session specific callback functions may still be executing or get
+    /// called, and the associated Realm files may still not have been closed.
+    ///
+    /// If a new wait operation is initiated while another wait operation is in
+    /// progress by another thread, the waiting period of fist operation may, or
+    /// may not get extended. The application must not assume either.
+    ///
+    /// Note: Session termination does not imply that the client has received an
+    /// UNBOUND message from the server (see the protocol specification). This
+    /// may happen later.
+    ///
+    /// \return True only if the completion condition was satisfied. False if
+    /// the client's event loop thread exited from Client::run() in which case
+    /// the completion condition may, or may not have been satisfied.
+    ///
+    /// Note: These functions are fully thread-safe. That is, they may be called
+    /// by any thread, and by multiple threads concurrently.
+    bool wait_for_session_terminations_or_client_stopped();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> 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
+
+
+/// \brief Client-side representation of a Realm file synchronization session.
+///
+/// A synchronization session deals with precisely one local Realm file. To
+/// synchronize multiple local Realm files, you need multiple sessions.
+///
+/// A session object is always associated with a particular client object (\ref
+/// Client). The application must ensure that the destruction of the associated
+/// client object never happens before the destruction of the session
+/// object. The consequences of a violation are unspecified.
+///
+/// A session object is always associated with a particular local Realm file,
+/// however, a session object does not represent a session until it is bound to
+/// a server side Realm, i.e., until bind() is called. From the point of view of
+/// the thread that calls bind(), the session starts precisely when the
+/// execution of bind() starts, i.e., before bind() returns.
+///
+/// At most one session is allowed to exist for a particular local Realm file
+/// (file system inode) at any point in time. Multiple session objects may
+/// coexists for a single file, as long as bind() has been called on at most one
+/// of them. Additionally, two bound session objects for the same file are
+/// allowed to exist at different times, if they have no overlap in time (in
+/// their bound state), as long as they are associated with the same client
+/// object, or with two different client objects that do not overlap in
+/// time. This means, in particular, that it is an error to create two bound
+/// session objects for the same local Realm file, it they are associated with
+/// two different client objects that overlap in time, even if the session
+/// objects do not overlap in time (in their bound state). It is the
+/// responsibility of the application to ensure that these rules are adhered
+/// to. The consequences of a violation are unspecified.
+///
+/// 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.
+///
+/// Callback semantics: All session specific callback functions will be executed
+/// by the event loop thread, i.e., the thread that calls Client::run(). No
+/// callback function will be called before Session::bind() is called. Callback
+/// functions that are specified prior to calling bind() (e.g., any passed to
+/// set_progress_handler()) may start to execute before bind() returns, as long
+/// as some thread is executing Client::run(). Likewise, completion handlers,
+/// such as those passed to async_wait_for_sync_completion() may start to
+/// execute before the submitting function returns. All session specific
+/// callback functions (including completion handlers) are guaranteed to no
+/// longer be executing when session termination completes, and they are
+/// guaranteed to not be called after session termination completes. Termination
+/// is an event that completes asynchronously with respect to the application,
+/// but is initiated by calling detach(), or implicitely by destroying a session
+/// object. After having initiated one or more session terminations, the
+/// application can wait for those terminations to complete by calling
+/// Client::wait_for_session_terminations_or_client_stopped(). Since callback
+/// functinos are always executed by the event loop thread, they are also
+/// guaranteed to not be executing after Client::run() has returned.
+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 ProgressHandler = void(std::uint_fast64_t downloaded_bytes,
+                                 std::uint_fast64_t downloadable_bytes,
+                                 std::uint_fast64_t uploaded_bytes,
+                                 std::uint_fast64_t uploadable_bytes,
+                                 std::uint_fast64_t progress_version,
+                                 std::uint_fast64_t snapshot_version);
+    using WaitOperCompletionHandler = std::function<void(std::error_code)>;
+    using SSLVerifyCallback = bool(const std::string& server_address,
+                                   port_type server_port,
+                                   const char* pem_data,
+                                   size_t pem_size,
+                                   int preverify_ok,
+                                   int depth);
+
+    class Config {
+    public:
+        Config() {}
+
+        /// server_address is the fully qualified host name, or IP address of
+        /// the server.
+        std::string server_address = "localhost";
+
+        /// server_port is the port at which the server listens. If server_port
+        /// is zero, the default port for the specified protocol is used. See \ref
+        /// Protocol for information on default ports.
+        port_type server_port = 0;
+
+        /// server_path is  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.
+        std::string server_path = "/";
+
+        /// The protocol used for communicating with the server. See \ref Protocol.
+        Protocol protocol = Protocol::realm;
+
+        /// url_prefix is a prefix that is prepended to the server_path
+        /// in the HTTP GET request that initiates a sync connection. The value
+        /// specified here must match with the server's expectation. Changing
+        /// the value of url_prefix should be matched with a corresponding
+        /// change of the server side proxy.
+        std::string url_prefix = "/realm-sync";
+
+        /// Sessions can be multiplexed over the same TCP/SSL connection.
+        /// Sessions might share connection if they have identical server_address,
+        /// server_port, and protocol. multiplex_ident is a parameter that allows
+        /// finer control over session multiplexing. If two sessions have distinct
+        /// multiplex_ident, they will never share connection. The typical use of
+        /// multilex_ident is to give sessions with incompatible SSL requirements
+        /// distinct multiplex_idents.
+        /// multiplex_ident can be any string and the value has no meaning except
+        /// for partitioning the sessions.
+        std::string multiplex_ident;
+
+        /// verify_servers_ssl_certificate controls whether the server
+        /// certificate is verified for SSL connections. It should generally be
+        /// true in production.
+        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.
+        /// ssl_trust_certificate_path is only used if the protocol is ssl and
+        /// verify_servers_ssl_certificate is true.
+        ///
+        /// 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 server_address 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).
+        ///
+        /// If ssl_trust_certificate_path is None (default), ssl_verify_callback
+        /// (see below) is used if set, and the default device trust/anchor
+        /// store is used otherwise.
+        Optional<std::string> ssl_trust_certificate_path;
+
+        /// If Client::Config::ssl_verify_callback is set, that function is called
+        /// to verify the certificate, unless verify_servers_ssl_certificate is
+        /// false.
+
+        /// ssl_verify_callback is used to implement custom SSL certificate
+        /// verification. it is only used if the protocol is SSL,
+        /// verify_servers_ssl_certificate is true and ssl_trust_certificate_path
+        /// is None.
+        ///
+        /// The signature of ssl_verify_callback is
+        ///
+        /// bool(const std::string& server_address,
+        ///      port_type server_port,
+        ///      const char* pem_data,
+        ///      size_t pem_size,
+        ///      int preverify_ok,
+        ///      int depth);
+        ///
+        /// server address and server_port is the address and port of the server
+        /// that a SSL connection is being established to. They are identical to
+        /// the server_address and server_port set in this config file and are
+        /// passed for convenience.
+        /// pem_data is the certificate of length pem_size in
+        /// the PEM format. preverify_ok is OpenSSL's preverification of the
+        /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1,
+        /// OpenSSL has accepted the certificate and it will generally be safe
+        /// to trust that certificate. depth represents the position of the
+        /// certificate in the certificate chain sent by the server. depth = 0
+        /// represents the actual server certificate that should contain the
+        /// host name(server address) of the server. The highest depth is the
+        /// root certificate.
+        /// The callback function will receive the certificates starting from
+        /// the root certificate and moving down the chain until it reaches the
+        /// server's own certificate with a host name. The depth of the last
+        /// certificate is 0. The depth of the first certificate is chain
+        /// length - 1.
+        ///
+        /// The return value of the callback function decides whether the
+        /// client accepts the certificate. If the return value is false, the
+        /// processing of the certificate chain is interrupted and the SSL
+        /// connection is rejected. If the return value is true, the verification
+        /// process continues. If the callback function returns true for all
+        /// presented certificates including the depth == 0 certificate, the
+        /// SSL connection is accepted.
+        ///
+        /// A recommended way of using the callback function is to return true
+        /// if preverify_ok = 1 and depth > 0,
+        /// always check the host name if depth = 0,
+        /// and use an independent verification step if preverify_ok = 0.
+        ///
+        /// Another possible way of using the callback is to collect all the
+        /// certificates until depth = 0, and present the entire chain for
+        /// independent verification.
+        std::function<SSLVerifyCallback> ssl_verify_callback;
+
+        /// signed_user_token is a cryptographically signed token describing the
+        /// identity and access rights of the current user.
+        std::string signed_user_token;
+
+        /// If not null, overrides whatever is specified by
+        /// Client::Config::changeset_cooker.
+        ///
+        /// The shared ownership over the cooker will be relinquished shortly
+        /// after the destruction of the session object as long as the event
+        /// loop of the client is being executed (Client::run()).
+        ///
+        /// CAUTION: ChangesetCooker::cook_changeset() of the specified cooker
+        /// may get called before the call to bind() returns, and it may get
+        /// called (or continue to execute) after the session object is
+        /// destroyed. Please see "Callback semantics" section under Client for
+        /// more on this.
+        ///
+        /// \sa make_client_history(), TrivialChangesetCooker.
+        std::shared_ptr<ClientHistory::ChangesetCooker> changeset_cooker;
+
+        /// The encryption key the SharedGroup will be opened with.
+        Optional<std::array<char, 64>> encryption_key;
+
+        /// FIXME: This value must currently be true in a cluster setup.
+        /// This restriction will be lifted in the future.
+        bool one_connection_per_session = true;
+    };
+
+    /// \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, Config = {});
+
+    /// This leaves the right-hand side session object detached. See "Thread
+    /// safety" section under detach().
+    Session(Session&&) noexcept;
+
+    /// Create a detached session object (see detach()).
+    Session() noexcept;
+
+    /// Implies detachment. See "Thread safety" section under detach().
+    ~Session() noexcept;
+
+    /// Detach the object on the left-hand side, then "steal" the session from
+    /// the object on the right-hand side, if there is one. This leaves the
+    /// object on the right-hand side detached. See "Thread safety" section
+    /// under detach().
+    Session& operator=(Session&&) noexcept;
+
+    /// Detach this sesion object from the client object (Client). If the
+    /// session object is already detached, this function has no effect
+    /// (idempotency).
+    ///
+    /// Detachment initiates session termination, which is an event that takes
+    /// place shortly therafter in the context of the client's event loop
+    /// thread.
+    ///
+    /// A detached session object may be destroyed, move-assigned to, and moved
+    /// from. Apart from that, it is an error to call any function other than
+    /// detach() on a detached session object.
+    ///
+    /// Thread safety: Detachment is not a thread-safe operation. This means
+    /// that detach() may not be executed by two threads concurrently, and may
+    /// not execute concurrently with object destruction. Additionally,
+    /// detachment must not execute concurrently with a moving operation
+    /// involving the session object on the left or right-hand side. See move
+    /// constructor and assigment operator.
+    void detach() 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 get called before the call
+    /// to bind() returns, and it may get called (or continue to execute) after
+    /// the session object is destroyed. Please see "Callback semantics" section
+    /// under Session for more on this.
+    void set_sync_transact_callback(std::function<SyncTransactCallback>);
+
+    /// \brief Set a handler to monitor the state of download and upload
+    /// progress.
+    ///
+    /// The handler must have signature
+    ///
+    ///     void(uint_fast64_t downloaded_bytes, uint_fast64_t downloadable_bytes,
+    ///          uint_fast64_t uploaded_bytes, uint_fast64_t uploadable_bytes,
+    ///          uint_fast64_t progress_version);
+    ///
+    /// downloaded_bytes is the size in bytes of all downloaded changesets.
+    /// downloadable_bytes is the size in bytes of the part of the server
+    /// history that do not originate from this client.
+    ///
+    /// uploaded_bytes is the size in bytes of all locally produced changesets
+    /// that have been received and acknowledged by the server.
+    /// uploadable_bytes is the size in bytes of all locally produced changesets.
+    ///
+    /// Due to the nature of the merge rules, it is possible that the size of an
+    /// uploaded changeset uploaded from one client is not equal to the size of
+    /// the changesets that other clients will download.
+    ///
+    /// Typical uses of this function:
+    ///
+    /// Upload completion can be checked by
+    ///
+    ///    bool upload_complete = (uploaded_bytes == uploadable_bytes);
+    ///
+    /// Download completion could be checked by
+    ///
+    ///     bool download_complete = (downloaded_bytes == downloadable_bytes);
+    ///
+    /// However, download completion might never be reached because the server
+    /// can receive new changesets from other clients.
+    /// An alternative strategy is to cache downloadable_bytes from the callback,
+    /// and use the cached value as the threshold.
+    ///
+    ///     bool download_complete = (downloaded_bytes == cached_downloadable_bytes);
+    ///
+    /// Upload progress can be calculated by caching an initial value of
+    /// uploaded_bytes from the last, or next, callback. Then
+    ///
+    ///     double upload_progress =
+    ///        (uploaded_bytes - initial_uploaded_bytes)
+    ///       -------------------------------------------
+    ///       (uploadable_bytes - initial_uploaded_bytes)
+    ///
+    /// Download progress can be calculates similarly:
+    ///
+    ///     double download_progress =
+    ///        (downloaded_bytes - initial_downloaded_bytes)
+    ///       -----------------------------------------------
+    ///       (downloadable_bytes - initial_downloaded_bytes)
+    ///
+    /// progress_version is 0 at the start of a session. When at least one
+    /// DOWNLOAD message has been received from the server, progress_version is
+    /// positive. progress_version can be used to ensure that the reported
+    /// progress contains information obtained from the server in the current
+    /// session. The server will send a message as soon as possible, and the
+    /// progress handler will eventually be called with a positive progress_version
+    /// unless the session is interrupted before a message from the server has
+    /// been received.
+    ///
+    /// The handler is called on the event loop thread.The handler after bind(),
+    /// after each DOWNLOAD message, and after each local transaction
+    /// (nonsync_transact_notify).
+    ///
+    /// set_progress_handler() is not thread safe and it must be called before
+    /// bind() is called. Subsequent calls to set_progress_handler() overwrite
+    /// the previous calls. Typically, this function is called once per session.
+    ///
+    /// CAUTION: The specified callback function may get called before the call
+    /// to bind() returns, and it may get called (or continue to execute) after
+    /// the session object is destroyed. Please see "Callback semantics" section
+    /// under Session for more on this.
+    void set_progress_handler(std::function<ProgressHandler>);
+
+    enum class ConnectionState { disconnected, connecting, connected };
+
+    /// \brief Information about an error causing a session to be temporarily
+    /// disconnected from the server.
+    ///
+    /// In general, the connection will be automatically reestablished
+    /// later. Whether this happens quickly, generally depends on \ref
+    /// is_fatal. If \ref is_fatal is true, it means that the error is deemed to
+    /// be of a kind that is likely to persist, and cause all future reconnect
+    /// attempts to fail. In that case, if another attempt is made at
+    /// reconnecting, the delay will be substantial (at least an hour).
+    ///
+    /// \ref error_code specifies the error that caused the connection to be
+    /// closed. For the list of errors reported by the server, see \ref
+    /// ProtocolError (or `protocol.md`). For the list of errors corresponding
+    /// to protocol violations that are detected by the client, see
+    /// Client::Error. The error may also be a system level error, or an error
+    /// from one of the potential intermediate protocol layers (SSL or
+    /// WebSocket).
+    ///
+    /// \ref detailed_message is the most detailed message available to describe
+    /// the error. It is generally equal to `error_code.message()`, but may also
+    /// be a more specific message (one that provides extra context). The
+    /// purpose of this message is mostly to aid in debugging. For non-debugging
+    /// purposes, `error_code.message()` should generally be considered
+    /// sufficient.
+    ///
+    /// \sa set_connection_state_change_listener().
+    struct ErrorInfo {
+        std::error_code error_code;
+        bool is_fatal;
+        const std::string& detailed_message;
+    };
+
+    using ConnectionStateChangeListener = void(ConnectionState, const ErrorInfo*);
+
+    /// \brief Install a connection state change listener.
+    ///
+    /// Sets a function to be called whenever the state of the underlying
+    /// network connection changes between "disconnected", "connecting", and
+    /// "connected". The initial state is always "disconnected". The next state
+    /// after "disconnected" is always "connecting". The next state after
+    /// "connecting" is either "connected" or "disconnected". The next state
+    /// after "connected" is always "disconnected". A switch to the
+    /// "disconnected" state only happens when an error occurs.
+    ///
+    /// Whenever the installed function is called, an ErrorInfo object is passed
+    /// when, and only when the passed state is ConnectionState::disconnected.
+    ///
+    /// When multiple sessions share a single connection, the state changes will
+    /// be reported for each session in turn.
+    ///
+    /// 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 get called before the call
+    /// to bind() returns, and it may get called (or continue to execute) after
+    /// the session object is destroyed. Please see "Callback semantics" section
+    /// under Session for more on this.
+    void set_connection_state_change_listener(std::function<ConnectionStateChangeListener>);
+
+    //@{
+    /// Deprecated! Use set_connection_state_change_listener() instead.
+    using ErrorHandler = void(std::error_code, bool is_fatal, const std::string& detailed_message);
+    void set_error_handler(std::function<ErrorHandler>);
+    //@}
+
+    /// @{ \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 therefore 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.
+    ///
+    /// bind() binds this session to the specified server side Realm using the
+    /// parameters specified in the Session::Config object.
+    ///
+    /// The two other forms of bind() are convenience functions.
+    /// void bind(std::string server_address, std::string server_path,
+    ///           std::string signed_user_token, port_type server_port = 0,
+    ///           Protocol protocol = Protocol::realm);
+    /// replaces the corresponding parameters from the Session::Config object
+    /// before the session is bound.
+    /// void bind(std::string server_url, std::string signed_user_token) parses
+    /// the \param server_url and replaces the parameters in the Session::Config object
+    /// before the session is bound.
+    ///
+    /// \param server_url For example "realm://sync.realm.io/test". See
+    /// server_address, server_path, and server_port in Session::Config for information
+    /// about the individual components of the URL. See \ref Protocol for the list of
+    /// available URL schemes and the associated default ports.
+    ///
+    /// \throw BadServerUrl if the specified server URL is malformed.
+    void bind();
+    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 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 behalf, 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, download, or upload+download completion.
+    ///
+    /// async_wait_for_upload_completion() initiates an asynchronous wait for
+    /// upload to complete, async_wait_for_download_completion() initiates an
+    /// asynchronous wait for download to complete, and
+    /// async_wait_for_sync_completion() initiates an asynchronous wait for
+    /// upload and download to complete.
+    ///
+    /// Upload is considered complete when all non-empty changesets of local
+    /// origin have been uploaded to the server, and the server has acknowledged
+    /// reception of them. Changesets of local origin introduced after the
+    /// initiation of the session (after bind() is called) will generally not be
+    /// considered for upload unless they are announced to this client through
+    /// nonsync_transact_notify() prior to the initiation of the wait operation,
+    /// i.e., prior to the invocation of async_wait_for_upload_completion() or
+    /// async_wait_for_sync_completion(). Unannounced changesets may get picked
+    /// up, but there is no guarantee that they will be, however, if a certain
+    /// changeset is announced, then all previous changesets are implicitly
+    /// announced. Also all preexisting changesets are implicitly announced
+    /// when the session is initiated.
+    ///
+    /// Download is considered complete when all non-empty changesets of remote
+    /// origin have been downloaded from the server, and integrated into the
+    /// local Realm state. To know what is currently outstanding on the server,
+    /// the client always sends a special "marker" message to the server, and
+    /// waits until it has downloaded all outstanding changesets that were
+    /// present on the server at the time when the server received that marker
+    /// message. Each call to async_wait_for_download_completion() and
+    /// async_wait_for_sync_completion() therefore requires a full client <->
+    /// server round-trip.
+    ///
+    /// If a new wait operation is initiated while another wait operation is in
+    /// progress by another thread, the waiting period of first operation may,
+    /// or may not get extended. The application must not assume either. The
+    /// application may assume, however, that async_wait_for_upload_completion()
+    /// will not affect the waiting period of
+    /// async_wait_for_download_completion(), and vice versa.
+    ///
+    /// It is an error to call these functions before bind() has been called,
+    /// and has returned.
+    ///
+    /// The specified completion handlers will always be executed by the thread
+    /// that executes the event loop (the thread that calls Client::run()). If
+    /// the handler throws an exception, that exception will "travel" out
+    /// through Client::run().
+    ///
+    /// If incomplete wait operations exist when the session is terminated,
+    /// those wait operations will be canceled. Session termination is an event
+    /// that happens in the context of the client's event loop thread shortly
+    /// after the destruction of the session object. The std::error_code
+    /// argument passed to the completion handler of a canceled wait operation
+    /// will be `util::error::operation_aborted`. For uncanceled wait operations
+    /// it will be `std::error_code{}`. Note that as long as the client's event
+    /// loop thread is running, all completion handlers will be called
+    /// regardless of whether the operations get canceled or not.
+    ///
+    /// CAUTION: The specified completion handlers may get called before the
+    /// call to the waiting function returns, and it may get called (or continue
+    /// to execute) after the session object is destroyed. Please see "Callback
+    /// semantics" section under Session for more on this.
+    ///
+    /// Note: These functions are fully thread-safe. That is, they may be called
+    /// by any thread, and by multiple threads concurrently.
+    void async_wait_for_sync_completion(WaitOperCompletionHandler);
+    void async_wait_for_upload_completion(WaitOperCompletionHandler);
+    void async_wait_for_download_completion(WaitOperCompletionHandler);
+    /// @}
+
+    /// @{ \brief Synchronous wait for upload or download completion.
+    ///
+    /// These functions are synchronous equivalents to
+    /// async_wait_for_upload_completion() and
+    /// async_wait_for_download_completion() respectively. This means that they
+    /// block the caller until the completion condition is satisfied, or the
+    /// client's event loop thread exits from Client::run(), whichever happens
+    /// first.
+    ///
+    /// It is an error to call these functions before bind() has been called,
+    /// and has returned.
+    ///
+    /// CAUTION: If Client::run() returns while a wait operation is in progress,
+    /// these waiting functions return immediately, even if the completion
+    /// condition is not yet satisfied. The completion condition is guaranteed
+    /// to be satisfied only when these functions return true.
+    ///
+    /// \return True only if the completion condition was satisfied. False if
+    /// the client's event loop thread exited from Client::run() in which case
+    /// the completion condition may, or may not have been satisfied.
+    ///
+    /// Note: These functions are fully thread-safe. That is, they may be called
+    /// by any thread, and by multiple threads concurrently.
+    bool wait_for_upload_complete_or_client_stopped();
+    bool wait_for_download_complete_or_client_stopped();
+    /// @}
+
+    /// \brief Cancel the current or next reconnect delay for the server
+    /// associated with this session.
+    ///
+    /// When the network connection is severed, or an attempt to establish
+    /// connection fails, a certain delay will take effect before the client
+    /// will attempt to reestablish the connection. This delay will generally
+    /// grow with the number of unsuccessful reconnect attempts, and can grow to
+    /// over a minute. In some cases however, the application will know when it
+    /// is a good time to stop waiting and retry immediately. One example is
+    /// when a device has been offline for a while, and the operating system
+    /// then tells the application that network connectivity has been restored.
+    ///
+    /// Clearly, this function should not be called too often and over extended
+    /// periods of time, as that would effectively disable the built-in "server
+    /// hammering" protection.
+    ///
+    /// It is an error to call this function before bind() has been called, and
+    /// has returned.
+    ///
+    /// This function is fully thread-safe. That is, it may be called by any
+    /// thread, and by multiple threads concurrently.
+    void cancel_reconnect_delay();
+
+    /// \brief Change address of server for this session.
+    void override_server(std::string address, port_type);
+
+private:
+    class Impl;
+    Impl* m_impl = nullptr;
+
+    void abandon() noexcept;
+    void async_wait_for(bool upload_completion, bool download_completion,
+                        WaitOperCompletionHandler);
+};
+
+
+/// \brief Protocol errors discovered by the client.
+///
+/// These errors will terminate the network connection (disconnect all sessions
+/// associated with the affected connection), and the error will be reported to
+/// the application via the connection state change listeners of the affected
+/// sessions.
+enum class Client::Error {
+    connection_closed           = 100, ///< Connection closed (no error)
+    unknown_message             = 101, ///< Unknown type of input message
+    bad_syntax                  = 102, ///< Bad syntax in input message head
+    limits_exceeded             = 103, ///< Limits exceeded in input message
+    bad_session_ident           = 104, ///< Bad session identifier in input message
+    bad_message_order           = 105, ///< Bad input message order
+    bad_file_ident_pair         = 106, ///< Bad file identifier pair (ALLOC)
+    bad_progress                = 107, ///< Bad progress information (DOWNLOAD)
+    bad_changeset_header_syntax = 108, ///< Bad syntax in changeset header (DOWNLOAD)
+    bad_changeset_size          = 109, ///< Bad changeset size in changeset header (DOWNLOAD)
+    bad_origin_file_ident       = 110, ///< Bad origin file identifier in changeset header (DOWNLOAD)
+    bad_server_version          = 111, ///< Bad server version in changeset header (DOWNLOAD)
+    bad_changeset               = 112, ///< Bad changeset (DOWNLOAD)
+    bad_request_ident           = 113, ///< Bad request identifier (MARK)
+    bad_error_code              = 114, ///< Bad error code (ERROR),
+    bad_compression             = 115, ///< Bad compression (DOWNLOAD)
+    bad_client_version          = 116, ///< Bad last integrated client version in changeset header (DOWNLOAD)
+    ssl_server_cert_rejected    = 117, ///< SSL server certificate rejected
+    pong_timeout                = 118, ///< Timeout on reception of PONG respone message
+};
+
+const std::error_category& client_error_category() noexcept;
+
+std::error_code make_error_code(Client::Error) noexcept;
+
+} // namespace sync
+} // namespace realm
+
+namespace std {
+
+template<> struct is_error_code_enum<realm::sync::Client::Error> {
+    static const bool value = true;
+};
+
+} // namespace std
+
+namespace realm {
+namespace sync {
+
+
+
+// Implementation
+
+class BadServerUrl: public std::exception {
+public:
+    const char* what() const noexcept override
+    {
+        return "Bad server URL";
+    }
+};
+
+inline Session::Session(Session&& sess) noexcept:
+    m_impl{sess.m_impl}
+{
+    sess.m_impl = nullptr;
+}
+
+inline Session::Session() noexcept
+{
+}
+
+inline Session::~Session() noexcept
+{
+    if (m_impl)
+        abandon();
+}
+
+inline Session& Session::operator=(Session&& sess) noexcept
+{
+    if (m_impl)
+        abandon();
+    m_impl = sess.m_impl;
+    sess.m_impl = nullptr;
+    return *this;
+}
+
+inline void Session::detach() noexcept
+{
+    if (m_impl)
+        abandon();
+    m_impl = nullptr;
+}
+
+inline void Session::set_error_handler(std::function<ErrorHandler> handler)
+{
+    auto handler_2 = [handler=std::move(handler)](ConnectionState state,
+                                                  const ErrorInfo* error_info) {
+        if (state != ConnectionState::disconnected)
+            return;
+        REALM_ASSERT(error_info);
+        std::error_code ec = error_info->error_code;
+        bool is_fatal = error_info->is_fatal;
+        const std::string& detailed_message = error_info->detailed_message;
+        handler(ec, is_fatal, detailed_message); // Throws
+    };
+    set_connection_state_change_listener(std::move(handler_2)); // Throws
+}
+
+inline void Session::async_wait_for_sync_completion(WaitOperCompletionHandler handler)
+{
+    bool upload_completion = true, download_completion = true;
+    async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
+}
+
+inline void Session::async_wait_for_upload_completion(WaitOperCompletionHandler handler)
+{
+    bool upload_completion = true, download_completion = false;
+    async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
+}
+
+inline void Session::async_wait_for_download_completion(WaitOperCompletionHandler handler)
+{
+    bool upload_completion = false, download_completion = true;
+    async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
+}
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_CLIENT_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/crypto.hpp b/iOS/Pods/Realm/include/core/realm/sync/crypto.hpp
new file mode 100644 (file)
index 0000000..81a92fe
--- /dev/null
@@ -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 <memory>
+#include <stdexcept>
+
+#include <realm/binary_data.hpp>
+#include <realm/util/buffer.hpp>
+
+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/iOS/Pods/Realm/include/core/realm/sync/crypto_server.hpp b/iOS/Pods/Realm/include/core/realm/sync/crypto_server.hpp
new file mode 100644 (file)
index 0000000..c241a71
--- /dev/null
@@ -0,0 +1,91 @@
+/*************************************************************************
+ *
+ * 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 <memory>
+#include <stdexcept>
+
+#include <realm/binary_data.hpp>
+#include <realm/util/buffer.hpp>
+
+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 key from a PEM buffer
+    static PKey load_public(BinaryData pem_buffer);
+
+    /// Load RSA public/private keypair from \a pemfile.
+    static PKey load_private(const std::string& pemfile);
+    /// Load RSA public/private keypair from a PEM buffer
+    static PKey load_private(BinaryData pem_buffer);
+
+    /// 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<unsigned char>& 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<Impl> m_impl;
+};
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_CRYPTO_SERVER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/feature_token.hpp b/iOS/Pods/Realm/include/core/realm/sync/feature_token.hpp
new file mode 100644 (file)
index 0000000..86db015
--- /dev/null
@@ -0,0 +1,69 @@
+/*************************************************************************
+ *
+ * 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_FEATURE_TOKEN_HPP
+#define REALM_SYNC_FEATURE_TOKEN_HPP
+
+#include <realm/util/features.h>
+
+#if !REALM_MOBILE && !defined(REALM_EXCLUDE_FEATURE_TOKENS)
+#define REALM_HAVE_FEATURE_TOKENS 1
+#else
+#define REALM_HAVE_FEATURE_TOKENS 0
+#endif
+
+#if REALM_HAVE_FEATURE_TOKENS
+
+#include <memory>
+
+#include <realm/string_data.hpp>
+
+namespace realm {
+namespace sync {
+
+class FeatureGate {
+public:
+
+    // The constructor takes a JWT token as argument.
+    // The constructor throws a std::runtime_error if
+    // the token is invalid. An invalid token is a token
+    // that has bad syntax, is not signed by Realm, or is 
+    // expired.
+    FeatureGate(StringData token);
+
+    // Constructs a feature gate without any features.
+    FeatureGate();
+    ~FeatureGate();
+
+    FeatureGate(FeatureGate&&);
+    FeatureGate& operator=(FeatureGate&&);
+
+    bool has_feature(StringData feature_name);
+
+private:
+    struct Impl;
+    std::unique_ptr<Impl> m_impl;
+};
+
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_HAVE_FEATURE_TOKENS
+#endif // REALM_SYNC_FEATURE_TOKEN_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/history.hpp b/iOS/Pods/Realm/include/core/realm/sync/history.hpp
new file mode 100644 (file)
index 0000000..d9e15d0
--- /dev/null
@@ -0,0 +1,386 @@
+/*************************************************************************
+ *
+ * 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 <memory>
+#include <string>
+
+#include <realm/impl/cont_transact_hist.hpp>
+#include <realm/sync/instruction_replication.hpp>
+#include <realm/sync/transform.hpp>
+
+#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;
+    std::int_fast64_t latest_server_session_ident = 0;
+    version_type latest_client_version = 0;
+    int_fast64_t downloadable_bytes = 0;
+};
+
+
+class ClientHistoryBase :
+        public InstructionReplication {
+public:
+    using version_type    = TrivialReplication::version_type;
+    using file_ident_type = HistoryEntry::file_ident_type;
+    using salt_type       = std::int_fast64_t;
+    using SyncTransactCallback = void(VersionID old_version, VersionID new_version);
+
+    /// 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,
+                            salt_type& client_file_ident_salt,
+                            SyncProgress& progress) const = 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 purpose 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,
+                                     salt_type client_file_ident_salt) = 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 `progress.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 preceding
+    /// `end_version` is to be considered, which is again the empty range if
+    /// `end_version` is also zero. Zero is a special value in that no changeset
+    /// produces that version. It is an error if `end_version` precedes
+    /// `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<char[]>& buffer) const = 0;
+*/
+
+
+    struct UploadChangeset {
+        HistoryEntry::timestamp_type timestamp;
+        version_type client_version;
+        version_type server_version;
+        ChunkedBinaryData changeset;
+        std::unique_ptr<char[]> buffer;
+    };
+
+    /// find_uploadable_changesets() returns a vector of history entries. The
+    /// history entries are returned in order and starts from the first available
+    /// entry. The number of entries returned is at least one, if possible, and
+    /// is size limited by a soft limit. Returned changesets produced a version
+    /// that succeeds `begin_version` and, and does not succeed `end_version`.
+    /// Returned changesets are also locally produced and non-empty.
+    virtual std::vector<UploadChangeset> find_uploadable_changesets(version_type begin_version,
+                                                                    version_type end_version) const = 0;
+
+    using RemoteChangeset = Transformer::RemoteChangeset;
+
+    // FIXME: Apparently, this feature is expected by object store, but why?
+    // What is it ultimately used for? (@tgoyne)
+    class SyncTransactReporter {
+    public:
+        virtual void report_sync_transact(VersionID old_version, VersionID new_version) = 0;
+    protected:
+        ~SyncTransactReporter() {}
+    };
+
+    /// \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,
+                                                     std::size_t num_changesets,
+                                                     util::Logger* replay_logger,
+                                                     SyncTransactReporter* = nullptr) = 0;
+
+protected:
+    ClientHistoryBase(const std::string& realm_path);
+};
+
+
+
+class ClientHistory : public ClientHistoryBase {
+public:
+    class ChangesetCooker;
+    class Config;
+
+    /// Get the persisted upload/download progress in bytes.
+    virtual void get_upload_download_bytes(uint_fast64_t& downloaded_bytes,
+                                           uint_fast64_t& downloadable_bytes,
+                                           uint_fast64_t& uploaded_bytes,
+                                           uint_fast64_t& uploadable_bytes,
+                                           uint_fast64_t& snapshot_version) = 0;
+
+    /// See set_cooked_progress().
+    struct CookedProgress {
+        std::int_fast64_t changeset_index = 0;
+        std::int_fast64_t intrachangeset_progress = 0;
+    };
+
+    /// Returns the persisted progress that was last stored by
+    /// set_cooked_progress().
+    ///
+    /// Initially, until explicitly modified, both
+    /// `CookedProgress::changeset_index` and
+    /// `CookedProgress::intrachangeset_progress` are zero.
+    virtual CookedProgress get_cooked_progress() const = 0;
+
+    /// Persistently stores the point of progress of the consumer of cooked
+    /// changesets.
+    ///
+    /// As well as allowing for later retrieval, the specification of the point
+    /// of progress of the consumer of cooked changesets also has the effect of
+    /// trimming obsolete cooked changesets from the Realm file. Indeed, if this
+    /// function is never called, but cooked changesets are continually being
+    /// produced, then the Realm file will grow without bounds.
+    ///
+    /// Behavior is undefined if the specified index
+    /// (CookedProgress::changeset_index) is lower than the index returned by
+    /// get_cooked_progress().
+    ///
+    /// The intrachangeset progress field
+    /// (CookedProgress::intrachangeset_progress) will be faithfully persisted,
+    /// but will otherwise be treated as an opaque object by the history
+    /// internals.
+    virtual void set_cooked_progress(CookedProgress) = 0;
+
+    /// Get the number of cooked changesets so far produced for this Realm. This
+    /// is the number of cooked changesets that are currently in the Realm file
+    /// plus the number of cooked changesets that have been trimmed off so far.
+    virtual std::int_fast64_t get_num_cooked_changesets() const = 0;
+
+    /// Fetch the cooked changeset at the specified index.
+    ///
+    /// Cooked changesets are made available in the order they are produced by
+    /// the changeset cooker (ChangesetCooker).
+    ///
+    /// Behaviour is undefined if the specified index is less than the index
+    /// (CookedProgress::changeset_index) returned by get_cooked_progress(), or
+    /// if it is greater than, or equal to the toal number of cooked changesets
+    /// (as returned by get_num_cooked_changesets()).
+    ///
+    /// The callee must append the bytes of the located cooked changeset to the
+    /// specified buffer, which does not have to be empty initially.
+    virtual void get_cooked_changeset(std::int_fast64_t index,
+                                      util::AppendBuffer<char>&) const = 0;
+
+protected:
+    ClientHistory(const std::string& realm_path);
+};
+
+
+/// \brief Abstract interface for changeset cookers.
+///
+/// Note, it is completely up to the application to decide what a cooked
+/// changeset is. History objects (instances of ClientHistory) are required to
+/// treat cooked changesets as opaque entities. For an example of a concrete
+/// changeset cooker, see TrivialChangesetCooker which defines the cooked
+/// changesets to be identical copies of the raw changesets.
+class ClientHistory::ChangesetCooker {
+public:
+    /// \brief An opportunity to produce a cooked changeset.
+    ///
+    /// When the implementation chooses to produce a cooked changeset, it must
+    /// write the cooked changeset to the specified buffer, and return
+    /// true. When the implementation chooses not to produce a cooked changeset,
+    /// it must return false. The implementation is allowed to write to the
+    /// buffer, and return false, and in that case, the written data will be
+    /// ignored.
+    ///
+    /// \param prior_state The state of the local Realm on which the specified
+    /// raw changeset is based.
+    ///
+    /// \param changeset, changeset_size The raw changeset.
+    ///
+    /// \param buffer The buffer to which the cooked changeset must be written.
+    ///
+    /// \return True if a cooked changeset was produced. Otherwise false.
+    virtual bool cook_changeset(const Group& prior_state,
+                                const char* changeset, std::size_t changeset_size,
+                                util::AppendBuffer<char>& buffer) = 0;
+};
+
+
+class ClientHistory::Config {
+public:
+    Config() {}
+
+    /// Must be set to true if, and only if the created history object
+    /// represents (is owned by) the sync agent of the specified Realm file. At
+    /// most one such instance is allowed to participate in a Realm file access
+    /// session at any point in time. Ordinarily the sync agent is encapsulated
+    /// by the sync::Client class, and the history instance representing the
+    /// agent is created transparently by sync::Client (one history instance per
+    /// sync::Session object).
+    bool owner_is_sync_agent = false;
+
+    /// If a changeset cooker is specified, then the created history object will
+    /// allow for a cooked changeset to be produced for each changeset of remote
+    /// origin; that is, for each changeset that is integrated during the
+    /// execution of ClientHistory::integrate_remote_changesets(). If no
+    /// changeset cooker is specified, then no cooked changesets will be
+    /// produced on behalf of the created history object.
+    ///
+    /// ClientHistory::integrate_remote_changesets() will pass each incoming
+    /// changeset to the cooker after operational transformation; that is, when
+    /// the chageset is ready to be applied to the local Realm state.
+    std::shared_ptr<ChangesetCooker> changeset_cooker;
+};
+
+/// \brief Create a "sync history" implementation of the realm::Replication
+/// interface.
+///
+/// The intended role for such an object is as a plugin for new
+/// realm::SharedGroup objects.
+std::unique_ptr<ClientHistory> make_client_history(const std::string& realm_path,
+                                                   ClientHistory::Config = {});
+
+
+
+// Implementation
+
+inline ClientHistoryBase::ClientHistoryBase(const std::string& realm_path):
+    InstructionReplication{realm_path} // Throws
+{
+}
+
+inline ClientHistory::ClientHistory(const std::string& realm_path):
+    ClientHistoryBase{realm_path} // Throws
+{
+}
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_HISTORY_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/instruction_applier.hpp b/iOS/Pods/Realm/include/core/realm/sync/instruction_applier.hpp
new file mode 100644 (file)
index 0000000..c722a9a
--- /dev/null
@@ -0,0 +1,103 @@
+/*************************************************************************
+ *
+ * Copyright 2017 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_SYNC_IMPL_INSTRUCTION_APPLIER_HPP
+#define REALM_SYNC_IMPL_INSTRUCTION_APPLIER_HPP
+
+#include <realm/sync/instructions.hpp>
+#include <realm/sync/changeset.hpp>
+#include <realm/util/logger.hpp>
+
+namespace realm {
+namespace sync {
+
+struct Changeset;
+
+struct InstructionApplier {
+    explicit InstructionApplier(Group& group) noexcept;
+
+    void apply(const Changeset& log, util::Logger* logger);
+
+protected:
+    StringData get_string(InternString) const;
+    StringData get_string(StringBufferRange) const;
+#define REALM_DECLARE_INSTRUCTION_HANDLER(X) void operator()(const Instruction::X&);
+    REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DECLARE_INSTRUCTION_HANDLER)
+#undef REALM_DECLARE_INSTRUCTION_HANDLER
+    friend struct Instruction; // to allow visitor
+
+    template<class A> static void apply(A& applier, const Changeset& log, util::Logger* logger);
+
+private:
+    const Changeset* m_log = nullptr;
+    util::Logger* m_logger = nullptr;
+    Group& m_group;
+    TableRef m_selected_table;
+    TableRef m_selected_array;
+    LinkViewRef m_selected_link_list;
+    TableRef m_link_target_table;
+
+    template <class... Args>
+    void log(const char* fmt, Args&&... args)
+    {
+        if (m_logger) {
+            m_logger->trace(fmt, std::forward<Args>(args)...); // Throws
+        }
+    }
+
+    void bad_transaction_log(const char*) const; // Throws
+
+    TableRef table_for_class_name(StringData) const; // Throws
+};
+
+
+
+
+// Implementation
+
+inline InstructionApplier::InstructionApplier(Group& group) noexcept:
+    m_group(group)
+{
+}
+
+template<class A>
+inline void InstructionApplier::apply(A& applier, const Changeset& log, util::Logger* logger)
+{
+    applier.m_log = &log;
+    applier.m_logger = logger;
+    for (auto instr: log) {
+        if (!instr)
+            continue;
+        instr->visit(applier); // Throws
+    }
+    applier.m_log = nullptr;
+    applier.m_logger = nullptr;
+    applier.m_selected_table = TableRef{};
+    applier.m_selected_link_list = LinkViewRef{};
+    applier.m_link_target_table = TableRef{};
+}
+
+inline void InstructionApplier::apply(const Changeset& log, util::Logger* logger)
+{
+    apply(*this, log, logger); // Throws
+}
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_IMPL_INSTRUCTION_APPLIER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/instruction_replication.hpp b/iOS/Pods/Realm/include/core/realm/sync/instruction_replication.hpp
new file mode 100644 (file)
index 0000000..c7f1223
--- /dev/null
@@ -0,0 +1,199 @@
+/*************************************************************************
+ *
+ * Copyright 2017 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_SYNC_IMPL_INSTRUCTION_REPLICATION_HPP
+#define REALM_SYNC_IMPL_INSTRUCTION_REPLICATION_HPP
+
+#include <realm/replication.hpp>
+#include <realm/sync/instructions.hpp>
+#include <realm/sync/object.hpp>
+#include <realm/sync/changeset_encoder.hpp>
+
+namespace realm {
+namespace sync {
+
+
+class InstructionReplication: public TrivialReplication, public ObjectIDProvider {
+public:
+    explicit InstructionReplication(const std::string& realm_path);
+    void set_short_circuit(bool) noexcept;
+    bool is_short_circuited() const noexcept;
+
+    // reset() resets the encoder, the selected tables and the cache. It is
+    // called by do_initiate_transact(), but can be called at the other times
+    // as well.
+    virtual void reset();
+
+    ChangesetEncoder& get_instruction_encoder() noexcept;
+
+    //@{
+    /// Generate instructions for Object Store tables. These must be called
+    /// prior to calling the equivalent functions in Core's API. When creating a
+    /// class-like table, `add_class()` must be called prior to
+    /// `Group::insert_group_level_table()`. Similarly, `create_object()` or
+    /// `create_object_with_primary_key()` must be called prior to
+    /// `Table::insert_empty_row()` and/or `Table::set_int_unique()` or
+    /// `Table::set_string_unique()` or `Table::set_null_unique()`.
+    ///
+    /// If a class-like table is added, or an object-like row is inserted,
+    /// without calling these methods first, an exception will be thrown.
+    ///
+    /// A "class-like table" is defined as a table whose name begins with
+    /// "class_" (this is the convention used by Object Store). Non-class-like
+    /// tables can be created and modified using Core's API without calling
+    /// these functions, because they do not result in instructions being
+    /// emitted.
+    void add_class(StringData table_name);
+    void add_class_with_primary_key(StringData table_name, DataType pk_type, StringData pk_field, bool nullable);
+    void create_object(const Table*, ObjectID);
+    void create_object_with_primary_key(const Table*, ObjectID, StringData);
+    void create_object_with_primary_key(const Table*, ObjectID, int_fast64_t);
+    void create_object_with_primary_key(const Table*, ObjectID, realm::util::None);
+    //@}
+
+    // TrivialReplication interface:
+    void initialize(SharedGroup&) override;
+
+    // TransactLogConvenientEncoder interface:
+    void insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name) override;
+    void erase_group_level_table(size_t table_ndx, size_t num_tables) override;
+    void rename_group_level_table(size_t table_ndx, StringData new_name) override;
+    void insert_column(const Descriptor&, size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link,
+                               bool nullable = false) override;
+    void erase_column(const Descriptor&, size_t col_ndx) override;
+    void rename_column(const Descriptor&, size_t col_ndx, StringData name) override;
+
+    void set_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value, _impl::Instruction variant) override;
+    void add_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value) override;
+    void set_bool(const Table*, size_t col_ndx, size_t ndx, bool value, _impl::Instruction variant) override;
+    void set_float(const Table*, size_t col_ndx, size_t ndx, float value, _impl::Instruction variant) override;
+    void set_double(const Table*, size_t col_ndx, size_t ndx, double value, _impl::Instruction variant) override;
+    void set_string(const Table*, size_t col_ndx, size_t ndx, StringData value, _impl::Instruction variant) override;
+    void set_binary(const Table*, size_t col_ndx, size_t ndx, BinaryData value, _impl::Instruction variant) override;
+    void set_olddatetime(const Table*, size_t col_ndx, size_t ndx, OldDateTime value,
+                                 _impl::Instruction variant) override;
+    void set_timestamp(const Table*, size_t col_ndx, size_t ndx, Timestamp value, _impl::Instruction variant) override;
+    void set_table(const Table*, size_t col_ndx, size_t ndx, _impl::Instruction variant) override;
+    void set_mixed(const Table*, size_t col_ndx, size_t ndx, const Mixed& value, _impl::Instruction variant) override;
+    void set_link(const Table*, size_t col_ndx, size_t ndx, size_t value, _impl::Instruction variant) override;
+    void set_null(const Table*, size_t col_ndx, size_t ndx, _impl::Instruction variant) override;
+    void set_link_list(const LinkView&, const IntegerColumn& values) override;
+    void insert_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, StringData) override;
+    void erase_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, size_t size) override;
+    void insert_empty_rows(const Table*, size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows) override;
+    void add_row_with_key(const Table*, size_t row_ndx, size_t prior_num_rows, size_t key_col_ndx, int64_t key) override;
+    void erase_rows(const Table*, size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rowsp,
+                            bool is_move_last_over) override;
+    void swap_rows(const Table*, size_t row_ndx_1, size_t row_ndx_2) override;
+    void move_row(const Table*, size_t row_ndx_1, size_t row_ndx_2) override;
+    void merge_rows(const Table*, size_t row_ndx, size_t new_row_ndx) override;
+    void add_search_index(const Descriptor&, size_t col_ndx) override;
+    void remove_search_index(const Descriptor&, size_t col_ndx) override;
+    void set_link_type(const Table*, size_t col_ndx, LinkType) override;
+    void clear_table(const Table*, size_t prior_num_rows) override;
+    void optimize_table(const Table*) override;
+    void link_list_set(const LinkView&, size_t ndx, size_t value) override;
+    void link_list_insert(const LinkView&, size_t ndx, size_t value) override;
+    void link_list_move(const LinkView&, size_t from_ndx, size_t to_ndx) override;
+    void link_list_swap(const LinkView&, size_t ndx_1, size_t ndx_2) override;
+    void link_list_erase(const LinkView&, size_t ndx) override;
+    void link_list_clear(const LinkView&) override;
+    void nullify_link(const Table*, size_t col_ndx, size_t ndx) override;
+    void link_list_nullify(const LinkView&, size_t ndx) override;
+
+protected:
+    // Replication interface:
+    void do_initiate_transact(version_type current_version, bool history_updated) override;
+private:
+    bool m_short_circuit = false;
+
+    ChangesetEncoder m_encoder;
+    SharedGroup* m_sg = nullptr;
+    std::unique_ptr<TableInfoCache> m_cache;
+
+    enum class TableBehavior {
+        Class,
+        Array,
+        Ignore
+    };
+
+    // FIXME: The base class already caches this.
+    const Table* m_selected_table = nullptr;
+    TableBehavior m_selected_table_behavior; // cache
+    const LinkView* m_selected_link_list = nullptr;
+
+    // Consistency checks:
+    std::string m_table_being_created;
+    std::string m_table_being_created_primary_key;
+    util::Optional<ObjectID> m_object_being_created;
+
+    void unsupported_instruction(); // Throws TransformError
+    TableBehavior select_table(const Table*);
+    TableBehavior select_table(const Descriptor&);
+    bool select_link_list(const LinkView&); // returns true if table behavior != ignored
+
+    TableBehavior get_table_behavior(const Table*) const;
+
+    template <class T>
+    void set(const Table*, size_t row_ndx, size_t col_ndx, T payload,
+             _impl::Instruction variant);
+    template <class T>
+    void set_pk(const Table*, size_t row_ndx, size_t col_ndx, T payload,
+                _impl::Instruction variant);
+    template <class T>
+    auto as_payload(T value);
+    template <class T>
+    void emit(T instruction);
+};
+
+inline void InstructionReplication::set_short_circuit(bool b) noexcept
+{
+    m_short_circuit = b;
+}
+
+inline bool InstructionReplication::is_short_circuited() const noexcept
+{
+    return m_short_circuit;
+}
+
+inline ChangesetEncoder& InstructionReplication::get_instruction_encoder() noexcept
+{
+    return m_encoder;
+}
+
+// Temporarily short-circuit replication
+class TempShortCircuitReplication {
+public:
+    TempShortCircuitReplication(InstructionReplication& bridge): m_bridge(bridge)
+    {
+        m_was_short_circuited = bridge.is_short_circuited();
+        bridge.set_short_circuit(true);
+    }
+
+    ~TempShortCircuitReplication() {
+        m_bridge.set_short_circuit(m_was_short_circuited);
+    }
+private:
+    InstructionReplication& m_bridge;
+    bool m_was_short_circuited;
+};
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_IMPL_INSTRUCTION_REPLICATION_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/instructions.hpp b/iOS/Pods/Realm/include/core/realm/sync/instructions.hpp
new file mode 100644 (file)
index 0000000..6b60891
--- /dev/null
@@ -0,0 +1,390 @@
+/*************************************************************************
+ *
+ * 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 <vector>
+#include <unordered_map>
+#include <iosfwd> // string conversion, debug prints
+#include <memory> // shared_ptr
+#include <type_traits>
+
+#include <realm/util/string_buffer.hpp>
+#include <realm/string_data.hpp>
+#include <realm/binary_data.hpp>
+#include <realm/data_type.hpp>
+#include <realm/timestamp.hpp>
+#include <realm/sync/object_id.hpp>
+#include <realm/impl/input_stream.hpp>
+#include <realm/table_ref.hpp>
+#include <realm/link_view_fwd.hpp>
+
+namespace realm {
+namespace sync {
+
+// CAUTION: Any change to the order or number of instructions is a
+// protocol-breaking change!
+#define REALM_FOR_EACH_INSTRUCTION_TYPE(X) \
+    X(SelectTable) \
+    X(SelectContainer) \
+    X(AddTable) \
+    X(EraseTable) \
+    X(CreateObject) \
+    X(EraseObject) \
+    X(Set) \
+    X(AddInteger) \
+    X(InsertSubstring) \
+    X(EraseSubstring) \
+    X(ClearTable) \
+    X(AddColumn) \
+    X(EraseColumn) \
+    X(ContainerSet) \
+    X(ContainerInsert) \
+    X(ContainerMove) \
+    X(ContainerSwap) \
+    X(ContainerErase) \
+    X(ContainerClear) \
+
+enum class ContainerType { none=0, links=1, array=2, dict=3 };
+
+struct Instruction {
+    // Base classes for instructions with common fields. They enable the merge
+    // algorithm to reuse some code without resorting to templates, and can be
+    // combined to allow optimal memory layout of instructions (size <= 64).
+    struct PayloadInstructionBase;
+    struct ObjectInstructionBase;
+    struct FieldInstructionBase;
+
+#define REALM_DECLARE_INSTRUCTION_STRUCT(X) struct X;
+    REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DECLARE_INSTRUCTION_STRUCT)
+#undef REALM_DECLARE_INSTRUCTION_STRUCT
+
+    enum class Type: uint8_t {
+#define REALM_DEFINE_INSTRUCTION_TYPE(X) X,
+    REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_TYPE)
+#undef REALM_DEFINE_INSTRUCTION_TYPE
+    };
+
+    struct Payload;
+    template <Type t> struct GetType;
+    template <class T> struct GetInstructionType;
+
+    Instruction() {}
+    template <class T>
+    Instruction(T instr);
+
+    static const size_t max_instruction_size = 63;
+    std::aligned_storage_t<max_instruction_size, 8> m_storage;
+    Type type;
+
+    template <class F>
+    void visit(F&& lambda);
+    template <class F>
+    void visit(F&& lambda) const;
+
+    template <class T> T& get_as()
+    {
+        REALM_ASSERT(type == GetInstructionType<T>::value);
+        return *reinterpret_cast<T*>(&m_storage);
+    }
+
+    template <class T>
+    const T& get_as() const
+    {
+        return const_cast<Instruction*>(this)->template get_as<T>();
+    }
+};
+
+// 0x3f is the largest value that fits in a single byte in the variable-length
+// encoded integer instruction format.
+static constexpr uint8_t InstrTypeInternString = 0x3f;
+
+// This instruction code is only ever used internally by the Changeset class
+// to allow insertion/removal while keeping iterators stable. Should never
+// make it onto the wire.
+static constexpr uint8_t InstrTypeMultiInstruction = 0xff;
+
+struct StringBufferRange {
+    uint32_t offset, size;
+
+    bool operator==(const StringBufferRange&) = delete;
+    bool operator!=(const StringBufferRange&) = delete;
+};
+
+struct InternString {
+    static const InternString npos;
+    explicit constexpr InternString(uint32_t v = uint32_t(-1)): value(v) {}
+
+    uint32_t value;
+
+    // Disabling comparison for safety, because it is usually not what you want.
+    bool operator==(const InternString&) = delete;
+    bool operator!=(const InternString&) = delete;
+};
+
+struct Instruction::Payload {
+    struct Link {
+        sync::ObjectID target; // can be nothing = null
+        InternString target_table;
+    };
+
+    union Data {
+        bool boolean;
+        int64_t integer;
+        float fnum;
+        double dnum;
+        StringBufferRange str;
+        Timestamp timestamp;
+        Link link;
+
+        Data() noexcept {}
+        Data(const Data&) noexcept = default;
+        Data& operator=(const Data&) noexcept = default;
+    };
+    Data data;
+    int8_t type; // -1 = null, -2 = implicit_nullify
+
+    Payload(): Payload(realm::util::none) {}
+    explicit Payload(bool value)      noexcept: type(type_Bool) { data.boolean = value; }
+    explicit Payload(int64_t value)   noexcept: type(type_Int) { data.integer = value; }
+    explicit Payload(float value)     noexcept: type(type_Float) { data.fnum = value; }
+    explicit Payload(double value)    noexcept: type(type_Double) { data.dnum = value; }
+    explicit Payload(Timestamp value) noexcept: type(type_Timestamp) { data.timestamp = value; }
+    explicit Payload(Link value)      noexcept: type(type_Link) { data.link = value; }
+    explicit Payload(StringBufferRange value) noexcept: type(type_String) { data.str = value; }
+    explicit Payload(realm::util::None, bool implicit_null = false) noexcept {
+        type = (implicit_null ? -2 : -1);
+    }
+
+    Payload(const Payload&) noexcept = default;
+    Payload& operator=(const Payload&) noexcept = default;
+
+    bool is_null() const;
+    bool is_implicit_null() const;
+};
+
+struct Instruction::ObjectInstructionBase {
+    sync::ObjectID object;
+};
+
+struct Instruction::FieldInstructionBase
+    : Instruction::ObjectInstructionBase
+{
+    InternString field;
+};
+
+struct Instruction::PayloadInstructionBase {
+    Payload payload;
+};
+
+
+
+struct Instruction::SelectTable {
+    InternString table;
+};
+
+struct Instruction::SelectContainer
+    : Instruction::FieldInstructionBase
+{
+    InternString link_target_table;
+};
+
+struct Instruction::AddTable {
+    InternString table;
+    InternString primary_key_field;
+    DataType primary_key_type;
+    bool has_primary_key;
+    bool primary_key_nullable;
+};
+
+struct Instruction::EraseTable {
+    InternString table;
+};
+
+struct Instruction::CreateObject
+    : Instruction::PayloadInstructionBase
+    , Instruction::ObjectInstructionBase
+{
+    bool has_primary_key;
+};
+
+struct Instruction::EraseObject
+    : Instruction::ObjectInstructionBase
+{};
+
+struct Instruction::Set
+    : Instruction::PayloadInstructionBase
+    , Instruction::FieldInstructionBase
+{
+    bool is_default;
+};
+
+struct Instruction::AddInteger
+    : Instruction::FieldInstructionBase
+{
+    int64_t value;
+};
+
+struct Instruction::InsertSubstring
+    : Instruction::FieldInstructionBase
+{
+    StringBufferRange value;
+    uint32_t pos;
+};
+
+struct Instruction::EraseSubstring
+    : Instruction::FieldInstructionBase
+{
+    uint32_t pos;
+    uint32_t size;
+};
+
+struct Instruction::ClearTable {
+};
+
+struct Instruction::ContainerSet {
+    Instruction::Payload payload;
+    uint32_t ndx;
+    uint32_t prior_size;
+};
+
+struct Instruction::ContainerInsert {
+    // payload carries the value in case of LinkList
+    // payload is empty in case of Array, Dict or any other container type
+    Instruction::Payload payload;
+    uint32_t ndx;
+    uint32_t prior_size;
+};
+
+struct Instruction::ContainerMove {
+    uint32_t ndx_1;
+    uint32_t ndx_2;
+};
+
+struct Instruction::ContainerErase {
+    uint32_t ndx;
+    uint32_t prior_size;
+    bool implicit_nullify;
+};
+
+struct Instruction::ContainerSwap {
+    uint32_t ndx_1;
+    uint32_t ndx_2;
+};
+
+struct Instruction::ContainerClear {
+    uint32_t prior_size;
+};
+
+// If container_type != ContainerType::none, creates a subtable:
+// +---+---+-------+
+// | a | b |   c   |
+// +---+---+-------+
+// |   |   | +---+ |
+// |   |   | | v | |
+// |   |   | +---+ |
+// | 1 | 2 | | 3 | |
+// |   |   | | 4 | |
+// |   |   | | 5 | |
+// |   |   | +---+ |
+// +---+---+-------+
+struct Instruction::AddColumn {
+    InternString field;
+    InternString link_target_table;
+    DataType type;
+    ContainerType container_type;
+    bool nullable;
+};
+
+struct Instruction::EraseColumn {
+    InternString field;
+};
+
+struct InstructionHandler {
+    /// Notify the handler that an InternString meta-instruction was found.
+    virtual void set_intern_string(uint32_t index, StringBufferRange) = 0;
+
+    /// Notify the handler of the string value. The handler guarantees that the
+    /// returned string range is valid at least until the next invocation of
+    /// add_string_range().
+    ///
+    /// Instances of `StringBufferRange` passed to operator() after invoking
+    /// this function are assumed to refer to ranges in this buffer.
+    virtual StringBufferRange add_string_range(StringData) = 0;
+
+    /// Handle an instruction.
+    virtual void operator()(const Instruction&) = 0;
+};
+
+
+/// Implementation:
+
+#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 // GCC 4.x does not support std::is_trivially_copyable
+#define REALM_CHECK_TRIVIALLY_COPYABLE(X) static_assert(std::is_trivially_copyable<Instruction::X>::value, #X" Instructions must be trivially copyable.");
+    REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_CHECK_TRIVIALLY_COPYABLE)
+#undef REALM_CHECK_TRIVIALLY_COPYABLE
+#endif // __GNUC__
+
+#ifdef _WIN32 // FIXME: Fails in VS. 
+#define REALM_CHECK_INSTRUCTION_SIZE(X)
+#else
+#define REALM_CHECK_INSTRUCTION_SIZE(X) static_assert(sizeof(Instruction::X) <= Instruction::max_instruction_size, #X" Instruction too big.");
+    REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_CHECK_INSTRUCTION_SIZE)
+#undef REALM_CHECK_INSTRUCTION_SIZE
+#endif
+
+#define REALM_DEFINE_INSTRUCTION_GET_TYPE(X) \
+    template <> struct Instruction::GetType<Instruction::Type::X> { using Type = Instruction::X; }; \
+    template <> struct Instruction::GetInstructionType<Instruction::X> { static const Instruction::Type value = Instruction::Type::X; };
+    REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_GET_TYPE)
+#undef REALM_DEFINE_INSTRUCTION_GET_TYPE
+
+
+template <class T>
+Instruction::Instruction(T instr): type(GetInstructionType<T>::value)
+{
+    new(&m_storage) T(std::move(instr));
+}
+
+template <class F>
+void Instruction::visit(F&& lambda)
+{
+    switch (type) {
+#define REALM_VISIT_INSTRUCTION(X) \
+        case Type::X: return lambda(get_as<Instruction::X>());
+        REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_VISIT_INSTRUCTION)
+#undef REALM_VISIT_INSTRUCTION
+    }
+    REALM_UNREACHABLE();
+}
+
+template <class F>
+void Instruction::visit(F&& lambda) const
+{
+    const_cast<Instruction*>(this)->visit(std::forward<F>(lambda));
+}
+
+std::ostream& operator<<(std::ostream&, Instruction::Type);
+
+} // namespace _impl
+} // namespace realm
+
+#endif // REALM_IMPL_INSTRUCTIONS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/metrics.hpp b/iOS/Pods/Realm/include/core/realm/sync/metrics.hpp
new file mode 100644 (file)
index 0000000..96beea2
--- /dev/null
@@ -0,0 +1,105 @@
+/*************************************************************************
+ *
+ * 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 <dogless.hpp>
+#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, int value = 1) = 0;
+
+    /// Send the timing identified by the specified key.
+    virtual void timing(const char* key, double value) = 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;
+
+    /// Allow the dogless library to send each metric to multiple endpoints, as
+    /// required
+    virtual void add_endpoint(const std::string& endpoint) = 0;
+
+    virtual ~Metrics() {}
+};
+
+#if REALM_HAVE_DOGLESS
+
+class DoglessMetrics: public sync::Metrics {
+public:
+    DoglessMetrics():
+        m_dogless(dogless::hostname_prefix("realm")) // Throws
+    {
+        m_dogless.loop_interval(1);
+    }
+
+    void increment(const char* key, int value = 1) override
+    {
+        const char* metric = key;
+        m_dogless.increment(metric, value); // Throws
+    }
+
+    void timing(const char* key, double value) override
+    {
+        const char* metric = key;
+        m_dogless.timing(metric, value); // Throws
+    }
+
+    void gauge(const char* key, double value) override
+    {
+        const char* metric = key;
+        m_gauges[key] = value;
+        m_dogless.gauge(metric, m_gauges[key]); // Throws
+    }
+
+    void gauge_relative(const char* key, double value) override
+    {
+        const char* metric = key;
+        m_gauges[key] += value;
+        m_dogless.gauge(metric, m_gauges[key]); // Throws
+    }
+
+    void add_endpoint(const std::string& endpoint) override
+    {
+        m_dogless.add_endpoint(endpoint);
+    }
+
+private:
+    dogless::BufferedStatsd m_dogless;
+    std::map<std::string, double> m_gauges;
+};
+
+#endif
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_METRICS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/object.hpp b/iOS/Pods/Realm/include/core/realm/sync/object.hpp
new file mode 100644 (file)
index 0000000..243ee24
--- /dev/null
@@ -0,0 +1,212 @@
+/*************************************************************************
+ *
+ * Copyright 2017 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_SYNC_OBJECT_HPP
+#define REALM_SYNC_OBJECT_HPP
+
+#include <realm/util/logger.hpp>
+#include <realm/table_ref.hpp>
+#include <realm/string_data.hpp>
+
+#include <realm/sync/object_id.hpp>
+
+#include <vector>
+
+/// This file presents a convenience API for making changes to a Realm file that
+/// adhere to the conventions of assigning stable IDs to every object.
+
+namespace realm {
+
+class Group;
+
+namespace sync {
+
+class SyncHistory;
+
+static const char object_id_column_name[] = "!OID";
+static const char array_value_column_name[] = "!ARRAY_VALUE"; // FIXME call Jorgen
+
+struct TableInfoCache;
+
+/// Determine whether the Group has a sync-type history, and therefore whether
+/// it supports globally stable object IDs.
+///
+/// The Group does not need to be in a transaction.
+bool has_object_ids(const Group&);
+
+/// Determine whether object IDs for objects without primary keys are globally
+/// stable. This is true if and only if the Group has been in touch with the
+/// server (or is the server), and will remain true forever thereafter.
+///
+/// It is an error to call this function for groups that do not have object IDs
+/// (i.e. where `has_object_ids()` returns false).
+///
+/// The Group is assumed to be in a read transaction.
+bool is_object_id_stability_achieved(const Group&);
+
+/// Create a table with an object ID column.
+///
+/// It is an error to add tables to Groups with a sync history type directly.
+/// This function or related functions must be used instead.
+///
+/// The resulting table will be born with 1 column, which is a column used
+/// in the maintenance of object IDs.
+///
+/// NOTE: The table name must begin with the prefix "class_" in accordance with
+/// Object Store conventions.
+///
+/// The Group must be in a write transaction.
+TableRef create_table(Group&, StringData name);
+
+/// Create a table with an object ID column and a primary key column.
+///
+/// It is an error to add tables to Groups with a sync history type directly.
+/// This function or related functions must be used instead.
+///
+/// The resulting table will be born with 2 columns, which is a column used
+/// in the maintenance of object IDs and the requested primary key column.
+/// The primary key column must have either integer or string type, and it
+/// will be given the name provided in the argument \a pk_column_name.
+///
+/// The 'pk' metadata table is updated with information about the primary key
+/// column. If the 'pk' table does not yet exist, it is created.
+///
+/// Please note: The 'pk' metadata table will not be synchronized directly,
+/// so subsequent updates to it will be lost (as they constitute schema-breaking
+/// changes).
+///
+/// NOTE: The table name must begin with the prefix "class_" in accordance with
+/// Object Store conventions.
+///
+/// The Group must be in a write transaction.
+TableRef create_table_with_primary_key(Group&, StringData name, DataType pk_type,
+                                       StringData pk_column_name, bool nullable = false);
+
+/// Create an array column with the specified element type.
+///
+/// The result will be a column of type type_Table with one subcolumn named
+/// "!ARRAY_VALUE" of the specified element type.
+///
+/// Return the column index of the inserted array column.
+size_t add_array_column(Table&, DataType element_type, StringData column_name);
+
+
+//@{
+/// Calculate the object ID from the argument, where the argument is a primary
+/// key value.
+ObjectID object_id_for_primary_key(StringData);
+ObjectID object_id_for_primary_key(util::Optional<int64_t>);
+//@}
+
+/// Determine whether it is safe to call `object_id_for_row()` on tables without
+/// primary keys. If the table has a primary key, always returns true.
+bool has_globally_stable_object_ids(const Table&);
+
+bool table_has_primary_key(const TableInfoCache&, const Table&);
+
+/// Get the globally unique object ID for the row.
+///
+/// If the table has a primary key, this is guaranteed to succeed. Otherwise, if
+/// the server has not been contacted yet (`has_globally_stable_object_ids()`
+/// returns false), an exception is thrown.
+ObjectID object_id_for_row(const TableInfoCache&, const Table&, size_t);
+
+/// Get the index of the row with the object ID.
+///
+/// \returns realm::npos if the object does not exist in the table.
+size_t row_for_object_id(const TableInfoCache&, const Table&, ObjectID);
+
+//@{
+/// Add a row to the table and populate the object ID with an appropriate value.
+///
+/// In the variant which takes an ObjectID parameter, a check is performed to see
+/// if the object already exists. If it does, the row index of the existing object
+/// is returned.
+///
+/// If the table has a primary key column, an exception is thrown.
+///
+/// \returns the row index of the object.
+size_t create_object(const TableInfoCache&, Table&);
+size_t create_object(const TableInfoCache&, Table&, ObjectID);
+//@}
+
+//@{
+/// Create an object with a primary key value and populate the object ID with an
+/// appropriate value.
+///
+/// If the table does not have a primary key column (as indicated by the Object
+/// Store's metadata in the special "pk" table), or the type of the primary key
+/// column does not match the argument provided, an exception is thrown.
+///
+/// The primary key column's value is populated with the appropriate
+/// `set_int_unique()`, `set_string_unique()`, or `set_null_unique()` method
+/// called on \a table.
+///
+/// If an object with the given primary key value already exists, its row number
+/// is returned without creating any new objects.
+///
+/// These are convenience functions, equivalent to the following:
+///   - Add an empty row to the table.
+///   - Obtain an `ObjectID` with `object_id_for_primary_key()`.
+///   - Obtain a local object ID with `global_to_local_object_id()`.
+///   - Store the local object ID in the object ID column.
+///   - Call `set_int_unique()`,`set_string_unique()`, or `set_null_unique()`
+///     to set the primary key value.
+///
+/// \returns the row index of the created object.
+size_t create_object_with_primary_key(const TableInfoCache&, Table&, util::Optional<int64_t> primary_key);
+size_t create_object_with_primary_key(const TableInfoCache&, Table&, StringData primary_key);
+//@}
+
+struct TableInfoCache {
+    const Group& m_group;
+
+    // Implicit conversion deliberately allowed for the purpose of calling the above
+    // functions without constructing a cache manually.
+    TableInfoCache(const Group&);
+    TableInfoCache(TableInfoCache&&) noexcept = default;
+
+    struct TableInfo {
+        struct VTable;
+
+        StringData name;
+        const VTable* vtable;
+        size_t object_id_index;
+        size_t primary_key_index;
+        DataType primary_key_type = DataType(-1);
+        bool primary_key_nullable = false;
+    };
+
+    mutable std::vector<util::Optional<TableInfo>> m_table_info;
+
+    const TableInfo& get_table_info(const Table&) const;
+    const TableInfo& get_table_info(size_t table_index) const;
+    void clear();
+};
+
+
+/// Migrate a server-side Realm file whose history type is
+/// `Replication::hist_SyncServer` and whose history schema version is 0 (i.e.,
+/// Realm files without stable identifiers).
+void import_from_legacy_format(const Group& old_group, Group& new_group, util::Logger&);
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_OBJECT_HPP
+
diff --git a/iOS/Pods/Realm/include/core/realm/sync/object_id.hpp b/iOS/Pods/Realm/include/core/realm/sync/object_id.hpp
new file mode 100644 (file)
index 0000000..db3794a
--- /dev/null
@@ -0,0 +1,241 @@
+/*************************************************************************
+ *
+ * Copyright 2017 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_SYNC_OBJECT_ID_HPP
+#define REALM_SYNC_OBJECT_ID_HPP
+
+#include <functional> // std::hash
+#include <string>
+#include <iosfwd> // operator<<
+#include <map>
+#include <set>
+
+#include <stdint.h>
+
+#include <realm/util/optional.hpp>
+#include <realm/string_data.hpp>
+#include <realm/data_type.hpp>
+
+// Only set this to one when testing the code paths that exercise object ID
+// hash collisions. It artificially limits the "optimistic" local ID to use
+// only the lower 15 bits of the ID rather than the lower 63 bits, making it
+// feasible to generate collisions within reasonable time.
+#define REALM_EXERCISE_OBJECT_ID_COLLISION 0
+
+namespace realm {
+
+class Group;
+
+namespace sync {
+
+/// ObjectIDs are globally unique, and up to 128 bits wide. They are represented
+/// as two 64-bit integers, each of which may frequently be small, for best
+/// on-wire compressibility.
+struct ObjectID {
+    constexpr ObjectID(uint64_t hi, uint64_t lo);
+    static ObjectID from_string(StringData);
+
+    // FIXME: Remove "empty" ObjectIDs, wrap in Optional instead.
+    constexpr ObjectID(realm::util::None = realm::util::none);
+    constexpr ObjectID(const ObjectID&) noexcept = default;
+    ObjectID& operator=(const ObjectID&) noexcept = default;
+
+    constexpr uint64_t lo() const { return m_lo; }
+    constexpr uint64_t hi() const { return m_hi; }
+
+    std::string to_string() const;
+
+    constexpr bool operator<(const ObjectID& other) const;
+    constexpr bool operator==(const ObjectID& other) const;
+    constexpr bool operator!=(const ObjectID& other) const;
+
+private:
+    uint64_t m_lo;
+    uint64_t m_hi;
+};
+
+/// Implementors of this interface should define a way to map from 128-bit
+/// on-write ObjectIDs to local 64-bit object IDs.
+///
+/// The three object ID types are:
+/// a. Object IDs for objects in tables without primary keys.
+/// b. Object IDs for objects in tables with integer primary keys.
+/// c. Object IDs for objects in tables with other primary key types.
+///
+/// For integer primary keys (b), the Object ID is just the integer value.
+///
+/// For objects without primary keys (a), a "squeezed" tuple of the
+/// client_file_ident and a peer-local sequence number is used as the local
+/// Object ID. The on-write Object ID is the "unsqueezed" format. The methods on
+/// this interface ending in "_squeezed" aid in the creation and conversion of
+/// these IDs.
+///
+/// For objects with other types of primary keys (c), the ObjectID
+/// is a 128-bit hash of the primary key value. However, the local object ID
+/// must be a 64-bit integer, because that is the maximum size integer that
+/// Realm is able to store. The solution is to optimistically use the lower 63
+/// bits of the on-wire Object ID, and use a local ID with the upper 64th bit
+/// set when there is a collision in the lower 63 bits between two different
+/// hash values.
+class ObjectIDProvider {
+public:
+    using LocalObjectID = int_fast64_t;
+
+    /// Calculate optimistic local ID that may collide with others. It is up to
+    /// the caller to ensure that collisions are detected and that
+    /// allocate_local_id_after_collision() is called to obtain a non-colliding
+    /// ID.
+    static LocalObjectID get_optimistic_local_id_hashed(ObjectID global_id);
+
+    /// Find the local 64-bit object ID for the provided global 128-bit ID.
+    virtual LocalObjectID global_to_local_object_id_hashed(size_t table_ndx, ObjectID global_id) const = 0;
+
+    /// After a local ID collision has been detected, this function may be
+    /// called to obtain a non-colliding local ID in such a way that subsequence
+    /// calls to global_to_local_object_id() will return the correct local ID
+    /// for both \a incoming_id and \a colliding_id.
+    virtual LocalObjectID allocate_local_id_after_hash_collision(size_t table_ndx,
+                                                                 ObjectID incoming_id,
+                                                                 ObjectID colliding_id,
+                                                                 LocalObjectID colliding_local_id) = 0;
+    static LocalObjectID make_tagged_local_id_after_hash_collision(uint64_t sequence_number);
+    virtual void free_local_id_after_hash_collision(size_t table_ndx, ObjectID object_id) = 0;
+
+    /// Some Object IDs are generated as a tuple of the client_file_ident and a
+    /// local sequence number. This function takes the next number in the
+    /// sequence for the given table and returns an appropriate globally unique
+    /// ObjectID.
+    virtual ObjectID allocate_object_id_squeezed(size_t table_ndx) = 0;
+    static LocalObjectID global_to_local_object_id_squeezed(ObjectID);
+    static ObjectID local_to_global_object_id_squeezed(LocalObjectID);
+
+    virtual int_fast64_t get_client_file_ident() const = 0;
+};
+
+// ObjectIDSet is a set of (table name, object id)
+class ObjectIDSet {
+public:
+
+    void insert(StringData table, ObjectID object_id);
+    void erase(StringData table, ObjectID object_id);
+    bool contains(StringData table, ObjectID object_id) const noexcept;
+
+private:
+
+    // A map from table name to a set of object ids.
+    std::map<std::string, std::set<ObjectID>, std::less<>> m_objects;
+};
+
+/// Implementation:
+
+
+constexpr ObjectID::ObjectID(uint64_t hi, uint64_t lo): m_lo(lo), m_hi(hi)
+{
+}
+
+constexpr ObjectID::ObjectID(realm::util::None): m_lo(-1), m_hi(-1)
+{
+}
+
+constexpr bool ObjectID::operator<(const ObjectID& other) const
+{
+    return (m_hi == other.m_hi) ? (m_lo < other.m_lo) : (m_hi < other.m_hi);
+}
+
+constexpr bool ObjectID::operator==(const ObjectID& other) const
+{
+    return m_hi == other.m_hi && m_lo == other.m_lo;
+}
+
+constexpr bool ObjectID::operator!=(const ObjectID& other) const
+{
+    return !(*this == other);
+}
+
+std::ostream& operator<<(std::ostream&, const realm::sync::ObjectID&);
+
+inline ObjectIDProvider::LocalObjectID
+ObjectIDProvider::get_optimistic_local_id_hashed(ObjectID global_id)
+{
+#if REALM_EXERCISE_OBJECT_ID_COLLISION
+    const uint64_t optimistic_mask = 0xff;
+#else
+    const uint64_t optimistic_mask = 0x7fffffffffffffff;
+#endif
+    static_assert(optimistic_mask < 0x8000000000000000, "optimistic Object ID mask must leave the 64th bit zero");
+    return global_id.lo() & optimistic_mask;
+}
+
+inline ObjectIDProvider::LocalObjectID
+ObjectIDProvider::make_tagged_local_id_after_hash_collision(uint64_t sequence_number)
+{
+    REALM_ASSERT(sequence_number < 0x8000000000000000);
+    return 0x8000000000000000 | sequence_number;
+}
+
+inline ObjectIDProvider::LocalObjectID
+ObjectIDProvider::global_to_local_object_id_squeezed(ObjectID object_id)
+{
+    REALM_ASSERT(object_id.hi() <= std::numeric_limits<uint32_t>::max());
+    REALM_ASSERT(object_id.lo() <= std::numeric_limits<uint32_t>::max());
+
+    uint64_t a =  object_id.lo() & 0xff;
+    uint64_t b = (object_id.hi() & 0xff) << 8;
+    uint64_t c = (object_id.lo() & 0xffffff00) << 8;
+    uint64_t d = (object_id.hi() & 0xffffff00) << 32;
+    union {
+        uint64_t u;
+        int64_t  s;
+    } bitcast;
+    bitcast.u = a | b | c | d;
+    return bitcast.s;
+}
+
+inline ObjectID
+ObjectIDProvider::local_to_global_object_id_squeezed(LocalObjectID squeezed)
+{
+    union {
+        uint64_t u;
+        int64_t  s;
+    } bitcast;
+    bitcast.s = squeezed;
+
+    uint64_t u = bitcast.u;
+
+    uint64_t lo = (u & 0xff) | ((u & 0xffffff0000) >> 8);
+    uint64_t hi = ((u & 0xff00) >> 8) | ((u & 0xffffff0000000000) >> 32);
+    return ObjectID{hi, lo};
+}
+
+} // namespace sync
+} // namespace realm
+
+namespace std {
+
+template <>
+struct hash<realm::sync::ObjectID> {
+    size_t operator()(realm::sync::ObjectID oid) const
+    {
+        return std::hash<uint64_t>{}(oid.lo()) ^ std::hash<uint64_t>{}(oid.hi());
+    }
+};
+
+} // namespace std
+
+#endif // REALM_SYNC_OBJECT_ID_HPP
+
diff --git a/iOS/Pods/Realm/include/core/realm/sync/protocol.hpp b/iOS/Pods/Realm/include/core/realm/sync/protocol.hpp
new file mode 100644 (file)
index 0000000..d933f82
--- /dev/null
@@ -0,0 +1,932 @@
+/*************************************************************************
+ *
+ * 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
+
+#include <system_error>
+
+#include <realm/util/buffer_stream.hpp>
+#include <realm/util/compression.hpp>
+#include <realm/util/logger.hpp>
+#include <realm/util/memory_stream.hpp>
+#include <realm/util/optional.hpp>
+#include <realm/impl/clamped_hex_dump.hpp>
+#include <realm/sync/transform.hpp>
+#include <realm/sync/history.hpp>
+
+#include <realm/sync/client.hpp> // Get rid of this?
+
+
+// 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, <client info> 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.
+//
+//  16 Downloadable bytes added to DOWNLOAD message. It is used for download progress
+//     by the client
+//
+//  17 Added PING and PONG messages. It is used for rtt monitoring and dead
+//     connection detection by both the client and the server.
+//
+//  18 Enhanced the session_ident to accept values of size up to at least 63 bits.
+//
+//  19 New instruction log format with stable object IDs and arrays of
+//     primitives (Generalized LinkList* commands to Container* commands)
+//     Message format is identical to version 18.
+//
+//  20 Added support for log compaction in DOWNLOAD message.
+//
+//  21 Removed "class_" prefix in instructions referencing tables.
+//
+//  22 Fixed a bug in the merge rule of MOVE vs SWAP.
+
+constexpr int get_current_protocol_version() noexcept
+{
+    return 22;
+}
+
+/// \brief Protocol errors discovered by the server, and reported to the client
+/// by way of ERROR messages.
+///
+/// These errors will be reported to the client-side application via the error
+/// handlers of the affected sessions.
+///
+/// ATTENTION: Please remember to update is_session_level_error() when
+/// adding/removing error codes.
+enum class ProtocolError {
+    // 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
+    bad_decompression            = 110, // Error in decompression (UPLOAD)
+    bad_changeset_header_syntax  = 111, // Bad syntax in a changeset header (UPLOAD)
+    bad_changeset_size           = 112, // Bad size specified in changeset header (UPLOAD)
+    bad_changesets               = 113, // Bad changesets (UPLOAD)
+
+    // 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) (obsolete!)
+    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)
+    disabled_session             = 213, // Disabled session
+    partial_sync_disabled        = 214, // Partial sync disabled (BIND)
+};
+
+inline constexpr bool is_session_level_error(ProtocolError error)
+{
+    return int(error) >= 200 && int(error) <= 299;
+}
+
+/// Returns null if the specified protocol error code is not defined by
+/// ProtocolError.
+const char* get_protocol_error_message(int error_code) noexcept;
+
+const std::error_category& protocol_error_category() noexcept;
+
+std::error_code make_error_code(ProtocolError) noexcept;
+
+} // namespace sync
+} // namespace realm
+
+namespace std {
+
+template<> struct is_error_code_enum<realm::sync::ProtocolError> {
+    static const bool value = true;
+};
+
+} // namespace std
+
+namespace realm {
+namespace sync {
+namespace protocol {
+
+using OutputBuffer = util::ResettableExpandableBufferOutputStream;
+using session_ident_type = uint_fast64_t;
+using file_ident_type = uint_fast64_t;
+using version_type = uint_fast64_t;
+using timestamp_type  = uint_fast64_t;
+using request_ident_type    = uint_fast64_t;
+using ReceivedChangesets = std::vector<Transformer::RemoteChangeset>;
+
+
+class ClientProtocol {
+public:
+    util::Logger& logger;
+
+    enum class Error {
+        unknown_message             = 101, // Unknown type of input message
+        bad_syntax                  = 102, // Bad syntax in input message head
+        limits_exceeded             = 103, // Limits exceeded in input message
+        bad_changeset_header_syntax = 108, // Bad syntax in changeset header (DOWNLOAD)
+        bad_changeset_size          = 109, // Bad changeset size in changeset header (DOWNLOAD)
+        bad_server_version          = 111, // Bad server version in changeset header (DOWNLOAD)
+        bad_error_code              = 114, ///< Bad error code (ERROR)
+        bad_decompression           = 115, // Error in decompression (DOWNLOAD)
+    };
+
+    ClientProtocol(util::Logger& logger);
+
+
+    /// Messages sent by the client.
+
+    void make_client_message(OutputBuffer& out, const std::string& client_info);
+
+    void make_bind_message(OutputBuffer& out, session_ident_type session_ident,
+                           const std::string& server_path,
+                           const std::string& signed_user_token,
+                           bool need_file_ident_pair);
+
+    void make_refresh_message(OutputBuffer& out, session_ident_type session_ident,
+                              const std::string& signed_user_token);
+
+    void make_ident_message(OutputBuffer& out, session_ident_type session_ident,
+                            file_ident_type server_file_ident,
+                            file_ident_type client_file_ident,
+                            int_fast64_t client_file_ident_secret,
+                            SyncProgress progress);
+
+    class UploadMessageBuilder {
+    public:
+        util::Logger& logger;
+
+        UploadMessageBuilder(util::Logger& logger,
+                             OutputBuffer& body_buffer,
+                             std::vector<char>& compression_buffer,
+                             util::compression::CompressMemoryArena& compress_memory_arena);
+
+        void add_changeset(version_type client_version, version_type server_version,
+                           timestamp_type timestamp, ChunkedBinaryData changeset);
+
+        void make_upload_message(OutputBuffer& out, session_ident_type session_ident);
+
+    private:
+        size_t m_num_changesets = 0;
+        OutputBuffer& m_body_buffer;
+        std::vector<char>& m_compression_buffer;
+        util::compression::CompressMemoryArena& m_compress_memory_arena;
+    };
+
+    UploadMessageBuilder make_upload_message_builder();
+
+    void make_upload_message(OutputBuffer& out, session_ident_type session_ident,
+                             version_type client_version, version_type server_version,
+                             size_t changeset_size, timestamp_type timestamp,
+                             const std::unique_ptr<char[]>& body_buffer);
+
+    void make_unbind_message(OutputBuffer& out, session_ident_type session_ident);
+
+    void make_mark_message(OutputBuffer& out, session_ident_type session_ident,
+                           request_ident_type request_ident);
+
+    void make_ping(OutputBuffer& out, uint_fast64_t timestamp, uint_fast64_t rtt);
+
+
+    // Messages received by the client.
+
+    // parse_pong_received takes a (WebSocket) pong and parses it.
+    // The result of the parsing is handled by an object of type Connection.
+    // Typically, Connection would be the Connection class from client.cpp
+    template <typename Connection>
+    void parse_pong_received(Connection& connection, const char* data, size_t size)
+    {
+        util::MemoryInputStream in;
+        in.set_buffer(data, data + size);
+        in.unsetf(std::ios_base::skipws);
+
+        uint_fast64_t timestamp;
+
+        char newline;
+        in >> timestamp >> newline;
+        bool good_syntax = in && size_t(in.tellg()) == size && newline == '\n';
+        if (!good_syntax)
+            goto bad_syntax;
+
+        connection.receive_pong(timestamp);
+        return;
+
+    bad_syntax:
+        logger.error("Bad syntax in input message '%1'",
+                     StringData(data, size));
+        connection.handle_protocol_error(Error::bad_syntax); // Throws
+        return;
+    }
+
+    // parse_message_received takes a (WebSocket) message and parses it.
+    // The result of the parsing is handled by an object of type Connection.
+    // Typically, Connection would be the Connection class from client.cpp
+    template <typename Connection>
+    void parse_message_received(Connection& connection, const char* data, size_t size)
+    {
+        util::MemoryInputStream in;
+        in.set_buffer(data, data + size);
+        in.unsetf(std::ios_base::skipws);
+        size_t header_size = 0;
+        std::string message_type;
+        in >> message_type;
+
+        if (message_type == "download") {
+            session_ident_type session_ident;
+            SyncProgress progress;
+            int is_body_compressed;
+            size_t uncompressed_body_size, compressed_body_size;
+            char sp_1, sp_2, sp_3, sp_4, sp_5, sp_6, sp_7, sp_8, sp_9, sp_10, newline;
+
+            in >> sp_1 >> session_ident >> sp_2 >> progress.scan_server_version >> sp_3 >>
+                progress.scan_client_version >> sp_4 >> progress.latest_server_version >>
+                sp_5 >> progress.latest_server_session_ident >> sp_6 >>
+                progress.latest_client_version >> sp_7 >> progress.downloadable_bytes >>
+                sp_8 >> is_body_compressed >> sp_9 >> uncompressed_body_size >> sp_10 >>
+                compressed_body_size >> newline; // Throws
+
+            bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' &&
+                sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' ' && sp_6 == ' ' &&
+                sp_7 == ' ' && sp_8 == ' ' && sp_9 == ' ' && sp_10 == ' ' &&
+                newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size_t(in.tellg());
+            if (uncompressed_body_size > s_max_body_size)
+                goto limits_exceeded;
+
+            size_t body_size = is_body_compressed ? compressed_body_size : uncompressed_body_size;
+            if (header_size + body_size != size)
+                goto bad_syntax;
+
+            BinaryData body(data + header_size, body_size);
+            BinaryData uncompressed_body;
+
+            std::unique_ptr<char[]> uncompressed_body_buffer;
+            // if is_body_compressed == true, we must decompress the received body.
+            if (is_body_compressed) {
+                uncompressed_body_buffer.reset(new char[uncompressed_body_size]);
+                std::error_code ec = util::compression::decompress(body.data(),  compressed_body_size,
+                                                                   uncompressed_body_buffer.get(),
+                                                                   uncompressed_body_size);
+
+                if (ec) {
+                    logger.error("compression::inflate: %1", ec.message());
+                    connection.handle_protocol_error(Error::bad_decompression);
+                    return;
+                }
+
+                uncompressed_body = BinaryData(uncompressed_body_buffer.get(), uncompressed_body_size);
+            }
+            else {
+                uncompressed_body = body;
+            }
+
+            logger.debug("Download message compression: is_body_compressed = %1, "
+                         "compressed_body_size=%2, uncompressed_body_size=%3",
+                         is_body_compressed, compressed_body_size, uncompressed_body_size);
+
+            util::MemoryInputStream in;
+            in.unsetf(std::ios_base::skipws);
+            in.set_buffer(uncompressed_body.data(), uncompressed_body.data() + uncompressed_body_size);
+
+            ReceivedChangesets received_changesets;
+
+            // Loop through the body and find the changesets.
+            size_t position = 0;
+            while (position < uncompressed_body_size) {
+                version_type server_version;
+                version_type client_version;
+                timestamp_type origin_timestamp;
+                file_ident_type origin_client_file_ident;
+                size_t original_changeset_size, changeset_size;
+                char sp_1, sp_2, sp_3, sp_4, sp_5, sp_6;
+
+                in >> server_version >> sp_1 >> client_version >> sp_2 >> origin_timestamp >>
+                    sp_3 >> origin_client_file_ident >> sp_4 >> original_changeset_size >>
+                    sp_5 >> changeset_size >> sp_6;
+
+                bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' &&
+                    sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' ' && sp_6 == ' ';
+
+                if (!good_syntax) {
+                    logger.error("Bad changeset header syntax");
+                    connection.handle_protocol_error(Error::bad_changeset_header_syntax);
+                    return;
+                }
+
+                // Update position to the end of the change set
+                position = size_t(in.tellg()) + changeset_size;
+
+                if (position > uncompressed_body_size) {
+                    logger.error("Bad changeset size");
+                    connection.handle_protocol_error(Error::bad_changeset_size);
+                    return;
+                }
+
+                if (server_version == 0) {
+                    // The received changeset can never have version 0.
+                    logger.error("Bad server version");
+                    connection.handle_protocol_error(Error::bad_server_version);
+                    return;
+                }
+
+                BinaryData changeset_data(uncompressed_body.data() + size_t(in.tellg()), changeset_size);
+                in.seekg(position);
+
+                if (logger.would_log(util::Logger::Level::trace)) {
+                    logger.trace("Received: DOWNLOAD CHANGESET(server_version=%1, client_version=%2, "
+                                  "origin_timestamp=%3, origin_client_file_ident=%4, original_changeset_size=%5, changeset_size=%6)",
+                                  server_version, client_version, origin_timestamp,
+                                  origin_client_file_ident, original_changeset_size, changeset_size); // Throws
+                    logger.trace("Changeset: %1",
+                                 _impl::clamped_hex_dump(changeset_data)); // Throws
+                }
+
+                Transformer::RemoteChangeset changeset_2(server_version, client_version,
+                                                         changeset_data, origin_timestamp,
+                                                         origin_client_file_ident);
+                changeset_2.original_changeset_size = original_changeset_size;
+                received_changesets.push_back(changeset_2); // Throws
+            }
+
+            connection.receive_download_message(session_ident, progress, received_changesets); // Throws
+            return;
+        }
+        if (message_type == "unbound") {
+            session_ident_type session_ident;
+            char sp_1, newline;
+            in >> sp_1 >> session_ident >> newline; // Throws
+            bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' &&
+                newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size_t(in.tellg());
+
+            connection.receive_unbound_message(session_ident); // Throws
+            return;
+        }
+        if (message_type == "error") {
+            int error_code;
+            size_t message_size;
+            bool try_again;
+            session_ident_type session_ident;
+            char sp_1, sp_2, sp_3, sp_4, newline;
+            in >> sp_1 >> error_code >> sp_2 >> message_size >> sp_3 >> try_again >> sp_4 >>
+                session_ident >> newline; // Throws
+            bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && sp_3 == ' ' &&
+                sp_4 == ' ' && newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size_t(in.tellg());
+            if (header_size + message_size != size)
+                goto bad_syntax;
+
+            bool unknown_error = !get_protocol_error_message(error_code);
+            if (unknown_error) {
+                logger.error("Bad error code"); // Throws
+                connection.handle_protocol_error(Error::bad_error_code);
+                return;
+            }
+
+            std::string message{data + header_size, message_size}; // Throws (copy)
+
+            connection.receive_error_message(error_code, message_size, try_again, session_ident, message); // Throws
+            return;
+        }
+        if (message_type == "mark") {
+            session_ident_type session_ident;
+            request_ident_type request_ident;
+            char sp_1, sp_2, newline;
+            in >> sp_1 >> session_ident >> sp_2 >> request_ident >> newline; // Throws
+            bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' &&
+                sp_2 == ' ' && newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size_t(in.tellg());
+
+            connection.receive_mark_message(session_ident, request_ident); // Throws
+            return;
+        }
+        if (message_type == "alloc") {
+            session_ident_type session_ident;
+            file_ident_type server_file_ident, client_file_ident;
+            int_fast64_t client_file_ident_secret;
+            char sp_1, sp_2, sp_3, sp_4, newline;
+            in >> sp_1 >> session_ident >> sp_2 >> server_file_ident >> sp_3 >>
+                client_file_ident >> sp_4 >> client_file_ident_secret >> newline; // Throws
+            bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' &&
+                sp_2 == ' ' && sp_3 == ' ' && sp_4 == ' ' && newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size_t(in.tellg());
+
+            connection.receive_alloc_message(session_ident,server_file_ident, client_file_ident,
+                                             client_file_ident_secret); // Throws
+            return;
+        }
+
+        logger.error("Unknown input message type '%1'",
+                     StringData(data, size));
+        connection.handle_protocol_error(Error::unknown_message);
+        return;
+    bad_syntax:
+        logger.error("Bad syntax in input message '%1'",
+                     StringData(data, size));
+        connection.handle_protocol_error(Error::bad_syntax);
+        return;
+    limits_exceeded:
+        logger.error("Limits exceeded in input message '%1'",
+                     StringData(data, header_size));
+        connection.handle_protocol_error(Error::limits_exceeded);
+        return;
+    }
+
+private:
+    static constexpr size_t s_max_body_size = std::numeric_limits<size_t>::max();
+
+    // Permanent buffer to use for building messages.
+    OutputBuffer m_output_buffer;
+
+    // Permanent buffers to use for internal purposes such as compression.
+    std::vector<char> m_buffer;
+
+    util::compression::CompressMemoryArena m_compress_memory_arena;
+};
+
+
+class ServerProtocol {
+public:
+    util::Logger& logger;
+
+    enum class Error {
+        unknown_message             = 101, // Unknown type of input message
+        bad_syntax                  = 102, // Bad syntax in input message head
+        limits_exceeded             = 103, // Limits exceeded in input message
+        bad_decompression           = 104, // Error in decompression (UPLOAD)
+        bad_changeset_header_syntax = 105, // Bad syntax in changeset header (UPLOAD)
+        bad_changeset_size          = 106, // Changeset size doesn't fit in message (UPLOAD)
+    };
+
+    ServerProtocol(util::Logger& logger);
+
+    // Messages sent by the server to the client
+
+    void make_alloc_message(OutputBuffer& out, session_ident_type session_ident,
+                            file_ident_type server_file_ident,
+                            file_ident_type client_file_ident,
+                            std::int_fast64_t client_file_ident_secret);
+
+    void make_unbound_message(OutputBuffer& out, session_ident_type session_ident);
+
+
+    struct ChangesetInfo {
+        version_type server_version;
+        version_type client_version;
+        HistoryEntry entry;
+        size_t original_size;
+    };
+
+    void make_download_message(int protocol_version, OutputBuffer& out, session_ident_type session_ident,
+                               version_type scan_server_version,
+                               version_type scan_client_version,
+                               version_type latest_server_version,
+                               int_fast64_t latest_server_session_ident,
+                               version_type latest_client_version,
+                               uint_fast64_t downloadable_bytes,
+                               std::size_t num_changesets, BinaryData body);
+
+    void make_error_message(OutputBuffer& out, ProtocolError error_code,
+                            const char* message, size_t message_size,
+                            bool try_again, session_ident_type session_ident);
+
+    void make_mark_message(OutputBuffer& out, session_ident_type session_ident,
+                           request_ident_type request_ident);
+
+    void make_pong(OutputBuffer& out, uint_fast64_t timestamp);
+
+    // Messages received by the server.
+
+    // parse_ping_received takes a (WebSocket) ping and parses it.
+    // The result of the parsing is handled by an object of type Connection.
+    // Typically, Connection would be the Connection class from server.cpp
+    template <typename Connection>
+    void parse_ping_received(Connection& connection, const char* data, size_t size)
+    {
+        util::MemoryInputStream in;
+        in.set_buffer(data, data + size);
+        in.unsetf(std::ios_base::skipws);
+
+        int_fast64_t timestamp, rtt;
+
+        char sp_1, newline;
+        in >> timestamp >> sp_1 >> rtt >> newline;
+        bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' &&
+            newline == '\n';
+        if (!good_syntax)
+            goto bad_syntax;
+
+        connection.receive_ping(timestamp, rtt);
+        return;
+
+    bad_syntax:
+        logger.error("Bad syntax in PING message '%1'",
+                     StringData(data, size));
+        connection.handle_protocol_error(Error::bad_syntax);
+        return;
+    }
+
+    // UploadChangeset is used to store received changesets in
+    // the UPLOAD message.
+    struct UploadChangeset {
+        version_type client_version;
+        version_type server_version;
+        timestamp_type timestamp;
+        BinaryData changeset;
+    };
+
+    // parse_message_received takes a (WebSocket) message and parses it.
+    // The result of the parsing is handled by an object of type Connection.
+    // Typically, Connection would be the Connection class from server.cpp
+    template <typename Connection>
+    void parse_message_received(Connection& connection, const char* data, size_t size)
+    {
+        util::MemoryInputStream in;
+        in.set_buffer(data, data + size);
+        in.unsetf(std::ios_base::skipws);
+        size_t header_size = 0;
+        std::string message_type;
+        in >> message_type;
+
+        if (message_type == "upload") {
+            session_ident_type session_ident;
+            int is_body_compressed;
+            size_t uncompressed_body_size, compressed_body_size;
+            char sp_1, sp_2, sp_3, sp_4, newline;
+            in >> sp_1 >> session_ident >> sp_2 >> is_body_compressed >> sp_3 >>
+                uncompressed_body_size >> sp_4 >> compressed_body_size >>
+                newline;
+            bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && sp_3 == ' ' &&
+                sp_4 == ' ' && newline == '\n';
+
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size_t(in.tellg());
+            if (uncompressed_body_size > s_max_body_size)
+                goto limits_exceeded;
+
+            size_t body_size = is_body_compressed ? compressed_body_size : uncompressed_body_size;
+            if (header_size + body_size != size)
+                goto bad_syntax;
+
+            BinaryData body(data + header_size, body_size);
+
+            BinaryData uncompressed_body;
+            std::unique_ptr<char[]> uncompressed_body_buffer;
+            // if is_body_compressed == true, we must decompress the received body.
+            if (is_body_compressed) {
+                uncompressed_body_buffer.reset(new char[uncompressed_body_size]);
+                std::error_code ec = util::compression::decompress(body.data(),  compressed_body_size,
+                                                                   uncompressed_body_buffer.get(),
+                                                                   uncompressed_body_size);
+
+                if (ec) {
+                    logger.error("compression::inflate: %1", ec.message());
+                    connection.handle_protocol_error(Error::bad_decompression);
+                    return;
+                }
+
+                uncompressed_body = BinaryData(uncompressed_body_buffer.get(), uncompressed_body_size);
+            }
+            else {
+                uncompressed_body = body;
+            }
+
+            logger.debug("Upload message compression: is_body_compressed = %1, "
+                         "compressed_body_size=%2, uncompressed_body_size=%3",
+                         is_body_compressed, compressed_body_size, uncompressed_body_size);
+
+            util::MemoryInputStream in;
+            in.unsetf(std::ios_base::skipws);
+            in.set_buffer(uncompressed_body.data(), uncompressed_body.data() + uncompressed_body_size);
+
+            std::vector<UploadChangeset> upload_changesets;
+
+            // Loop through the body and find the changesets.
+            size_t position = 0;
+            while (position < uncompressed_body_size) {
+                version_type client_version;
+                version_type server_version;
+                timestamp_type timestamp;
+                size_t changeset_size;
+                char sp_1, sp_2, sp_3, sp_4;
+
+                in >> client_version >> sp_1 >> server_version >> sp_2 >> timestamp >>
+                    sp_3 >> changeset_size >> sp_4;
+
+                bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' &&
+                    sp_3 == ' ' && sp_4 == ' ';
+
+                if (!good_syntax) {
+                    logger.error("Bad changeset header syntax");
+                    connection.handle_protocol_error(Error::bad_changeset_header_syntax);
+                    return;
+                }
+
+                // Update position to the end of the change set
+                position = size_t(in.tellg()) + changeset_size;
+
+                if (position > uncompressed_body_size) {
+                    logger.error("Bad changeset size");
+                    connection.handle_protocol_error(Error::bad_changeset_size);
+                    return;
+                }
+
+                BinaryData changeset_data(uncompressed_body.data() +
+                                          size_t(in.tellg()), changeset_size);
+                in.seekg(position);
+
+                if (logger.would_log(util::Logger::Level::trace)) {
+                    logger.trace(
+                        "Received: UPLOAD CHANGESET(client_version=%1, "
+                        "server_version=%2, timestamp=%3, changeset_size=%4)",
+                        client_version, server_version, timestamp,
+                        changeset_size); // Throws
+                    logger.trace("Changeset: %1",
+                                 _impl::clamped_hex_dump(changeset_data)); // Throws
+                }
+
+                UploadChangeset upload_changeset {
+                    client_version,
+                    server_version,
+                    timestamp,
+                    changeset_data
+                };
+
+                upload_changesets.push_back(upload_changeset); // Throws
+            }
+
+            connection.receive_upload_message(session_ident,upload_changesets); // Throws
+            return;
+        }
+        if (message_type == "mark") {
+            session_ident_type session_ident;
+            request_ident_type request_ident;
+            char sp_1, sp_2, newline;
+            in >> sp_1 >> session_ident >> sp_2 >> request_ident >> newline;
+            bool good_syntax = in && size_t(in.tellg()) == size &&
+                sp_1 == ' ' && sp_2 == ' ' && newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size;
+
+            connection.receive_mark_message(session_ident, request_ident); // Throws
+            return;
+        }
+        if (message_type == "bind") {
+            session_ident_type session_ident;
+            size_t path_size;
+            size_t signed_user_token_size;
+            bool need_file_ident_pair;
+            char sp_1, sp_2, sp_3, sp_4, newline;
+            in >> sp_1 >> session_ident >> sp_2 >> path_size >> sp_3 >>
+                signed_user_token_size >> sp_4 >> need_file_ident_pair >>
+                newline;
+            bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' &&
+                sp_3 == ' ' && sp_4 == ' ' && newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size_t(in.tellg());
+            if (path_size == 0)
+                goto bad_syntax;
+            if (path_size > s_max_path_size)
+                goto limits_exceeded;
+            if (signed_user_token_size > s_max_signed_user_token_size)
+                goto limits_exceeded;
+            if (header_size + path_size + signed_user_token_size != size)
+                goto bad_syntax;
+
+            std::string path {data + header_size, path_size}; // Throws
+            std::string signed_user_token {data + header_size + path_size,
+                signed_user_token_size}; // Throws
+
+            connection.receive_bind_message(session_ident, std::move(path),
+                                            std::move(signed_user_token),
+                                            need_file_ident_pair); // Throws
+            return;
+        }
+        if (message_type == "refresh") {
+            session_ident_type session_ident;
+            size_t signed_user_token_size;
+            char sp_1, sp_2, newline;
+            in >> sp_1 >> session_ident >> sp_2 >> signed_user_token_size >>
+                newline;
+            bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size_t(in.tellg());
+            if (signed_user_token_size > s_max_signed_user_token_size)
+                goto limits_exceeded;
+            if (header_size + signed_user_token_size != size)
+                goto bad_syntax;
+
+            std::string signed_user_token {data + header_size, signed_user_token_size};
+
+            connection.receive_refresh_message(session_ident, std::move(signed_user_token)); // Throws
+            return;
+        }
+        if (message_type == "ident") {
+            session_ident_type session_ident;
+            file_ident_type server_file_ident, client_file_ident;
+            int_fast64_t client_file_ident_secret;
+            version_type scan_server_version, scan_client_version, latest_server_version;
+            int_fast64_t latest_server_session_ident;
+            char sp_1, sp_2, sp_3, sp_4, sp_5, sp_6, sp_7, sp_8, newline;
+            in >> sp_1 >> session_ident >> sp_2 >> server_file_ident >> sp_3 >>
+                client_file_ident >> sp_4 >> client_file_ident_secret >> sp_5 >>
+                scan_server_version >> sp_6 >> scan_client_version >> sp_7 >>
+                latest_server_version >> sp_8 >> latest_server_session_ident >>
+                newline;
+            bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' &&
+                sp_2 == ' ' && sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' ' &&
+                sp_6 == ' ' && sp_7 == ' ' && sp_8 == ' ' && newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size;
+
+            connection.receive_ident_message(session_ident, server_file_ident, client_file_ident,
+                                             client_file_ident_secret, scan_server_version,
+                                             scan_client_version, latest_server_version,
+                                             latest_server_session_ident); // Throws
+            return;
+        }
+        if (message_type == "unbind") {
+            session_ident_type session_ident;
+            char sp_1, newline;
+            in >> sp_1 >> session_ident >> newline;
+            bool good_syntax = in && size_t(in.tellg()) == size &&
+                sp_1 == ' ' && newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size;
+
+            connection.receive_unbind_message(session_ident); // Throws
+            return;
+        }
+        if (message_type == "client") {
+            int_fast64_t protocol_version;
+            char sp_1, sp_2, newline;
+            size_t client_info_size;
+            in >> sp_1 >> protocol_version >> sp_2 >> client_info_size >> newline;
+            bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && newline == '\n';
+            if (!good_syntax)
+                goto bad_syntax;
+            header_size = size_t(in.tellg());
+            bool limits_exceeded = (client_info_size > s_max_client_info_size);
+            if (limits_exceeded)
+                goto limits_exceeded;
+            if (header_size + client_info_size != size)
+                goto bad_syntax;
+
+            std::string client_info {data + header_size, client_info_size}; // Throws
+
+            connection.receive_client_message(protocol_version, std::move(client_info)); // Throws
+            return;
+        }
+
+        // unknown message
+        if (size < 256)
+            logger.error("Unknown input message type '%1'", StringData(data, size)); // Throws
+        else
+            logger.error("Unknown input message type '%1'.......", StringData(data, 256)); // Throws
+
+        connection.handle_protocol_error(Error::unknown_message);
+        return;
+
+    bad_syntax:
+        logger.error("Bad syntax in input message '%1'",
+                     StringData(data, size));
+        connection.handle_protocol_error(Error::bad_syntax); // Throws
+        return;
+    limits_exceeded:
+        logger.error("Limits exceeded in input message '%1'",
+                     StringData(data, header_size));
+        connection.handle_protocol_error(Error::limits_exceeded); // Throws
+        return;
+    }
+
+    void insert_single_changeset_download_message(OutputBuffer& out, const ChangesetInfo& changeset_info);
+
+private:
+    static constexpr size_t s_max_head_size              =  256;
+    static constexpr size_t s_max_signed_user_token_size = 2048;
+    static constexpr size_t s_max_client_info_size       = 1024;
+    static constexpr size_t s_max_path_size              = 1024;
+    static constexpr size_t s_max_body_size = std::numeric_limits<size_t>::max();
+
+    util::compression::CompressMemoryArena m_compress_memory_arena;
+
+    // Permanent buffer to use for internal purposes such as compression.
+    std::vector<char> m_buffer;
+
+    // Outputbuffer to use for internal purposes such as creating the
+    // download body.
+    OutputBuffer m_output_buffer;
+};
+
+// make_authorization_header() makes the value of the Authorization header used in the
+// sync Websocket handshake.
+std::string make_authorization_header(const std::string& signed_user_token);
+
+// parse_authorization_header() parses the value of the Authorization header and returns
+// the signed_user_token. None is returned in case of syntax error.
+util::Optional<StringData> parse_authorization_header(const std::string& authorization_header);
+
+} // namespace protocol
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_PROTOCOL_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/server.hpp b/iOS/Pods/Realm/include/core/realm/sync/server.hpp
new file mode 100644 (file)
index 0000000..8229687
--- /dev/null
@@ -0,0 +1,261 @@
+/*************************************************************************
+ *
+ * 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 <stdint.h>
+#include <memory>
+#include <string>
+
+#include <realm/util/network.hpp>
+#include <realm/util/logger.hpp>
+#include <realm/util/optional.hpp>
+#include <realm/sync/crypto_server.hpp>
+#include <realm/sync/metrics.hpp>
+
+namespace realm {
+namespace sync {
+
+class Server {
+public:
+    class TokenExpirationClock;
+
+    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 custom clock to be used for token expiration checks. If
+        /// no clock is specified, the server will use the system clock.
+        TokenExpirationClock* token_expiration_clock = nullptr;
+
+        /// 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. For the list of counters and gauges see
+        /// "doc/monitoring.md".
+        Metrics* metrics = nullptr;
+
+        /// A unique id of this server. Used in the backup protocol to tell
+        /// slaves apart.
+        std::string id = "unknown";
+
+        /// 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;
+
+        // A connection which has not been sending any messages or pings for
+        // `idle_timeout_ms` is considered dead and will be dropped by the server.
+        uint_fast64_t idle_timeout_ms = 1800000; // 30 minutes
+
+        // How often the server scans through the connection list to drop idle ones.
+        uint_fast64_t drop_period_ms = 60000; // 1 minute
+
+        /// @{ \brief The operating mode of the Sync worker.
+        ///
+        /// MasterWithNoSlave is a standard Sync worker without backup.
+        /// If a backup slave attempts to contact a MasterNoBackup server,
+        /// the slave will be rejected.
+        ///
+        /// MasterWithAsynchronousSlave represents a Sync worker that operates
+        /// independently of a backup slave. If a slave connects to the
+        /// MasterAsynchronousSlave server, the server will accept the connection
+        /// and send backup information to the slave. This type of master server
+        /// will never wait for the slave, however.
+        ///
+        /// MasterWithSynchronousSlave represents a Sync worker that works in
+        /// coordination with a slave. The master will send all updates to the
+        /// slave and wait for acknowledgment before the master sends its own
+        /// acknowledgment to the clients. This mode of operation is the safest
+        /// type of backup, but it generally will have higher latency than the previous
+        /// two types of server.
+        ///
+        /// Slave represents a backup server. A slave is used to backup a master.
+        /// The slave connects to the master and reconnects in case a network fallout.
+        /// The slave receives updates from the master and acknowledges them.
+        /// A slave rejects all connections from Sync clients.
+        enum class OperatingMode {
+            MasterWithNoSlave,
+            MasterWithAsynchronousSlave,
+            MasterWithSynchronousSlave,
+            Slave
+        };
+        OperatingMode operating_mode = OperatingMode::MasterWithNoSlave;
+        /// @}
+
+        /// @{ \brief Adress of master sync work.
+        ///
+        /// master_address and master_port are only meaningful in Slave mode.
+        /// The parameters represent the address of the master from which this
+        /// slave obtains Realm updates.
+        std::string master_address;
+        std::string master_port;
+        /// @}
+
+        /// @{ \brief SSL for master slave communication.
+        ///
+        /// The master and slave communicate over a SSL connection if
+        /// master_slave_ssl is set to true(default = false). The certificate of the
+        /// master is verified if master_verify_ssl_certificate is set to true.
+        /// The certificate verification attempts to use the default trust store of the
+        /// instance if master_ssl_trust_certificate_path is none(default), otherwise
+        /// the certificate at the master_ssl_trust_certificate_path is used for
+        /// verification.
+        bool master_slave_ssl = false;
+        bool master_verify_ssl_certificate = true;
+        util::Optional<std::string> master_ssl_trust_certificate_path = util::none;
+        /// @}
+
+        /// A master Sync server will only accept a backup connection from a slave
+        /// that can present the correct master_slave_shared_secret.
+        /// The configuration of the master and the slave must contain the same
+        /// secret string.
+        /// The secret is sent in a HTTP header and must be a valid HTTP header value.
+        std::string master_slave_shared_secret = "replace-this-string-with-a-secret";
+
+        /// A callback which gets called by the backup master every time the slave
+        /// changes its status to up-to-date or back. The arguments carry the
+        /// slave's id (string) and its up-to-dateness state (bool).
+        std::function<void(std::string, bool)> slave_status_callback;
+
+        /// The feature token is used by the server to gate access to various
+        /// features.
+        util::Optional<std::string> feature_token;
+
+        /// The server can try to eliminate redundant instructions from
+        /// changesets before sending them to clients, minimizing download sizes
+        /// at the expense of server CPU usage.
+        bool enable_download_log_compaction = true;
+
+        /// The accumulated size of changesets that are included in download
+        /// messages. The size of the changesets is calculated before log
+        /// compaction (if enabled). A larger value leads to more efficient
+        /// log compaction and download, at the expense of higher memory pressure,
+        /// higher latency for sending the first changeset, and a higher probability
+        /// for the need to resend the same changes after network disconnects.
+        size_t max_download_size = 0x20000; // 128 KiB
+
+        /// Set the `TCP_NODELAY` option on all TCP/IP sockets. This disables
+        /// the Nagle algorithm. Disabling it, can in some cases be used to
+        /// decrease latencies, but possibly at the expense of scalability. Be
+        /// sure to research the subject before you enable this option.
+        bool tcp_no_delay = false;
+    };
+
+    Server(const std::string& root_dir, util::Optional<PKey> public_key, 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;
+
+    /// A connection which has not been sending any messages or pings for
+    /// `idle_timeout_ms` is considered idle and will be dropped by the server.
+    void set_idle_timeout_ms(uint_fast64_t idle_timeout_ms);
+
+    /// Close all connections with error code ProtocolError::connection_closed.
+    ///
+    /// This function exists mainly for debugging purposes.
+    void close_connections();
+
+private:
+    class Implementation;
+    std::unique_ptr<Implementation> m_impl;
+};
+
+
+class Server::TokenExpirationClock {
+public:
+    /// Number of seconds since the Epoch. The Epoch is the epoch of
+    /// std::chrono::system_clock.
+    virtual std::int_fast64_t now() noexcept = 0;
+
+    virtual ~TokenExpirationClock() {}
+};
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_SERVER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/server_configuration.hpp b/iOS/Pods/Realm/include/core/realm/sync/server_configuration.hpp
new file mode 100644 (file)
index 0000000..bd43a04
--- /dev/null
@@ -0,0 +1,94 @@
+/*************************************************************************
+ *
+ * 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
+
+#include <vector>
+
+// Realm headers
+#include <realm/util/logger.hpp>
+#include <realm/util/optional.hpp>
+#include <realm/sync/server.hpp>
+
+namespace realm {
+namespace config {
+
+struct Configuration {
+    std::string id = "";
+    std::string listen_address = "127.0.0.1";
+    std::string listen_port = ""; // Empty means choose default based on `ssl`.
+    realm::util::Optional<std::string> root_dir;
+    std::string user_data_dir;
+    realm::util::Optional<std::string> public_key_path;
+    realm::util::Optional<std::string> 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<std::string> log_path;
+    long max_open_files = 256;
+    bool ssl = false;
+    std::string ssl_certificate_path;
+    std::string ssl_certificate_key_path;
+    std::string dashboard_stats_endpoint = "localhost:28125";
+    uint_fast64_t drop_period_s = 60; // 1 minute
+    uint_fast64_t idle_timeout_s = 1800; // 30 minutes
+    realm::sync::Server::Config::OperatingMode operating_mode =
+       realm::sync::Server::Config::OperatingMode::MasterWithNoSlave;
+    std::string master_address;
+    std::string master_port;
+    bool master_slave_ssl = false;
+    bool master_verify_ssl_certificate = true;
+    util::Optional<std::string> master_ssl_trust_certificate_path;
+    std::string master_slave_shared_secret;
+    util::Optional<std::string> feature_token;
+    util::Optional<std::string> feature_token_path;
+    bool enable_download_log_compaction = true;
+    size_t max_download_size = 0x20000; // 128 KiB
+};
+
+#if !REALM_MOBILE
+void show_help(const std::string& program_name);
+Configuration build_configuration(int argc, char* argv[]);
+#endif
+Configuration load_configuration(std::string configuration_file_path);
+
+} // namespace config
+
+
+namespace sync {
+
+/// Initialise the directory structure as required (create missing directory
+/// structure) for correct operation of the server. This function is supposed to
+/// be executed prior to instantiating the \c Server object.
+///
+/// Note: This function also handles migration of server-side Realm files from
+/// the legacy format (see _impl::ensure_legacy_migration_1()).
+///
+/// The type of migration performed by this function is nonatomic, and it
+/// therefore requires that no other thread or process has any of the servers
+/// Realm files open concurrently. The application is advised to make sure that
+/// all agents (including the sync server), that might open server-side Realm
+/// files are not started until after this function has completed sucessfully.
+void prepare_server_directory(const config::Configuration&, util::Logger&);
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_SERVER_CONFIGURATION_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/sync/transform.hpp b/iOS/Pods/Realm/include/core/realm/sync/transform.hpp
new file mode 100644 (file)
index 0000000..33c6624
--- /dev/null
@@ -0,0 +1,315 @@
+/*************************************************************************
+ *
+ * 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 <stddef.h>
+
+#include <realm/util/buffer.hpp>
+#include <realm/impl/cont_transact_hist.hpp>
+#include <realm/group_shared.hpp>
+#include <realm/chunked_binary.hpp>
+
+namespace realm {
+namespace sync {
+
+struct Changeset;
+
+/// Represents an entry in the history of changes in a sync-enabled Realm
+/// file. Server and client use different history formats, but this class is
+/// used both on the server and the client side. Each history entry corresponds
+/// to a version of the Realm state. For server and client-side histories, these
+/// versions are referred to as *server versions* and *client versions*
+/// respectively. These versions may, or may not correspond to Realm snapshot
+/// numbers (on the server-side they currently do not).
+///
+/// FIXME: Move this class into a separate header
+/// (`<realm/sync/history_entry.hpp>`).
+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`.
+    timestamp_type origin_timestamp;
+
+    /// For changes of local origin, this is the identifier of the local
+    /// file. On the client side, the special value, zero, is used as a stand-in
+    /// for the actual file identifier. This is necessary because changes may
+    /// occur on the client-side before it obtains the the actual identifier
+    /// from the server. Depending on context, the special value, zero, will, or
+    /// will not have been replaced by the actual local file identifier.
+    ///
+    /// For changes of remote origin, this is the identifier of the file in the
+    /// context of which this change originated. This may be a client, or a
+    /// server-side file. For example, when a change "travels" from client file
+    /// A with identifier 2, through the server, to client file B with
+    /// identifier 3, then `origin_client_file_ident` will be 2 on the server
+    /// and in client file A. On the other hand, if the change originates on the
+    /// server, and the server-side file identifier is 1, then
+    /// `origin_client_file_ident` will be 1 in both client files.
+    ///
+    /// FIXME: Rename this member to `origin_file_ident`. It is no longer
+    /// necessarily a client-side file.
+    file_ident_type origin_client_file_ident;
+
+    /// For changes of local origin on the client side, this is the last server
+    /// version integrated locally prior to this history entry. In other words,
+    /// it is a copy of `remote_version` of the last preceding history entry
+    /// that carries changes of remote origin, or zero if there is no such
+    /// preceding history entry.
+    ///
+    /// For changes of local origin on the server-side, this is always zero.
+    ///
+    /// For changes of remote origin, this is the version produced within the
+    /// remote-side Realm file by the change that gave rise to this history
+    /// entry. The remote-side Realm file is not always the same Realm file from
+    /// which the change originated. On the client side, the remote side is
+    /// always the server side, and `remote_version` is always a server version
+    /// (since clients do not speak directly to each other). On the server side,
+    /// the remote side is always a client side, and `remote_version` is always
+    /// a client version.
+    version_type remote_version;
+
+    /// Referenced memory is not owned by this class.
+    ChunkedBinaryData 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 where the produced version is greater than
+    /// `begin_version` and less than or equal to `end_version`, and whose
+    /// changeset is neither empty, nor produced by integration of a changeset
+    /// received from the specified remote peer.
+    ///
+    /// If \a buffer is non-null, memory will be allocated and transferred to
+    /// \a buffer. The changeset will be copied into the newly allocated memory.
+    ///
+    /// If \a buffer is null, the changeset is not copied out of the Realm,
+    /// and entry.changeset.data() does not point to the changeset.
+    /// The changeset in the Realm could be chunked, hence it is not possible
+    /// to point to it with BinaryData. entry.changeset.size() always gives the
+    /// size of the changeset.
+    ///
+    /// \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;
+
+    /// Get the specified reciprocal changeset. The targeted history entry is
+    /// the one whose untransformed changeset produced the specified version.
+    ///
+    /// \param remote_client_file_ident Zero if the remote peer is the server,
+    /// otherwise the peer identifier of a client.
+    virtual ChunkedBinaryData get_reciprocal_transform(version_type version,
+                                                       file_ident_type remote_client_file_ident) const = 0;
+
+    /// Replace the specified reciprocally transformed changeset. The targeted
+    /// history entry is the one whose untransformed changeset produced the
+    /// specified version.
+    ///
+    /// \param remote_client_file_ident See get_reciprocal_transform().
+    ///
+    /// \param encoded_changeset The new reciprocally transformed changeset.
+    virtual void set_reciprocal_transform(version_type version,
+                                          file_ident_type remote_client_file_ident,
+                                          BinaryData encoded_changeset) = 0;
+
+    virtual ~TransformHistory() 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;
+
+    class RemoteChangeset;
+    class Reporter;
+
+    /// Produce an operationally transformed version of the specified changesets,
+    /// which are 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
+    /// changesets 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
+    /// changesets. Upon return, the transformed changesets are concatenated
+    /// and placed in \a output_buffer.
+    ///
+    /// \throw TransformError Thrown if operational transformation fails due to
+    /// a problem with the specified changeset.
+    virtual void transform_remote_changesets(TransformHistory&,
+                                             version_type current_local_version,
+                                             Changeset* changesets,
+                                             std::size_t num_changesets,
+                                             Reporter* = nullptr) = 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<Transformer> make_transformer(Transformer::file_ident_type local_client_file_ident);
+
+class Transformer::RemoteChangeset {
+public:
+    /// 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 = 0;
+
+    /// 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 = 0;
+
+    /// The changeset itself.
+    ChunkedBinaryData data;
+
+    /// Same meaning as `HistoryEntry::origin_timestamp`.
+    timestamp_type origin_timestamp = 0;
+
+    /// Same meaning as `HistoryEntry::origin_client_file_ident`.
+    file_ident_type origin_client_file_ident = 0;
+
+    /// If the changeset was compacted during download, the size of the original
+    /// changeset.
+    size_t original_changeset_size = 0;
+
+    RemoteChangeset() {}
+    RemoteChangeset(version_type rv, version_type lv, ChunkedBinaryData d, timestamp_type ot,
+                    file_ident_type fi);
+};
+
+class Transformer::Reporter {
+public:
+    virtual void report_merges(long num_merges) = 0;
+};
+
+
+void parse_remote_changeset(const Transformer::RemoteChangeset&, Changeset&);
+
+
+
+
+// Implementation
+
+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,
+                                                     ChunkedBinaryData 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/iOS/Pods/Realm/include/core/realm/sync/version.hpp b/iOS/Pods/Realm/include/core/realm/sync/version.hpp
new file mode 100644 (file)
index 0000000..88ce3bc
--- /dev/null
@@ -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 <realm/util/features.h>
+
+#define REALM_SYNC_VER_MAJOR 2
+#define REALM_SYNC_VER_MINOR 2
+#define REALM_SYNC_VER_PATCH 9
+#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/iOS/Pods/Realm/include/core/realm/table.hpp b/iOS/Pods/Realm/include/core/realm/table.hpp
new file mode 100644 (file)
index 0000000..5f92a9d
--- /dev/null
@@ -0,0 +1,2751 @@
+/*************************************************************************
+ *
+ * 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 <algorithm>
+#include <map>
+#include <utility>
+#include <typeinfo>
+#include <memory>
+#include <mutex>
+
+#include <realm/util/features.h>
+#include <realm/util/thread.hpp>
+#include <realm/table_ref.hpp>
+#include <realm/link_view_fwd.hpp>
+#include <realm/row.hpp>
+#include <realm/descriptor_fwd.hpp>
+#include <realm/spec.hpp>
+#include <realm/mixed.hpp>
+#include <realm/query.hpp>
+#include <realm/column.hpp>
+#include <realm/column_binary.hpp>
+
+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>
+class Columns;
+template <class>
+class SubQuery;
+struct LinkTargetInfo;
+
+struct SubTable {
+};
+struct Link {
+};
+typedef Link LinkList;
+typedef Link BackLink;
+
+namespace _impl {
+class TableFriend;
+}
+namespace metrics {
+class QueryInfo;
+}
+
+class Replication;
+
+
+/// 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 the
+    /// 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 the 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. If you want to manipulate subtable indexes, you must use
+    /// the Descriptor interface.
+    ///
+    /// \param column_ndx The index of a column of the 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<size_t> 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 <class T>
+    Columns<T> column(size_t column); // FIXME: Should this one have been declared noexcept?
+    template <class T>
+    Columns<T> column(const Table& origin, size_t origin_column_ndx);
+
+    template <class T>
+    SubQuery<T> column(size_t column, Query subquery);
+    template <class T>
+    SubQuery<T> 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<Table> RowExpr;
+    typedef BasicRowExpr<const Table> 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.
+    ///
+    /// remove_recursive() will delete linked rows if the removed link was the
+    /// last one holding on to the row in question. This will be done recursively.
+    ///
+    /// 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);
+    size_t add_row_with_key(size_t col_ndx, int64_t key);
+    void remove(size_t row_ndx);
+    void remove_recursive(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);
+    void move_row(size_t from_ndx, size_t to_ndx);
+    //@}
+
+    /// 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.
+    ///
+    /// When fetching from a nullable column and the value is null, a default
+    /// value will be returned, except for object like types (StringData,
+    /// BinaryData, Timestamp) which have support for storing nulls. In that
+    /// case, call the `is_null()` method on the returned object to check
+    /// whether the stored value was null. If nullability matters and returning
+    /// a default value is unacceptable, check Table::is_null() before getting a
+    /// cell value.
+    ///
+    /// \sa Table::is_nullable(size_t col_ndx)
+    /// \sa Table::is_null(size_t col_ndx, size_t row_ndx)
+    /// \sa StringData::is_null()
+    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;
+    BinaryIterator get_binary_iterator(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;
+
+    //@}
+
+    /// Return data from position 'pos' and onwards. If the blob is distributed
+    /// across multiple arrays, 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_binary_at(size_t col_ndx, size_t ndx, size_t& pos) const noexcept;
+
+    template <class T>
+    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;
+
+    //@{
+
+    /// 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
+    /// transformation. 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<int64_t>::max();
+    static constexpr int_fast64_t min_integer = std::numeric_limits<int64_t>::min();
+
+    template <class T>
+    void set(size_t c, size_t r, T value, bool is_default = false);
+
+    template <class T>
+    size_t set_unique(size_t c, size_t r, T value);
+
+    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 <class E>
+    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);
+
+    // Sync needs to store blobs bigger than 16 M. This function can be used for that. Data should be read
+    // out again using the get_binary_at() function. Should not be used for user data as normal get_binary()
+    // will just return null if the data is bigger than the limit.
+    void set_binary_big(size_t column_ndx, size_t row_ndx, BinaryData value, bool is_default = false);
+
+    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.)
+    // Currently, subtables of subtables are not supported.
+    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, bool only_strong_links = false) const noexcept;
+    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
+    template <class T>
+    size_t find_first(size_t column_ndx, T value) const;
+
+    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 <class T>
+    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) &ge; v`, and upper_bound_int(0,v)
+    /// returns the index '`u`' of the first row such that
+    /// `get_int(0,u) &gt; 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<std::string, std::string>* 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;
+
+    /// Compute the sum of the sizes in number of bytes of all the array nodes
+    /// that currently make up this table. See also
+    /// Group::compute_aggregate_byte_size().
+    ///
+    /// If this table accessor is the detached state, this function returns
+    /// zero.
+    size_t compute_aggregated_byte_size() 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<HandoverPatch>& patch);
+    static TableRef create_from_and_consume_patch(std::unique_ptr<HandoverPatch>& 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<>.
+    TableRef get_subtable_tableref(size_t col_ndx, size_t row_ndx);
+
+    /// See non-const get_subtable_tableref().
+    ConstTableRef get_subtable_tableref(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)
+
+    // Management class for the spec object. Only if the table has an independent
+    // spec, the spec object should be deleted when the table object is deleted.
+    // If the table has a shared spec, the spec object is managed by the spec object
+    // of the containing table.
+    class SpecPtr {
+    public:
+        ~SpecPtr()
+         {
+            optionally_delete();
+         }
+        void manage(Spec* ptr)
+        {
+            optionally_delete();
+            m_p = ptr;
+            m_is_managed = true;
+        }
+        void detach()
+        {
+            if (m_is_managed) {
+                m_p->detach();
+            }
+        }
+        SpecPtr& operator=(Spec* ptr)
+        {
+            optionally_delete();
+            m_p = ptr;
+            m_is_managed = false;
+            return *this;
+        }
+        Spec* operator->() const
+        {
+            return m_p;
+        }
+        Spec* get() const
+        {
+            return m_p;
+        }
+        Spec& operator*() const
+        {
+            return *m_p;
+        }
+        operator bool() const
+        {
+            return m_p != nullptr;
+        }
+        bool is_managed() const
+        {
+            return m_is_managed;
+        }
+
+    private:
+        Spec* m_p = nullptr;
+        bool m_is_managed = false;
+
+        void optionally_delete()
+        {
+            if (m_is_managed) {
+                delete m_p;
+            }
+        }
+    };
+
+    SpecPtr 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<ColumnBase*> column_accessors;
+    column_accessors m_cols;
+
+    mutable std::atomic<size_t> 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<Descriptor> m_descriptor;
+
+    // Table view instances
+    // Access needs to be protected by m_accessor_mutex
+    typedef std::vector<TableViewBase*> 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<size_t> 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_move_row(size_t from_ndx, size_t to_ndx);
+    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 <class ColType, class T>
+    size_t do_find_unique(ColType& col, size_t ndx, T&& value, bool& conflict);
+    template <class ColType>
+    size_t do_set_unique_null(ColType& col, size_t ndx, bool& conflict);
+    template <class ColType, class T>
+    size_t do_set_unique(ColType& column, size_t row_ndx, T&& value, bool& conflict);
+
+    void _add_search_index(size_t column_ndx);
+    void _remove_search_index(size_t column_ndx);
+
+    void rebuild_search_index(size_t current_file_format_version);
+
+    // Upgrades OldDateTime columns to Timestamp columns
+    void upgrade_olddatetime();
+
+    // Indicate that the current global state version has been "observed". Until this
+    // happens, bumping of the global version counter can be bypassed, as any query
+    // checking for a version change will see the older version change anyways.
+    // Also returns the table-local version.
+    uint64_t observe_version() const noexcept;
+
+    /// 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&) = delete;
+
+    /// 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(Spec* 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_add_search_index(Descriptor&, size_t col_ndx);
+    static void do_remove_search_index(Descriptor&, size_t col_ndx);
+
+    struct InsertSubtableColumns;
+    struct EraseSubtableColumns;
+    struct RenameSubtableColumns;
+
+    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 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_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<size_t>& widths) const;
+    void to_string_row(size_t row_ndx, std::ostream& out, const std::vector<size_t>& widths) const;
+
+    // recursive methods called by to_json, to follow links
+    void to_json(std::ostream& out, size_t link_depth, std::map<std::string, std::string>& renames,
+                 std::vector<ref_type>& followed) const;
+    void to_json_row(size_t row_ndx, std::ostream& out, size_t link_depth,
+                     std::map<std::string, std::string>& renames, std::vector<ref_type>& followed) const;
+    void to_json_row(size_t row_ndx, std::ostream& out, size_t link_depth = 0,
+                     std::map<std::string, std::string>* 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 <class T, ColumnType col_type>
+    T& get_column(size_t ndx);
+
+    template <class T, ColumnType col_type>
+    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 verify_column(size_t col_ndx, const ColumnBase* col) const;
+
+    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;
+
+    //@{
+
+    /// 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<size_t>& 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.)
+    TableRef 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_move_row(size_t from_ndx, size_t to_ndx) 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_move_row(size_t from_ndx, size_t to_ndx) 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;
+
+    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_spec_accessor();
+
+    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;
+    std::recursive_mutex* get_parent_accessor_management_lock() const;
+#ifdef REALM_DEBUG
+    void to_dot_internal(std::ostream&) const;
+#endif
+
+    friend class SubtableNode;
+    friend class _impl::TableFriend;
+    friend class Query;
+    friend class metrics::QueryInfo;
+    template <class>
+    friend class util::bind_ptr;
+    template <class>
+    friend class SimpleQuerySupport;
+    friend class LangBindHelper;
+    friend class TableViewBase;
+    template <class T>
+    friend class Columns;
+    friend class Columns<StringData>;
+    friend class ParentNode;
+    template <class>
+    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;
+
+    virtual Spec* get_subtable_spec() 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;
+    virtual std::recursive_mutex* get_accessor_management_lock() noexcept = 0;
+
+    friend class Table;
+};
+
+
+// Implementation:
+
+
+inline uint_fast64_t Table::get_version_counter() const noexcept
+{
+    return observe_version();
+}
+
+inline uint64_t Table::observe_version() const noexcept
+{
+    m_top.get_alloc().observe_version();
+    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);
+
+    std::recursive_mutex* lock = get_parent_accessor_management_lock();
+    if (lock) {
+        std::lock_guard<std::recursive_mutex> lg(*lock);
+        if (m_ref_count == 0)
+            delete this;
+    }
+    else {
+        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<TableViewBase*>(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*>(parent));
+    return static_cast<Parent*>(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 <class Col, ColumnType col_type>
+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&>(col);
+}
+
+template <class Col, ColumnType col_type>
+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<const Col&>(col);
+}
+
+inline bool Table::has_shared_type() const noexcept
+{
+    REALM_ASSERT(is_attached());
+    return !m_top.is_attached();
+}
+
+inline void Table::verify_column(size_t col_ndx, const ColumnBase* col) const
+{
+    // Check if the column exists at the expected location
+    if (REALM_LIKELY(col_ndx < m_cols.size() && m_cols[col_ndx] == col))
+        return;
+    // The column might be elsewhere in the list
+    for (auto c : m_cols) {
+        if (c == col)
+            return;
+    }
+    throw LogicError(LogicError::column_does_not_exist);
+}
+
+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_ref_count = 1; // Explicitly 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_ref_count = 1; // Explicitly 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_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> 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> 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 <class T>
+inline Columns<T> Table::column(size_t column_ndx)
+{
+    std::vector<size_t> 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<T, int64_t>::value && ct != type_Int)
+        throw(LogicError::type_mismatch);
+    else if (std::is_same<T, bool>::value && ct != type_Bool)
+        throw(LogicError::type_mismatch);
+    else if (std::is_same<T, realm::OldDateTime>::value && ct != type_OldDateTime)
+        throw(LogicError::type_mismatch);
+    else if (std::is_same<T, float>::value && ct != type_Float)
+        throw(LogicError::type_mismatch);
+    else if (std::is_same<T, double>::value && ct != type_Double)
+        throw(LogicError::type_mismatch);
+
+    if (std::is_same<T, Link>::value || std::is_same<T, LinkList>::value || std::is_same<T, BackLink>::value) {
+        link_chain.push_back(column_ndx);
+    }
+
+    return Columns<T>(column_ndx, this, std::move(link_chain));
+}
+
+template <class T>
+inline Columns<T> Table::column(const Table& origin, size_t origin_col_ndx)
+{
+    static_assert(std::is_same<T, BackLink>::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<size_t> link_chain = std::move(m_link_chain);
+    m_link_chain.clear();
+    link_chain.push_back(backlink_col_ndx);
+
+    return Columns<T>(backlink_col_ndx, this, std::move(link_chain));
+}
+
+template <class T>
+SubQuery<T> Table::column(size_t column_ndx, Query subquery)
+{
+    static_assert(std::is_same<T, LinkList>::value, "A subquery must involve a link list or backlink column");
+    return SubQuery<T>(column<T>(column_ndx), std::move(subquery));
+}
+
+template <class T>
+SubQuery<T> Table::column(const Table& origin, size_t origin_col_ndx, Query subquery)
+{
+    static_assert(std::is_same<T, BackLink>::value, "A subquery must involve a link list or backlink column");
+    return SubQuery<T>(column<T>(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 ConstTableRef Table::get_subtable_tableref(size_t col_ndx, size_t row_ndx) const
+{
+    return const_cast<Table*>(this)->get_subtable_tableref(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<Table*>(this)->get_link_target(col_ndx);
+}
+
+template <class E>
+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 get_subtable_tableref(column_ndx, row_ndx);
+}
+
+inline ConstTableRef Table::get_subtable(size_t column_ndx, size_t row_ndx) const
+{
+    return get_subtable_tableref(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
+{
+    if (!is_attached()) {
+        throw LogicError{LogicError::detached_accessor};
+    }
+
+    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<int_least64_t, int_least64_t> 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<const Table*>(this)->get_parent_table_ptr(column_ndx_out);
+    return const_cast<Table*>(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*>(parent));
+    return static_cast<Parent*>(parent)->record_subtable_path(begin, end);
+}
+
+inline size_t* Table::Parent::record_subtable_path(size_t* begin, size_t*) noexcept
+{
+    return begin;
+}
+
+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);
+    }
+}
+
+// Declare our explicit specializations so that the inline wrappers don't try
+// to instantiate them
+template<> int64_t Table::get<int64_t>(size_t, size_t) const noexcept;
+template<> util::Optional<int64_t> Table::get<util::Optional<int64_t>>(size_t, size_t) const noexcept;
+template<> bool Table::get<bool>(size_t, size_t) const noexcept;
+template<> Optional<bool> Table::get<Optional<bool>>(size_t, size_t) const noexcept;
+template<> float Table::get<float>(size_t, size_t) const noexcept;
+template<> util::Optional<float> Table::get<util::Optional<float>>(size_t, size_t) const noexcept;
+template<> double Table::get<double>(size_t, size_t) const noexcept;
+template<> util::Optional<double> Table::get<util::Optional<double>>(size_t, size_t) const noexcept;
+template<> OldDateTime Table::get<OldDateTime>(size_t, size_t) const noexcept;
+template<> Timestamp Table::get<Timestamp>(size_t, size_t) const noexcept;
+template<> StringData Table::get<StringData>(size_t, size_t) const noexcept;
+template<> BinaryData Table::get<BinaryData>(size_t, size_t) const noexcept;
+template<> BinaryIterator Table::get<BinaryIterator>(size_t, size_t) const noexcept;
+template<> Mixed Table::get<Mixed>(size_t, size_t) const noexcept;
+
+template<> void Table::set<int64_t>(size_t, size_t, int64_t, bool);
+template<> void Table::set<bool>(size_t, size_t, bool, bool);
+template<> void Table::set<float>(size_t, size_t, float, bool);
+template<> void Table::set<double>(size_t, size_t, double, bool);
+template<> void Table::set<OldDateTime>(size_t, size_t, OldDateTime, bool);
+template<> void Table::set<Timestamp>(size_t, size_t, Timestamp, bool);
+template<> void Table::set<StringData>(size_t, size_t, StringData, bool);
+template<> void Table::set<BinaryData>(size_t, size_t, BinaryData, bool);
+template<> void Table::set<Mixed>(size_t, size_t, Mixed, bool);
+template<> void Table::set<null>(size_t, size_t, null, bool);
+
+template<> size_t Table::set_unique<int64_t>(size_t, size_t, int64_t);
+template<> size_t Table::set_unique<StringData>(size_t, size_t, StringData);
+template<> size_t Table::set_unique<null>(size_t, size_t, null);
+
+
+inline int64_t Table::get_int(size_t col_ndx, size_t ndx) const noexcept
+{
+    if (is_nullable(col_ndx))
+        return get<util::Optional<int64_t>>(col_ndx, ndx).value_or(0);
+    else
+        return get<int64_t>(col_ndx, ndx);
+}
+
+inline size_t Table::set_int_unique(size_t col_ndx, size_t ndx, int_fast64_t value)
+{
+    return set_unique(col_ndx, ndx, value);
+}
+
+inline void Table::set_int(size_t col_ndx, size_t ndx, int_fast64_t value, bool is_default)
+{
+    return set(col_ndx, ndx, value, is_default);
+}
+
+inline Timestamp Table::get_timestamp(size_t col_ndx, size_t ndx) const noexcept
+{
+    return get<Timestamp>(col_ndx, ndx);
+}
+
+inline void Table::set_timestamp(size_t col_ndx, size_t ndx, Timestamp value, bool is_default)
+{
+    return set(col_ndx, ndx, value, is_default);
+}
+
+inline bool Table::get_bool(size_t col_ndx, size_t ndx) const noexcept
+{
+    if (is_nullable(col_ndx))
+        return get<util::Optional<bool>>(col_ndx, ndx).value_or(false);
+    else
+        return get<bool>(col_ndx, ndx);
+}
+
+inline void Table::set_bool(size_t col_ndx, size_t ndx, bool value, bool is_default)
+{
+    return set(col_ndx, ndx, value, is_default);
+}
+
+inline OldDateTime Table::get_olddatetime(size_t col_ndx, size_t ndx) const noexcept
+{
+    return get<OldDateTime>(col_ndx, ndx);
+}
+
+inline void Table::set_olddatetime(size_t col_ndx, size_t ndx, OldDateTime value, bool is_default)
+{
+    return set(col_ndx, ndx, value, is_default);
+}
+
+inline float Table::get_float(size_t col_ndx, size_t ndx) const noexcept
+{
+    float f = get<float>(col_ndx, ndx);
+    return null::is_null_float(f) ? 0.0f : f;
+}
+
+inline void Table::set_float(size_t col_ndx, size_t ndx, float value, bool is_default)
+{
+    return set(col_ndx, ndx, value, is_default);
+}
+
+inline double Table::get_double(size_t col_ndx, size_t ndx) const noexcept
+{
+    double d = get<double>(col_ndx, ndx);
+    return null::is_null_float(d) ? 0.0 : d;
+}
+
+inline void Table::set_double(size_t col_ndx, size_t ndx, double value, bool is_default)
+{
+    return set(col_ndx, ndx, value, is_default);
+}
+
+inline StringData Table::get_string(size_t col_ndx, size_t ndx) const noexcept
+{
+    return get<StringData>(col_ndx, ndx);
+}
+
+inline void Table::set_string(size_t col_ndx, size_t ndx, StringData value, bool is_default)
+{
+    return set(col_ndx, ndx, value, is_default);
+}
+
+inline size_t Table::set_string_unique(size_t col_ndx, size_t ndx, StringData value)
+{
+    return set_unique(col_ndx, ndx, value);
+}
+
+inline BinaryData Table::get_binary(size_t col_ndx, size_t ndx) const noexcept
+{
+    return get<BinaryData>(col_ndx, ndx);
+}
+
+inline BinaryIterator Table::get_binary_iterator(size_t col_ndx, size_t ndx) const noexcept
+{
+    return get<BinaryIterator>(col_ndx, ndx);
+}
+
+inline void Table::set_binary(size_t col_ndx, size_t ndx, BinaryData value, bool is_default)
+{
+    set(col_ndx, ndx, value, is_default);
+}
+
+inline Mixed Table::get_mixed(size_t col_ndx, size_t ndx) const noexcept
+{
+    return get<Mixed>(col_ndx, ndx);
+}
+
+inline void Table::set_mixed(size_t col_ndx, size_t ndx, Mixed value, bool is_default)
+{
+    set(col_ndx, ndx, value, is_default);
+}
+
+inline void Table::set_null(size_t col_ndx, size_t ndx, bool is_default)
+{
+    set(col_ndx, ndx, null(), is_default);
+}
+
+inline void Table::set_null_unique(size_t col_ndx, size_t ndx)
+{
+    set_unique(col_ndx, ndx, null());
+}
+
+
+// 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> 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(Spec* shared_spec, Table::Parent* parent_column, size_t parent_row_ndx)
+    {
+        Allocator& alloc = shared_spec->get_alloc();
+        std::unique_ptr<Table> 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> 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_move_row(Table& table, size_t from_ndx, size_t to_ndx)
+    {
+        table.do_move_row(from_ndx, to_ndx); // 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_backlink_count(const Table& table, size_t row_ndx, bool only_strong_links) noexcept
+    {
+        return table.get_backlink_count(row_ndx, only_strong_links);
+    }
+
+    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 add_search_index(Descriptor& desc, size_t column_ndx)
+    {
+        Table::do_add_search_index(desc, column_ndx); // Throws
+    }
+
+    static void remove_search_index(Descriptor& desc, size_t column_ndx)
+    {
+        Table::do_remove_search_index(desc, column_ndx); // 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 TableRef 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&>(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_move_row(Table& table, size_t from_ndx, size_t to_ndx) noexcept
+    {
+        table.adj_acc_move_row(from_ndx, to_ndx);
+    }
+
+    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 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 refresh_spec_accessor(Table& table)
+    {
+        table.refresh_spec_accessor(); // Throws
+    }
+
+    static void set_ndx_in_parent(Table& table, size_t ndx_in_parent) noexcept
+    {
+        table.set_ndx_in_parent(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/iOS/Pods/Realm/include/core/realm/table_ref.hpp b/iOS/Pods/Realm/include/core/realm/table_ref.hpp
new file mode 100644 (file)
index 0000000..6e5c02b
--- /dev/null
@@ -0,0 +1,481 @@
+/*************************************************************************
+ *
+ * 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_REF_HPP
+#define REALM_TABLE_REF_HPP
+
+#include <cstddef>
+#include <algorithm>
+
+#include <realm/util/bind_ptr.hpp>
+
+namespace realm {
+
+
+class Table;
+
+
+/// A reference-counting "smart pointer" for referring to table
+/// accessors.
+///
+/// The purpose of this smart pointer is to keep the referenced table
+/// accessor alive for as long as anybody is referring to it, however,
+/// for stack allocated table accessors, the lifetime is necessarily
+/// determined by scope (see below).
+///
+/// Please take note of the distinction between a "table" and a "table
+/// accessor" here. A table accessor is an instance of `Table`,
+/// and it may, or may not be attached to an
+/// actual table at any specific point in time, but this state of
+/// attachment of the accessor has nothing to do with the function of
+/// the smart pointer. Also, in the rest of the documentation of this
+/// class, whenever you see `Table::%foo`, you are supposed to read it
+/// as, `Table::%foo`.
+///
+///
+/// Table accessors are either created directly by an application via
+/// a call to one of the public table constructors, or they are
+/// created internally by the Realm library, such as when the
+/// application calls Group::get_table(), Table::get_subtable(), or
+/// Table::create().
+///
+/// Applications can safely assume that all table accessors, created
+/// internally by the Realm library, have a lifetime that is managed
+/// by reference counting. This means that the application can prolong
+/// the lifetime of *such* table accessors indefinitely by holding on
+/// to at least one smart pointer, but note that the guarantee of the
+/// continued existence of the accessor, does not imply that the
+/// accessor remains attached to the underlying table (see
+/// Table::is_attached() for details). Accessors whose lifetime are
+/// controlled by reference counting are destroyed exactly when the
+/// reference count drops to zero.
+///
+/// When an application creates a new table accessor by a direct call
+/// to one of the public constructors, the lifetime of that table
+/// accessor is *not*, and cannot be managed by reference
+/// counting. This is true regardless of the way the accessor is
+/// created (i.e., regardless of whether it is an automatic variable
+/// on the stack, or created on the heap using `new`). However, for
+/// convenience, but with one important caveat, it is still possible
+/// to use smart pointers to refer to such accessors. The caveat is
+/// that no smart pointers are allowed to refer to the accessor at the
+/// point in time when its destructor is called. It is entirely the
+/// responsibility of the application to ensure that this requirement
+/// is met. Failing to do so, will result in undefined
+/// behavior. Finally, please note that an application is always free
+/// to use Table::create() as an alternative to creating free-standing
+/// top-level tables on the stack, and that this is indeed neccessary
+/// when fully reference counted lifetimes are required.
+///
+/// So, at any time, and for any table accessor, an application can
+/// call Table::get_table_ref() to obtain a smart pointer that refers
+/// to that table, however, while that is always possible and safe, it
+/// is not always possible to extend the lifetime of an accessor by
+/// holding on to a smart pointer. The question of whether that is
+/// possible, depends directly on the way the accessor was created.
+///
+///
+/// Apart from keeping track of the number of references, these smart
+/// pointers behaves almost exactly like regular pointers. In
+/// particular, it is possible to dereference a TableRef and get a
+/// `Table&` out of it, however, if you are not careful, this can
+/// easily lead to dangling references:
+///
+/// \code{.cpp}
+///
+///   Table& sub_1 = *(table.get_subtable(0,0));
+///   sub_1.add_empty_row(); // Oops, sub_1 may be dangling!
+///
+/// \endcode
+///
+/// Whether `sub_1` is actually dangling in the example above will
+/// depend on whether other references to the same subtable accessor
+/// already exist, but it is never wise to rely in this. Here is a
+/// safe and proper alternative:
+///
+/// \code{.cpp}
+///
+///   TableRef sub_2 = table.get_subtable(0,0);
+///   sub_2.add_empty_row(); // Safe!
+///
+///   void do_something(Table&);
+///   do_something(*(table.get_subtable(0,0))); // Also safe!
+///
+/// \endcode
+///
+///
+/// \sa Table
+/// \sa TableRef
+template <class T>
+class BasicTableRef : util::bind_ptr<T> {
+public:
+    constexpr BasicTableRef() noexcept
+    {
+    }
+    ~BasicTableRef() noexcept
+    {
+    }
+
+    // Copy construct
+    BasicTableRef(const BasicTableRef& r) noexcept
+        : util::bind_ptr<T>(r)
+    {
+    }
+    template <class U>
+    BasicTableRef(const BasicTableRef<U>& r) noexcept
+        : util::bind_ptr<T>(r)
+    {
+    }
+
+    // Copy assign
+    BasicTableRef& operator=(const BasicTableRef&) noexcept;
+    template <class U>
+    BasicTableRef& operator=(const BasicTableRef<U>&) noexcept;
+
+    // Move construct
+    BasicTableRef(BasicTableRef&& r) noexcept
+        : util::bind_ptr<T>(std::move(r))
+    {
+    }
+    template <class U>
+    BasicTableRef(BasicTableRef<U>&& r) noexcept
+        : util::bind_ptr<T>(std::move(r))
+    {
+    }
+
+    // Move assign
+    BasicTableRef& operator=(BasicTableRef&&) noexcept;
+    template <class U>
+    BasicTableRef& operator=(BasicTableRef<U>&&) noexcept;
+
+    //@{
+    /// Comparison
+    template <class U>
+    bool operator==(const BasicTableRef<U>&) const noexcept;
+
+    template <class U>
+    bool operator==(U*) const noexcept;
+
+    template <class U>
+    bool operator!=(const BasicTableRef<U>&) const noexcept;
+
+    template <class U>
+    bool operator!=(U*) const noexcept;
+
+    template <class U>
+    bool operator<(const BasicTableRef<U>&) const noexcept;
+
+    template <class U>
+    bool operator<(U*) const noexcept;
+
+    template <class U>
+    bool operator>(const BasicTableRef<U>&) const noexcept;
+
+    template <class U>
+    bool operator>(U*) const noexcept;
+
+    template <class U>
+    bool operator<=(const BasicTableRef<U>&) const noexcept;
+
+    template <class U>
+    bool operator<=(U*) const noexcept;
+
+    template <class U>
+    bool operator>=(const BasicTableRef<U>&) const noexcept;
+
+    template <class U>
+    bool operator>=(U*) const noexcept;
+//@}
+
+// Dereference
+#ifdef __clang__
+    // Clang has a bug that causes it to effectively ignore the 'using' declaration.
+    T& operator*() const noexcept
+    {
+        return util::bind_ptr<T>::operator*();
+    }
+#else
+    using util::bind_ptr<T>::operator*;
+#endif
+    using util::bind_ptr<T>::operator->;
+
+    using util::bind_ptr<T>::operator bool;
+
+    T* get() const noexcept
+    {
+        return util::bind_ptr<T>::get();
+    }
+    void reset() noexcept
+    {
+        util::bind_ptr<T>::reset();
+    }
+    void reset(T* t) noexcept
+    {
+        util::bind_ptr<T>::reset(t);
+    }
+
+    void swap(BasicTableRef& r) noexcept
+    {
+        this->util::bind_ptr<T>::swap(r);
+    }
+    friend void swap(BasicTableRef& a, BasicTableRef& b) noexcept
+    {
+        a.swap(b);
+    }
+
+    template <class U>
+    friend BasicTableRef<U> unchecked_cast(BasicTableRef<Table>) noexcept;
+
+    template <class U>
+    friend BasicTableRef<const U> unchecked_cast(BasicTableRef<const Table>) noexcept;
+
+private:
+    template <class>
+    struct GetRowAccType {
+        typedef void type;
+    };
+
+    typedef typename GetRowAccType<T>::type RowAccessor;
+
+public:
+    /// Same as 'table[i]' where 'table' is the referenced table.
+    RowAccessor operator[](size_t i) const noexcept
+    {
+        return (*this->get())[i];
+    }
+
+    explicit BasicTableRef(T* t) noexcept
+        : util::bind_ptr<T>(t)
+    {
+    }
+
+    T* release() { return util::bind_ptr<T>::release(); }
+private:
+    friend class SubtableColumnBase;
+    friend class Table;
+    friend class Group;
+
+    template <class>
+    friend class BasicTableRef;
+
+    typedef typename util::bind_ptr<T>::casting_move_tag casting_move_tag;
+    template <class U>
+    BasicTableRef(BasicTableRef<U>* r, casting_move_tag) noexcept
+        : util::bind_ptr<T>(r, casting_move_tag())
+    {
+    }
+};
+
+
+typedef BasicTableRef<Table> TableRef;
+typedef BasicTableRef<const Table> ConstTableRef;
+
+
+template <class C, class T, class U>
+inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, const BasicTableRef<U>& p)
+{
+    out << static_cast<const void*>(&*p);
+    return out;
+}
+
+template <class T>
+inline BasicTableRef<T> unchecked_cast(TableRef t) noexcept
+{
+    return BasicTableRef<T>(&t, typename BasicTableRef<T>::casting_move_tag());
+}
+
+template <class T>
+inline BasicTableRef<const T> unchecked_cast(ConstTableRef t) noexcept
+{
+    return BasicTableRef<const T>(&t, typename BasicTableRef<T>::casting_move_tag());
+}
+
+
+//@{
+/// Comparison
+template <class T, class U>
+bool operator==(T*, const BasicTableRef<U>&) noexcept;
+template <class T, class U>
+bool operator!=(T*, const BasicTableRef<U>&) noexcept;
+template <class T, class U>
+bool operator<(T*, const BasicTableRef<U>&) noexcept;
+template <class T, class U>
+bool operator>(T*, const BasicTableRef<U>&) noexcept;
+template <class T, class U>
+bool operator<=(T*, const BasicTableRef<U>&) noexcept;
+template <class T, class U>
+bool operator>=(T*, const BasicTableRef<U>&) noexcept;
+//@}
+
+
+// Implementation:
+
+template <class T>
+inline BasicTableRef<T>& BasicTableRef<T>::operator=(const BasicTableRef& r) noexcept
+{
+    this->util::bind_ptr<T>::operator=(r);
+    return *this;
+}
+
+template <class T>
+template <class U>
+inline BasicTableRef<T>& BasicTableRef<T>::operator=(const BasicTableRef<U>& r) noexcept
+{
+    this->util::bind_ptr<T>::operator=(r);
+    return *this;
+}
+
+template <class T>
+inline BasicTableRef<T>& BasicTableRef<T>::operator=(BasicTableRef&& r) noexcept
+{
+    this->util::bind_ptr<T>::operator=(std::move(r));
+    return *this;
+}
+
+template <class T>
+template <class U>
+inline BasicTableRef<T>& BasicTableRef<T>::operator=(BasicTableRef<U>&& r) noexcept
+{
+    this->util::bind_ptr<T>::operator=(std::move(r));
+    return *this;
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator==(const BasicTableRef<U>& p) const noexcept
+{
+    return get() == p.get();
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator==(U* p) const noexcept
+{
+    return get() == p;
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator!=(const BasicTableRef<U>& p) const noexcept
+{
+    return get() != p.get();
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator!=(U* p) const noexcept
+{
+    return get() != p;
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator<(const BasicTableRef<U>& p) const noexcept
+{
+    return get() < p.get();
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator<(U* p) const noexcept
+{
+    return get() < p;
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator>(const BasicTableRef<U>& p) const noexcept
+{
+    return get() > p.get();
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator>(U* p) const noexcept
+{
+    return get() > p;
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator<=(const BasicTableRef<U>& p) const noexcept
+{
+    return get() <= p.get();
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator<=(U* p) const noexcept
+{
+    return get() <= p;
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator>=(const BasicTableRef<U>& p) const noexcept
+{
+    return get() >= p.get();
+}
+
+template <class T>
+template <class U>
+bool BasicTableRef<T>::operator>=(U* p) const noexcept
+{
+    return get() >= p;
+}
+
+template <class T, class U>
+bool operator==(T* a, const BasicTableRef<U>& b) noexcept
+{
+    return b == a;
+}
+
+template <class T, class U>
+bool operator!=(T* a, const BasicTableRef<U>& b) noexcept
+{
+    return b != a;
+}
+
+template <class T, class U>
+bool operator<(T* a, const BasicTableRef<U>& b) noexcept
+{
+    return b > a;
+}
+
+template <class T, class U>
+bool operator>(T* a, const BasicTableRef<U>& b) noexcept
+{
+    return b < a;
+}
+
+template <class T, class U>
+bool operator<=(T* a, const BasicTableRef<U>& b) noexcept
+{
+    return b >= a;
+}
+
+template <class T, class U>
+bool operator>=(T* a, const BasicTableRef<U>& b) noexcept
+{
+    return b <= a;
+}
+
+
+} // namespace realm
+
+#endif // REALM_TABLE_REF_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/table_view.hpp b/iOS/Pods/Realm/include/core/realm/table_view.hpp
new file mode 100644 (file)
index 0000000..fefa550
--- /dev/null
@@ -0,0 +1,1603 @@
+/*************************************************************************
+ *
+ * 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_VIEW_HPP
+#define REALM_TABLE_VIEW_HPP
+
+#include <realm/column.hpp>
+#include <realm/link_view.hpp>
+#include <realm/table.hpp>
+#include <realm/util/features.h>
+#include <realm/views.hpp>
+
+namespace realm {
+
+// Views, tables and synchronization between them:
+//
+// Views are built through queries against either tables or another view.
+// Views may be restricted to only hold entries provided by another view.
+// this other view is called the "restricting view".
+// Views may be sorted in ascending or descending order of values in one ore more columns.
+//
+// Views remember the query from which it was originally built.
+// Views remember the table from which it was originally built.
+// Views remember a restricting view if one was used when it was originally built.
+// Views remember the sorting criteria (columns and direction)
+//
+// A view may be operated in one of two distinct modes: *reflective* and *imperative*.
+// Sometimes the term "reactive" is used instead of "reflective" with the same meaning.
+//
+// Reflective views:
+// - A reflective view *always* *reflect* the result of running the query.
+//   If the underlying tables or tableviews change, the reflective view changes as well.
+//   A reflective view may need to rerun the query it was generated from, a potentially
+//   costly operation which happens on demand.
+// - It does not matter whether changes are explicitly done within the transaction, or
+//   occur implicitly as part of advance_read() or promote_to_write().
+//
+// Imperative views:
+// - An imperative view only *initially* holds the result of the query. An imperative
+//   view *never* reruns the query. To force the view to match it's query (by rerunning it),
+//   the view must be operated in reflective mode.
+//   An imperative view can be modified explicitly. References can be added, removed or
+//   changed.
+//
+// - In imperative mode, the references in the view tracks movement of the referenced data:
+//   If you delete an entry which is referenced from a view, said reference is detached,
+//   not removed.
+// - It does not matter whether the delete is done in-line (as part of the current transaction),
+//   or if it is done implicitly as part of advance_read() or promote_to_write().
+//
+// The choice between reflective and imperative views might eventually be represented by a
+// switch on the tableview, but isn't yet. For now, clients (bindings) must call sync_if_needed()
+// to get reflective behavior.
+//
+// Use cases:
+//
+// 1. Presenting data
+// The first use case (and primary motivator behind the reflective view) is to just track
+// and present the state of the database. In this case, the view is operated in reflective
+// mode, it is not modified within the transaction, and it is not used to modify data in
+// other parts of the database.
+//
+// 2. Handover
+// The second use case is "handover." The implicit rerun of the query in our first use case
+// may be too costly to be acceptable on the main thread. Instead you want to run the query
+// on a worker thread, but display it on the main thread. To achieve this, you need two
+// SharedGroups locked on to the same version of the database. If you have that, you can
+// *handover* a view from one thread/SharedGroup to the other.
+//
+// Handover is a two-step procedure. First, the accessors are *exported* from one SharedGroup,
+// called the sourcing group, then it is *imported* into another SharedGroup, called the
+// receiving group. The thread associated with the sourcing SharedGroup will be
+// responsible for the export operation, while the thread associated with the receiving
+// SharedGroup will do the import operation.
+//
+// 3. Iterating a view and changing data
+// The third use case (and a motivator behind the imperative view) is when you want
+// to make changes to the database in accordance with a query result. Imagine you want to
+// find all employees with a salary below a limit and raise their salaries to the limit (pseudocode):
+//
+//    promote_to_write();
+//    view = table.where().less_than(salary_column,limit).find_all();
+//    for (size_t i = 0; i < view.size(); ++i) {
+//        view.set_int(salary_column, i, limit);
+//        // add this to get reflective mode: view.sync_if_needed();
+//    }
+//    commit_and_continue_as_read();
+//
+// This is idiomatic imperative code and it works if the view is operated in imperative mode.
+//
+// If the view is operated in reflective mode, the behaviour surprises most people: When the
+// first salary is changed, the entry no longer fullfills the query, so it is dropped from the
+// view implicitly. view[0] is removed, view[1] moves to view[0] and so forth. But the next
+// loop iteration has i=1 and refers to view[1], thus skipping view[0]. The end result is that
+// every other employee get a raise, while the others don't.
+//
+// 4. Iterating intermixed with implicit updates
+// This leads us to use case 4, which is similar to use case 3, but uses promote_to_write()
+// intermixed with iterating a view. This is actually quite important to some, who do not want
+// to end up with a large write transaction.
+//
+//    view = table.where().less_than(salary_column,limit).find_all();
+//    for (size_t i = 0; i < view.size(); ++i) {
+//        promote_to_write();
+//        view.set_int(salary_column, i, limit);
+//        commit_and_continue_as_write();
+//    }
+//
+// Anything can happen at the call to promote_to_write(). The key question then becomes: how
+// do we support a safe way of realising the original goal (raising salaries) ?
+//
+// using the imperative operating mode:
+//
+//    view = table.where().less_than(salary_column,limit).find_all();
+//    for (size_t i = 0; i < view.size(); ++i) {
+//        promote_to_write();
+//        // add r.sync_if_needed(); to get reflective mode
+//        if (r.is_row_attached(i)) {
+//            Row r = view[i];
+//            r.set_int(salary_column, limit);
+//        }
+//        commit_and_continue_as_write();
+//    }
+//
+// This is safe, and we just aim for providing low level safety: is_row_attached() can tell
+// if the reference is valid, and the references in the view continue to point to the
+// same object at all times, also following implicit updates. The rest is up to the
+// application logic.
+//
+// It is important to see, that there is no guarantee that all relevant employees get
+// their raise in cases whith concurrent updates. At every call to promote_to_write() new
+// employees may be added to the underlying table, but as the view is in imperative mode,
+// these new employees are not added to the view. Also at promote_to_write() an existing
+// employee could recieve a (different, larger) raise which would then be overwritten and lost.
+// However, these are problems that you should expect, since the activity is spread over multiple
+// transactions.
+
+
+/// Common base class for TableView and ConstTableView.
+class TableViewBase : public RowIndexes {
+public:
+    // - not in use / implemented yet:   ... explicit calls to sync_if_needed() must be used
+    //                                       to get 'reflective' mode.
+    //    enum mode { mode_Reflective, mode_Imperative };
+    //    void set_operating_mode(mode);
+    //    mode get_operating_mode();
+    bool is_empty() const noexcept;
+
+    // Tells if the table that this TableView points at still exists or has been deleted.
+    bool is_attached() const noexcept;
+
+    bool is_row_attached(size_t row_ndx) const noexcept;
+    size_t size() const noexcept;
+    size_t num_attached_rows() const noexcept;
+
+    // Get the query used to create this TableView
+    // The query will have a null source table if this tv was not created from
+    // a query
+    const Query& get_query() const noexcept;
+
+    // Column information
+    const ColumnBase& get_column_base(size_t index) const;
+
+    size_t get_column_count() const noexcept;
+    StringData get_column_name(size_t column_ndx) const noexcept;
+    size_t get_column_index(StringData name) const;
+    DataType get_column_type(size_t column_ndx) const noexcept;
+
+    // Getting values
+    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;
+    Timestamp get_timestamp(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;
+    size_t get_link(size_t column_ndx, size_t row_ndx) const noexcept;
+
+    // Links
+    bool is_null_link(size_t column_ndx, size_t row_ndx) const noexcept;
+
+    // Subtables
+    size_t get_subtable_size(size_t column_ndx, size_t row_ndx) const noexcept;
+
+    // Searching
+    template<typename T>
+    size_t find_first(size_t column_ndx, T value) 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_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_timestamp(size_t column_ndx, Timestamp value) const;
+
+    // Aggregate functions. count_target is ignored by all <int
+    // function> except Count. Hack because of bug in optional
+    // arguments in clang and vs2010 (fixed in 2012)
+    template <int function, typename T, typename R, class ColType>
+    R aggregate(R (ColType::*aggregateMethod)(size_t, size_t, size_t, size_t*) const, size_t column_ndx,
+                T count_target, size_t* return_ndx = nullptr) const;
+
+    int64_t sum_int(size_t column_ndx) const;
+    int64_t maximum_int(size_t column_ndx, size_t* return_ndx = nullptr) const;
+    int64_t minimum_int(size_t column_ndx, size_t* return_ndx = nullptr) const;
+    double average_int(size_t column_ndx, size_t* value_count = nullptr) const;
+    size_t count_int(size_t column_ndx, int64_t target) const;
+
+    double sum_float(size_t column_ndx) const;
+    float maximum_float(size_t column_ndx, size_t* return_ndx = nullptr) const;
+    float minimum_float(size_t column_ndx, size_t* return_ndx = nullptr) const;
+    double average_float(size_t column_ndx, size_t* value_count = nullptr) const;
+    size_t count_float(size_t column_ndx, float target) const;
+
+    double sum_double(size_t column_ndx) const;
+    double maximum_double(size_t column_ndx, size_t* return_ndx = nullptr) const;
+    double minimum_double(size_t column_ndx, size_t* return_ndx = nullptr) const;
+    double average_double(size_t column_ndx, size_t* value_count = nullptr) const;
+    size_t count_double(size_t column_ndx, double target) const;
+
+    OldDateTime maximum_olddatetime(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;
+    Timestamp maximum_timestamp(size_t column_ndx, size_t* return_ndx = nullptr) const;
+    size_t count_timestamp(size_t column_ndx, Timestamp target) const;
+
+    // Simple pivot aggregate method. Experimental! Please do not
+    // document method publicly.
+    void aggregate(size_t group_by_column, size_t aggr_column, Table::AggrType op, Table& result) const;
+
+    // Get row index in the source table this view is "looking" at.
+    size_t get_source_ndx(size_t row_ndx) const noexcept;
+
+    /// Search this view for the specified source table row (specified by its
+    /// index in the source table). If found, the index of that row within this
+    /// view is returned, otherwise `realm::not_found` is returned.
+    size_t find_by_source_ndx(size_t source_ndx) const noexcept;
+
+    // Conversion
+    void to_json(std::ostream&) const;
+    void to_string(std::ostream&, size_t limit = 500) const;
+    void row_to_string(size_t row_ndx, std::ostream&) const;
+
+    // Determine if the view is 'in sync' with the underlying table
+    // as well as other views used to generate the view. Note that updates
+    // through views maintains synchronization between view and table.
+    // It doesnt by itself maintain other views as well. So if a view
+    // is generated from another view (not a table), updates may cause
+    // that view to be outdated, AND as the generated view depends upon
+    // it, it too will become outdated.
+    bool is_in_sync() const;
+
+    // Tells if this TableView depends on a LinkList or row that has been deleted.
+    bool depends_on_deleted_object() const;
+
+    // Synchronize a view to match a table or tableview from which it
+    // has been derived. Synchronization is achieved by rerunning the
+    // query used to generate the view. If derived from another view, that
+    // view will be synchronized as well.
+    //
+    // "live" or "reactive" views are implemented by calling sync_if_needed
+    // before any of the other access-methods whenever the view may have become
+    // outdated.
+    //
+    // This will make the TableView empty and in sync with the highest possible table version
+    // if the TableView depends on an object (LinkView or row) that has been deleted.
+    uint_fast64_t sync_if_needed() const;
+
+    // Sort m_row_indexes according to one column
+    void sort(size_t column, bool ascending = true);
+
+    // Sort m_row_indexes according to multiple columns
+    void sort(SortDescriptor order);
+
+    // Remove rows that are duplicated with respect to the column set passed as argument.
+    // distinct() will preserve the original order of the row pointers, also if the order is a result of sort()
+    // If two rows are indentical (for the given set of distinct-columns), then the last row is removed.
+    // You can call sync_if_needed() to update the distinct view, just like you can for a sorted view.
+    // Each time you call distinct() it will compound on the previous calls
+    void distinct(size_t column);
+    void distinct(DistinctDescriptor columns);
+
+    // Replace the order of sort and distinct operations, bypassing manually
+    // calling sort and distinct. This is a convenience method for bindings.
+    void apply_descriptor_ordering(DescriptorOrdering new_ordering);
+
+    // Returns whether the rows are guaranteed to be in table order.
+    // This is true only of unsorted TableViews created from either:
+    // - Table::find_all()
+    // - Query::find_all() when the query is not restricted to a view.
+    bool is_in_table_order() const;
+
+    virtual ~TableViewBase() noexcept;
+
+    virtual std::unique_ptr<TableViewBase> clone() const = 0;
+
+protected:
+    // This TableView can be "born" from 4 different sources:
+    // - LinkView
+    // - Query::find_all()
+    // - Table::get_distinct_view()
+    // - Table::get_backlink_view()
+    // Return the version of the source it was created from.
+    uint64_t outside_version() const;
+
+    void do_sync();
+
+    // Null if, and only if, the view is detached.
+    mutable TableRef m_table;
+
+    // The link column that this view contain backlinks for.
+    const BacklinkColumn* m_linked_column = nullptr;
+    // The target row that rows in this view link to.
+    ConstRow m_linked_row;
+
+    // If this TableView was created from a LinkView, then this reference points to it. Otherwise it's 0
+    mutable ConstLinkViewRef m_linkview_source;
+
+    // m_distinct_column_source != npos if this view was created from distinct values in a column of m_table.
+    size_t m_distinct_column_source = npos;
+
+    // Stores the ordering criteria of applied sort and distinct operations.
+    DescriptorOrdering m_descriptor_ordering;
+
+    // A valid query holds a reference to its table which must match our m_table.
+    // hence we can use a query with a null table reference to indicate that the view
+    // was NOT generated by a query, but follows a table directly.
+    Query m_query;
+    // parameters for findall, needed to rerun the query
+    size_t m_start;
+    size_t m_end;
+    size_t m_limit;
+
+    mutable util::Optional<uint_fast64_t> m_last_seen_version;
+
+    size_t m_num_detached_refs = 0;
+    /// Construct null view (no memory allocated).
+    TableViewBase();
+
+    /// Construct empty view, ready for addition of row indices.
+    TableViewBase(Table* parent);
+    TableViewBase(Table* parent, Query& query, size_t start, size_t end, size_t limit);
+    TableViewBase(Table* parent, size_t column, BasicRowExpr<const Table> row);
+    TableViewBase(Table* parent, ConstLinkViewRef link_view);
+
+    enum DistinctViewTag { DistinctView };
+    TableViewBase(DistinctViewTag, Table* parent, size_t column_ndx);
+
+    /// Copy constructor.
+    TableViewBase(const TableViewBase&);
+
+    /// Move constructor.
+    TableViewBase(TableViewBase&&) noexcept;
+
+    TableViewBase& operator=(const TableViewBase&);
+    TableViewBase& operator=(TableViewBase&&) noexcept;
+
+    template <class R, class V>
+    static R find_all_integer(V*, size_t, int64_t);
+
+    template <class R, class V>
+    static R find_all_float(V*, size_t, float);
+
+    template <class R, class V>
+    static R find_all_double(V*, size_t, double);
+
+    template <class R, class V>
+    static R find_all_string(V*, size_t, StringData);
+
+    using HandoverPatch = TableViewHandoverPatch;
+
+    // handover machinery entry points based on dynamic type. These methods:
+    // a) forward their calls to the static type entry points.
+    // b) new/delete patch data structures.
+    virtual std::unique_ptr<TableViewBase> clone_for_handover(std::unique_ptr<HandoverPatch>& patch,
+                                                              ConstSourcePayload mode) const = 0;
+
+    virtual std::unique_ptr<TableViewBase> clone_for_handover(std::unique_ptr<HandoverPatch>& patch,
+                                                              MutableSourcePayload mode) = 0;
+
+    void apply_and_consume_patch(std::unique_ptr<HandoverPatch>& patch, Group& group)
+    {
+        apply_patch(*patch, group);
+        patch.reset();
+    }
+    // handover machinery entry points based on static type
+    void apply_patch(HandoverPatch& patch, Group& group);
+    TableViewBase(const TableViewBase& source, HandoverPatch& patch, ConstSourcePayload mode);
+    TableViewBase(TableViewBase& source, HandoverPatch& patch, MutableSourcePayload mode);
+
+private:
+    void allocate_row_indexes();
+    void detach() const noexcept; // may have to remove const
+    size_t find_first_integer(size_t column_ndx, int64_t value) const;
+    template <class oper>
+    Timestamp minmax_timestamp(size_t column_ndx, size_t* return_ndx) const;
+
+    friend class Table;
+    friend class Query;
+    friend class SharedGroup;
+
+    // Called by table to adjust any row references:
+    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_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept;
+    void adj_row_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept;
+    void adj_row_acc_move_row(size_t from_row_ndx, size_t to_row_ndx) noexcept;
+    void adj_row_acc_clear() noexcept;
+};
+
+
+inline void TableViewBase::detach() const noexcept // may have to remove const
+{
+    m_table = TableRef();
+}
+
+
+class ConstTableView;
+
+
+enum class RemoveMode { ordered, unordered };
+
+
+/// A TableView gives read and write access to the parent table.
+///
+/// A 'const TableView' cannot be changed (e.g. sorted), nor can the
+/// parent table be modified through it.
+///
+/// A TableView is both copyable and movable.
+class TableView : public TableViewBase {
+public:
+    using TableViewBase::TableViewBase;
+
+    TableView() = default;
+
+    // Rows
+    typedef BasicRowExpr<Table> RowExpr;
+    typedef BasicRowExpr<const Table> 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;
+
+    // Setting values
+    void set_int(size_t column_ndx, size_t row_ndx, int64_t value);
+    void set_bool(size_t column_ndx, size_t row_ndx, bool value);
+    void set_olddatetime(size_t column_ndx, size_t row_ndx, OldDateTime value);
+    void set_timestamp(size_t column_ndx, size_t row_ndx, Timestamp value);
+    template <class E>
+    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);
+    void set_double(size_t column_ndx, size_t row_ndx, double value);
+    void set_string(size_t column_ndx, size_t row_ndx, StringData value);
+    void set_binary(size_t column_ndx, size_t row_ndx, BinaryData value);
+    void set_mixed(size_t column_ndx, size_t row_ndx, Mixed value);
+    void set_subtable(size_t column_ndx, size_t row_ndx, const Table* table);
+    void set_link(size_t column_ndx, size_t row_ndx, size_t target_row_ndx);
+
+    // Subtables
+    TableRef get_subtable(size_t column_ndx, size_t row_ndx);
+    ConstTableRef get_subtable(size_t column_ndx, size_t row_ndx) const;
+    void clear_subtable(size_t column_ndx, size_t row_ndx);
+
+    // Links
+    TableRef get_link_target(size_t column_ndx) noexcept;
+    ConstTableRef get_link_target(size_t column_ndx) const noexcept;
+    void nullify_link(size_t column_ndx, size_t row_ndx);
+
+    /// \defgroup table_view_removes
+    //@{
+    /// \brief Remove the specified row (or rows) from the underlying table.
+    ///
+    /// remove() removes the specified row from the underlying table,
+    /// remove_last() removes the last row in the table view from the underlying
+    /// table, and clear removes all the rows in the table view from the
+    /// underlying table.
+    ///
+    /// When rows are removed from the underlying table, they will by necessity
+    /// also be removed from the table view.
+    ///
+    /// The order of the remaining rows in the the table view will be maintained
+    /// regardless of the value passed for \a underlying_mode.
+    ///
+    /// \param row_ndx The index within this table view of the row to be
+    /// removed.
+    ///
+    /// \param underlying_mode If set to RemoveMode::ordered (the default), the
+    /// rows will be removed from the underlying table in a way that maintains
+    /// the order of the remaining rows in the underlying table. If set to
+    /// RemoveMode::unordered, the order of the remaining rows in the underlying
+    /// table will not in general be maintaind, but the operation will generally
+    /// be much faster. In any case, the order of remaining rows in the table
+    /// view will not be affected.
+    void remove(size_t row_ndx, RemoveMode underlying_mode = RemoveMode::ordered);
+    void remove_last(RemoveMode underlying_mode = RemoveMode::ordered);
+    void clear(RemoveMode underlying_mode = RemoveMode::ordered);
+    //@}
+
+    // Searching (Int and String)
+    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;
+    // FIXME: Need: TableView find_all_binary(size_t column_ndx, BinaryData value);
+    // FIXME: Need: ConstTableView find_all_binary(size_t column_ndx, BinaryData value) const;
+
+    Table& get_parent() noexcept;
+    const Table& get_parent() const noexcept;
+
+    std::unique_ptr<TableViewBase> clone() const override
+    {
+        return std::unique_ptr<TableViewBase>(new TableView(*this));
+    }
+
+    std::unique_ptr<TableViewBase> clone_for_handover(std::unique_ptr<HandoverPatch>& patch,
+                                                      ConstSourcePayload mode) const override
+    {
+        patch.reset(new HandoverPatch);
+        std::unique_ptr<TableViewBase> retval(new TableView(*this, *patch, mode));
+        return retval;
+    }
+
+    std::unique_ptr<TableViewBase> clone_for_handover(std::unique_ptr<HandoverPatch>& patch,
+                                                      MutableSourcePayload mode) override
+    {
+        patch.reset(new HandoverPatch);
+        std::unique_ptr<TableViewBase> retval(new TableView(*this, *patch, mode));
+        return retval;
+    }
+
+private:
+    TableView(Table& parent);
+    TableView(Table& parent, Query& query, size_t start, size_t end, size_t limit);
+    TableView(Table& parent, ConstLinkViewRef);
+
+    TableView(DistinctViewTag, Table& parent, size_t column_ndx);
+
+    TableView find_all_integer(size_t column_ndx, int64_t value);
+    ConstTableView find_all_integer(size_t column_ndx, int64_t value) const;
+
+    friend class ConstTableView;
+    friend class Table;
+    friend class Query;
+    friend class TableViewBase;
+    friend class LinkView;
+};
+
+
+/// A ConstTableView gives read access to the parent table, but no
+/// write access. The view itself, though, can be changed, for
+/// example, it can be sorted.
+///
+/// Note that methods are declared 'const' if, and only if they leave
+/// the view unmodified, and this is irrespective of whether they
+/// modify the parent table.
+///
+/// A ConstTableView has both copy and move semantics. See TableView
+/// for more on this.
+class ConstTableView : public TableViewBase {
+public:
+    using TableViewBase::TableViewBase;
+
+    ConstTableView() = default;
+
+    ConstTableView(const TableView&);
+    ConstTableView(TableView&&);
+    ConstTableView& operator=(const TableView&);
+    ConstTableView& operator=(TableView&&);
+
+    // Rows
+    typedef BasicRowExpr<const Table> ConstRowExpr;
+    ConstRowExpr get(size_t row_ndx) const noexcept;
+    ConstRowExpr front() const noexcept;
+    ConstRowExpr back() const noexcept;
+    ConstRowExpr operator[](size_t row_ndx) const noexcept;
+
+    // Subtables
+    ConstTableRef get_subtable(size_t column_ndx, size_t row_ndx) const;
+
+    // Links
+    ConstTableRef get_link_target(size_t column_ndx) const noexcept;
+
+    // Searching (Int and String)
+    ConstTableView find_all_int(size_t column_ndx, int64_t value) const;
+    ConstTableView find_all_bool(size_t column_ndx, bool value) const;
+    ConstTableView find_all_olddatetime(size_t column_ndx, OldDateTime value) const;
+    ConstTableView find_all_float(size_t column_ndx, float value) const;
+    ConstTableView find_all_double(size_t column_ndx, double value) const;
+    ConstTableView find_all_string(size_t column_ndx, StringData value) const;
+
+    const Table& get_parent() const noexcept;
+
+    std::unique_ptr<TableViewBase> clone() const override
+    {
+        return std::unique_ptr<TableViewBase>(new ConstTableView(*this));
+    }
+
+    std::unique_ptr<TableViewBase> clone_for_handover(std::unique_ptr<HandoverPatch>& patch,
+                                                      ConstSourcePayload mode) const override
+    {
+        patch.reset(new HandoverPatch);
+        std::unique_ptr<TableViewBase> retval(new ConstTableView(*this, *patch, mode));
+        return retval;
+    }
+
+    std::unique_ptr<TableViewBase> clone_for_handover(std::unique_ptr<HandoverPatch>& patch,
+                                                      MutableSourcePayload mode) override
+    {
+        patch.reset(new HandoverPatch);
+        std::unique_ptr<TableViewBase> retval(new ConstTableView(*this, *patch, mode));
+        return retval;
+    }
+
+private:
+    ConstTableView(const Table& parent);
+
+    ConstTableView find_all_integer(size_t column_ndx, int64_t value) const;
+
+    friend class TableView;
+    friend class Table;
+    friend class Query;
+    friend class TableViewBase;
+};
+
+
+// ================================================================================================
+// TableViewBase Implementation:
+
+inline const Query& TableViewBase::get_query() const noexcept
+{
+    return m_query;
+}
+
+inline bool TableViewBase::is_empty() const noexcept
+{
+    return m_row_indexes.is_empty();
+}
+
+inline bool TableViewBase::is_attached() const noexcept
+{
+    return bool(m_table);
+}
+
+inline bool TableViewBase::is_row_attached(size_t row_ndx) const noexcept
+{
+    return m_row_indexes.get(row_ndx) != detached_ref;
+}
+
+inline size_t TableViewBase::size() const noexcept
+{
+    return m_row_indexes.size();
+}
+
+inline size_t TableViewBase::num_attached_rows() const noexcept
+{
+    return m_row_indexes.size() - m_num_detached_refs;
+}
+
+inline size_t TableViewBase::get_source_ndx(size_t row_ndx) const noexcept
+{
+    return to_size_t(m_row_indexes.get(row_ndx));
+}
+
+inline size_t TableViewBase::find_by_source_ndx(size_t source_ndx) const noexcept
+{
+    REALM_ASSERT(source_ndx < m_table->size());
+    return m_row_indexes.find_first(source_ndx);
+}
+
+inline void TableViewBase::allocate_row_indexes()
+{
+    // FIXME: This code is unreasonably complicated because it uses `IntegerColumn` as
+    // a free-standing container, and beause `IntegerColumn` does not conform to the
+    // RAII idiom (nor should it).
+    Allocator& alloc = m_row_indexes.get_alloc();
+    _impl::DeepArrayRefDestroyGuard ref_guard(alloc);
+    ref_guard.reset(IntegerColumn::create(alloc)); // Throws
+    m_table->register_view(this);                   // Throws
+    m_row_indexes.init_from_ref(alloc, ref_guard.release());
+}
+
+inline TableViewBase::TableViewBase()
+    : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) // Throws
+{
+    ref_type ref = IntegerColumn::create(m_row_indexes.get_alloc()); // Throws
+    m_row_indexes.get_root_array()->init_from_ref(ref);
+}
+
+inline TableViewBase::TableViewBase(Table* parent)
+    : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default())
+    , m_table(parent->get_table_ref()) // Throws
+    , m_last_seen_version(m_table ? util::make_optional(m_table->m_version) : util::none)
+{
+    allocate_row_indexes();
+}
+
+inline TableViewBase::TableViewBase(Table* parent, Query& query, size_t start, size_t end, size_t limit)
+    : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) // Throws
+    , m_table(parent->get_table_ref())
+    , m_query(query)
+    , m_start(start)
+    , m_end(end)
+    , m_limit(limit)
+    , m_last_seen_version(outside_version())
+{
+    allocate_row_indexes();
+}
+
+inline TableViewBase::TableViewBase(Table* parent, size_t column, BasicRowExpr<const Table> row)
+    : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default())
+    , m_table(parent->get_table_ref()) // Throws
+    , m_linked_column(&parent->get_column_link_base(column).get_backlink_column())
+    , m_linked_row(row)
+    , m_last_seen_version(m_table ? util::make_optional(m_table->m_version) : util::none)
+{
+    allocate_row_indexes();
+}
+
+inline TableViewBase::TableViewBase(DistinctViewTag, Table* parent, size_t column_ndx)
+    : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default())
+    , m_table(parent->get_table_ref()) // Throws
+    , m_distinct_column_source(column_ndx)
+    , m_last_seen_version(m_table ? util::make_optional(m_table->m_version) : util::none)
+{
+    REALM_ASSERT(m_distinct_column_source != npos);
+
+    allocate_row_indexes();
+}
+
+inline TableViewBase::TableViewBase(Table* parent, ConstLinkViewRef link_view)
+    : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default())
+    , m_table(parent->get_table_ref()) // Throws
+    , m_linkview_source(std::move(link_view))
+    , m_last_seen_version(m_table ? util::make_optional(m_table->m_version) : util::none)
+{
+    REALM_ASSERT(m_linkview_source);
+
+    allocate_row_indexes();
+}
+
+inline TableViewBase::TableViewBase(const TableViewBase& tv)
+    : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default())
+    , m_table(tv.m_table)
+    , m_linked_column(tv.m_linked_column)
+    , m_linked_row(tv.m_linked_row)
+    , m_linkview_source(tv.m_linkview_source)
+    , m_distinct_column_source(tv.m_distinct_column_source)
+    , m_descriptor_ordering(std::move(tv.m_descriptor_ordering))
+    , m_query(tv.m_query)
+    , m_start(tv.m_start)
+    , m_end(tv.m_end)
+    , m_limit(tv.m_limit)
+    , m_last_seen_version(tv.m_last_seen_version)
+    , m_num_detached_refs(tv.m_num_detached_refs)
+{
+    // FIXME: This code is unreasonably complicated because it uses `IntegerColumn` as
+    // a free-standing container, and because `IntegerColumn` does not conform to the
+    // RAII idiom (nor should it).
+    Allocator& alloc = m_row_indexes.get_alloc();
+    MemRef mem = tv.m_row_indexes.get_root_array()->clone_deep(alloc); // Throws
+    _impl::DeepArrayRefDestroyGuard ref_guard(mem.get_ref(), alloc);
+    if (m_table)
+        m_table->register_view(this); // Throws
+    m_row_indexes.init_from_mem(alloc, mem);
+    ref_guard.release();
+}
+
+inline TableViewBase::TableViewBase(TableViewBase&& tv) noexcept
+    : RowIndexes(std::move(tv.m_row_indexes))
+    , m_table(std::move(tv.m_table))
+    , m_linked_column(tv.m_linked_column)
+    , m_linked_row(tv.m_linked_row)
+    , m_linkview_source(std::move(tv.m_linkview_source))
+    , m_distinct_column_source(tv.m_distinct_column_source)
+    , m_descriptor_ordering(std::move(tv.m_descriptor_ordering))
+    , m_query(std::move(tv.m_query))
+    , m_start(tv.m_start)
+    , m_end(tv.m_end)
+    , m_limit(tv.m_limit)
+    ,
+    // if we are created from a table view which is outdated, take care to use the outdated
+    // version number so that we can later trigger a sync if needed.
+    m_last_seen_version(tv.m_last_seen_version)
+    , m_num_detached_refs(tv.m_num_detached_refs)
+{
+    if (m_table)
+        m_table->move_registered_view(&tv, this);
+}
+
+inline TableViewBase::~TableViewBase() noexcept
+{
+    if (m_table) {
+        m_table->unregister_view(this);
+        m_table = TableRef();
+    }
+    m_row_indexes.destroy(); // Shallow
+}
+
+inline TableViewBase& TableViewBase::operator=(TableViewBase&& tv) noexcept
+{
+    if (m_table)
+        m_table->unregister_view(this);
+    m_table = std::move(tv.m_table);
+    if (m_table)
+        m_table->move_registered_view(&tv, this);
+
+    m_row_indexes.move_assign(tv.m_row_indexes);
+    m_query = std::move(tv.m_query);
+    m_num_detached_refs = tv.m_num_detached_refs;
+    m_last_seen_version = tv.m_last_seen_version;
+    m_start = tv.m_start;
+    m_end = tv.m_end;
+    m_limit = tv.m_limit;
+    m_linked_column = tv.m_linked_column;
+    m_linked_row = tv.m_linked_row;
+    m_linkview_source = std::move(tv.m_linkview_source);
+    m_descriptor_ordering = std::move(tv.m_descriptor_ordering);
+    m_distinct_column_source = tv.m_distinct_column_source;
+
+    return *this;
+}
+
+inline TableViewBase& TableViewBase::operator=(const TableViewBase& tv)
+{
+    if (this == &tv)
+        return *this;
+
+    if (m_table != tv.m_table) {
+        if (m_table)
+            m_table->unregister_view(this);
+        m_table = tv.m_table;
+        if (m_table)
+            m_table->register_view(this);
+    }
+
+    Allocator& alloc = m_row_indexes.get_alloc();
+    MemRef mem = tv.m_row_indexes.get_root_array()->clone_deep(alloc); // Throws
+    _impl::DeepArrayRefDestroyGuard ref_guard(mem.get_ref(), alloc);
+    m_row_indexes.destroy();
+    m_row_indexes.get_root_array()->init_from_mem(mem);
+    ref_guard.release();
+
+    m_query = tv.m_query;
+    m_num_detached_refs = tv.m_num_detached_refs;
+    m_last_seen_version = tv.m_last_seen_version;
+    m_start = tv.m_start;
+    m_end = tv.m_end;
+    m_limit = tv.m_limit;
+    m_linked_column = tv.m_linked_column;
+    m_linked_row = tv.m_linked_row;
+    m_linkview_source = tv.m_linkview_source;
+    m_descriptor_ordering = tv.m_descriptor_ordering;
+    m_distinct_column_source = tv.m_distinct_column_source;
+
+    return *this;
+}
+
+#define REALM_ASSERT_COLUMN(column_ndx)                                                                              \
+    REALM_ASSERT(m_table);                                                                                           \
+    REALM_ASSERT(column_ndx < m_table->get_column_count())
+
+#define REALM_ASSERT_ROW(row_ndx)                                                                                    \
+    REALM_ASSERT(m_table);                                                                                           \
+    REALM_ASSERT(row_ndx < m_row_indexes.size())
+
+#define REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, column_type)                                                        \
+    REALM_ASSERT_COLUMN(column_ndx);                                                                                 \
+    REALM_DIAG_PUSH();                                                                                               \
+    REALM_DIAG_IGNORE_TAUTOLOGICAL_COMPARE();                                                                        \
+    REALM_ASSERT(m_table->get_column_type(column_ndx) == column_type ||                                              \
+                 (m_table->get_column_type(column_ndx) == type_OldDateTime && column_type == type_Int));             \
+    REALM_DIAG_POP()
+
+#define REALM_ASSERT_INDEX(column_ndx, row_ndx)                                                                      \
+    REALM_ASSERT_COLUMN(column_ndx);                                                                                 \
+    REALM_ASSERT(row_ndx < m_row_indexes.size())
+
+#define REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, column_type)                                                \
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, column_type);                                                           \
+    REALM_ASSERT(row_ndx < m_row_indexes.size())
+
+#define REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx)                                              \
+    REALM_ASSERT_COLUMN(column_ndx);                                                                                 \
+    REALM_DIAG_PUSH();                                                                                               \
+    REALM_DIAG_IGNORE_TAUTOLOGICAL_COMPARE();                                                                        \
+    REALM_ASSERT(m_table->get_column_type(column_ndx) == type_Table ||                                               \
+                 (m_table->get_column_type(column_ndx) == type_Mixed));                                              \
+    REALM_DIAG_POP();                                                                                                \
+    REALM_ASSERT(row_ndx < m_row_indexes.size())
+
+// Column information
+
+inline const ColumnBase& TableViewBase::get_column_base(size_t index) const
+{
+    return m_table->get_column_base(index);
+}
+
+inline size_t TableViewBase::get_column_count() const noexcept
+{
+    REALM_ASSERT(m_table);
+    return m_table->get_column_count();
+}
+
+inline StringData TableViewBase::get_column_name(size_t column_ndx) const noexcept
+{
+    REALM_ASSERT(m_table);
+    return m_table->get_column_name(column_ndx);
+}
+
+inline size_t TableViewBase::get_column_index(StringData name) const
+{
+    REALM_ASSERT(m_table);
+    return m_table->get_column_index(name);
+}
+
+inline DataType TableViewBase::get_column_type(size_t column_ndx) const noexcept
+{
+    REALM_ASSERT(m_table);
+    return m_table->get_column_type(column_ndx);
+}
+
+
+// Getters
+
+
+inline int64_t TableViewBase::get_int(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX(column_ndx, row_ndx);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_int(column_ndx, to_size_t(real_ndx));
+}
+
+inline bool TableViewBase::get_bool(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Bool);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_bool(column_ndx, to_size_t(real_ndx));
+}
+
+inline OldDateTime TableViewBase::get_olddatetime(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_OldDateTime);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_olddatetime(column_ndx, to_size_t(real_ndx));
+}
+
+inline Timestamp TableViewBase::get_timestamp(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Timestamp);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_timestamp(column_ndx, to_size_t(real_ndx));
+}
+
+inline float TableViewBase::get_float(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Float);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_float(column_ndx, to_size_t(real_ndx));
+}
+
+inline double TableViewBase::get_double(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Double);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_double(column_ndx, to_size_t(real_ndx));
+}
+
+inline StringData TableViewBase::get_string(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_String);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_string(column_ndx, to_size_t(real_ndx));
+}
+
+inline BinaryData TableViewBase::get_binary(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Binary);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_binary(column_ndx, to_size_t(real_ndx));
+}
+
+inline Mixed TableViewBase::get_mixed(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Mixed);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_mixed(column_ndx, to_size_t(real_ndx));
+}
+
+inline DataType TableViewBase::get_mixed_type(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Mixed);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_mixed_type(column_ndx, to_size_t(real_ndx));
+}
+
+inline size_t TableViewBase::get_subtable_size(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_subtable_size(column_ndx, to_size_t(real_ndx));
+}
+
+inline size_t TableViewBase::get_link(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Link);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_link(column_ndx, to_size_t(real_ndx));
+}
+
+inline TableRef TableView::get_link_target(size_t column_ndx) noexcept
+{
+    return m_table->get_link_target(column_ndx);
+}
+
+inline ConstTableRef TableView::get_link_target(size_t column_ndx) const noexcept
+{
+    return m_table->get_link_target(column_ndx);
+}
+
+inline ConstTableRef ConstTableView::get_link_target(size_t column_ndx) const noexcept
+{
+    return m_table->get_link_target(column_ndx);
+}
+
+inline bool TableViewBase::is_null_link(size_t column_ndx, size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Link);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->is_null_link(column_ndx, to_size_t(real_ndx));
+}
+
+
+// Searching
+
+
+inline size_t TableViewBase::find_first_int(size_t column_ndx, int64_t value) const
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Int);
+    return find_first_integer(column_ndx, value);
+}
+
+inline size_t TableViewBase::find_first_bool(size_t column_ndx, bool value) const
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Bool);
+    return find_first_integer(column_ndx, value ? 1 : 0);
+}
+
+inline size_t TableViewBase::find_first_olddatetime(size_t column_ndx, OldDateTime value) const
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_OldDateTime);
+    return find_first_integer(column_ndx, int64_t(value.get_olddatetime()));
+}
+
+inline size_t TableViewBase::find_first_integer(size_t column_ndx, int64_t value) const
+{
+    return find_first<int64_t>(column_ndx, value);
+}
+
+inline size_t TableViewBase::find_first_float(size_t column_ndx, float value) const
+{
+    return find_first<float>(column_ndx, value);
+}
+
+inline size_t TableViewBase::find_first_double(size_t column_ndx, double value) const
+{
+    return find_first<double>(column_ndx, value);
+}
+
+inline size_t TableViewBase::find_first_string(size_t column_ndx, StringData value) const
+{
+    return find_first<StringData>(column_ndx, value);
+}
+
+inline size_t TableViewBase::find_first_binary(size_t column_ndx, BinaryData value) const
+{
+    return find_first<BinaryData>(column_ndx, value);
+}
+
+inline size_t TableViewBase::find_first_timestamp(size_t column_ndx, Timestamp value) const
+{
+    return find_first<Timestamp>(column_ndx, value);
+}
+
+
+template <class R, class V>
+R TableViewBase::find_all_integer(V* view, size_t column_ndx, int64_t value)
+{
+    typedef typename std::remove_const<V>::type TNonConst;
+    return view->m_table->where(const_cast<TNonConst*>(view)).equal(column_ndx, value).find_all();
+}
+
+template <class R, class V>
+R TableViewBase::find_all_float(V* view, size_t column_ndx, float value)
+{
+    typedef typename std::remove_const<V>::type TNonConst;
+    return view->m_table->where(const_cast<TNonConst*>(view)).equal(column_ndx, value).find_all();
+}
+
+template <class R, class V>
+R TableViewBase::find_all_double(V* view, size_t column_ndx, double value)
+{
+    typedef typename std::remove_const<V>::type TNonConst;
+    return view->m_table->where(const_cast<TNonConst*>(view)).equal(column_ndx, value).find_all();
+}
+
+template <class R, class V>
+R TableViewBase::find_all_string(V* view, size_t column_ndx, StringData value)
+{
+    typedef typename std::remove_const<V>::type TNonConst;
+    return view->m_table->where(const_cast<TNonConst*>(view)).equal(column_ndx, value).find_all();
+}
+
+
+//-------------------------- TableView, ConstTableView implementation:
+
+inline ConstTableView::ConstTableView(const TableView& tv)
+    : TableViewBase(tv)
+{
+}
+
+inline ConstTableView::ConstTableView(TableView&& tv)
+    : TableViewBase(std::move(tv))
+{
+}
+
+inline void TableView::remove_last(RemoveMode underlying_mode)
+{
+    if (!is_empty())
+        remove(size() - 1, underlying_mode);
+}
+
+inline Table& TableView::get_parent() noexcept
+{
+    return *m_table;
+}
+
+inline const Table& TableView::get_parent() const noexcept
+{
+    return *m_table;
+}
+
+inline const Table& ConstTableView::get_parent() const noexcept
+{
+    return *m_table;
+}
+
+inline TableView::TableView(Table& parent)
+    : TableViewBase(&parent)
+{
+}
+
+inline TableView::TableView(Table& parent, Query& query, size_t start, size_t end, size_t limit)
+    : TableViewBase(&parent, query, start, end, limit)
+{
+}
+
+inline TableView::TableView(Table& parent, ConstLinkViewRef link_view)
+: TableViewBase(&parent, std::move(link_view))
+{
+}
+
+inline TableView::TableView(TableViewBase::DistinctViewTag, Table& parent, size_t column_ndx)
+    : TableViewBase(TableViewBase::DistinctView, &parent, column_ndx)
+{
+}
+
+inline ConstTableView::ConstTableView(const Table& parent)
+    : TableViewBase(const_cast<Table*>(&parent))
+{
+}
+
+inline ConstTableView& ConstTableView::operator=(const TableView& tv)
+{
+    TableViewBase::operator=(tv);
+    return *this;
+}
+
+inline ConstTableView& ConstTableView::operator=(TableView&& tv)
+{
+    TableViewBase::operator=(std::move(tv));
+    return *this;
+}
+
+
+// - string
+inline TableView TableView::find_all_string(size_t column_ndx, StringData value)
+{
+    return TableViewBase::find_all_string<TableView>(this, column_ndx, value);
+}
+
+inline ConstTableView TableView::find_all_string(size_t column_ndx, StringData value) const
+{
+    return TableViewBase::find_all_string<ConstTableView>(this, column_ndx, value);
+}
+
+inline ConstTableView ConstTableView::find_all_string(size_t column_ndx, StringData value) const
+{
+    return TableViewBase::find_all_string<ConstTableView>(this, column_ndx, value);
+}
+
+// - float
+inline TableView TableView::find_all_float(size_t column_ndx, float value)
+{
+    return TableViewBase::find_all_float<TableView>(this, column_ndx, value);
+}
+
+inline ConstTableView TableView::find_all_float(size_t column_ndx, float value) const
+{
+    return TableViewBase::find_all_float<ConstTableView>(this, column_ndx, value);
+}
+
+inline ConstTableView ConstTableView::find_all_float(size_t column_ndx, float value) const
+{
+    return TableViewBase::find_all_float<ConstTableView>(this, column_ndx, value);
+}
+
+
+// - double
+inline TableView TableView::find_all_double(size_t column_ndx, double value)
+{
+    return TableViewBase::find_all_double<TableView>(this, column_ndx, value);
+}
+
+inline ConstTableView TableView::find_all_double(size_t column_ndx, double value) const
+{
+    return TableViewBase::find_all_double<ConstTableView>(this, column_ndx, value);
+}
+
+inline ConstTableView ConstTableView::find_all_double(size_t column_ndx, double value) const
+{
+    return TableViewBase::find_all_double<ConstTableView>(this, column_ndx, value);
+}
+
+
+// -- 3 variants of the 3 find_all_{int, bool, date} all based on integer
+
+inline TableView TableView::find_all_integer(size_t column_ndx, int64_t value)
+{
+    return TableViewBase::find_all_integer<TableView>(this, column_ndx, value);
+}
+
+inline ConstTableView TableView::find_all_integer(size_t column_ndx, int64_t value) const
+{
+    return TableViewBase::find_all_integer<ConstTableView>(this, column_ndx, value);
+}
+
+inline ConstTableView ConstTableView::find_all_integer(size_t column_ndx, int64_t value) const
+{
+    return TableViewBase::find_all_integer<ConstTableView>(this, column_ndx, value);
+}
+
+
+inline TableView TableView::find_all_int(size_t column_ndx, int64_t value)
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Int);
+    return find_all_integer(column_ndx, value);
+}
+
+inline TableView TableView::find_all_bool(size_t column_ndx, bool value)
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Bool);
+    return find_all_integer(column_ndx, value ? 1 : 0);
+}
+
+inline TableView TableView::find_all_olddatetime(size_t column_ndx, OldDateTime value)
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_OldDateTime);
+    return find_all_integer(column_ndx, int64_t(value.get_olddatetime()));
+}
+
+
+inline ConstTableView TableView::find_all_int(size_t column_ndx, int64_t value) const
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Int);
+    return find_all_integer(column_ndx, value);
+}
+
+inline ConstTableView TableView::find_all_bool(size_t column_ndx, bool value) const
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Bool);
+    return find_all_integer(column_ndx, value ? 1 : 0);
+}
+
+inline ConstTableView TableView::find_all_olddatetime(size_t column_ndx, OldDateTime value) const
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_OldDateTime);
+    return find_all_integer(column_ndx, int64_t(value.get_olddatetime()));
+}
+
+
+inline ConstTableView ConstTableView::find_all_int(size_t column_ndx, int64_t value) const
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Int);
+    return find_all_integer(column_ndx, value);
+}
+
+inline ConstTableView ConstTableView::find_all_bool(size_t column_ndx, bool value) const
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_Bool);
+    return find_all_integer(column_ndx, value ? 1 : 0);
+}
+
+inline ConstTableView ConstTableView::find_all_olddatetime(size_t column_ndx, OldDateTime value) const
+{
+    REALM_ASSERT_COLUMN_AND_TYPE(column_ndx, type_OldDateTime);
+    return find_all_integer(column_ndx, int64_t(value.get_olddatetime()));
+}
+
+
+// Rows
+
+
+inline TableView::RowExpr TableView::get(size_t row_ndx) noexcept
+{
+    REALM_ASSERT_ROW(row_ndx);
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get(to_size_t(real_ndx));
+}
+
+inline TableView::ConstRowExpr TableView::get(size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_ROW(row_ndx);
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get(to_size_t(real_ndx));
+}
+
+inline ConstTableView::ConstRowExpr ConstTableView::get(size_t row_ndx) const noexcept
+{
+    REALM_ASSERT_ROW(row_ndx);
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get(to_size_t(real_ndx));
+}
+
+inline TableView::RowExpr TableView::front() noexcept
+{
+    return get(0);
+}
+
+inline TableView::ConstRowExpr TableView::front() const noexcept
+{
+    return get(0);
+}
+
+inline ConstTableView::ConstRowExpr ConstTableView::front() const noexcept
+{
+    return get(0);
+}
+
+inline TableView::RowExpr TableView::back() noexcept
+{
+    size_t last_row_ndx = size() - 1;
+    return get(last_row_ndx);
+}
+
+inline TableView::ConstRowExpr TableView::back() const noexcept
+{
+    size_t last_row_ndx = size() - 1;
+    return get(last_row_ndx);
+}
+
+inline ConstTableView::ConstRowExpr ConstTableView::back() const noexcept
+{
+    size_t last_row_ndx = size() - 1;
+    return get(last_row_ndx);
+}
+
+inline TableView::RowExpr TableView::operator[](size_t row_ndx) noexcept
+{
+    return get(row_ndx);
+}
+
+inline TableView::ConstRowExpr TableView::operator[](size_t row_ndx) const noexcept
+{
+    return get(row_ndx);
+}
+
+inline ConstTableView::ConstRowExpr ConstTableView::operator[](size_t row_ndx) const noexcept
+{
+    return get(row_ndx);
+}
+
+
+// Subtables
+
+
+inline TableRef TableView::get_subtable(size_t column_ndx, size_t row_ndx)
+{
+    REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_subtable(column_ndx, to_size_t(real_ndx));
+}
+
+inline ConstTableRef TableView::get_subtable(size_t column_ndx, size_t row_ndx) const
+{
+    REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_subtable(column_ndx, to_size_t(real_ndx));
+}
+
+inline ConstTableRef ConstTableView::get_subtable(size_t column_ndx, size_t row_ndx) const
+{
+    REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->get_subtable(column_ndx, to_size_t(real_ndx));
+}
+
+inline void TableView::clear_subtable(size_t column_ndx, size_t row_ndx)
+{
+    REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    return m_table->clear_subtable(column_ndx, to_size_t(real_ndx));
+}
+
+
+// Setters
+
+
+inline void TableView::set_int(size_t column_ndx, size_t row_ndx, int64_t value)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Int);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_int(column_ndx, to_size_t(real_ndx), value);
+}
+
+inline void TableView::set_bool(size_t column_ndx, size_t row_ndx, bool value)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Bool);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_bool(column_ndx, to_size_t(real_ndx), value);
+}
+
+inline void TableView::set_olddatetime(size_t column_ndx, size_t row_ndx, OldDateTime value)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_OldDateTime);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_olddatetime(column_ndx, to_size_t(real_ndx), value);
+}
+
+inline void TableView::set_timestamp(size_t column_ndx, size_t row_ndx, Timestamp value)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Timestamp);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_timestamp(column_ndx, to_size_t(real_ndx), value);
+}
+
+inline void TableView::set_float(size_t column_ndx, size_t row_ndx, float value)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Float);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_float(column_ndx, to_size_t(real_ndx), value);
+}
+
+inline void TableView::set_double(size_t column_ndx, size_t row_ndx, double value)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Double);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_double(column_ndx, to_size_t(real_ndx), value);
+}
+
+template <class E>
+inline void TableView::set_enum(size_t column_ndx, size_t row_ndx, E value)
+{
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_int(column_ndx, real_ndx, value);
+}
+
+inline void TableView::set_string(size_t column_ndx, size_t row_ndx, StringData value)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_String);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_string(column_ndx, to_size_t(real_ndx), value);
+}
+
+inline void TableView::set_binary(size_t column_ndx, size_t row_ndx, BinaryData value)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Binary);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_binary(column_ndx, to_size_t(real_ndx), value);
+}
+
+inline void TableView::set_mixed(size_t column_ndx, size_t row_ndx, Mixed value)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Mixed);
+
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_mixed(column_ndx, to_size_t(real_ndx), value);
+}
+
+inline void TableView::set_subtable(size_t column_ndx, size_t row_ndx, const Table* value)
+{
+    REALM_ASSERT_INDEX_AND_TYPE_TABLE_OR_MIXED(column_ndx, row_ndx);
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_subtable(column_ndx, to_size_t(real_ndx), value);
+}
+
+inline void TableView::set_link(size_t column_ndx, size_t row_ndx, size_t target_row_ndx)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Link);
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->set_link(column_ndx, to_size_t(real_ndx), target_row_ndx);
+}
+
+inline void TableView::nullify_link(size_t column_ndx, size_t row_ndx)
+{
+    REALM_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, type_Link);
+    const int64_t real_ndx = m_row_indexes.get(row_ndx);
+    REALM_ASSERT(real_ndx != detached_ref);
+    m_table->nullify_link(column_ndx, to_size_t(real_ndx));
+}
+
+} // namespace realm
+
+#endif // REALM_TABLE_VIEW_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/timestamp.hpp b/iOS/Pods/Realm/include/core/realm/timestamp.hpp
new file mode 100644 (file)
index 0000000..1e847e4
--- /dev/null
@@ -0,0 +1,167 @@
+/*************************************************************************
+ *
+ * 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_TIMESTAMP_HPP
+#define REALM_TIMESTAMP_HPP
+
+#include <cstdint>
+#include <ostream>
+#include <realm/util/assert.hpp>
+#include <realm/null.hpp>
+
+namespace realm {
+
+class Timestamp {
+public:
+    // Construct from the number of seconds and nanoseconds since the UNIX epoch: 00:00:00 UTC on 1 January 1970
+    //
+    // To split a native nanosecond representation, only division and modulo are necessary:
+    //
+    //     s = native_nano / nanoseconds_per_second
+    //     n = native_nano % nanoseconds_per_second
+    //     Timestamp ts(s, n);
+    //
+    // To convert back into native nanosecond representation, simple multiply and add:
+    //
+    //     native_nano = ts.s * nanoseconds_per_second + ts.n
+    //
+    // Specifically this allows the nanosecond part to become negative (only) for Timestamps before the UNIX epoch.
+    // Usually this will not need special attention, but for reference, valid Timestamps will have one of the
+    // following sign combinations:
+    //
+    //     s | n
+    //     -----
+    //     + | +
+    //     + | 0
+    //     0 | +
+    //     0 | 0
+    //     0 | -
+    //     - | 0
+    //     - | -
+    //
+    // Examples:
+    //     The UNIX epoch is constructed by Timestamp(0, 0)
+    //     Relative times are constructed as follows:
+    //       +1 second is constructed by Timestamp(1, 0)
+    //       +1 nanosecond is constructed by Timestamp(0, 1)
+    //       +1.1 seconds (1100 milliseconds after the epoch) is constructed by Timestamp(1, 100000000)
+    //       -1.1 seconds (1100 milliseconds before the epoch) is constructed by Timestamp(-1, -100000000)
+    //
+    Timestamp(int64_t seconds, int32_t nanoseconds)
+        : m_seconds(seconds)
+        , m_nanoseconds(nanoseconds)
+        , m_is_null(false)
+    {
+        REALM_ASSERT_EX(-nanoseconds_per_second < nanoseconds && nanoseconds < nanoseconds_per_second, nanoseconds);
+        const bool both_non_negative = seconds >= 0 && nanoseconds >= 0;
+        const bool both_non_positive = seconds <= 0 && nanoseconds <= 0;
+        REALM_ASSERT_EX(both_non_negative || both_non_positive, both_non_negative, both_non_positive);
+    }
+    Timestamp(realm::null)
+        : m_is_null(true)
+    {
+    }
+    Timestamp()
+        : Timestamp(null{})
+    {
+    }
+
+    bool is_null() const
+    {
+        return m_is_null;
+    }
+
+    int64_t get_seconds() const noexcept
+    {
+        REALM_ASSERT(!m_is_null);
+        return m_seconds;
+    }
+
+    int32_t get_nanoseconds() const noexcept
+    {
+        REALM_ASSERT(!m_is_null);
+        return m_nanoseconds;
+    }
+
+    // Note that only == and != operators work if one of the Timestamps are null! Else use realm::Greater,
+    // realm::Less, etc, instead. This is in order to collect all treatment of null behaviour in a single place for all
+    // types (query_conditions.hpp) to ensure that all types sort and compare null vs. non-null in the same manner,
+    // especially for int/float where we cannot override operators. This design is open for discussion, though,
+    // because it has usability drawbacks
+    bool operator==(const Timestamp& rhs) const
+    {
+        if (is_null() && rhs.is_null())
+            return true;
+
+        if (is_null() != rhs.is_null())
+            return false;
+
+        return m_seconds == rhs.m_seconds && m_nanoseconds == rhs.m_nanoseconds;
+    }
+    bool operator!=(const Timestamp& rhs) const
+    {
+        return !(*this == rhs);
+    }
+    bool operator>(const Timestamp& rhs) const
+    {
+        REALM_ASSERT(!is_null());
+        REALM_ASSERT(!rhs.is_null());
+        return (m_seconds > rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds > rhs.m_nanoseconds);
+    }
+    bool operator<(const Timestamp& rhs) const
+    {
+        REALM_ASSERT(!is_null());
+        REALM_ASSERT(!rhs.is_null());
+        return (m_seconds < rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds < rhs.m_nanoseconds);
+    }
+    bool operator<=(const Timestamp& rhs) const
+    {
+        REALM_ASSERT(!is_null());
+        REALM_ASSERT(!rhs.is_null());
+        return *this < rhs || *this == rhs;
+    }
+    bool operator>=(const Timestamp& rhs) const
+    {
+        REALM_ASSERT(!is_null());
+        REALM_ASSERT(!rhs.is_null());
+        return *this > rhs || *this == rhs;
+    }
+    Timestamp& operator=(const Timestamp& rhs) = default;
+
+    template <class Ch, class Tr>
+    friend std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& out, const Timestamp&);
+    static constexpr int32_t nanoseconds_per_second = 1000000000;
+
+private:
+    int64_t m_seconds;
+    int32_t m_nanoseconds;
+    bool m_is_null;
+};
+
+// LCOV_EXCL_START
+template <class C, class T>
+inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, const Timestamp& d)
+{
+    out << "Timestamp(" << d.m_seconds << ", " << d.m_nanoseconds << ")";
+    return out;
+}
+// LCOV_EXCL_STOP
+
+} // namespace realm
+
+#endif // REALM_TIMESTAMP_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/unicode.hpp b/iOS/Pods/Realm/include/core/realm/unicode.hpp
new file mode 100644 (file)
index 0000000..d8b3d33
--- /dev/null
@@ -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_UNICODE_HPP
+#define REALM_UNICODE_HPP
+
+#include <locale>
+#include <cstdint>
+#include <string>
+
+#include <realm/string_data.hpp>
+#include <realm/util/features.h>
+#include <realm/utilities.hpp>
+
+
+namespace realm {
+
+enum string_compare_method_t {
+    STRING_COMPARE_CORE,
+    STRING_COMPARE_CPP11,
+    STRING_COMPARE_CALLBACK,
+    STRING_COMPARE_CORE_SIMILAR
+};
+
+extern StringCompareCallback string_compare_callback;
+extern string_compare_method_t string_compare_method;
+
+// Description for set_string_compare_method():
+//
+// Short summary: iOS language binding: call
+//     set_string_compare_method() for fast but slightly inaccurate sort in some countries, or
+//     set_string_compare_method(2, callbackptr) for slow but precise sort (see callbackptr below)
+//
+// Different countries ('locales') have different sorting order for strings and letters. Because there unfortunatly
+// doesn't exist any unified standardized way to compare strings in C++ on multiple platforms, we need this method.
+//
+// It determins how sorting a TableView by a String column must take place. The 'method' argument can be:
+//
+// 0: Fast core-only compare (no OS/framework calls). LIMITATIONS: Works only upto 'Latin Extended 2' (unicodes
+// 0...591). Also, sorting order is according to 'en_US' so it may be slightly inaccurate for some countries.
+// 'callback' argument is ignored.
+//
+// Return value: Always 'true'
+//
+// 1: Native C++11 method if core is compiled as C++11. Gives precise sorting according
+// to user's current locale. LIMITATIONS: Currently works only on Windows and on Linux with clang. Does NOT work on
+// iOS (due to only 'C' locale being available in CoreFoundation, which puts 'Z' before 'a'). Unknown if works on
+// Windows Phone / Android. Furthermore it does NOT work on Linux with gcc 4.7 or 4.8 (lack of c++11 feature that
+// can convert utf8->wstring without calls to setlocale()).
+//
+// Return value: 'true' if supported, otherwise 'false' (if so, then previous setting, if any, is preserved).
+//
+// 2: Callback method. Language binding / C++ user must provide a utf-8 callback method of prototype:
+// bool callback(const char* string1, const char* string2) where 'callback' must return bool(string1 < string2).
+//
+// Return value: Always 'true'
+//
+// Default is method = 0 if the function is never called
+//
+// NOT THREAD SAFE! Call once during initialization or make sure it's not called simultaneously with different
+// arguments. The setting is remembered per-process; it does NOT need to be called prior to each sort
+bool set_string_compare_method(string_compare_method_t method, StringCompareCallback callback);
+
+
+// Return size in bytes of utf8 character. No error checking
+size_t sequence_length(char lead);
+
+// Limitations for case insensitive string search
+// Case insensitive search (equal, begins_with, ends_with, like and contains)
+// only works for unicodes 0...0x7f which is the same as the 0...127
+// ASCII character set (letters a-z and A-Z).
+
+// In does *not* work for the 0...255 ANSI character set that contains
+// characters from many European countries like Germany, France, Denmark,
+// etc.
+
+// It also does not work for characters from non-western countries like
+// Japan, Russia, Arabia, etc.
+
+// If there exists characters outside the ASCII range either in the text
+// to be searched for, or in the Realm string column which is searched
+// in, then the compare yields a random result such that the row may or
+// may not be included in the result set.
+
+// Return bool(string1 < string2)
+bool utf8_compare(StringData string1, StringData string2);
+
+// Return unicode value of character.
+uint32_t utf8value(const char* character);
+
+inline bool equal_sequence(const char*& begin, const char* end, const char* begin2);
+
+// FIXME: The current approach to case insensitive comparison requires
+// that case mappings can be done in a way that does not change he
+// number of bytes used to encode the individual Unicode
+// character. This is not generally the case, so, as far as I can see,
+// this approach has no future.
+//
+// FIXME: The current approach to case insensitive comparison relies
+// on checking each "haystack" character against the corresponding
+// character in both a lower cased and an upper cased version of the
+// "needle". While this leads to efficient comparison, it ignores the
+// fact that "case folding" is the only correct approach to case
+// insensitive comparison in a locale agnostic Unicode
+// environment.
+//
+// See
+//   http://www.w3.org/International/wiki/Case_folding
+//   http://userguide.icu-project.org/transforms/casemappings#TOC-Case-Folding.
+//
+// The ideal API would probably be something like this:
+//
+//   case_fold:        utf_8 -> case_folded
+//   equal_case_fold:  (needle_case_folded, single_haystack_entry_utf_8) -> found
+//   search_case_fold: (needle_case_folded, huge_haystack_string_utf_8) -> found_at_position
+//
+// The case folded form would probably be using UTF-32 or UTF-16.
+
+
+/// If successful, returns a string of the same size as \a source.
+/// Returns none if invalid UTF-8 encoding was encountered.
+util::Optional<std::string> case_map(StringData source, bool upper);
+
+enum IgnoreErrorsTag { IgnoreErrors };
+std::string case_map(StringData source, bool upper, IgnoreErrorsTag);
+
+/// Assumes that the sizes of \a needle_upper and \a needle_lower are
+/// identical to the size of \a haystack. Returns false if the needle
+/// is different from the haystack.
+bool equal_case_fold(StringData haystack, const char* needle_upper, const char* needle_lower);
+
+/// Assumes that the sizes of \a needle_upper and \a needle_lower are
+/// both equal to \a needle_size. Returns haystack.size() if the
+/// needle was not found.
+size_t search_case_fold(StringData haystack, const char* needle_upper, const char* needle_lower, size_t needle_size);
+    
+/// Assumes that the sizes of \a needle_upper and \a needle_lower are
+/// both equal to \a needle_size. Returns false if the
+/// needle was not found.
+bool contains_ins(StringData haystack, const char* needle_upper, const char* needle_lower, size_t needle_size, const std::array<uint8_t, 256> &charmap);
+
+/// Case insensitive wildcard matching ('?' for single char, '*' for zero or more chars)
+bool string_like_ins(StringData text, StringData pattern) noexcept;
+bool string_like_ins(StringData text, StringData upper, StringData lower) noexcept;
+
+} // namespace realm
+
+#endif // REALM_UNICODE_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/aes_cryptor.hpp b/iOS/Pods/Realm/include/core/realm/util/aes_cryptor.hpp
new file mode 100644 (file)
index 0000000..86adb96
--- /dev/null
@@ -0,0 +1,101 @@
+/*************************************************************************
+ *
+ * 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 <cstddef>
+#include <memory>
+#include <realm/util/features.h>
+#include <cstdint>
+#include <vector>
+#include <realm/util/file.hpp>
+
+#if REALM_ENABLE_ENCRYPTION
+
+#if REALM_PLATFORM_APPLE
+#include <CommonCrypto/CommonCrypto.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#include <stdio.h>
+#include <bcrypt.h>
+#pragma comment(lib, "bcrypt.lib")
+#else
+#include <openssl/aes.h>
+#include <openssl/sha.h>
+#endif
+
+namespace realm {
+namespace util {
+
+struct iv_table;
+class EncryptedFileMapping;
+
+class AESCryptor {
+public:
+    AESCryptor(const uint8_t* key);
+    ~AESCryptor() noexcept;
+
+    void set_file_size(off_t new_size);
+
+    bool read(FileDesc fd, off_t pos, char* dst, size_t size);
+    void write(FileDesc fd, off_t pos, const char* src, size_t size) noexcept;
+
+private:
+    enum EncryptionMode {
+#if REALM_PLATFORM_APPLE
+        mode_Encrypt = kCCEncrypt,
+        mode_Decrypt = kCCDecrypt
+#elif defined(_WIN32)
+        mode_Encrypt = 0,
+        mode_Decrypt = 1
+#else
+        mode_Encrypt = AES_ENCRYPT,
+        mode_Decrypt = AES_DECRYPT
+#endif
+    };
+
+#if REALM_PLATFORM_APPLE
+    CCCryptorRef m_encr;
+    CCCryptorRef m_decr;
+#elif defined(_WIN32)
+    BCRYPT_KEY_HANDLE m_aes_key_handle;
+#else
+    AES_KEY m_ectx;
+    AES_KEY m_dctx;
+#endif
+
+    uint8_t m_hmacKey[32];
+    std::vector<iv_table> m_iv_buffer;
+    std::unique_ptr<char[]> m_rw_buffer;
+    std::unique_ptr<char[]> m_dst_buffer;
+
+    void calc_hmac(const void* src, size_t len, uint8_t* dst, const uint8_t* key) const;
+    bool check_hmac(const void* data, size_t len, const uint8_t* hmac) const;
+    void crypt(EncryptionMode mode, off_t pos, char* dst, const char* src, const char* stored_iv) noexcept;
+    iv_table& get_iv_table(FileDesc fd, off_t data_pos) noexcept;
+};
+
+struct SharedFileInfo {
+    FileDesc fd;
+    AESCryptor cryptor;
+    std::vector<EncryptedFileMapping*> mappings;
+
+    SharedFileInfo(const uint8_t* key, FileDesc file_descriptor);
+};
+}
+}
+
+#endif // REALM_ENABLE_ENCRYPTION
diff --git a/iOS/Pods/Realm/include/core/realm/util/any.hpp b/iOS/Pods/Realm/include/core/realm/util/any.hpp
new file mode 100644 (file)
index 0000000..90baef8
--- /dev/null
@@ -0,0 +1,161 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_UTIL_ANY_HPP
+#define REALM_UTIL_ANY_HPP
+
+#include <memory>
+#include <stdexcept>
+#include <type_traits>
+#include <typeinfo>
+
+namespace realm {
+namespace util {
+
+// A naive implementation of C++17's std::any
+// This does not perform the small-object optimization or make any particular
+// attempt at being performant
+class Any final {
+public:
+    // Constructors
+
+    Any() = default;
+    Any(Any&&) noexcept = default;
+    ~Any() = default;
+    Any& operator=(Any&&) noexcept = default;
+
+    Any(Any const& rhs)
+    : m_value(rhs.m_value ? rhs.m_value->copy() : nullptr)
+    {
+    }
+
+    template<typename T, typename = typename std::enable_if<!std::is_same<typename std::decay<T>::type, Any>::value>::type>
+    Any(T&& value)
+    : m_value(std::make_unique<Value<typename std::decay<T>::type>>(std::forward<T>(value)))
+    {
+    }
+
+    Any& operator=(Any const& rhs)
+    {
+        m_value = rhs.m_value ? rhs.m_value->copy() : nullptr;
+        return *this;
+    }
+
+    template<typename T, typename = typename std::enable_if<!std::is_same<typename std::decay<T>::type, Any>::value>::type>
+    Any& operator=(T&& value)
+    {
+        m_value = std::make_unique<Value<typename std::decay<T>::type>>(std::forward<T>(value));
+        return *this;
+    }
+
+    // Modifiers
+
+    void reset() noexcept { m_value.reset(); }
+    void swap(Any& rhs) noexcept { std::swap(m_value, rhs.m_value); }
+
+    // Observers
+
+    bool has_value() const noexcept { return m_value != nullptr; }
+    std::type_info const& type() const noexcept { return m_value ? m_value->type() : typeid(void); }
+
+private:
+    struct ValueBase {
+        virtual ~ValueBase() noexcept { }
+        virtual std::type_info const& type() const noexcept = 0;
+        virtual std::unique_ptr<ValueBase> copy() const = 0;
+    };
+    template<typename T>
+    struct Value : ValueBase {
+        T value;
+        template<typename U> Value(U&& v) : value(std::forward<U>(v)) { }
+
+        std::type_info const& type() const noexcept override { return typeid(T); }
+        std::unique_ptr<ValueBase> copy() const override
+        {
+            return std::make_unique<Value<T>>(value);
+        }
+    };
+    std::unique_ptr<ValueBase> m_value;
+
+    template<typename T>
+    friend const T* any_cast(const Any* operand) noexcept;
+    template<typename T>
+    friend T* any_cast(Any* operand) noexcept;
+
+    template<typename T>
+    const T* cast() const noexcept
+    {
+        return &static_cast<Value<T>*>(m_value.get())->value;
+    }
+
+    template<typename T>
+    T* cast() noexcept
+    {
+        return &static_cast<Value<T>*>(m_value.get())->value;
+    }
+};
+
+template<typename T>
+T any_cast(Any const& value)
+{
+    auto ptr = any_cast<typename std::add_const<typename std::remove_reference<T>::type>::type>(&value);
+    if (!ptr)
+        throw std::bad_cast();
+    return *ptr;
+}
+
+template<typename T>
+T any_cast(Any& value)
+{
+    auto ptr = any_cast<typename std::remove_reference<T>::type>(&value);
+    if (!ptr)
+        throw std::bad_cast();
+    return *ptr;
+}
+
+template<typename T>
+T any_cast(Any&& value)
+{
+    auto ptr = any_cast<typename std::remove_reference<T>::type>(&value);
+    if (!ptr)
+        throw std::bad_cast();
+    return std::move(*ptr);
+}
+
+template<typename T>
+T* any_cast(Any* value) noexcept
+{
+    return value && value->type() == typeid(T) ? value->cast<T>() : nullptr;
+}
+
+template<typename T>
+const T* any_cast(const Any* value) noexcept
+{
+    return value && value->type() == typeid(T) ? value->cast<T>() : nullptr;
+}
+} // namespace util
+} // namespace realm
+
+namespace std {
+inline void swap(realm::util::Any& lhs, realm::util::Any& rhs) noexcept
+{
+    lhs.swap(rhs);
+}
+} // namespace std
+
+#endif // REALM_UTIL_ANY_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/assert.hpp b/iOS/Pods/Realm/include/core/realm/util/assert.hpp
new file mode 100644 (file)
index 0000000..5e75066
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_UTIL_ASSERT_HPP
+#define REALM_UTIL_ASSERT_HPP
+
+#include <realm/util/features.h>
+#include <realm/util/terminate.hpp>
+
+#if REALM_ENABLE_ASSERTIONS || defined(REALM_DEBUG)
+#define REALM_ASSERTIONS_ENABLED 1
+#else
+#define REALM_ASSERTIONS_ENABLED 0
+#endif
+
+#define REALM_ASSERT_RELEASE(condition)                                                                              \
+    (REALM_LIKELY(condition) ? static_cast<void>(0)                                                                  \
+                             : realm::util::terminate("Assertion failed: " #condition, __FILE__, __LINE__))
+
+#if REALM_ASSERTIONS_ENABLED
+#define REALM_ASSERT(condition) REALM_ASSERT_RELEASE(condition)
+#else
+#define REALM_ASSERT(condition) static_cast<void>(sizeof bool(condition))
+#endif
+
+#ifdef REALM_DEBUG
+#define REALM_ASSERT_DEBUG(condition) REALM_ASSERT_RELEASE(condition)
+#else
+#define REALM_ASSERT_DEBUG(condition) static_cast<void>(sizeof bool(condition))
+#endif
+
+#define REALM_STRINGIFY(X) #X
+
+#define REALM_ASSERT_RELEASE_EX(condition, ...)                                                                      \
+    (REALM_LIKELY(condition) ? static_cast<void>(0)                                                                  \
+                             : realm::util::terminate_with_info("Assertion failed: " #condition, __LINE__, __FILE__, \
+                                                                REALM_STRINGIFY((__VA_ARGS__)), __VA_ARGS__))
+
+#ifdef REALM_DEBUG
+#define REALM_ASSERT_DEBUG_EX REALM_ASSERT_RELEASE_EX
+#else
+#define REALM_ASSERT_DEBUG_EX(condition, ...) static_cast<void>(sizeof bool(condition))
+#endif
+
+// Becase the assert is used in noexcept methods, it's a bad idea to allocate
+// buffer space for the message so therefore we must pass it to terminate which
+// will 'cerr' it for us without needing any buffer
+#if REALM_ENABLE_ASSERTIONS || defined(REALM_DEBUG)
+
+#define REALM_ASSERT_EX REALM_ASSERT_RELEASE_EX
+
+#define REALM_ASSERT_3(left, cmp, right)                                                                             \
+    (REALM_LIKELY((left)cmp(right)) ? static_cast<void>(0)                                                           \
+                                    : realm::util::terminate("Assertion failed: "                                    \
+                                                             "" #left " " #cmp " " #right,                           \
+                                                             __FILE__, __LINE__, left, right))
+
+#define REALM_ASSERT_7(left1, cmp1, right1, logical, left2, cmp2, right2)                                            \
+    (REALM_LIKELY(((left1)cmp1(right1))logical((left2)cmp2(right2)))                                                 \
+         ? static_cast<void>(0)                                                                                      \
+         : realm::util::terminate("Assertion failed: "                                                               \
+                                  "" #left1 " " #cmp1 " " #right1 " " #logical " "                                   \
+                                  "" #left2 " " #cmp2 " " #right2,                                                   \
+                                  __FILE__, __LINE__, left1, right1, left2, right2))
+
+#define REALM_ASSERT_11(left1, cmp1, right1, logical1, left2, cmp2, right2, logical2, left3, cmp3, right3)           \
+    (REALM_LIKELY(((left1)cmp1(right1))logical1((left2)cmp2(right2)) logical2((left3)cmp3(right3)))                  \
+         ? static_cast<void>(0)                                                                                      \
+         : realm::util::terminate("Assertion failed: "                                                               \
+                                  "" #left1 " " #cmp1 " " #right1 " " #logical1 " "                                  \
+                                  "" #left2 " " #cmp2 " " #right2 " " #logical2 " "                                  \
+                                  "" #left3 " " #cmp3 " " #right3,                                                   \
+                                  __FILE__, __LINE__, left1, right1, left2, right2, left3, right3))
+#else
+#define REALM_ASSERT_EX(condition, ...) static_cast<void>(sizeof bool(condition))
+#define REALM_ASSERT_3(left, cmp, right) static_cast<void>(sizeof bool((left)cmp(right)))
+#define REALM_ASSERT_7(left1, cmp1, right1, logical, left2, cmp2, right2)                                            \
+    static_cast<void>(sizeof bool(((left1)cmp1(right1))logical((left2)cmp2(right2))))
+#define REALM_ASSERT_11(left1, cmp1, right1, logical1, left2, cmp2, right2, logical2, left3, cmp3, right3)           \
+    static_cast<void>(sizeof bool(((left1)cmp1(right1))logical1((left2)cmp2(right2)) logical2((left3)cmp3(right3))))
+#endif
+
+#define REALM_UNREACHABLE() realm::util::terminate("Unreachable code", __FILE__, __LINE__)
+#ifdef REALM_COVER
+#define REALM_COVER_NEVER(x) false
+#define REALM_COVER_ALWAYS(x) true
+#else
+#define REALM_COVER_NEVER(x) (x)
+#define REALM_COVER_ALWAYS(x) (x)
+#endif
+
+#endif // REALM_UTIL_ASSERT_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/base64.hpp b/iOS/Pods/Realm/include/core/realm/util/base64.hpp
new file mode 100644 (file)
index 0000000..8d808a6
--- /dev/null
@@ -0,0 +1,79 @@
+/*************************************************************************
+ *
+ * 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_UTIL_BASE64_HPP
+#define REALM_UTIL_BASE64_HPP
+
+#include <vector>
+#include <realm/string_data.hpp>
+#include <realm/util/optional.hpp>
+
+namespace realm {
+namespace util {
+
+
+/// base64_encode() encodes the bnary data in \param in_buffer of size \param in_buffer_size.
+/// The encoded data is placed in \param out_buffer. The size of \param \out_buffer is passed in
+/// \param out_buffer_size. The output buffer \param out_buffer must be
+/// large enough to hold the base64 encoded data. The size can be obtained from the function
+/// base64_encoded_size. \param out_buffer_size is only used to assert that the output buffer is
+/// large enough.
+size_t base64_encode(const char *in_buffer, size_t in_buffer_size, char* out_buffer, size_t out_buffer_size) noexcept;
+
+/// base64_encoded_size() returns the exact size of the base64 encoded
+/// data as a function of the size of the input data.
+inline size_t base64_encoded_size(size_t in_buffer_size) noexcept
+{
+    return 4 * ((in_buffer_size + 2) / 3);
+}
+
+
+/// Decode base64-encoded string in input, and places the result in out_buffer.
+/// The length of the out_buffer must be at least 3 * input.size() / 4.
+///
+/// The input must be padded base64 (i.e. the number of non-whitespace
+/// characters in the input must be a multiple of 4). Whitespace (spaces, tabs,
+/// newlines) is ignored.
+///
+/// The algorithm stops when the first character not in the base64 character
+/// set is encountered, or when the end of the input is reached.
+///
+/// \returns the number of successfully decoded bytes written to out_buffer, or
+/// none if the whole input was not valid base64.
+Optional<size_t> base64_decode(StringData input, char* out_buffer, size_t out_buffer_len) noexcept;
+
+/// Return an upper bound on the decoded size of a Base64-encoded data
+/// stream of length \a base64_size. The returned value is suitable for
+/// allocation of buffers containing decoded data.
+inline size_t base64_decoded_size(size_t base64_size) noexcept
+{
+    return (base64_size * 3 + 3) / 4;
+}
+
+
+
+/// base64_decode_to_vector() is a convenience function that decodes \param
+/// encoded and returns the result in a std::vector<char> with the correct size.
+/// This function returns none if the input is invalid.
+Optional<std::vector<char>> base64_decode_to_vector(StringData encoded);
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_BASE64_HPP
+
diff --git a/iOS/Pods/Realm/include/core/realm/util/basic_system_errors.hpp b/iOS/Pods/Realm/include/core/realm/util/basic_system_errors.hpp
new file mode 100644 (file)
index 0000000..8f7a626
--- /dev/null
@@ -0,0 +1,89 @@
+/*************************************************************************
+ *
+ * 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_UTIL_BASIC_SYSTEM_ERRORS_HPP
+#define REALM_UTIL_BASIC_SYSTEM_ERRORS_HPP
+
+#include <cerrno>
+#include <system_error>
+
+
+namespace realm {
+namespace util {
+namespace error {
+
+enum basic_system_errors {
+    /// Address family not supported by protocol.
+    address_family_not_supported = EAFNOSUPPORT,
+
+    /// Invalid argument.
+    invalid_argument = EINVAL,
+
+    /// Cannot allocate memory.
+    no_memory = ENOMEM,
+
+    /// Operation cancelled.
+    operation_aborted = ECANCELED,
+
+    /// Connection aborted.
+    connection_aborted = ECONNABORTED,
+
+    /// Connection reset by peer
+    connection_reset = ECONNRESET,
+
+    /// Broken pipe
+    broken_pipe = EPIPE,
+
+    /// Resource temporarily unavailable
+    resource_unavailable_try_again = EAGAIN,
+};
+
+std::error_code make_error_code(basic_system_errors) noexcept;
+
+} // namespace error
+} // namespace util
+} // namespace realm
+
+namespace std {
+
+template <>
+class is_error_code_enum<realm::util::error::basic_system_errors> {
+public:
+    static const bool value = true;
+};
+
+} // namespace std
+
+namespace realm {
+namespace util {
+
+std::error_code make_basic_system_error_code(int) noexcept;
+
+
+// implementation
+
+inline std::error_code make_basic_system_error_code(int err) noexcept
+{
+    using namespace error;
+    return make_error_code(basic_system_errors(err));
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_BASIC_SYSTEM_ERRORS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/bind_ptr.hpp b/iOS/Pods/Realm/include/core/realm/util/bind_ptr.hpp
new file mode 100644 (file)
index 0000000..e5b8a99
--- /dev/null
@@ -0,0 +1,484 @@
+/*************************************************************************
+ *
+ * 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_UTIL_BIND_PTR_HPP
+#define REALM_UTIL_BIND_PTR_HPP
+
+#include <algorithm>
+#include <atomic>
+#include <ostream>
+#include <utility>
+
+#include <realm/util/features.h>
+#include <realm/util/assert.hpp>
+
+
+namespace realm {
+namespace util {
+
+class bind_ptr_base {
+public:
+    struct adopt_tag {
+    };
+};
+
+
+/// A generic intrusive smart pointer that binds itself explicitely to
+/// the target object.
+///
+/// This class is agnostic towards what 'binding' means for the target
+/// object, but a common use is 'reference counting'. See RefCountBase
+/// for an example of that.
+///
+/// This smart pointer implementation assumes that the target object
+/// destructor never throws.
+template <class T>
+class bind_ptr : public bind_ptr_base {
+public:
+    constexpr bind_ptr() noexcept
+        : m_ptr(nullptr)
+    {
+    }
+    ~bind_ptr() noexcept
+    {
+        unbind();
+    }
+
+    explicit bind_ptr(T* p) noexcept
+    {
+        bind(p);
+    }
+    template <class U>
+    explicit bind_ptr(U* p) noexcept
+    {
+        bind(p);
+    }
+
+    bind_ptr(T* p, adopt_tag) noexcept
+    {
+        m_ptr = p;
+    }
+    template <class U>
+    bind_ptr(U* p, adopt_tag) noexcept
+    {
+        m_ptr = p;
+    }
+
+    // Copy construct
+    bind_ptr(const bind_ptr& p) noexcept
+    {
+        bind(p.m_ptr);
+    }
+    template <class U>
+    bind_ptr(const bind_ptr<U>& p) noexcept
+    {
+        bind(p.m_ptr);
+    }
+
+    // Copy assign
+    bind_ptr& operator=(const bind_ptr& p) noexcept
+    {
+        bind_ptr(p).swap(*this);
+        return *this;
+    }
+    template <class U>
+    bind_ptr& operator=(const bind_ptr<U>& p) noexcept
+    {
+        bind_ptr(p).swap(*this);
+        return *this;
+    }
+
+    // Move construct
+    bind_ptr(bind_ptr&& p) noexcept
+        : m_ptr(p.release())
+    {
+    }
+    template <class U>
+    bind_ptr(bind_ptr<U>&& p) noexcept
+        : m_ptr(p.release())
+    {
+    }
+
+    // Move assign
+    bind_ptr& operator=(bind_ptr&& p) noexcept
+    {
+        bind_ptr(std::move(p)).swap(*this);
+        return *this;
+    }
+    template <class U>
+    bind_ptr& operator=(bind_ptr<U>&& p) noexcept
+    {
+        bind_ptr(std::move(p)).swap(*this);
+        return *this;
+    }
+
+    //@{
+    // Comparison
+    template <class U>
+    bool operator==(const bind_ptr<U>&) const noexcept;
+
+    template <class U>
+    bool operator==(U*) const noexcept;
+
+    template <class U>
+    bool operator!=(const bind_ptr<U>&) const noexcept;
+
+    template <class U>
+    bool operator!=(U*) const noexcept;
+
+    template <class U>
+    bool operator<(const bind_ptr<U>&) const noexcept;
+
+    template <class U>
+    bool operator<(U*) const noexcept;
+
+    template <class U>
+    bool operator>(const bind_ptr<U>&) const noexcept;
+
+    template <class U>
+    bool operator>(U*) const noexcept;
+
+    template <class U>
+    bool operator<=(const bind_ptr<U>&) const noexcept;
+
+    template <class U>
+    bool operator<=(U*) const noexcept;
+
+    template <class U>
+    bool operator>=(const bind_ptr<U>&) const noexcept;
+
+    template <class U>
+    bool operator>=(U*) const noexcept;
+    //@}
+
+    // Dereference
+    T& operator*() const noexcept
+    {
+        return *m_ptr;
+    }
+    T* operator->() const noexcept
+    {
+        return m_ptr;
+    }
+
+    explicit operator bool() const noexcept
+    {
+        return m_ptr != 0;
+    }
+
+    T* get() const noexcept
+    {
+        return m_ptr;
+    }
+    void reset() noexcept
+    {
+        bind_ptr().swap(*this);
+    }
+    void reset(T* p) noexcept
+    {
+        bind_ptr(p).swap(*this);
+    }
+    template <class U>
+    void reset(U* p) noexcept
+    {
+        bind_ptr(p).swap(*this);
+    }
+
+    T* release() noexcept
+    {
+        T* const p = m_ptr;
+        m_ptr = nullptr;
+        return p;
+    }
+
+    void swap(bind_ptr& p) noexcept
+    {
+        std::swap(m_ptr, p.m_ptr);
+    }
+    friend void swap(bind_ptr& a, bind_ptr& b) noexcept
+    {
+        a.swap(b);
+    }
+
+protected:
+    struct casting_move_tag {
+    };
+    template <class U>
+    bind_ptr(bind_ptr<U>* p, casting_move_tag) noexcept
+        : m_ptr(static_cast<T*>(p->release()))
+    {
+    }
+
+private:
+    T* m_ptr;
+
+    void bind(T* p) noexcept
+    {
+        if (p)
+            p->bind_ptr();
+        m_ptr = p;
+    }
+    void unbind() noexcept
+    {
+        if (m_ptr)
+            m_ptr->unbind_ptr();
+    }
+
+    template <class>
+    friend class bind_ptr;
+};
+
+
+template <class C, class T, class U>
+inline std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, const bind_ptr<U>& p)
+{
+    out << static_cast<const void*>(p.get());
+    return out;
+}
+
+
+//@{
+// Comparison
+template <class T, class U>
+bool operator==(T*, const bind_ptr<U>&) noexcept;
+template <class T, class U>
+bool operator!=(T*, const bind_ptr<U>&) noexcept;
+template <class T, class U>
+bool operator<(T*, const bind_ptr<U>&) noexcept;
+template <class T, class U>
+bool operator>(T*, const bind_ptr<U>&) noexcept;
+template <class T, class U>
+bool operator<=(T*, const bind_ptr<U>&) noexcept;
+template <class T, class U>
+bool operator>=(T*, const bind_ptr<U>&) noexcept;
+//@}
+
+
+/// Polymorphic convenience base class for reference counting objects.
+///
+/// Together with bind_ptr, this class delivers simple instrusive
+/// reference counting.
+///
+/// \sa bind_ptr
+class RefCountBase {
+public:
+    RefCountBase() noexcept
+        : m_ref_count(0)
+    {
+    }
+    virtual ~RefCountBase() noexcept
+    {
+        REALM_ASSERT(m_ref_count == 0);
+    }
+
+    RefCountBase(const RefCountBase&) = delete;
+    RefCountBase(RefCountBase&&) = delete;
+
+    void operator=(const RefCountBase&) = delete;
+    void operator=(RefCountBase&&) = delete;
+
+protected:
+    void bind_ptr() const noexcept
+    {
+        ++m_ref_count;
+    }
+    void unbind_ptr() const noexcept
+    {
+        if (--m_ref_count == 0)
+            delete this;
+    }
+
+private:
+    mutable unsigned long m_ref_count;
+
+    template <class>
+    friend class bind_ptr;
+};
+
+
+/// Same as RefCountBase, but this one makes the copying of, and the
+/// destruction of counted references thread-safe.
+///
+/// \sa RefCountBase
+/// \sa bind_ptr
+class AtomicRefCountBase {
+public:
+    AtomicRefCountBase() noexcept
+        : m_ref_count(0)
+    {
+    }
+    virtual ~AtomicRefCountBase() noexcept
+    {
+        REALM_ASSERT(m_ref_count == 0);
+    }
+
+    AtomicRefCountBase(const AtomicRefCountBase&) = delete;
+    AtomicRefCountBase(AtomicRefCountBase&&) = delete;
+
+    void operator=(const AtomicRefCountBase&) = delete;
+    void operator=(AtomicRefCountBase&&) = delete;
+
+protected:
+    // FIXME: Operators ++ and -- as used below use
+    // std::memory_order_seq_cst. This can be optimized.
+    void bind_ptr() const noexcept
+    {
+        ++m_ref_count;
+    }
+    void unbind_ptr() const noexcept
+    {
+        if (--m_ref_count == 0) {
+            delete this;
+        }
+    }
+
+private:
+    mutable std::atomic<unsigned long> m_ref_count;
+
+    template <class>
+    friend class bind_ptr;
+};
+
+
+// Implementation:
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator==(const bind_ptr<U>& p) const noexcept
+{
+    return m_ptr == p.m_ptr;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator==(U* p) const noexcept
+{
+    return m_ptr == p;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator!=(const bind_ptr<U>& p) const noexcept
+{
+    return m_ptr != p.m_ptr;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator!=(U* p) const noexcept
+{
+    return m_ptr != p;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator<(const bind_ptr<U>& p) const noexcept
+{
+    return m_ptr < p.m_ptr;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator<(U* p) const noexcept
+{
+    return m_ptr < p;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator>(const bind_ptr<U>& p) const noexcept
+{
+    return m_ptr > p.m_ptr;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator>(U* p) const noexcept
+{
+    return m_ptr > p;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator<=(const bind_ptr<U>& p) const noexcept
+{
+    return m_ptr <= p.m_ptr;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator<=(U* p) const noexcept
+{
+    return m_ptr <= p;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator>=(const bind_ptr<U>& p) const noexcept
+{
+    return m_ptr >= p.m_ptr;
+}
+
+template <class T>
+template <class U>
+bool bind_ptr<T>::operator>=(U* p) const noexcept
+{
+    return m_ptr >= p;
+}
+
+template <class T, class U>
+bool operator==(T* a, const bind_ptr<U>& b) noexcept
+{
+    return b == a;
+}
+
+template <class T, class U>
+bool operator!=(T* a, const bind_ptr<U>& b) noexcept
+{
+    return b != a;
+}
+
+template <class T, class U>
+bool operator<(T* a, const bind_ptr<U>& b) noexcept
+{
+    return b > a;
+}
+
+template <class T, class U>
+bool operator>(T* a, const bind_ptr<U>& b) noexcept
+{
+    return b < a;
+}
+
+template <class T, class U>
+bool operator<=(T* a, const bind_ptr<U>& b) noexcept
+{
+    return b >= a;
+}
+
+template <class T, class U>
+bool operator>=(T* a, const bind_ptr<U>& b) noexcept
+{
+    return b <= a;
+}
+
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_BIND_PTR_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/buffer.hpp b/iOS/Pods/Realm/include/core/realm/util/buffer.hpp
new file mode 100644 (file)
index 0000000..024510e
--- /dev/null
@@ -0,0 +1,301 @@
+/*************************************************************************
+ *
+ * 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_UTIL_BUFFER_HPP
+#define REALM_UTIL_BUFFER_HPP
+
+#include <cstddef>
+#include <algorithm>
+#include <exception>
+#include <limits>
+#include <utility>
+
+#include <realm/util/features.h>
+#include <realm/utilities.hpp>
+#include <realm/util/safe_int_ops.hpp>
+#include <memory>
+
+namespace realm {
+namespace util {
+
+
+/// A simple buffer concept that owns a region of memory and knows its
+/// size.
+template <class T>
+class Buffer {
+public:
+    Buffer() noexcept
+        : m_size(0)
+    {
+    }
+    explicit Buffer(size_t initial_size);
+    Buffer(Buffer<T>&&) noexcept = default;
+    ~Buffer() noexcept
+    {
+    }
+
+    Buffer<T>& operator=(Buffer<T>&&) noexcept = default;
+
+    T& operator[](size_t i) noexcept
+    {
+        return m_data[i];
+    }
+    const T& operator[](size_t i) const noexcept
+    {
+        return m_data[i];
+    }
+
+    T* data() noexcept
+    {
+        return m_data.get();
+    }
+    const T* data() const noexcept
+    {
+        return m_data.get();
+    }
+    size_t size() const noexcept
+    {
+        return m_size;
+    }
+
+    /// False iff the data() returns null.
+    explicit operator bool() const noexcept
+    {
+        return bool(m_data);
+    }
+
+    /// Discards the original contents.
+    void set_size(size_t new_size);
+
+    /// \param new_size Specifies the new buffer size.
+    /// \param copy_begin, copy_end Specifies a range of element
+    /// values to be retained. \a copy_end must be less than, or equal
+    /// to size().
+    ///
+    /// \param copy_to Specifies where the retained range should be
+    /// copied to. `\a copy_to + \a copy_end - \a copy_begin` must be
+    /// less than, or equal to \a new_size.
+    void resize(size_t new_size, size_t copy_begin, size_t copy_end, size_t copy_to);
+
+    void reserve(size_t used_size, size_t min_capacity);
+
+    void reserve_extra(size_t used_size, size_t min_extra_capacity);
+
+    T* release() noexcept;
+
+    friend void swap(Buffer& a, Buffer& b) noexcept
+    {
+        using std::swap;
+        swap(a.m_data, b.m_data);
+        swap(a.m_size, b.m_size);
+    }
+
+private:
+    std::unique_ptr<T[]> m_data;
+    size_t m_size;
+};
+
+
+/// A buffer that can be efficiently resized. It acheives this by
+/// using an underlying buffer that may be larger than the logical
+/// size, and is automatically expanded in progressively larger steps.
+template <class T>
+class AppendBuffer {
+public:
+    AppendBuffer() noexcept;
+    ~AppendBuffer() noexcept
+    {
+    }
+
+    AppendBuffer(AppendBuffer&&) noexcept = default;
+    AppendBuffer& operator=(AppendBuffer&&) noexcept = default;
+
+    /// Returns the current size of the buffer.
+    size_t size() const noexcept;
+
+    /// Gives read and write access to the elements.
+    T* data() noexcept;
+
+    /// Gives read access the elements.
+    const T* data() const noexcept;
+
+    /// Append the specified elements. This increases the size of this
+    /// buffer by \a append_data_size. If the caller has previously requested
+    /// a minimum capacity that is greater than, or equal to the
+    /// resulting size, this function is guaranteed to not throw.
+    void append(const T* append_data, size_t append_data_size);
+
+    /// If the specified size is less than the current size, then the
+    /// buffer contents is truncated accordingly. If the specified
+    /// size is greater than the current size, then the extra elements
+    /// will have undefined values. If the caller has previously
+    /// requested a minimum capacity that is greater than, or equal to
+    /// the specified size, this function is guaranteed to not throw.
+    void resize(size_t new_size);
+
+    /// This operation does not change the size of the buffer as
+    /// returned by size(). If the specified capacity is less than the
+    /// current capacity, this operation has no effect.
+    void reserve(size_t min_capacity);
+
+    /// Set the size to zero. The capacity remains unchanged.
+    void clear() noexcept;
+
+    /// Release the underlying buffer and reset the size. Note: The returned
+    /// buffer may be larger than the amount of data appended to this buffer.
+    /// Callers should call `size()` prior to releasing the buffer to know the
+    /// usable/logical size.
+    Buffer<T> release() noexcept;
+
+private:
+    util::Buffer<T> m_buffer;
+    size_t m_size;
+};
+
+
+// Implementation:
+
+class BufferSizeOverflow : public std::exception {
+public:
+    const char* what() const noexcept override
+    {
+        return "Buffer size overflow";
+    }
+};
+
+template <class T>
+inline Buffer<T>::Buffer(size_t initial_size)
+    : m_data(new T[initial_size]) // Throws
+    , m_size(initial_size)
+{
+}
+
+template <class T>
+inline void Buffer<T>::set_size(size_t new_size)
+{
+    m_data.reset(new T[new_size]); // Throws
+    m_size = new_size;
+}
+
+template <class T>
+inline void Buffer<T>::resize(size_t new_size, size_t copy_begin, size_t copy_end, size_t copy_to)
+{
+    std::unique_ptr<T[]> new_data(new T[new_size]); // Throws
+    realm::safe_copy_n(m_data.get() + copy_begin, copy_end - copy_begin, new_data.get() + copy_to);
+    m_data.reset(new_data.release());
+    m_size = new_size;
+}
+
+template <class T>
+inline void Buffer<T>::reserve(size_t used_size, size_t min_capacity)
+{
+    size_t current_capacity = m_size;
+    if (REALM_LIKELY(current_capacity >= min_capacity))
+        return;
+    size_t new_capacity = current_capacity;
+
+    // Use growth factor 1.5.
+    if (REALM_UNLIKELY(int_multiply_with_overflow_detect(new_capacity, 3)))
+        new_capacity = std::numeric_limits<size_t>::max();
+    new_capacity /= 2;
+
+    if (REALM_UNLIKELY(new_capacity < min_capacity))
+        new_capacity = min_capacity;
+    resize(new_capacity, 0, used_size, 0); // Throws
+}
+
+template <class T>
+inline void Buffer<T>::reserve_extra(size_t used_size, size_t min_extra_capacity)
+{
+    size_t min_capacity = used_size;
+    if (REALM_UNLIKELY(int_add_with_overflow_detect(min_capacity, min_extra_capacity)))
+        throw BufferSizeOverflow();
+    reserve(used_size, min_capacity); // Throws
+}
+
+template <class T>
+inline T* Buffer<T>::release() noexcept
+{
+    m_size = 0;
+    return m_data.release();
+}
+
+
+template <class T>
+inline AppendBuffer<T>::AppendBuffer() noexcept
+    : m_size(0)
+{
+}
+
+template <class T>
+inline size_t AppendBuffer<T>::size() const noexcept
+{
+    return m_size;
+}
+
+template <class T>
+inline T* AppendBuffer<T>::data() noexcept
+{
+    return m_buffer.data();
+}
+
+template <class T>
+inline const T* AppendBuffer<T>::data() const noexcept
+{
+    return m_buffer.data();
+}
+
+template <class T>
+inline void AppendBuffer<T>::append(const T* append_data, size_t append_data_size)
+{
+    m_buffer.reserve_extra(m_size, append_data_size); // Throws
+    realm::safe_copy_n(append_data, append_data_size, m_buffer.data() + m_size);
+    m_size += append_data_size;
+}
+
+template <class T>
+inline void AppendBuffer<T>::reserve(size_t min_capacity)
+{
+    m_buffer.reserve(m_size, min_capacity);
+}
+
+template <class T>
+inline void AppendBuffer<T>::resize(size_t new_size)
+{
+    reserve(new_size);
+    m_size = new_size;
+}
+
+template <class T>
+inline void AppendBuffer<T>::clear() noexcept
+{
+    m_size = 0;
+}
+
+template <class T>
+inline Buffer<T> AppendBuffer<T>::release() noexcept
+{
+    m_size = 0;
+    return std::move(m_buffer);
+}
+
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_BUFFER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/buffer_stream.hpp b/iOS/Pods/Realm/include/core/realm/util/buffer_stream.hpp
new file mode 100644 (file)
index 0000000..65feb8b
--- /dev/null
@@ -0,0 +1,146 @@
+/*************************************************************************
+ *
+ * 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_UTIL_BUFFER_STREAM_HPP
+#define REALM_UTIL_BUFFER_STREAM_HPP
+
+#include <sstream>
+
+namespace realm {
+namespace util {
+
+
+template<class C, class T = std::char_traits<C>, class A = std::allocator<C> >
+class BasicResettableExpandableOutputStreambuf: public std::basic_stringbuf<C,T,A> {
+public:
+    using char_type = typename std::basic_stringbuf<C,T,A>::char_type;
+
+    /// Reset current writing position (std::basic_streambuf::pptr()) to the
+    /// beginning of the output buffer without reallocating buffer memory.
+    void reset() noexcept;
+
+    /// Get a pointer to the beginning of the output buffer
+    /// (std::basic_streambuf::pbase()). Note that this will change as the
+    /// buffer is reallocated.
+    char_type* data() noexcept;
+    const char_type* data() const noexcept;
+
+    /// Get the number of bytes written to the output buffer since the creation
+    /// of the stream buffer, or since the last invocation of reset()
+    /// (std::basic_streambuf::pptr() - std::basic_streambuf::pbase()).
+    std::streamsize size() const noexcept;
+};
+
+
+template<class C, class T = std::char_traits<C>, class A = std::allocator<C> >
+class BasicResettableExpandableBufferOutputStream: public std::basic_ostream<C,T> {
+public:
+    using char_type = typename std::basic_ostream<C,T>::char_type;
+
+    BasicResettableExpandableBufferOutputStream();
+
+    /// Calls BasicResettableExpandableOutputStreambuf::reset().
+    void reset() noexcept;
+
+    /// Calls BasicResettableExpandableOutputStreambuf::data().
+    char_type* data() noexcept;
+    const char_type* data() const noexcept;
+
+    /// Calls BasicResettableExpandableOutputStreambuf::size().
+    std::streamsize size() const noexcept;
+
+private:
+    BasicResettableExpandableOutputStreambuf<C,T,A> m_streambuf;
+};
+
+
+using ResettableExpandableBufferOutputStream = BasicResettableExpandableBufferOutputStream<char>;
+
+
+
+
+// Implementation
+
+template<class C, class T, class A>
+inline void BasicResettableExpandableOutputStreambuf<C,T,A>::reset() noexcept
+{
+    char_type* pbeg = this->pbase();
+    char_type* pend = this->epptr();
+    this->setp(pbeg, pend);
+}
+
+template<class C, class T, class A>
+inline typename BasicResettableExpandableOutputStreambuf<C,T,A>::char_type*
+BasicResettableExpandableOutputStreambuf<C,T,A>::data() noexcept
+{
+    return this->pbase();
+}
+
+template<class C, class T, class A>
+inline const typename BasicResettableExpandableOutputStreambuf<C,T,A>::char_type*
+BasicResettableExpandableOutputStreambuf<C,T,A>::data() const noexcept
+{
+    return this->pbase();
+}
+
+template<class C, class T, class A>
+inline std::streamsize BasicResettableExpandableOutputStreambuf<C,T,A>::size() const noexcept
+{
+    std::streamsize s = std::streamsize(this->pptr() - this->pbase());
+    return s;
+}
+
+template<class C, class T, class A>
+inline BasicResettableExpandableBufferOutputStream<C,T,A>::
+BasicResettableExpandableBufferOutputStream():
+    std::basic_ostream<C,T>(&m_streambuf) // Throws
+{
+}
+
+template<class C, class T, class A>
+inline void BasicResettableExpandableBufferOutputStream<C,T,A>::reset() noexcept
+{
+    m_streambuf.reset();
+}
+
+template<class C, class T, class A>
+inline typename BasicResettableExpandableBufferOutputStream<C,T,A>::char_type*
+BasicResettableExpandableBufferOutputStream<C,T,A>::data() noexcept
+{
+    return m_streambuf.data();
+}
+
+template<class C, class T, class A>
+inline const typename BasicResettableExpandableBufferOutputStream<C,T,A>::char_type*
+BasicResettableExpandableBufferOutputStream<C,T,A>::data() const noexcept
+{
+    return m_streambuf.data();
+}
+
+template<class C, class T, class A>
+inline std::streamsize BasicResettableExpandableBufferOutputStream<C,T,A>::size() const noexcept
+{
+    return m_streambuf.size();
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_BUFFER_STREAM_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/call_with_tuple.hpp b/iOS/Pods/Realm/include/core/realm/util/call_with_tuple.hpp
new file mode 100644 (file)
index 0000000..7d2eab0
--- /dev/null
@@ -0,0 +1,66 @@
+/*************************************************************************
+ *
+ * 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_UTIL_CALL_WITH_TUPLE_HPP
+#define REALM_UTIL_CALL_WITH_TUPLE_HPP
+
+#include <cstddef>
+#include <tuple>
+
+namespace realm {
+namespace _impl {
+
+/// \cond doxygen_skip
+/// Doxygen warns about a recursive class relation, but this is intentional.
+
+template <size_t...>
+struct Indexes {
+};
+template <size_t N, size_t... I>
+struct GenIndexes : GenIndexes<N - 1, N - 1, I...> {
+};
+template <size_t... I>
+struct GenIndexes<0, I...> {
+    typedef Indexes<I...> type;
+};
+
+/// \endcond
+
+template <class F, class... A, size_t... I>
+auto call_with_tuple(F func, std::tuple<A...> args, Indexes<I...>) -> decltype(func(std::get<I>(args)...))
+{
+    static_cast<void>(args); // Prevent GCC warning when tuple is empty
+    return func(std::get<I>(args)...);
+}
+
+} // namespace _impl
+
+namespace util {
+
+template <class F, class... A>
+auto call_with_tuple(F func, std::tuple<A...> args)
+    -> decltype(_impl::call_with_tuple(std::move(func), std::move(args),
+                                       typename _impl::GenIndexes<sizeof...(A)>::type()))
+{
+    return _impl::call_with_tuple(std::move(func), std::move(args), typename _impl::GenIndexes<sizeof...(A)>::type());
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_CALL_WITH_TUPLE_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/cf_ptr.hpp b/iOS/Pods/Realm/include/core/realm/util/cf_ptr.hpp
new file mode 100644 (file)
index 0000000..a1ec431
--- /dev/null
@@ -0,0 +1,108 @@
+/*************************************************************************
+ *
+ * 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_UTIL_CF_PTR_HPP
+#define REALM_UTIL_CF_PTR_HPP
+
+#include <realm/util/assert.hpp>
+
+#if REALM_PLATFORM_APPLE
+
+#include <CoreFoundation/CoreFoundation.h>
+
+namespace realm {
+namespace util {
+
+template <class Ref>
+class CFPtr {
+public:
+    explicit CFPtr(Ref ref = nullptr) noexcept
+        : m_ref(ref)
+    {
+    }
+
+    CFPtr(CFPtr&& rg) noexcept
+        : m_ref(rg.m_ref)
+    {
+        rg.m_ref = nullptr;
+    }
+
+    ~CFPtr() noexcept
+    {
+        if (m_ref)
+            CFRelease(m_ref);
+    }
+
+    CFPtr& operator=(CFPtr&& rg) noexcept
+    {
+        REALM_ASSERT(!m_ref || m_ref != rg.m_ref);
+        if (m_ref)
+            CFRelease(m_ref);
+        m_ref = rg.m_ref;
+        rg.m_ref = nullptr;
+        return *this;
+    }
+
+    explicit operator bool() const noexcept
+    {
+        return bool(m_ref);
+    }
+
+    Ref get() const noexcept
+    {
+        return m_ref;
+    }
+
+    Ref release() noexcept
+    {
+        Ref ref = m_ref;
+        m_ref = nullptr;
+        return ref;
+    }
+
+    void reset(Ref ref = nullptr) noexcept
+    {
+        REALM_ASSERT(!m_ref || m_ref != ref);
+        if (m_ref)
+            CFRelease(m_ref);
+        m_ref = ref;
+    }
+
+private:
+    Ref m_ref;
+};
+
+template <class Ref>
+CFPtr<Ref> adoptCF(Ref ptr)
+{
+    return CFPtr<Ref>(ptr);
+}
+
+template <class Ref>
+CFPtr<Ref> retainCF(Ref ptr)
+{
+    CFRetain(ptr);
+    return CFPtr<Ref>(ptr);
+}
+}
+}
+
+
+#endif // REALM_PLATFORM_APPLE
+
+#endif // REALM_UTIL_CF_PTR_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/compression.hpp b/iOS/Pods/Realm/include/core/realm/util/compression.hpp
new file mode 100644 (file)
index 0000000..227ad51
--- /dev/null
@@ -0,0 +1,159 @@
+/*************************************************************************
+ *
+ * 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_UTIL_COMPRESSION_HPP
+#define REALM_UTIL_COMPRESSION_HPP
+
+#include <system_error>
+#include <vector>
+#include <string>
+#include <stdint.h>
+#include <stddef.h>
+#include <memory>
+
+#include <realm/binary_data.hpp>
+
+namespace realm {
+namespace util {
+namespace compression {
+
+enum class error {
+    out_of_memory = 1,
+    compress_buffer_too_small = 2,
+    compress_error = 3,
+    corrupt_input = 4,
+    incorrect_decompressed_size = 5,
+    decompress_error = 6
+};
+
+const std::error_category& error_category() noexcept;
+
+std::error_code make_error_code(error) noexcept;
+
+} // namespace compression
+} // namespace util
+} // namespace realm
+
+namespace std {
+
+template<> struct is_error_code_enum<realm::util::compression::error> {
+    static const bool value = true;
+};
+
+} // namespace std
+
+namespace realm {
+namespace util {
+namespace compression {
+
+class Alloc {
+public:
+    // Returns null on "out of memory"
+    virtual void* alloc(size_t size) = 0;
+    virtual void free(void* addr) noexcept = 0;
+    virtual ~Alloc() {}
+};
+
+class CompressMemoryArena: public Alloc {
+public:
+    void* alloc(size_t size) override final
+    {
+        size_t offset = m_offset;
+        size_t padding = offset % alignof (std::max_align_t);
+        if (padding > m_size - offset)
+            return nullptr;
+        offset += padding;
+        void* addr = m_buffer.get() + offset;
+        if (size > m_size - offset)
+            return nullptr;
+        m_offset = offset + size;
+        return addr;
+    }
+
+    void free(void*) noexcept override final
+    {
+        // No-op
+    }
+
+    void reset() noexcept
+    {
+        m_offset = 0;
+    }
+
+    size_t size() const noexcept
+    {
+        return m_size;
+    }
+
+    void resize(size_t size)
+    {
+        m_buffer = std::make_unique<char[]>(size); // Throws
+        m_size = size;
+        m_offset = 0;
+    }
+
+private:
+    size_t m_size = 0, m_offset = 0;
+    std::unique_ptr<char[]> m_buffer;
+};
+
+
+/// compress_bound() calculates an upper bound on the size of the compressed
+/// data. The caller can use this function to allocate memory buffer calling
+/// compress(). \a uncompressed_buf is the buffer with uncompresed data. The
+/// size of the uncompressed data is \a uncompressed_size. \a compression_level
+/// is described under compress(). \a bound is set to the upper bound at
+/// return. The returned error code is of category compression::error_category.
+std::error_code compress_bound(const char* uncompressed_buf, size_t uncompressed_size,
+                               size_t& bound, int compression_level = 1);
+
+/// compress() compresses the data in the \a uncompressed_buf of size \a
+/// uncompressed_size into \a compressed_buf. compress() resizes \a
+/// compressed_buf. At return, \a compressed_buf has the size of the compressed
+/// data. \a compression_level is [1-9] with 1 the fastest for the current zlib
+/// implementation. The returned error code is of category
+/// compression::error_category.
+std::error_code compress(const char* uncompressed_buf, size_t uncompressed_size,
+                         char* compressed_buf, size_t compressed_buf_size,
+                         size_t& compressed_size, int compression_level = 1,
+                         Alloc* custom_allocator = nullptr);
+
+/// decompress() decompresses the data in \param compressed_buf of size \a
+/// compresed_size into \a decompressed_buf. \a decompressed_size is the
+/// expected size of the decompressed data. \a decompressed_buf must have size
+/// at least \a decompressed_size. decompress() throws on errors, including the
+/// error where the size of the decompressed data is unequal to
+/// decompressed_size.  The returned error code is of category
+/// compression::error_category.
+std::error_code decompress(const char* compressed_buf, size_t compressed_size,
+                           char* decompressed_buf, size_t decompressed_size);
+
+
+size_t allocate_and_compress(CompressMemoryArena& compress_memory_arena,
+                             BinaryData uncompressed_buf,
+                             std::vector<char>& compressed_buf);
+
+
+
+} // namespace compression
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_COMPRESSION_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/config.h b/iOS/Pods/Realm/include/core/realm/util/config.h
new file mode 100644 (file)
index 0000000..90229b0
--- /dev/null
@@ -0,0 +1,24 @@
+// Version information
+#define REALM_VERSION ""
+
+// Specific headers
+#define HAVE_MALLOC_H 0
+
+// Realm-specific configuration
+#define REALM_MAX_BPNODE_SIZE 1000
+/* #undef REALM_MAX_BPNODE_SIZE_DEBUG */
+#define REALM_ENABLE_ASSERTIONS 0
+#define REALM_ENABLE_ALLOC_SET_ZERO 0
+#define REALM_ENABLE_ENCRYPTION 1
+#define REALM_ENABLE_MEMDEBUG 0
+#define REALM_VALGRIND 0
+#define REALM_METRICS 1
+#define REALM_ASAN 0
+#define REALM_TSAN 0
+
+#define REALM_INSTALL_PREFIX "/usr/local"
+#define REALM_INSTALL_INCLUDEDIR "include"
+#define REALM_INSTALL_BINDIR "bin"
+#define REALM_INSTALL_LIBDIR "lib"
+#define REALM_INSTALL_LIBEXECDIR "libexec"
+#define REALM_INSTALL_EXEC_PREFIX "/usr/local"
diff --git a/iOS/Pods/Realm/include/core/realm/util/encrypted_file_mapping.hpp b/iOS/Pods/Realm/include/core/realm/util/encrypted_file_mapping.hpp
new file mode 100644 (file)
index 0000000..e110e97
--- /dev/null
@@ -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_UTIL_ENCRYPTED_FILE_MAPPING_HPP
+#define REALM_UTIL_ENCRYPTED_FILE_MAPPING_HPP
+
+#include <realm/util/file.hpp>
+#include <realm/util/thread.hpp>
+#include <realm/util/features.h>
+
+#if REALM_ENABLE_ENCRYPTION
+
+typedef size_t (*Header_to_size)(const char* addr);
+
+#include <vector>
+
+namespace realm {
+namespace util {
+
+struct SharedFileInfo;
+class EncryptedFileMapping;
+
+class EncryptedFileMapping {
+public:
+    // Adds the newly-created object to file.mappings iff it's successfully constructed
+    EncryptedFileMapping(SharedFileInfo& file, size_t file_offset, void* addr, size_t size, File::AccessMode access);
+    ~EncryptedFileMapping();
+
+    // Default implementations of copy/assign can trigger multiple destructions
+    EncryptedFileMapping(const EncryptedFileMapping&) = delete;
+    EncryptedFileMapping& operator=(const EncryptedFileMapping&) = delete;
+
+    // Write all dirty pages to disk and mark them read-only
+    // Does not call fsync
+    void flush() noexcept;
+
+    // Sync this file to disk
+    void sync() noexcept;
+
+    // Make sure that memory in the specified range is synchronized with any
+    // changes made globally visible through call to write_barrier
+    void read_barrier(const void* addr, size_t size, UniqueLock& lock, Header_to_size header_to_size);
+
+    // Ensures that any changes made to memory in the specified range
+    // becomes visible to any later calls to read_barrier()
+    void write_barrier(const void* addr, size_t size) noexcept;
+
+    // Set this mapping to a new address and size
+    // Flushes any remaining dirty pages from the old mapping
+    void set(void* new_addr, size_t new_size, size_t new_file_offset);
+
+    bool contains_page(size_t page_in_file) const;
+    size_t get_local_index_of_address(const void* addr, size_t offset = 0) const;
+
+private:
+    SharedFileInfo& m_file;
+
+    size_t m_page_shift;
+    size_t m_blocks_per_page;
+
+    void* m_addr = nullptr;
+
+    size_t m_first_page;
+
+    // MUST be of type char because of coherence issues when writing inside mutex and reading outside 
+    // it. FIXME: We're investigating if this is good enough, or if we need further mechanisms
+    std::vector<char> m_up_to_date_pages;
+    std::vector<bool> m_dirty_pages;
+
+    File::AccessMode m_access;
+
+#ifdef REALM_DEBUG
+    std::unique_ptr<char[]> m_validate_buffer;
+#endif
+
+    char* page_addr(size_t local_page_ndx) const noexcept;
+
+    void mark_outdated(size_t local_page_ndx) noexcept;
+    bool copy_up_to_date_page(size_t local_page_ndx) noexcept;
+    void refresh_page(size_t local_page_ndx);
+    void write_page(size_t local_page_ndx) noexcept;
+
+    void validate_page(size_t local_page_ndx) noexcept;
+    void validate() noexcept;
+};
+
+inline size_t EncryptedFileMapping::get_local_index_of_address(const void* addr, size_t offset) const
+{
+    REALM_ASSERT_EX(addr >= m_addr, addr, m_addr);
+
+    size_t local_ndx = ((reinterpret_cast<uintptr_t>(addr) - reinterpret_cast<uintptr_t>(m_addr) + offset) >> m_page_shift);
+    REALM_ASSERT_EX(local_ndx < m_up_to_date_pages.size(), local_ndx, m_up_to_date_pages.size());
+    return local_ndx;
+}
+
+inline bool EncryptedFileMapping::contains_page(size_t page_in_file) const
+{
+    // first check for (page_in_file >= m_first_page) so that the following
+    // subtraction using unsigned types never wraps under 0
+    return page_in_file >= m_first_page && page_in_file - m_first_page < m_up_to_date_pages.size();
+}
+
+inline void EncryptedFileMapping::read_barrier(const void* addr, size_t size, UniqueLock& lock,
+                                               Header_to_size header_to_size)
+{
+    size_t first_accessed_local_page = get_local_index_of_address(addr);
+
+    // make sure the first page is available
+    // Checking before taking the lock is important to performance.
+    if (!m_up_to_date_pages[first_accessed_local_page]) {
+        if (!lock.holds_lock())
+            lock.lock();
+        // after taking the lock, we must repeat the check so that we never
+        // call refresh_page() on a page which is already up to date.
+        if (!m_up_to_date_pages[first_accessed_local_page])
+            refresh_page(first_accessed_local_page);
+    }
+
+    if (header_to_size) {
+
+        // We know it's an array, and array headers are 8-byte aligned, so it is
+        // included in the first page which was handled above.
+        size = header_to_size(static_cast<const char*>(addr));
+    }
+
+    size_t last_idx = get_local_index_of_address(addr, size == 0 ? 0 : size - 1);
+    size_t up_to_date_pages_size = m_up_to_date_pages.size();
+
+    // We already checked first_accessed_local_page above, so we start the loop
+    // at first_accessed_local_page + 1 to check the following page.
+    for (size_t idx = first_accessed_local_page + 1; idx <= last_idx && idx < up_to_date_pages_size; ++idx) {
+        if (!m_up_to_date_pages[idx]) {
+            if (!lock.holds_lock())
+                lock.lock();
+            // after taking the lock, we must repeat the check so that we never
+            // call refresh_page() on a page which is already up to date.
+            if (!m_up_to_date_pages[idx])
+                refresh_page(idx);
+        }
+    }
+}
+}
+}
+
+#endif // REALM_ENABLE_ENCRYPTION
+
+namespace realm {
+namespace util {
+
+/// Thrown by EncryptedFileMapping if a file opened is non-empty and does not
+/// contain valid encrypted data
+struct DecryptionFailed : util::File::AccessError {
+    DecryptionFailed()
+        : util::File::AccessError("Decryption failed", std::string())
+    {
+    }
+};
+}
+}
+
+#endif // REALM_UTIL_ENCRYPTED_FILE_MAPPING_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/errno.hpp b/iOS/Pods/Realm/include/core/realm/util/errno.hpp
new file mode 100644 (file)
index 0000000..4907f36
--- /dev/null
@@ -0,0 +1,39 @@
+/*************************************************************************
+ *
+ * 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_UTIL_ERRNO_HPP
+#define REALM_UTIL_ERRNO_HPP
+
+#include <string>
+
+#include <realm/util/basic_system_errors.hpp>
+
+
+namespace realm {
+namespace util {
+
+// Get the error message for a given error code, and append it to `prefix`
+inline std::string get_errno_msg(const char* prefix, int err)
+{
+    return prefix + make_basic_system_error_code(err).message();
+}
+
+} // namespace util
+} // namespace realm
+
+#endif
diff --git a/iOS/Pods/Realm/include/core/realm/util/features.h b/iOS/Pods/Realm/include/core/realm/util/features.h
new file mode 100644 (file)
index 0000000..2628903
--- /dev/null
@@ -0,0 +1,304 @@
+/*************************************************************************
+ *
+ * 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_UTIL_FEATURES_H
+#define REALM_UTIL_FEATURES_H
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4800) // Visual Studio int->bool performance warnings
+#endif
+
+#if defined(_WIN32) && !defined(NOMINMAX)
+#define NOMINMAX
+#endif
+
+#include <realm/util/config.h>
+
+/* The maximum number of elements in a B+-tree node. Applies to inner nodes and
+ * to leaves. The minimum allowable value is 2.
+ */
+#ifndef REALM_MAX_BPNODE_SIZE
+#define REALM_MAX_BPNODE_SIZE 1000
+#endif
+
+
+#define REALM_QUOTE_2(x) #x
+#define REALM_QUOTE(x) REALM_QUOTE_2(x)
+
+/* See these links for information about feature check macroes in GCC,
+ * Clang, and MSVC:
+ *
+ * http://gcc.gnu.org/projects/cxx0x.html
+ * http://clang.llvm.org/cxx_status.html
+ * http://clang.llvm.org/docs/LanguageExtensions.html#checks-for-standard-language-features
+ * http://msdn.microsoft.com/en-us/library/vstudio/hh567368.aspx
+ * http://sourceforge.net/p/predef/wiki/Compilers
+ */
+
+
+/* Compiler is GCC and version is greater than or equal to the specified version */
+#define REALM_HAVE_AT_LEAST_GCC(maj, min) \
+    (__GNUC__ > (maj) || __GNUC__ == (maj) && __GNUC_MINOR__ >= (min))
+
+#if defined(__clang__)
+#define REALM_HAVE_CLANG_FEATURE(feature) __has_feature(feature)
+#define REALM_HAVE_CLANG_WARNING(warning) __has_warning(warning)
+#else
+#define REALM_HAVE_CLANG_FEATURE(feature) 0
+#define REALM_HAVE_CLANG_WARNING(warning) 0
+#endif
+
+#ifdef __has_cpp_attribute
+#define REALM_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr)
+#else
+#define REALM_HAS_CPP_ATTRIBUTE(attr) 0
+#endif
+
+#if REALM_HAS_CPP_ATTRIBUTE(clang::fallthrough)
+#define REALM_FALLTHROUGH [[clang::fallthrough]]
+#elif REALM_HAS_CPP_ATTRIBUTE(fallthrough)
+#define REALM_FALLTHROUGH [[fallthrough]]
+#else
+#define REALM_FALLTHROUGH
+#endif
+
+// This should be renamed to REALM_UNREACHABLE as soon as REALM_UNREACHABLE is renamed to
+// REALM_ASSERT_NOT_REACHED which will better reflect its nature
+#if defined(__GNUC__) || defined(__clang__)
+#define REALM_COMPILER_HINT_UNREACHABLE __builtin_unreachable
+#else
+#define REALM_COMPILER_HINT_UNREACHABLE abort
+#endif
+
+#if defined(__GNUC__) // clang or GCC
+#define REALM_PRAGMA(v) _Pragma(REALM_QUOTE_2(v))
+#elif defined(_MSC_VER) // VS
+#define REALM_PRAGMA(v) __pragma(v)
+#else
+#define REALM_PRAGMA(v)
+#endif
+
+#if defined(__clang__)
+#define REALM_DIAG(v) REALM_PRAGMA(clang diagnostic v)
+#elif defined(__GNUC__)
+#define REALM_DIAG(v) REALM_PRAGMA(GCC diagnostic v)
+#else
+#define REALM_DIAG(v)
+#endif
+
+#define REALM_DIAG_PUSH() REALM_DIAG(push)
+#define REALM_DIAG_POP() REALM_DIAG(pop)
+
+#ifdef _MSC_VER
+#define REALM_VS_WARNING_DISABLE #pragma warning (default: 4297)
+#endif
+
+#if REALM_HAVE_CLANG_WARNING("-Wtautological-compare") || REALM_HAVE_AT_LEAST_GCC(6, 0)
+#define REALM_DIAG_IGNORE_TAUTOLOGICAL_COMPARE() REALM_DIAG(ignored "-Wtautological-compare")
+#else
+#define REALM_DIAG_IGNORE_TAUTOLOGICAL_COMPARE()
+#endif
+
+#ifdef _MSC_VER
+#  define REALM_DIAG_IGNORE_UNSIGNED_MINUS() REALM_PRAGMA(warning(disable:4146))
+#else
+#define REALM_DIAG_IGNORE_UNSIGNED_MINUS()
+#endif
+
+/* Compiler is MSVC (Microsoft Visual C++) */
+#if defined(_MSC_VER) && _MSC_VER >= 1600
+#define REALM_HAVE_AT_LEAST_MSVC_10_2010 1
+#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1700
+#define REALM_HAVE_AT_LEAST_MSVC_11_2012 1
+#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1800
+#define REALM_HAVE_AT_LEAST_MSVC_12_2013 1
+#endif
+
+
+/* The way to specify that a function never returns. */
+#if REALM_HAVE_AT_LEAST_GCC(4, 8) || REALM_HAVE_CLANG_FEATURE(cxx_attributes)
+#define REALM_NORETURN [[noreturn]]
+#elif __GNUC__
+#define REALM_NORETURN __attribute__((noreturn))
+#elif defined(_MSC_VER)
+#define REALM_NORETURN __declspec(noreturn)
+#else
+#define REALM_NORETURN
+#endif
+
+
+/* The way to specify that a variable or type is intended to possibly
+ * not be used. Use it to suppress a warning from the compiler. */
+#if __GNUC__
+#define REALM_UNUSED __attribute__((unused))
+#else
+#define REALM_UNUSED
+#endif
+
+/* The way to specify that a function is deprecated
+ * not be used. Use it to suppress a warning from the compiler. */
+#if __GNUC__
+#define REALM_DEPRECATED(x) [[deprecated(x)]]
+#else
+#define REALM_DEPRECATED(x) __declspec(deprecated(x))
+#endif
+
+
+#if __GNUC__ || defined __INTEL_COMPILER
+#define REALM_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
+#define REALM_LIKELY(expr) __builtin_expect(!!(expr), 1)
+#else
+#define REALM_UNLIKELY(expr) (expr)
+#define REALM_LIKELY(expr) (expr)
+#endif
+
+
+#if defined(__GNUC__) || defined(__HP_aCC)
+#define REALM_FORCEINLINE inline __attribute__((always_inline))
+#elif defined(_MSC_VER)
+#define REALM_FORCEINLINE __forceinline
+#else
+#define REALM_FORCEINLINE inline
+#endif
+
+
+#if defined(__GNUC__) || defined(__HP_aCC)
+#define REALM_NOINLINE __attribute__((noinline))
+#elif defined(_MSC_VER)
+#define REALM_NOINLINE __declspec(noinline)
+#else
+#define REALM_NOINLINE
+#endif
+
+
+/* Thread specific data (only for POD types) */
+#if defined __clang__
+#define REALM_THREAD_LOCAL __thread
+#else
+#define REALM_THREAD_LOCAL thread_local
+#endif
+
+
+#if defined ANDROID
+#define REALM_ANDROID 1
+#else
+#define REALM_ANDROID 0
+#endif
+
+#if defined _WIN32
+#  include <winapifamily.h>
+#  if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
+#    define REALM_WINDOWS 1
+#    define REALM_UWP 0
+#  elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
+#    define REALM_WINDOWS 0
+#    define REALM_UWP 1
+#  endif
+#else
+#define REALM_WINDOWS 0
+#define REALM_UWP 0
+#endif
+
+// Some documentation of the defines provided by Apple:
+// http://developer.apple.com/library/mac/documentation/Porting/Conceptual/PortingUnix/compiling/compiling.html#//apple_ref/doc/uid/TP40002850-SW13
+#if defined __APPLE__ && defined __MACH__
+#define REALM_PLATFORM_APPLE 1
+/* Apple OSX and iOS (Darwin). */
+#include <Availability.h>
+#include <TargetConditionals.h>
+#if TARGET_OS_IPHONE == 1
+/* Device (iPhone or iPad) or simulator. */
+#define REALM_IOS 1
+#else
+#define REALM_IOS 0
+#endif
+#if TARGET_OS_WATCH == 1
+/* Device (Apple Watch) or simulator. */
+#define REALM_WATCHOS 1
+#else
+#define REALM_WATCHOS 0
+#endif
+#if TARGET_OS_TV
+/* Device (Apple TV) or simulator. */
+#define REALM_TVOS 1
+#else
+#define REALM_TVOS 0
+#endif
+#else
+#define REALM_PLATFORM_APPLE 0
+#define REALM_IOS 0
+#define REALM_WATCHOS 0
+#define REALM_TVOS 0
+#endif
+
+// asl_log is deprecated in favor of os_log as of the following versions:
+// macos(10.12), ios(10.0), watchos(3.0), tvos(10.0)
+// versions are defined in /usr/include/Availability.h
+// __MAC_10_12   101200
+// __IPHONE_10_0 100000
+// __WATCHOS_3_0  30000
+// __TVOS_10_0   100000
+#if REALM_PLATFORM_APPLE \
+    && ( \
+        (REALM_IOS && defined(__IPHONE_OS_VERSION_MIN_REQUIRED) \
+         && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) \
+     || (REALM_TVOS && defined(__TV_OS_VERSION_MIN_REQUIRED) \
+         &&  __TV_OS_VERSION_MIN_REQUIRED >= 100000) \
+     || (REALM_WATCHOS && defined(__WATCH_OS_VERSION_MIN_REQUIRED) \
+         && __WATCH_OS_VERSION_MIN_REQUIRED >= 30000) \
+     || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
+         && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) \
+       )
+#define REALM_APPLE_OS_LOG 1
+#else
+#define REALM_APPLE_OS_LOG 0
+#endif
+
+#if REALM_ANDROID || REALM_IOS || REALM_WATCHOS || REALM_TVOS || REALM_UWP
+#define REALM_MOBILE 1
+#else
+#define REALM_MOBILE 0
+#endif
+
+
+#if defined(REALM_DEBUG) && !defined(REALM_COOKIE_CHECK)
+#define REALM_COOKIE_CHECK
+#endif
+
+#if !REALM_IOS && !REALM_WATCHOS && !REALM_TVOS && !defined(_WIN32) && !REALM_ANDROID
+#define REALM_ASYNC_DAEMON
+#endif
+
+// We're in i686 mode
+#if defined(__i386) || defined(__i386__) || defined(__i686__) || defined(_M_I86) || defined(_M_IX86)
+#define REALM_ARCHITECTURE_X86_32 1
+#else
+#define REALM_ARCHITECTURE_X86_32 0
+#endif
+
+// We're in amd64 mode
+#if defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
+    defined(_M_AMD64)
+#define REALM_ARCHITECTURE_X86_64 1
+#else
+#define REALM_ARCHITECTURE_X86_64 0
+#endif
+
+#endif /* REALM_UTIL_FEATURES_H */
diff --git a/iOS/Pods/Realm/include/core/realm/util/file.hpp b/iOS/Pods/Realm/include/core/realm/util/file.hpp
new file mode 100644 (file)
index 0000000..5cc56fc
--- /dev/null
@@ -0,0 +1,1317 @@
+/*************************************************************************
+ *
+ * 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_UTIL_FILE_HPP
+#define REALM_UTIL_FILE_HPP
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <functional>
+#include <stdexcept>
+#include <string>
+#include <streambuf>
+
+#ifndef _WIN32
+#include <dirent.h> // POSIX.1-2001
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1900 // compiling with at least Visual Studio 2015
+#include <experimental/filesystem>
+namespace std {
+    namespace filesystem = std::experimental::filesystem::v1;
+}
+#define REALM_HAVE_STD_FILESYSTEM 1
+#else
+#define REALM_HAVE_STD_FILESYSTEM 0
+#endif
+
+#include <realm/utilities.hpp>
+#include <realm/util/features.h>
+#include <realm/util/assert.hpp>
+#include <realm/util/safe_int_ops.hpp>
+
+
+namespace realm {
+namespace util {
+
+class EncryptedFileMapping;
+
+/// Create the specified directory in the file system.
+///
+/// \throw File::AccessError If the directory could not be created. If
+/// the reason corresponds to one of the exception types that are
+/// derived from File::AccessError, the derived exception type is
+/// thrown (as long as the underlying system provides the information
+/// to unambiguously distinguish that particular reason).
+void make_dir(const std::string& path);
+
+/// Same as make_dir() except that this one returns false, rather than throwing
+/// an exception, if the specified directory already existed. If the directory
+// did not already exist and was newly created, this returns true.
+bool try_make_dir(const std::string& path);
+
+/// Remove the specified empty directory path from the file system. It is an
+/// error if the specified path is not a directory, or if it is a nonempty
+/// directory. In so far as the specified path is a directory, std::remove(const
+/// char*) is equivalent to this function.
+///
+/// \throw File::AccessError If the directory could not be removed. If the
+/// reason corresponds to one of the exception types that are derived from
+/// File::AccessError, the derived exception type is thrown (as long as the
+/// underlying system provides the information to unambiguously distinguish that
+/// particular reason).
+void remove_dir(const std::string& path);
+
+/// Same as remove_dir() except that this one returns false, rather
+/// than throwing an exception, if the specified directory did not
+/// exist. If the directory did exist, and was deleted, this function
+/// returns true.
+bool try_remove_dir(const std::string& path);
+
+/// Remove the specified directory after removing all its contents. Files
+/// (nondirectory entries) will be removed as if by a call to File::remove(),
+/// and empty directories as if by a call to remove_dir().
+///
+/// \throw File::AccessError If removal of the directory, or any of its contents
+/// fail.
+///
+/// remove_dir_recursive() assumes that no other process or thread is making
+/// simultaneous changes in the directory.
+void remove_dir_recursive(const std::string& path);
+
+/// Same as remove_dir_recursive() except that this one returns false, rather
+/// than throwing an exception, if the specified directory did not
+/// exist. If the directory did exist, and was deleted, this function
+/// returns true.
+///
+/// try_remove_dir_recursive() assumes that no other process or thread is making
+/// simultaneous changes in the directory.
+bool try_remove_dir_recursive(const std::string& path);
+
+/// Create a new unique directory for temporary files. The absolute
+/// path to the new directory is returned without a trailing slash.
+std::string make_temp_dir();
+
+size_t page_size();
+
+
+/// This class provides a RAII abstraction over the concept of a file
+/// descriptor (or file handle).
+///
+/// Locks are automatically and immediately released when the File
+/// instance is closed.
+///
+/// You can use CloseGuard and UnlockGuard to acheive exception-safe
+/// closing or unlocking prior to the File instance being detroyed.
+///
+/// A single File instance must never be accessed concurrently by
+/// multiple threads.
+///
+/// You can write to a file via an std::ostream as follows:
+///
+/// \code{.cpp}
+///
+///   File::Streambuf my_streambuf(&my_file);
+///   std::ostream out(&my_strerambuf);
+///   out << 7945.9;
+///
+/// \endcode
+class File {
+public:
+    enum Mode {
+        mode_Read,   ///< access_ReadOnly,  create_Never             (fopen: rb)
+        mode_Update, ///< access_ReadWrite, create_Never             (fopen: rb+)
+        mode_Write,  ///< access_ReadWrite, create_Auto, flag_Trunc  (fopen: wb+)
+        mode_Append  ///< access_ReadWrite, create_Auto, flag_Append (fopen: ab+)
+    };
+
+    /// Equivalent to calling open(const std::string&, Mode) on a
+    /// default constructed instance.
+    explicit File(const std::string& path, Mode = mode_Read);
+
+    /// Create an instance that is not initially attached to an open
+    /// file.
+    File() noexcept;
+
+    ~File() noexcept;
+
+    File(File&&) noexcept;
+    File& operator=(File&&) noexcept;
+
+    // Disable copying by l-value. Copying an open file will create a scenario
+    // where the same file descriptor will be opened once but closed twice.
+    File(const File&) = delete;
+    File& operator=(const File&) = delete;
+
+    /// Calling this function on an instance that is already attached
+    /// to an open file has undefined behavior.
+    ///
+    /// \throw AccessError If the file could not be opened. If the
+    /// reason corresponds to one of the exception types that are
+    /// derived from AccessError, the derived exception type is thrown
+    /// (as long as the underlying system provides the information to
+    /// unambiguously distinguish that particular reason).
+    void open(const std::string& path, Mode = mode_Read);
+
+    /// This function is idempotent, that is, it is valid to call it
+    /// regardless of whether this instance currently is attached to
+    /// an open file.
+    void close() noexcept;
+
+    /// Check whether this File instance is currently attached to an
+    /// open file.
+    bool is_attached() const noexcept;
+
+    enum AccessMode {
+        access_ReadOnly,
+        access_ReadWrite,
+    };
+
+    enum CreateMode {
+        create_Auto,  ///< Create the file if it does not already exist.
+        create_Never, ///< Fail if the file does not already exist.
+        create_Must   ///< Fail if the file already exists.
+    };
+
+    enum {
+        flag_Trunc = 1, ///< Truncate the file if it already exists.
+        flag_Append = 2 ///< Move to end of file before each write.
+    };
+
+    /// See open(const std::string&, Mode).
+    ///
+    /// Specifying access_ReadOnly together with a create mode that is
+    /// not create_Never, or together with a non-zero \a flags
+    /// argument, results in undefined behavior. Specifying flag_Trunc
+    /// together with create_Must results in undefined behavior.
+    void open(const std::string& path, AccessMode, CreateMode, int flags);
+
+    /// Same as open(path, access_ReadWrite, create_Auto, 0), except
+    /// that this one returns an indication of whether a new file was
+    /// created, or an existing file was opened.
+    void open(const std::string& path, bool& was_created);
+
+    /// Read data into the specified buffer and return the number of
+    /// bytes read. If the returned number of bytes is less than \a
+    /// size, then the end of the file has been reached.
+    ///
+    /// Calling this function on an instance, that is not currently
+    /// attached to an open file, has undefined behavior.
+    size_t read(char* data, size_t size);
+    static size_t read_static(FileDesc fd, char* data, size_t size);
+
+    /// Write the specified data to this file.
+    ///
+    /// Calling this function on an instance, that is not currently
+    /// attached to an open file, has undefined behavior.
+    ///
+    /// Calling this function on an instance, that was opened in
+    /// read-only mode, has undefined behavior.
+    void write(const char* data, size_t size);
+    static void write_static(FileDesc fd, const char* data, size_t size);
+
+    // Tells current file pointer of fd
+    static uint64_t get_file_pos(FileDesc fd);
+
+    /// Calls write(s.data(), s.size()).
+    void write(const std::string& s)
+    {
+        write(s.data(), s.size());
+    }
+
+    /// Calls read(data, N).
+    template <size_t N>
+    size_t read(char (&data)[N])
+    {
+        return read(data, N);
+    }
+
+    /// Calls write(data(), N).
+    template <size_t N>
+    void write(const char (&data)[N])
+    {
+        write(data, N);
+    }
+
+    /// Plays the same role as off_t in POSIX
+    typedef int_fast64_t SizeType;
+
+    /// Calling this function on an instance that is not attached to
+    /// an open file has undefined behavior.
+    SizeType get_size() const;
+    static SizeType get_size_static(FileDesc fd);
+
+    /// If this causes the file to grow, then the new section will
+    /// have undefined contents. Setting the size with this function
+    /// does not necessarily allocate space on the target device. If
+    /// you want to ensure allocation, call alloc(). Calling this
+    /// function will generally affect the read/write offset
+    /// associated with this File instance.
+    ///
+    /// Calling this function on an instance that is not attached to
+    /// an open file has undefined behavior. Calling this function on
+    /// a file that is opened in read-only mode, is an error.
+    void resize(SizeType);
+
+    /// The same as prealloc_if_supported() but when the operation is
+    /// not supported by the system, this function will still increase
+    /// the file size when the specified region extends beyond the
+    /// current end of the file. This allows you to both extend and
+    /// allocate in one operation.
+    ///
+    /// The downside is that this function is not guaranteed to have
+    /// atomic behaviour on all systems, that is, two processes, or
+    /// two threads should never call this function concurrently for
+    /// the same underlying file even though they access the file
+    /// through distinct File instances.
+    ///
+    /// \sa prealloc_if_supported()
+    void prealloc(SizeType offset, size_t size);
+
+    /// When supported by the system, allocate space on the target
+    /// device for the specified region of the file. If the region
+    /// extends beyond the current end of the file, the file size is
+    /// increased as necessary.
+    ///
+    /// On systems that do not support this operation, this function
+    /// has no effect. You may call is_prealloc_supported() to
+    /// determine if it is supported on your system.
+    ///
+    /// Calling this function on an instance, that is not attached to
+    /// an open file, has undefined behavior. Calling this function on
+    /// a file, that is opened in read-only mode, is an error.
+    ///
+    /// This function is guaranteed to have atomic behaviour, that is,
+    /// there is never any risk of the file size being reduced even
+    /// with concurrently executing invocations.
+    ///
+    /// \sa prealloc()
+    /// \sa is_prealloc_supported()
+    void prealloc_if_supported(SizeType offset, size_t size);
+
+    /// See prealloc_if_supported().
+    static bool is_prealloc_supported();
+
+    /// Reposition the read/write offset of this File
+    /// instance. Distinct File instances have separate independent
+    /// offsets, as long as the cucrrent process is not forked.
+    void seek(SizeType);
+    static void seek_static(FileDesc, SizeType);
+
+    /// Flush in-kernel buffers to disk. This blocks the caller until the
+    /// synchronization operation is complete. On POSIX systems this function
+    /// calls `fsync()`. On Apple platforms if calls `fcntl()` with command
+    /// `F_FULLFSYNC`.
+    void sync();
+
+    /// Place an exclusive lock on this file. This blocks the caller
+    /// until all other locks have been released.
+    ///
+    /// Locks acquired on distinct File instances have fully recursive
+    /// behavior, even if they are acquired in the same process (or
+    /// thread) and are attached to the same underlying file.
+    ///
+    /// Calling this function on an instance that is not attached to
+    /// an open file, or on an instance that is already locked has
+    /// undefined behavior.
+    void lock_exclusive();
+
+    /// Place an shared lock on this file. This blocks the caller
+    /// until all other exclusive locks have been released.
+    ///
+    /// Locks acquired on distinct File instances have fully recursive
+    /// behavior, even if they are acquired in the same process (or
+    /// thread) and are attached to the same underlying file.
+    ///
+    /// Calling this function on an instance that is not attached to
+    /// an open file, or on an instance that is already locked has
+    /// undefined behavior.
+    void lock_shared();
+
+    /// Non-blocking version of lock_exclusive(). Returns true iff it
+    /// succeeds.
+    bool try_lock_exclusive();
+
+    /// Non-blocking version of lock_shared(). Returns true iff it
+    /// succeeds.
+    bool try_lock_shared();
+
+    /// Release a previously acquired lock on this file. This function
+    /// is idempotent.
+    void unlock() noexcept;
+
+    /// Set the encryption key used for this file. Must be called before any
+    /// mappings are created or any data is read from or written to the file.
+    ///
+    /// \param key A 64-byte encryption key, or null to disable encryption.
+    void set_encryption_key(const char* key);
+
+    /// Get the encryption key set by set_encryption_key(),
+    /// null_ptr if no key set.
+    const char* get_encryption_key();
+    enum {
+        /// If possible, disable opportunistic flushing of dirted
+        /// pages of a memory mapped file to physical medium. On some
+        /// systems this cannot be disabled. On other systems it is
+        /// the default behavior. An explicit call to sync_map() will
+        /// flush the buffers regardless of whether this flag is
+        /// specified or not.
+        map_NoSync = 1
+    };
+
+    /// Map this file into memory. The file is mapped as shared
+    /// memory. This allows two processes to interact under exatly the
+    /// same rules as applies to the interaction via regular memory of
+    /// multiple threads inside a single process.
+    ///
+    /// This File instance does not need to remain in existence after
+    /// the mapping is established.
+    ///
+    /// Multiple concurrent mappings may be created from the same File
+    /// instance.
+    ///
+    /// Specifying access_ReadWrite for a file that is opened in
+    /// read-only mode, is an error.
+    ///
+    /// Calling this function on an instance that is not attached to
+    /// an open file, or one that is attached to an empty file has
+    /// undefined behavior.
+    ///
+    /// Calling this function with a size that is greater than the
+    /// size of the file has undefined behavior.
+    void* map(AccessMode, size_t size, int map_flags = 0, size_t offset = 0) const;
+
+    /// The same as unmap(old_addr, old_size) followed by map(a,
+    /// new_size, map_flags), but more efficient on some systems.
+    ///
+    /// The old address range must have been acquired by a call to
+    /// map() or remap() on this File instance, the specified access
+    /// mode and flags must be the same as the ones specified
+    /// previously, and this File instance must not have been reopend
+    /// in the meantime. Failing to adhere to these rules will result
+    /// in undefined behavior.
+    ///
+    /// If this function throws, the old address range will remain
+    /// mapped.
+    void* remap(void* old_addr, size_t old_size, AccessMode a, size_t new_size, int map_flags = 0,
+                size_t file_offset = 0) const;
+
+#if REALM_ENABLE_ENCRYPTION
+    void* map(AccessMode, size_t size, EncryptedFileMapping*& mapping, int map_flags = 0, size_t offset = 0) const;
+#endif
+    /// Unmap the specified address range which must have been
+    /// previously returned by map().
+    static void unmap(void* addr, size_t size) noexcept;
+
+    /// Flush in-kernel buffers to disk. This blocks the caller until
+    /// the synchronization operation is complete. The specified
+    /// address range must be (a subset of) one that was previously returned by
+    /// map().
+    static void sync_map(FileDesc fd, void* addr, size_t size);
+
+    /// Check whether the specified file or directory exists. Note
+    /// that a file or directory that resides in a directory that the
+    /// calling process has no access to, will necessarily be reported
+    /// as not existing.
+    static bool exists(const std::string& path);
+
+    /// Check whether the specified path exists and refers to a directory. If
+    /// the referenced file system object resides in an inaccessible directory,
+    /// this function returns false.
+    static bool is_dir(const std::string& path);
+
+    /// Remove the specified file path from the file system. It is an error if
+    /// the specified path is a directory. If the specified file is a symbolic
+    /// link, the link is removed, leaving the liked file intact. In so far as
+    /// the specified path is not a directory, std::remove(const char*) is
+    /// equivalent to this function.
+    ///
+    /// The specified file must not be open by the calling process. If
+    /// it is, this function has undefined behaviour. Note that an
+    /// open memory map of the file counts as "the file being open".
+    ///
+    /// \throw AccessError If the specified directory entry could not
+    /// be removed. If the reason corresponds to one of the exception
+    /// types that are derived from AccessError, the derived exception
+    /// type is thrown (as long as the underlying system provides the
+    /// information to unambiguously distinguish that particular
+    /// reason).
+    static void remove(const std::string& path);
+
+    /// Same as remove() except that this one returns false, rather
+    /// than throwing an exception, if the specified file does not
+    /// exist. If the file did exist, and was deleted, this function
+    /// returns true.
+    static bool try_remove(const std::string& path);
+
+    /// Change the path of a directory entry. This can be used to
+    /// rename a file, and/or to move it from one directory to
+    /// another. This function is equivalent to std::rename(const
+    /// char*, const char*).
+    ///
+    /// \throw AccessError If the path of the directory entry could
+    /// not be changed. If the reason corresponds to one of the
+    /// exception types that are derived from AccessError, the derived
+    /// exception type is thrown (as long as the underlying system
+    /// provides the information to unambiguously distinguish that
+    /// particular reason).
+    static void move(const std::string& old_path, const std::string& new_path);
+
+    /// Copy the file at the specified origin path to the specified target path.
+    static void copy(const std::string& origin_path, const std::string& target_path);
+
+    /// Compare the two files at the specified paths for equality. Returns true
+    /// if, and only if they are equal.
+    static bool compare(const std::string& path_1, const std::string& path_2);
+
+    /// Check whether two open file descriptors refer to the same
+    /// underlying file, that is, if writing via one of them, will
+    /// affect what is read from the other. In UNIX this boils down to
+    /// comparing inode numbers.
+    ///
+    /// Both instances have to be attached to open files. If they are
+    /// not, this function has undefined behavior.
+    bool is_same_file(const File&) const;
+    static bool is_same_file_static(FileDesc f1, FileDesc f2);
+
+    // FIXME: Get rid of this method
+    bool is_removed() const;
+
+    /// Resolve the specified path against the specified base directory.
+    ///
+    /// If \a path is absolute, or if \a base_dir is empty, \p path is returned
+    /// unmodified, otherwise \a path is resolved against \a base_dir.
+    ///
+    /// Examples (assuming POSIX):
+    ///
+    ///    resolve("file", "dir")        -> "dir/file"
+    ///    resolve("../baz", "/foo/bar") -> "/foo/baz"
+    ///    resolve("foo", ".")           -> "./foo"
+    ///    resolve(".", "/foo/")         -> "/foo"
+    ///    resolve("..", "foo")          -> "."
+    ///    resolve("../..", "foo")       -> ".."
+    ///    resolve("..", "..")           -> "../.."
+    ///    resolve("", "")               -> "."
+    ///    resolve("", "/")              -> "/."
+    ///    resolve("..", "/")            -> "/."
+    ///    resolve("..", "foo//bar")     -> "foo"
+    ///
+    /// This function does not access the file system.
+    ///
+    /// \param path The path to be resolved. An empty string produces the same
+    /// result as as if "." was passed. The result has a trailing directory
+    /// separator (`/`) if, and only if this path has a trailing directory
+    /// separator.
+    ///
+    /// \param base_dir The base directory path, which may be relative or
+    /// absolute. A final directory separator (`/`) is optional. The empty
+    /// string is interpreted as a relative path.
+    static std::string resolve(const std::string& path, const std::string& base_dir);
+
+    using ForEachHandler = std::function<bool(const std::string& file, const std::string& dir)>;
+
+    /// Scan the specified directory recursivle, and report each file
+    /// (nondirectory entry) via the specified handler.
+    ///
+    /// The first argument passed to the handler is the name of a file (not the
+    /// whole path), and the second argument is the directory in which that file
+    /// resides. The directory will be specified as a path, and relative to \a
+    /// dir_path. The directory will be the empty string for files residing
+    /// directly in \a dir_path.
+    ///
+    /// If the handler returns false, scanning will be aborted immediately, and
+    /// for_each() will return false. Otherwise for_each() will return true.
+    ///
+    /// Scanning is done as if by a recursive set of DirScanner objects.
+    static bool for_each(const std::string& dir_path, ForEachHandler handler);
+
+    struct UniqueID {
+#ifdef _WIN32 // Windows version
+// FIXME: This is not implemented for Windows
+#else
+        // NDK r10e has a bug in sys/stat.h dev_t ino_t are 4 bytes,
+        // but stat.st_dev and st_ino are 8 bytes. So we just use uint64 instead.
+        uint_fast64_t device;
+        uint_fast64_t inode;
+#endif
+    };
+    // Return the unique id for the current opened file descriptor.
+    // Same UniqueID means they are the same file.
+    UniqueID get_unique_id() const;
+    // Return false if the file doesn't exist. Otherwise uid will be set.
+    static bool get_unique_id(const std::string& path, UniqueID& uid);
+
+    class ExclusiveLock;
+    class SharedLock;
+
+    template <class>
+    class Map;
+
+    class CloseGuard;
+    class UnlockGuard;
+    class UnmapGuard;
+
+    class Streambuf;
+
+    // Exceptions
+    class AccessError;
+    class PermissionDenied;
+    class NotFound;
+    class Exists;
+
+private:
+#ifdef _WIN32
+    void* m_fd;
+    bool m_have_lock; // Only valid when m_fd is not null
+#else
+    int m_fd;
+#endif
+    std::unique_ptr<const char[]> m_encryption_key = nullptr;
+
+    bool lock(bool exclusive, bool non_blocking);
+    void open_internal(const std::string& path, AccessMode, CreateMode, int flags, bool* success);
+
+    struct MapBase {
+        void* m_addr = nullptr;
+        size_t m_size = 0;
+        FileDesc m_fd;
+
+        MapBase() noexcept;
+        ~MapBase() noexcept;
+
+        // Disable copying. Copying an opened MapBase will create a scenario
+        // where the same memory will be mapped once but unmapped twice.
+        MapBase(const MapBase&) = delete;
+        MapBase& operator=(const MapBase&) = delete;
+
+        void map(const File&, AccessMode, size_t size, int map_flags, size_t offset = 0);
+        void remap(const File&, AccessMode, size_t size, int map_flags);
+        void unmap() noexcept;
+        void sync();
+#if REALM_ENABLE_ENCRYPTION
+        util::EncryptedFileMapping* m_encrypted_mapping = nullptr;
+        inline util::EncryptedFileMapping* get_encrypted_mapping() const
+        {
+            return m_encrypted_mapping;
+        }
+#else
+        inline util::EncryptedFileMapping* get_encrypted_mapping() const
+        {
+            return nullptr;
+        }
+#endif
+    };
+};
+
+
+class File::ExclusiveLock {
+public:
+    ExclusiveLock(File& f)
+        : m_file(f)
+    {
+        f.lock_exclusive();
+    }
+    ~ExclusiveLock() noexcept
+    {
+        m_file.unlock();
+    }
+    // Disable copying. It is not how this class should be used.
+    ExclusiveLock(const ExclusiveLock&) = delete;
+    ExclusiveLock& operator=(const ExclusiveLock&) = delete;
+
+private:
+    File& m_file;
+};
+
+class File::SharedLock {
+public:
+    SharedLock(File& f)
+        : m_file(f)
+    {
+        f.lock_shared();
+    }
+    ~SharedLock() noexcept
+    {
+        m_file.unlock();
+    }
+    // Disable copying. It is not how this class should be used.
+    SharedLock(const SharedLock&) = delete;
+    SharedLock& operator=(const SharedLock&) = delete;
+
+private:
+    File& m_file;
+};
+
+
+/// This class provides a RAII abstraction over the concept of a
+/// memory mapped file.
+///
+/// Once created, the Map instance makes no reference to the File
+/// instance that it was based upon, and that File instance may be
+/// destroyed before the Map instance is destroyed.
+///
+/// Multiple concurrent mappings may be created from the same File
+/// instance.
+///
+/// You can use UnmapGuard to acheive exception-safe unmapping prior
+/// to the Map instance being detroyed.
+///
+/// A single Map instance must never be accessed concurrently by
+/// multiple threads.
+template <class T>
+class File::Map : private MapBase {
+public:
+    /// Equivalent to calling map() on a default constructed instance.
+    explicit Map(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0);
+
+    explicit Map(const File&, size_t offset, AccessMode = access_ReadOnly, size_t size = sizeof(T),
+                 int map_flags = 0);
+
+    /// Create an instance that is not initially attached to a memory
+    /// mapped file.
+    Map() noexcept;
+
+    ~Map() noexcept;
+
+    // Disable copying. Copying an opened Map will create a scenario
+    // where the same memory will be mapped once but unmapped twice.
+    Map(const Map&) = delete;
+    Map& operator=(const Map&) = delete;
+
+    /// Move the mapping from another Map object to this Map object
+    File::Map<T>& operator=(File::Map<T>&& other)
+    {
+        if (m_addr)
+            unmap();
+        m_addr = other.get_addr();
+        m_size = other.m_size;
+        other.m_addr = 0;
+        other.m_size = 0;
+#if REALM_ENABLE_ENCRYPTION
+        m_encrypted_mapping = other.m_encrypted_mapping;
+        other.m_encrypted_mapping = nullptr;
+#endif
+        return *this;
+    }
+
+    /// See File::map().
+    ///
+    /// Calling this function on a Map instance that is already
+    /// attached to a memory mapped file has undefined behavior. The
+    /// returned pointer is the same as what will subsequently be
+    /// returned by get_addr().
+    T* map(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0, size_t offset = 0);
+
+    /// See File::unmap(). This function is idempotent, that is, it is
+    /// valid to call it regardless of whether this instance is
+    /// currently attached to a memory mapped file.
+    void unmap() noexcept;
+
+    /// See File::remap().
+    ///
+    /// Calling this function on a Map instance that is not currently
+    /// attached to a memory mapped file has undefined behavior. The
+    /// returned pointer is the same as what will subsequently be
+    /// returned by get_addr().
+    T* remap(const File&, AccessMode = access_ReadOnly, size_t size = sizeof(T), int map_flags = 0);
+
+    /// See File::sync_map().
+    ///
+    /// Calling this function on an instance that is not currently
+    /// attached to a memory mapped file, has undefined behavior.
+    void sync();
+
+    /// Check whether this Map instance is currently attached to a
+    /// memory mapped file.
+    bool is_attached() const noexcept;
+
+    /// Returns a pointer to the beginning of the memory mapped file,
+    /// or null if this instance is not currently attached.
+    T* get_addr() const noexcept;
+
+    /// Returns the size of the mapped region, or zero if this
+    /// instance does not currently refer to a memory mapped
+    /// file. When this instance refers to a memory mapped file, the
+    /// returned value will always be identical to the size passed to
+    /// the constructor or to map().
+    size_t get_size() const noexcept;
+
+    /// Release the currently attached memory mapped file from this
+    /// Map instance. The address range may then be unmapped later by
+    /// a call to File::unmap().
+    T* release() noexcept;
+
+#if REALM_ENABLE_ENCRYPTION
+    /// Get the encrypted file mapping corresponding to this mapping
+    inline EncryptedFileMapping* get_encrypted_mapping() const
+    {
+        return m_encrypted_mapping;
+    }
+#else
+    inline EncryptedFileMapping* get_encrypted_mapping() const
+    {
+        return nullptr;
+    }
+#endif
+
+    friend class UnmapGuard;
+};
+
+
+class File::CloseGuard {
+public:
+    CloseGuard(File& f) noexcept
+        : m_file(&f)
+    {
+    }
+    ~CloseGuard() noexcept
+    {
+        if (m_file)
+            m_file->close();
+    }
+    void release() noexcept
+    {
+        m_file = nullptr;
+    }
+    // Disallow the default implementation of copy/assign, this is not how this
+    // class is intended to be used. For example we could get unexpected
+    // behaviour if one CloseGuard is copied and released but the other is not.
+    CloseGuard(const CloseGuard&) = delete;
+    CloseGuard& operator=(const CloseGuard&) = delete;
+
+private:
+    File* m_file;
+};
+
+
+class File::UnlockGuard {
+public:
+    UnlockGuard(File& f) noexcept
+        : m_file(&f)
+    {
+    }
+    ~UnlockGuard() noexcept
+    {
+        if (m_file)
+            m_file->unlock();
+    }
+    void release() noexcept
+    {
+        m_file = nullptr;
+    }
+    // Disallow the default implementation of copy/assign, this is not how this
+    // class is intended to be used. For example we could get unexpected
+    // behaviour if one UnlockGuard is copied and released but the other is not.
+    UnlockGuard(const UnlockGuard&) = delete;
+    UnlockGuard& operator=(const UnlockGuard&) = delete;
+
+private:
+    File* m_file;
+};
+
+
+class File::UnmapGuard {
+public:
+    template <class T>
+    UnmapGuard(Map<T>& m) noexcept
+        : m_map(&m)
+    {
+    }
+    ~UnmapGuard() noexcept
+    {
+        if (m_map)
+            m_map->unmap();
+    }
+    void release() noexcept
+    {
+        m_map = nullptr;
+    }
+    // Disallow the default implementation of copy/assign, this is not how this
+    // class is intended to be used. For example we could get unexpected
+    // behaviour if one UnmapGuard is copied and released but the other is not.
+    UnmapGuard(const UnmapGuard&) = delete;
+    UnmapGuard& operator=(const UnmapGuard&) = delete;
+
+private:
+    MapBase* m_map;
+};
+
+
+/// Only output is supported at this point.
+class File::Streambuf : public std::streambuf {
+public:
+    explicit Streambuf(File*);
+    ~Streambuf() noexcept;
+
+    // Disable copying
+    Streambuf(const Streambuf&) = delete;
+    Streambuf& operator=(const Streambuf&) = delete;
+
+private:
+    static const size_t buffer_size = 4096;
+
+    File& m_file;
+    std::unique_ptr<char[]> const m_buffer;
+
+    int_type overflow(int_type) override;
+    int sync() override;
+    pos_type seekpos(pos_type, std::ios_base::openmode) override;
+    void flush();
+};
+
+
+/// Used for any I/O related exception. Note the derived exception
+/// types that are used for various specific types of errors.
+class File::AccessError : public std::runtime_error {
+public:
+    AccessError(const std::string& msg, const std::string& path);
+
+    /// Return the associated file system path, or the empty string if there is
+    /// no associated file system path, or if the file system path is unknown.
+    std::string get_path() const;
+
+private:
+    std::string m_path;
+};
+
+
+/// Thrown if the user does not have permission to open or create
+/// the specified file in the specified access mode.
+class File::PermissionDenied : public AccessError {
+public:
+    PermissionDenied(const std::string& msg, const std::string& path);
+};
+
+
+/// Thrown if the directory part of the specified path was not
+/// found, or create_Never was specified and the file did no
+/// exist.
+class File::NotFound : public AccessError {
+public:
+    NotFound(const std::string& msg, const std::string& path);
+};
+
+
+/// Thrown if create_Always was specified and the file did already
+/// exist.
+class File::Exists : public AccessError {
+public:
+    Exists(const std::string& msg, const std::string& path);
+};
+
+
+class DirScanner {
+public:
+    DirScanner(const std::string& path, bool allow_missing = false);
+    ~DirScanner() noexcept;
+    bool next(std::string& name);
+
+private:
+#ifndef _WIN32
+    DIR* m_dirp;
+#elif REALM_HAVE_STD_FILESYSTEM
+    std::filesystem::directory_iterator m_iterator;
+#endif
+};
+
+
+// Implementation:
+
+inline File::File(const std::string& path, Mode m)
+{
+#ifdef _WIN32
+    m_fd = nullptr;
+#else
+    m_fd = -1;
+#endif
+
+    open(path, m);
+}
+
+inline File::File() noexcept
+{
+#ifdef _WIN32
+    m_fd = nullptr;
+#else
+    m_fd = -1;
+#endif
+}
+
+inline File::~File() noexcept
+{
+    close();
+}
+
+inline File::File(File&& f) noexcept
+{
+#ifdef _WIN32
+    m_fd = f.m_fd;
+    m_have_lock = f.m_have_lock;
+    f.m_fd = nullptr;
+#else
+    m_fd = f.m_fd;
+    f.m_fd = -1;
+#endif
+    m_encryption_key = std::move(f.m_encryption_key);
+}
+
+inline File& File::operator=(File&& f) noexcept
+{
+    close();
+#ifdef _WIN32
+    m_fd = f.m_fd;
+    m_have_lock = f.m_have_lock;
+    f.m_fd = nullptr;
+#else
+    m_fd = f.m_fd;
+    f.m_fd = -1;
+#endif
+    m_encryption_key = std::move(f.m_encryption_key);
+    return *this;
+}
+
+inline void File::open(const std::string& path, Mode m)
+{
+    AccessMode a = access_ReadWrite;
+    CreateMode c = create_Auto;
+    int flags = 0;
+    switch (m) {
+        case mode_Read:
+            a = access_ReadOnly;
+            c = create_Never;
+            break;
+        case mode_Update:
+            c = create_Never;
+            break;
+        case mode_Write:
+            flags = flag_Trunc;
+            break;
+        case mode_Append:
+            flags = flag_Append;
+            break;
+    }
+    open(path, a, c, flags);
+}
+
+inline void File::open(const std::string& path, AccessMode am, CreateMode cm, int flags)
+{
+    open_internal(path, am, cm, flags, nullptr);
+}
+
+
+inline void File::open(const std::string& path, bool& was_created)
+{
+    while (1) {
+        bool success;
+        open_internal(path, access_ReadWrite, create_Must, 0, &success);
+        if (success) {
+            was_created = true;
+            return;
+        }
+        open_internal(path, access_ReadWrite, create_Never, 0, &success);
+        if (success) {
+            was_created = false;
+            return;
+        }
+    }
+}
+
+inline bool File::is_attached() const noexcept
+{
+#ifdef _WIN32
+    return (m_fd != nullptr);
+#else
+    return 0 <= m_fd;
+#endif
+}
+
+inline void File::lock_exclusive()
+{
+    lock(true, false);
+}
+
+inline void File::lock_shared()
+{
+    lock(false, false);
+}
+
+inline bool File::try_lock_exclusive()
+{
+    return lock(true, true);
+}
+
+inline bool File::try_lock_shared()
+{
+    return lock(false, true);
+}
+
+inline File::MapBase::MapBase() noexcept
+{
+    m_addr = nullptr;
+}
+
+inline File::MapBase::~MapBase() noexcept
+{
+    unmap();
+}
+
+inline void File::MapBase::map(const File& f, AccessMode a, size_t size, int map_flags, size_t offset)
+{
+    REALM_ASSERT(!m_addr);
+#if REALM_ENABLE_ENCRYPTION
+    m_addr = f.map(a, size, m_encrypted_mapping, map_flags, offset);
+#else
+    m_addr = f.map(a, size, map_flags, offset);
+#endif
+    m_size = size;
+    m_fd = f.m_fd;
+}
+
+inline void File::MapBase::unmap() noexcept
+{
+    if (!m_addr)
+        return;
+    File::unmap(m_addr, m_size);
+    m_addr = nullptr;
+#if REALM_ENABLE_ENCRYPTION
+    m_encrypted_mapping = nullptr;
+#endif
+    m_fd = 0;
+}
+
+inline void File::MapBase::remap(const File& f, AccessMode a, size_t size, int map_flags)
+{
+    REALM_ASSERT(m_addr);
+
+    m_addr = f.remap(m_addr, m_size, a, size, map_flags);
+    m_size = size;
+    m_fd = f.m_fd;
+}
+
+inline void File::MapBase::sync()
+{
+    REALM_ASSERT(m_addr);
+
+    File::sync_map(m_fd, m_addr, m_size);
+}
+
+template <class T>
+inline File::Map<T>::Map(const File& f, AccessMode a, size_t size, int map_flags)
+{
+    map(f, a, size, map_flags);
+}
+
+template <class T>
+inline File::Map<T>::Map(const File& f, size_t offset, AccessMode a, size_t size, int map_flags)
+{
+    map(f, a, size, map_flags, offset);
+}
+
+template <class T>
+inline File::Map<T>::Map() noexcept
+{
+}
+
+template <class T>
+inline File::Map<T>::~Map() noexcept
+{
+}
+
+template <class T>
+inline T* File::Map<T>::map(const File& f, AccessMode a, size_t size, int map_flags, size_t offset)
+{
+    MapBase::map(f, a, size, map_flags, offset);
+    return static_cast<T*>(m_addr);
+}
+
+template <class T>
+inline void File::Map<T>::unmap() noexcept
+{
+    MapBase::unmap();
+}
+
+template <class T>
+inline T* File::Map<T>::remap(const File& f, AccessMode a, size_t size, int map_flags)
+{
+    MapBase::remap(f, a, size, map_flags);
+    return static_cast<T*>(m_addr);
+}
+
+template <class T>
+inline void File::Map<T>::sync()
+{
+    MapBase::sync();
+}
+
+template <class T>
+inline bool File::Map<T>::is_attached() const noexcept
+{
+    return (m_addr != nullptr);
+}
+
+template <class T>
+inline T* File::Map<T>::get_addr() const noexcept
+{
+    return static_cast<T*>(m_addr);
+}
+
+template <class T>
+inline size_t File::Map<T>::get_size() const noexcept
+{
+    return m_addr ? m_size : 0;
+}
+
+template <class T>
+inline T* File::Map<T>::release() noexcept
+{
+    T* addr = static_cast<T*>(m_addr);
+    m_addr = nullptr;
+    m_fd = 0;
+    return addr;
+}
+
+
+inline File::Streambuf::Streambuf(File* f)
+    : m_file(*f)
+    , m_buffer(new char[buffer_size])
+{
+    char* b = m_buffer.get();
+    setp(b, b + buffer_size);
+}
+
+inline File::Streambuf::~Streambuf() noexcept
+{
+    try {
+        if (m_file.is_attached())
+            flush();
+    }
+    catch (...) {
+        // Errors deliberately ignored
+    }
+}
+
+inline File::Streambuf::int_type File::Streambuf::overflow(int_type c)
+{
+    flush();
+    if (c == traits_type::eof())
+        return traits_type::not_eof(c);
+    *pptr() = traits_type::to_char_type(c);
+    pbump(1);
+    return c;
+}
+
+inline int File::Streambuf::sync()
+{
+    flush();
+    return 0;
+}
+
+inline File::Streambuf::pos_type File::Streambuf::seekpos(pos_type pos, std::ios_base::openmode)
+{
+    flush();
+    SizeType pos2 = 0;
+    if (int_cast_with_overflow_detect(std::streamsize(pos), pos2))
+        throw std::runtime_error("Seek position overflow");
+    m_file.seek(pos2);
+    return pos;
+}
+
+inline void File::Streambuf::flush()
+{
+    size_t n = pptr() - pbase();
+    if (n > 0) {
+        m_file.write(pbase(), n);
+        setp(m_buffer.get(), epptr());
+    }
+}
+
+inline File::AccessError::AccessError(const std::string& msg, const std::string& path)
+    : std::runtime_error(msg)
+    , m_path(path)
+{
+}
+
+inline std::string File::AccessError::get_path() const
+{
+    return m_path;
+}
+
+inline File::PermissionDenied::PermissionDenied(const std::string& msg, const std::string& path)
+    : AccessError(msg, path)
+{
+}
+
+inline File::NotFound::NotFound(const std::string& msg, const std::string& path)
+    : AccessError(msg, path)
+{
+}
+
+inline File::Exists::Exists(const std::string& msg, const std::string& path)
+    : AccessError(msg, path)
+{
+}
+
+inline bool operator==(const File::UniqueID& lhs, const File::UniqueID& rhs)
+{
+#ifdef _WIN32 // Windows version
+    throw std::runtime_error("Not yet supported");
+#else // POSIX version
+    return lhs.device == rhs.device && lhs.inode == rhs.inode;
+#endif
+}
+
+inline bool operator!=(const File::UniqueID& lhs, const File::UniqueID& rhs)
+{
+    return !(lhs == rhs);
+}
+
+inline bool operator<(const File::UniqueID& lhs, const File::UniqueID& rhs)
+{
+#ifdef _WIN32 // Windows version
+    throw std::runtime_error("Not yet supported");
+#else // POSIX version
+    if (lhs.device < rhs.device)
+        return true;
+    if (lhs.device > rhs.device)
+        return false;
+    if (lhs.inode < rhs.inode)
+        return true;
+    return false;
+#endif
+}
+
+inline bool operator>(const File::UniqueID& lhs, const File::UniqueID& rhs)
+{
+    return rhs < lhs;
+}
+
+inline bool operator<=(const File::UniqueID& lhs, const File::UniqueID& rhs)
+{
+    return !(lhs > rhs);
+}
+
+inline bool operator>=(const File::UniqueID& lhs, const File::UniqueID& rhs)
+{
+    return !(lhs < rhs);
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_FILE_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/file_mapper.hpp b/iOS/Pods/Realm/include/core/realm/util/file_mapper.hpp
new file mode 100644 (file)
index 0000000..693bda0
--- /dev/null
@@ -0,0 +1,120 @@
+/*************************************************************************
+ *
+ * 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_UTIL_FILE_MAPPER_HPP
+#define REALM_UTIL_FILE_MAPPER_HPP
+
+#include <realm/util/file.hpp>
+#include <realm/utilities.hpp>
+#include <realm/util/thread.hpp>
+#include <realm/util/encrypted_file_mapping.hpp>
+
+namespace realm {
+namespace util {
+
+void* mmap(FileDesc fd, size_t size, File::AccessMode access, size_t offset, const char* encryption_key);
+void munmap(void* addr, size_t size) noexcept;
+void* mremap(FileDesc fd, size_t file_offset, void* old_addr, size_t old_size, File::AccessMode a, size_t new_size,
+             const char* encryption_key);
+void msync(FileDesc fd, void* addr, size_t size);
+
+// A function which may be given to encryption_read_barrier. If present, the read barrier is a
+// a barrier for a full array. If absent, the read barrier is a barrier only for the address
+// range give as argument. If the barrier is for a full array, it will read the array header
+// and determine the address range from the header.
+using HeaderToSize = size_t (*)(const char* addr);
+class EncryptedFileMapping;
+
+#if REALM_ENABLE_ENCRYPTION
+
+
+// This variant allows the caller to obtain direct access to the encrypted file mapping
+// for optimization purposes.
+void* mmap(FileDesc fd, size_t size, File::AccessMode access, size_t offset, const char* encryption_key,
+           EncryptedFileMapping*& mapping);
+
+void do_encryption_read_barrier(const void* addr, size_t size, HeaderToSize header_to_size,
+                                EncryptedFileMapping* mapping);
+
+void do_encryption_write_barrier(const void* addr, size_t size, EncryptedFileMapping* mapping);
+
+void inline encryption_read_barrier(const void* addr, size_t size, EncryptedFileMapping* mapping,
+                                    HeaderToSize header_to_size = nullptr)
+{
+    if (mapping)
+        do_encryption_read_barrier(addr, size, header_to_size, mapping);
+}
+
+void inline encryption_write_barrier(const void* addr, size_t size, EncryptedFileMapping* mapping)
+{
+    if (mapping)
+        do_encryption_write_barrier(addr, size, mapping);
+}
+
+
+extern util::Mutex& mapping_mutex;
+
+inline void do_encryption_read_barrier(const void* addr, size_t size, HeaderToSize header_to_size,
+                                       EncryptedFileMapping* mapping)
+{
+    UniqueLock lock(mapping_mutex, defer_lock_tag());
+    mapping->read_barrier(addr, size, lock, header_to_size);
+}
+
+inline void do_encryption_write_barrier(const void* addr, size_t size, EncryptedFileMapping* mapping)
+{
+    LockGuard lock(mapping_mutex);
+    mapping->write_barrier(addr, size);
+}
+
+
+#else
+void inline encryption_read_barrier(const void*, size_t, EncryptedFileMapping*, HeaderToSize header_to_size = nullptr)
+{
+    static_cast<void>(header_to_size);
+}
+void inline encryption_write_barrier(const void*, size_t)
+{
+}
+void inline encryption_write_barrier(const void*, size_t, EncryptedFileMapping*)
+{
+}
+#endif
+
+// helpers for encrypted Maps
+template <typename T>
+void encryption_read_barrier(File::Map<T>& map, size_t index, size_t num_elements = 1)
+{
+    T* addr = map.get_addr();
+    encryption_read_barrier(addr + index, sizeof(T) * num_elements, map.get_encrypted_mapping());
+}
+
+template <typename T>
+void encryption_write_barrier(File::Map<T>& map, size_t index, size_t num_elements = 1)
+{
+    T* addr = map.get_addr();
+    encryption_write_barrier(addr + index, sizeof(T) * num_elements, map.get_encrypted_mapping());
+}
+
+File::SizeType encrypted_size_to_data_size(File::SizeType size) noexcept;
+File::SizeType data_size_to_encrypted_size(File::SizeType size) noexcept;
+
+size_t round_up_to_page_size(size_t size) noexcept;
+}
+}
+#endif
diff --git a/iOS/Pods/Realm/include/core/realm/util/hex_dump.hpp b/iOS/Pods/Realm/include/core/realm/util/hex_dump.hpp
new file mode 100644 (file)
index 0000000..08f5d2b
--- /dev/null
@@ -0,0 +1,54 @@
+/*************************************************************************
+ *
+ * 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_UTIL_HEX_DUMP_HPP
+#define REALM_UTIL_HEX_DUMP_HPP
+
+#include <cstddef>
+#include <type_traits>
+#include <limits>
+#include <string>
+#include <sstream>
+#include <iomanip>
+
+#include <realm/util/safe_int_ops.hpp>
+
+namespace realm {
+namespace util {
+
+template <class T>
+std::string hex_dump(const T* data, size_t size, const char* separator = " ", int min_digits = -1)
+{
+    using U = typename std::make_unsigned<T>::type;
+
+    if (min_digits < 0)
+        min_digits = (std::numeric_limits<U>::digits + 3) / 4;
+
+    std::ostringstream out;
+    for (const T* i = data; i != data + size; ++i) {
+        if (i != data)
+            out << separator;
+        out << std::setw(min_digits) << std::setfill('0') << std::hex << std::uppercase << util::promote(U(*i));
+    }
+    return out.str();
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_HEX_DUMP_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/http.hpp b/iOS/Pods/Realm/include/core/realm/util/http.hpp
new file mode 100644 (file)
index 0000000..6ab6591
--- /dev/null
@@ -0,0 +1,511 @@
+/*************************************************************************
+ *
+ * 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_UTIL_HTTP_HPP
+#define REALM_UTIL_HTTP_HPP
+
+#include <map>
+#include <system_error>
+#include <iosfwd>
+#include <locale>
+
+#include <realm/util/optional.hpp>
+#include <realm/util/network.hpp>
+#include <realm/string_data.hpp>
+
+namespace realm {
+namespace util {
+enum class HTTPParserError {
+    None = 0,
+    ContentTooLong,
+    HeaderLineTooLong,
+    MalformedResponse,
+    MalformedRequest,
+};
+std::error_code make_error_code(HTTPParserError);
+} // namespace util
+} // namespace realm
+
+namespace std {
+    template<>
+    struct is_error_code_enum<realm::util::HTTPParserError>: std::true_type {};
+}
+
+namespace realm {
+namespace util {
+
+/// See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+///
+/// It is guaranteed that the backing integer value of this enum corresponds
+/// to the numerical code representing the status.
+enum class HTTPStatus {
+    Unknown = 0,
+
+    Continue = 100,
+    SwitchingProtocols = 101,
+
+    Ok = 200,
+    Created = 201,
+    Accepted = 202,
+    NonAuthoritative = 203,
+    NoContent = 204,
+    ResetContent = 205,
+    PartialContent = 206,
+
+    MultipleChoices = 300,
+    MovedPermanently = 301,
+    Found = 302,
+    SeeOther = 303,
+    NotModified = 304,
+    UseProxy = 305,
+    SwitchProxy = 306,
+    TemporaryRedirect = 307,
+    PermanentRedirect = 308,
+
+    BadRequest = 400,
+    Unauthorized = 401,
+    PaymentRequired = 402,
+    Forbidden = 403,
+    NotFound = 404,
+    MethodNotAllowed = 405,
+    NotAcceptable = 406,
+    ProxyAuthenticationRequired = 407,
+    RequestTimeout = 408,
+    Conflict = 409,
+    Gone = 410,
+    LengthRequired = 411,
+    PreconditionFailed = 412,
+    PayloadTooLarge = 413,
+    UriTooLong = 414,
+    UnsupportedMediaType = 415,
+    RangeNotSatisfiable = 416,
+    ExpectationFailed = 417,
+    ImATeapot = 418,
+    MisdirectedRequest = 421,
+    UpgradeRequired = 426,
+    PreconditionRequired = 428,
+    TooManyRequests = 429,
+    RequestHeaderFieldsTooLarge = 431,
+    UnavailableForLegalReasons = 451,
+
+    InternalServerError = 500,
+    NotImplemented = 501,
+    BadGateway = 502,
+    ServiceUnavailable = 503,
+    GatewayTimeout = 504,
+    HttpVersionNotSupported = 505,
+    VariantAlsoNegotiates = 506,
+    NotExtended = 510,
+    NetworkAuthenticationRequired = 511,
+};
+
+/// See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+enum class HTTPMethod {
+    Options,
+    Get,
+    Head,
+    Post,
+    Put,
+    Delete,
+    Trace,
+    Connect,
+};
+
+struct HTTPAuthorization {
+    std::string scheme;
+    std::map<std::string, std::string> values;
+};
+
+HTTPAuthorization parse_authorization(const std::string&);
+
+struct CaseInsensitiveCompare {
+    bool operator()(const std::string& a, const std::string& b) const
+    {
+        auto cmp = [](char lhs, char rhs) {
+            return std::tolower(lhs, std::locale::classic()) <
+                   std::tolower(rhs, std::locale::classic());
+        };
+        return std::lexicographical_compare(begin(a), end(a), begin(b), end(b), cmp);
+    }
+};
+
+/// Case-insensitive map suitable for storing HTTP headers.
+using HTTPHeaders = std::map<std::string, std::string, CaseInsensitiveCompare>;
+
+struct HTTPRequest {
+    HTTPMethod method = HTTPMethod::Get;
+    HTTPHeaders headers;
+    std::string path;
+
+    /// If the request object has a body, the Content-Length header MUST be
+    /// set to a string representation of the number of bytes in the body.
+    /// FIXME: Relax this restriction, and also support Transfer-Encoding
+    /// and other HTTP/1.1 features.
+    Optional<std::string> body;
+};
+
+struct HTTPResponse {
+    HTTPStatus status = HTTPStatus::Unknown;
+    HTTPHeaders headers;
+
+    // A body is only read from the response stream if the server sent the
+    // Content-Length header.
+    // FIXME: Support other transfer methods, including Transfer-Encoding and
+    // HTTP/1.1 features.
+    Optional<std::string> body;
+};
+
+
+/// Serialize HTTP request to output stream.
+std::ostream& operator<<(std::ostream&, const HTTPRequest&);
+/// Serialize HTTP response to output stream.
+std::ostream& operator<<(std::ostream&, const HTTPResponse&);
+/// Serialize HTTP method to output stream ("GET", "POST", etc.).
+std::ostream& operator<<(std::ostream&, HTTPMethod);
+/// Serialize HTTP status to output stream, include reason string ("200 OK" etc.)
+std::ostream& operator<<(std::ostream&, HTTPStatus);
+
+struct HTTPParserBase {
+    // FIXME: Generally useful?
+    struct CallocDeleter {
+        void operator()(void* ptr)
+        {
+            std::free(ptr);
+        }
+    };
+
+    HTTPParserBase()
+    {
+        // Allocating read buffer with calloc to avoid accidentally spilling
+        // data from other sessions in case of a buffer overflow exploit.
+        m_read_buffer.reset(static_cast<char*>(std::calloc(read_buffer_size, 1)));
+    }
+    virtual ~HTTPParserBase() {}
+
+    std::string m_write_buffer;
+    std::unique_ptr<char[], CallocDeleter> m_read_buffer;
+    Optional<size_t> m_found_content_length;
+    static const size_t read_buffer_size = 8192;
+    static const size_t max_header_line_length = read_buffer_size;
+
+    /// Parses the contents of m_read_buffer as a HTTP header line,
+    /// and calls on_header() as appropriate. on_header() will be called at
+    /// most once per invocation.
+    /// Returns false if the contents of m_read_buffer is not a valid HTTP
+    /// header line.
+    bool parse_header_line(size_t len);
+
+    virtual std::error_code on_first_line(StringData line) = 0;
+    virtual void on_header(StringData key, StringData value) = 0;
+    virtual void on_body(StringData body) = 0;
+    virtual void on_complete(std::error_code = std::error_code{}) = 0;
+
+    /// If the input matches a known HTTP method string, return the appropriate
+    /// HTTPMethod enum value. Otherwise, returns none.
+    static Optional<HTTPMethod> parse_method_string(StringData method);
+
+    /// Interpret line as the first line of an HTTP request. If the return value
+    /// is true, out_method and out_uri have been assigned the appropriate
+    /// values found in the request line.
+    static bool parse_first_line_of_request(StringData line, HTTPMethod& out_method,
+                                            StringData& out_uri);
+
+    /// Interpret line as the first line of an HTTP response. If the return
+    /// value is true, out_status and out_reason have been assigned the
+    /// appropriate values found in the response line.
+    static bool parse_first_line_of_response(StringData line, HTTPStatus& out_status,
+                                             StringData& out_reason);
+
+    void set_write_buffer(const HTTPRequest&);
+    void set_write_buffer(const HTTPResponse&);
+};
+
+template<class Socket>
+struct HTTPParser: protected HTTPParserBase {
+    explicit HTTPParser(Socket& socket) : m_socket(socket)
+    {}
+
+    void read_first_line()
+    {
+        auto handler = [this](std::error_code ec, size_t n) {
+            if (ec == error::operation_aborted) {
+                return;
+            }
+            if (ec) {
+                on_complete(ec);
+                return;
+            }
+            ec = on_first_line(StringData(m_read_buffer.get(), n));
+            if (ec) {
+                on_complete(ec);
+                return;
+            }
+            read_headers();
+        };
+        m_socket.async_read_until(m_read_buffer.get(), max_header_line_length, '\n',
+                                  std::move(handler));
+    }
+
+    void read_headers()
+    {
+        auto handler = [this](std::error_code ec, size_t n) {
+            if (ec == error::operation_aborted) {
+                return;
+            }
+            if (ec) {
+                on_complete(ec);
+                return;
+            }
+            if (n <= 2) {
+                read_body();
+                return;
+            }
+            parse_header_line(n);
+            // FIXME: Limit the total size of headers. Apache uses 8K.
+            read_headers();
+        };
+        m_socket.async_read_until(m_read_buffer.get(), max_header_line_length, '\n',
+                                  std::move(handler));
+    }
+
+    void read_body()
+    {
+        if (m_found_content_length) {
+            // FIXME: Support longer bodies.
+            // FIXME: Support multipart and other body types (no body shaming).
+            if (*m_found_content_length > read_buffer_size) {
+                on_complete(HTTPParserError::ContentTooLong);
+                return;
+            }
+
+            auto handler = [this](std::error_code ec, size_t n) {
+                if (ec == error::operation_aborted) {
+                    return;
+                }
+                if (!ec) {
+                    on_body(StringData(m_read_buffer.get(), n));
+                }
+                on_complete(ec);
+            };
+            m_socket.async_read(m_read_buffer.get(), *m_found_content_length,
+                                std::move(handler));
+        }
+        else {
+            // No body, just finish.
+            on_complete();
+        }
+    }
+
+    void write_buffer(std::function<void(std::error_code, size_t)> handler)
+    {
+        m_socket.async_write(m_write_buffer.data(), m_write_buffer.size(),
+                             std::move(handler));
+    }
+
+    Socket& m_socket;
+};
+
+template<class Socket>
+struct HTTPClient: protected HTTPParser<Socket> {
+    using Handler = void(HTTPResponse, std::error_code);
+
+    explicit HTTPClient(Socket& socket) : HTTPParser<Socket>(socket) {}
+
+    /// Serialize and send \a request over the connected socket asynchronously.
+    ///
+    /// When the response has been received, or an error occurs, \a handler will
+    /// be invoked with the appropriate parameters. The HTTPResponse object
+    /// passed to \a handler will only be complete in non-error conditions, but
+    /// may be partially populated.
+    ///
+    /// It is an error to start a request before the \a handler of a previous
+    /// request has been invoked. It is permitted to call async_request() from
+    /// the handler, unless an error has been reported representing a condition
+    /// where the underlying socket is no longer able to communicate (for
+    /// example, if it has been closed).
+    ///
+    /// If a request is already in progress, an exception will be thrown.
+    ///
+    /// This method is *NOT* thread-safe.
+    void async_request(const HTTPRequest& request, std::function<Handler> handler)
+    {
+        if (REALM_UNLIKELY(m_handler)) {
+            throw std::runtime_error("Request already in progress.");
+        }
+        this->set_write_buffer(request);
+        m_handler = std::move(handler);
+        this->write_buffer([this](std::error_code ec, size_t bytes_written) {
+            static_cast<void>(bytes_written);
+            if (ec == error::operation_aborted) {
+                return;
+            }
+            if (ec) {
+                this->on_complete(ec);
+                return;
+            }
+            this->read_first_line();
+        });
+    }
+
+private:
+    std::function<Handler> m_handler;
+    HTTPResponse m_response;
+
+    std::error_code on_first_line(StringData line) override final
+    {
+        HTTPStatus status;
+        StringData reason;
+        if (this->parse_first_line_of_response(line, status, reason)) {
+            m_response.status = status;
+            static_cast<void>(reason); // Ignore for now.
+            return std::error_code{};
+        }
+        return HTTPParserError::MalformedResponse;
+    }
+
+    void on_header(StringData key, StringData value) override final
+    {
+        // FIXME: Multiple headers with the same key should show up as a
+        // comma-separated list of their values, rather than overwriting.
+        m_response.headers[std::string(key)] = std::string(value);
+    }
+
+    void on_body(StringData body) override final
+    {
+        m_response.body = std::string(body);
+    }
+
+    void on_complete(std::error_code ec) override final
+    {
+        auto handler = std::move(m_handler); // Nullifies m_handler
+        handler(std::move(m_response), ec);
+    }
+};
+
+template<class Socket>
+struct HTTPServer: protected HTTPParser<Socket> {
+    using RequestHandler = void(HTTPRequest, std::error_code);
+    using RespondHandler = void(std::error_code);
+
+    explicit HTTPServer(Socket& socket): HTTPParser<Socket>(socket)
+    {}
+
+    /// Receive a request on the underlying socket asynchronously.
+    ///
+    /// This function starts an asynchronous read operation and keeps reading
+    /// until an HTTP request has been received. \a handler is invoked when a
+    /// request has been received, or an error occurs.
+    ///
+    /// After a request is received, callers MUST invoke async_send_response()
+    /// to provide the client with a valid HTTP response, unless the error
+    /// passed to the handler represents a condition where the underlying socket
+    /// is no longer able to communicate (for example, if it has been closed).
+    ///
+    /// It is an error to attempt to receive a request before any previous
+    /// requests have been fully responded to, i.e. the \a handler argument of
+    /// async_send_response() must have been invoked before attempting to
+    /// receive the next request.
+    ///
+    /// This function is *NOT* thread-safe.
+    void async_receive_request(std::function<RequestHandler> handler)
+    {
+        if (REALM_UNLIKELY(m_request_handler)) {
+            throw std::runtime_error("Response already in progress.");
+        }
+        m_request_handler = std::move(handler);
+        this->read_first_line();
+    }
+
+    /// Send an HTTP response to a client asynchronously.
+    ///
+    /// This function starts an asynchronous write operation on the underlying
+    /// socket. \a handler is invoked when the response has been written to the
+    /// socket, or an error occurs.
+    ///
+    /// It is an error to call async_receive_request() again before \a handler
+    /// has been invoked, and it is an error to call async_send_response()
+    /// before the \a handler of a previous invocation has been invoked.
+    ///
+    /// This function is *NOT* thread-safe.
+    void async_send_response(const HTTPResponse& response,
+                             std::function<RespondHandler> handler)
+    {
+        if (REALM_UNLIKELY(!m_request_handler)) {
+            throw std::runtime_error("No request in progress.");
+        }
+        if (m_respond_handler) {
+            // FIXME: Proper exception type.
+            throw std::runtime_error("Already responding to request");
+        }
+        m_respond_handler = std::move(handler);
+        this->set_write_buffer(response);
+        this->write_buffer([this](std::error_code ec, size_t) {
+            if (ec == error::operation_aborted) {
+                return;
+            }
+            m_request_handler = nullptr;
+            auto handler = std::move(m_respond_handler);
+            handler(ec);
+        });;
+    }
+
+private:
+    std::function<RequestHandler> m_request_handler;
+    std::function<RespondHandler> m_respond_handler;
+    HTTPRequest m_request;
+
+    std::error_code on_first_line(StringData line) override final
+    {
+        HTTPMethod method;
+        StringData uri;
+        if (this->parse_first_line_of_request(line, method, uri)) {
+            m_request.method = method;
+            m_request.path = uri;
+            return std::error_code{};
+        }
+        return HTTPParserError::MalformedRequest;
+    }
+
+    void on_header(StringData key, StringData value) override final
+    {
+        // FIXME: Multiple headers with the same key should show up as a
+        // comma-separated list of their values, rather than overwriting.
+        m_request.headers[std::string(key)] = std::string(value);
+    }
+
+    void on_body(StringData body) override final
+    {
+        m_request.body = std::string(body);
+    }
+
+    void on_complete(std::error_code ec) override final
+    {
+        // Deliberately not nullifying m_request_handler so that we can
+        // check for invariants in async_send_response.
+        m_request_handler(std::move(m_request), ec);
+    }
+};
+
+} // namespace util
+} // namespace realm
+
+
+#endif // REALM_UTIL_HTTP_HPP
+
diff --git a/iOS/Pods/Realm/include/core/realm/util/inspect.hpp b/iOS/Pods/Realm/include/core/realm/util/inspect.hpp
new file mode 100644 (file)
index 0000000..84a669d
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_UTIL_INSPECT_HPP
+#define REALM_UTIL_INSPECT_HPP
+
+#include <string>
+
+namespace realm {
+namespace util {
+
+// LCOV_EXCL_START
+//
+// Because these are templated functions, every combination of output stream
+// type and value(s) type(s) generates a new function.  This makes LCOV/GCOVR
+// report over 70 functions in this file, with only 6.6% function coverage,
+// even though line coverage is at 100%.
+
+template <class OS, class T>
+void inspect_value(OS& os, const T& value)
+{
+    os << value;
+}
+
+template <class OS>
+void inspect_value(OS& os, const std::string& value)
+{
+    // FIXME: Escape the string.
+    os << "\"" << value << "\"";
+}
+
+template <class OS>
+void inspect_value(OS& os, const char* value)
+{
+    // FIXME: Escape the string.
+    os << "\"" << value << "\"";
+}
+
+template <class OS>
+void inspect_all(OS&)
+{
+    // No-op
+}
+
+/// Convert all arguments to strings, and quote string arguments.
+template <class OS, class First, class... Args>
+void inspect_all(OS& os, First&& first, Args&&... args)
+{
+    inspect_value(os, std::forward<First>(first));
+    if (sizeof...(Args) != 0) {
+        os << ", ";
+    }
+    inspect_all(os, std::forward<Args>(args)...);
+}
+
+// LCOV_EXCL_STOP
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_INSPECT_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/interprocess_condvar.hpp b/iOS/Pods/Realm/include/core/realm/util/interprocess_condvar.hpp
new file mode 100644 (file)
index 0000000..0e5b586
--- /dev/null
@@ -0,0 +1,151 @@
+/*************************************************************************
+ *
+ * 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_UTIL_INTERPROCESS_CONDVAR
+#define REALM_UTIL_INTERPROCESS_CONDVAR
+
+
+#include <realm/util/features.h>
+#include <realm/util/thread.hpp>
+#include <realm/util/interprocess_mutex.hpp>
+#include <cstdint>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <mutex>
+
+// Condvar Emulation is required if RobustMutex emulation is enabled
+#if defined(REALM_ROBUST_MUTEX_EMULATION) || defined(_WIN32)
+#define REALM_CONDVAR_EMULATION
+#endif
+
+namespace realm {
+namespace util {
+
+
+/// Condition variable for use in synchronization monitors.
+/// This condition variable uses emulation based on named pipes
+/// for the inter-process case, if enabled by REALM_CONDVAR_EMULATION.
+///
+/// FIXME: This implementation will never release/delete pipes. This is unlikely
+/// to be a problem as long as only a modest number of different database names
+/// are in use
+///
+/// A InterprocessCondVar is always process shared.
+class InterprocessCondVar {
+public:
+    InterprocessCondVar();
+    ~InterprocessCondVar() noexcept;
+
+    // Disable copying. Copying an open file will create a scenario
+    // where the same file descriptor will be opened once but closed twice.
+    InterprocessCondVar(const InterprocessCondVar&) = delete;
+    InterprocessCondVar& operator=(const InterprocessCondVar&) = delete;
+
+/// To use the InterprocessCondVar, you also must place a structure of type
+/// InterprocessCondVar::SharedPart in memory shared by multiple processes
+/// or in a memory mapped file, and use set_shared_part() to associate
+/// the condition variable with it's shared part. You must initialize
+/// the shared part using InterprocessCondVar::init_shared_part(), but only before
+/// first use and only when you have exclusive access to the shared part.
+
+#ifdef REALM_CONDVAR_EMULATION
+    struct SharedPart {
+#ifdef _WIN32
+        // Number of waiting threads.
+        int32_t m_waiters_count;
+        size_t m_was_broadcast;
+#else
+        uint64_t signal_counter;
+        uint64_t wait_counter;
+#endif
+    };
+#else
+    typedef CondVar SharedPart;
+#endif
+
+    /// You need to bind the emulation to a SharedPart in shared/mmapped memory.
+    /// The SharedPart is assumed to have been initialized (possibly by another process)
+    /// earlier through a call to init_shared_part.
+    void set_shared_part(SharedPart& shared_part, std::string path, std::string condvar_name, std::string tmp_path);
+
+    /// Initialize the shared part of a process shared condition variable.
+    /// A process shared condition variables may be represented by any number of
+    /// InterprocessCondVar instances in any number of different processes,
+    /// all sharing a common SharedPart instance, which must be in shared memory.
+    static void init_shared_part(SharedPart& shared_part);
+
+    /// Release any system resources allocated for the shared part. This should
+    /// be used *only* when you are certain, that nobody is using it.
+    void release_shared_part();
+
+    /// Wait for someone to call notify() or notify_all() on this condition
+    /// variable. The call to wait() may return spuriously, so the caller should
+    /// always re-evaluate the condition on which to wait and loop on wait()
+    /// if necessary.
+    void wait(InterprocessMutex& m, const struct timespec* tp);
+
+    /// If any threads are waiting for this condition, wake up at least one.
+    /// (Current implementation may actually wake all :-O ). The caller must
+    /// hold the lock associated with the condvar at the time of calling notify()
+    void notify() noexcept;
+
+    /// Wake up every thread that is currently waiting on this condition.
+    /// The caller must hold the lock associated with the condvar at the time
+    /// of calling notify_all().
+    void notify_all() noexcept;
+
+    /// Cleanup and release system resources if possible.
+    void close() noexcept;
+
+private:
+    // non-zero if a shared part has been registered (always 0 on process local instances)
+    SharedPart* m_shared_part = nullptr;
+#ifdef REALM_CONDVAR_EMULATION
+    // keep the path to allocated system resource so we can remove them again
+    std::string m_resource_path;
+    // pipe used for emulation. When using a named pipe, m_fd_read is read-write and m_fd_write is unused.
+    // When using an anonymous pipe (currently only for tvOS) m_fd_read is read-only and m_fd_write is write-only.
+    int m_fd_read = -1;
+    int m_fd_write = -1;
+
+#ifdef _WIN32
+    // Semaphore used to queue up threads waiting for the condition to
+    // become signaled. 
+    HANDLE m_sema = 0;
+    // An auto-reset event used by the broadcast/signal thread to wait
+    // for all the waiting thread(s) to wake up and be released from the
+    // semaphore. 
+    HANDLE m_waiters_done = 0;
+    std::string m_name;
+
+    // Serialize access to m_waiters_count
+    InterprocessMutex m_waiters_lockcount;
+#endif
+
+#endif
+};
+
+
+// Implementation:
+
+
+} // namespace util
+} // namespace realm
+
+
+#endif
diff --git a/iOS/Pods/Realm/include/core/realm/util/interprocess_mutex.hpp b/iOS/Pods/Realm/include/core/realm/util/interprocess_mutex.hpp
new file mode 100644 (file)
index 0000000..588cf00
--- /dev/null
@@ -0,0 +1,375 @@
+/*************************************************************************
+ *
+ * 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_UTIL_INTERPROCESS_MUTEX
+#define REALM_UTIL_INTERPROCESS_MUTEX
+
+#include <realm/util/features.h>
+#include <realm/util/thread.hpp>
+#include <realm/util/file.hpp>
+#include <realm/utilities.hpp>
+#include <mutex>
+#include <map>
+#include <iostream>
+
+// Enable this only on platforms where it might be needed
+#if REALM_PLATFORM_APPLE || REALM_ANDROID
+#define REALM_ROBUST_MUTEX_EMULATION
+#endif
+
+namespace realm {
+namespace util {
+
+// fwd decl to support friend decl below
+class InterprocessCondVar;
+
+
+/// Emulation of a Robust Mutex.
+/// A Robust Mutex is an interprocess mutex which will automatically
+/// release any locks held by a process when it crashes. Contrary to
+/// Posix robust mutexes, this robust mutex is not capable of informing
+/// participants that they have been granted a lock after a crash of
+/// the process holding it (though it could be added if needed).
+
+class InterprocessMutex {
+public:
+    InterprocessMutex();
+    ~InterprocessMutex() noexcept;
+
+    // Disable copying. Copying a locked Mutex will create a scenario
+    // where the same file descriptor will be locked once but unlocked twice.
+    InterprocessMutex(const InterprocessMutex&) = delete;
+    InterprocessMutex& operator=(const InterprocessMutex&) = delete;
+
+#if defined(REALM_ROBUST_MUTEX_EMULATION) || defined(_WIN32)
+    struct SharedPart {
+    };
+#else
+    using SharedPart = RobustMutex;
+#endif
+
+    /// You need to bind the emulation to a SharedPart in shared/mmapped memory.
+    /// The SharedPart is assumed to have been initialized (possibly by another process)
+    /// elsewhere.
+    void set_shared_part(SharedPart& shared_part, const std::string& path, const std::string& mutex_name);
+    void set_shared_part(SharedPart& shared_part, File&& lock_file);
+
+    /// Destroy shared object. Potentially release system resources. Caller must
+    /// ensure that the shared_part is not in use at the point of call.
+    void release_shared_part();
+
+    /// Lock the mutex. If the mutex is already locked, wait for it to be unlocked.
+    void lock();
+
+    /// Non-blocking attempt to lock the mutex. Returns true if the lock is obtained.
+    /// If the lock can not be obtained return false immediately.
+    bool try_lock();
+
+    /// Unlock the mutex
+    void unlock();
+
+    /// Attempt to check if the mutex is valid (only relevant if not emulating)
+    bool is_valid() noexcept;
+
+    static bool is_robust_on_this_platform()
+    {
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+        return true; // we're faking it!
+#else
+        return RobustMutex::is_robust_on_this_platform();
+#endif
+    }
+
+private:
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    struct LockInfo {
+        File m_file;
+        Mutex m_local_mutex;
+        LockInfo() {}
+        ~LockInfo() noexcept;
+        // Disable copying.
+        LockInfo(const LockInfo&) = delete;
+        LockInfo& operator=(const LockInfo&) = delete;
+    };
+    /// InterprocessMutex created on the same file (same inode on POSIX) share the same LockInfo.
+    /// LockInfo will be saved in a static map as a weak ptr and use the UniqueID as the key.
+    /// Operations on the map need to be protected by s_mutex
+    static std::map<File::UniqueID, std::weak_ptr<LockInfo>>* s_info_map;
+    static Mutex* s_mutex;
+    /// We manually initialize these static variables when first needed,
+    /// creating them on the heap so that they last for the entire lifetime
+    /// of the process. The destructor of these is never called; the
+    /// process will clean up their memory when exiting. It is not enough
+    /// to count instances of InterprocessMutex and clean up these statics when
+    /// the count reaches zero because the program can create more
+    /// InterprocessMutex instances before the process ends, so we really need
+    /// these variables for the entire lifetime of the process.
+    static std::once_flag s_init_flag;
+    static void initialize_statics();
+
+    /// Only used for release_shared_part
+    std::string m_filename;
+    File::UniqueID m_fileuid;
+    std::shared_ptr<LockInfo> m_lock_info;
+
+    /// Free the lock info hold by this instance.
+    /// If it is the last reference, underly resources will be freed as well.
+    void free_lock_info();
+#else
+    SharedPart* m_shared_part = nullptr;
+
+#ifdef _WIN32
+    HANDLE m_handle = 0;
+#endif
+
+#endif
+    friend class InterprocessCondVar;
+};
+
+inline InterprocessMutex::InterprocessMutex()
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    std::call_once(s_init_flag, initialize_statics);
+#endif
+}
+
+inline InterprocessMutex::~InterprocessMutex() noexcept
+{
+#ifdef _WIN32
+    if (m_handle) {
+        bool b = CloseHandle(m_handle);
+        REALM_ASSERT_RELEASE(b);
+    }
+#endif
+
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    free_lock_info();
+#endif
+}
+
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+inline InterprocessMutex::LockInfo::~LockInfo() noexcept
+{
+    if (m_file.is_attached()) {
+        m_file.close();
+    }
+}
+
+inline void InterprocessMutex::free_lock_info()
+{
+    // It has not been initialized yet.
+    if (!m_lock_info)
+        return;
+
+    std::lock_guard<Mutex> guard(*s_mutex);
+
+    m_lock_info.reset();
+    if ((*s_info_map)[m_fileuid].expired()) {
+        s_info_map->erase(m_fileuid);
+    }
+    m_filename.clear();
+}
+
+inline void InterprocessMutex::initialize_statics()
+{
+    s_mutex = new Mutex();
+    s_info_map = new std::map<File::UniqueID, std::weak_ptr<LockInfo>>();
+}
+#endif
+
+inline void InterprocessMutex::set_shared_part(SharedPart& shared_part, const std::string& path,
+                                               const std::string& mutex_name)
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    static_cast<void>(shared_part);
+
+    free_lock_info();
+
+    m_filename = path + "." + mutex_name + ".mx";
+
+    std::lock_guard<Mutex> guard(*s_mutex);
+
+    // Try to get the file uid if the file exists
+    if (File::get_unique_id(m_filename, m_fileuid)) {
+        auto result = s_info_map->find(m_fileuid);
+        if (result != s_info_map->end()) {
+            // File exists and the lock info has been created in the map.
+            m_lock_info = result->second.lock();
+            return;
+        }
+    }
+
+    // LockInfo has not been created yet.
+    m_lock_info = std::make_shared<LockInfo>();
+    // Always use mod_Write to open file and retreive the uid in case other process
+    // deletes the file.
+    m_lock_info->m_file.open(m_filename, File::mode_Write);
+    m_fileuid = m_lock_info->m_file.get_unique_id();
+
+    (*s_info_map)[m_fileuid] = m_lock_info;
+#elif defined(_WIN32)
+    if (m_handle) {
+        bool b = CloseHandle(m_handle);
+        REALM_ASSERT_RELEASE(b);
+    }
+    // replace backslashes because they're significant in object namespace names
+    std::string path_escaped = path;
+    std::replace(path_escaped.begin(), path_escaped.end(), '\\', '/');
+    std::string name = "Local\\realm_named_intermutex_" + path_escaped + mutex_name;
+
+    std::wstring wname(name.begin(), name.end());
+    m_handle = CreateMutexW(0, false, wname.c_str());
+    if (!m_handle) {
+        throw std::system_error(std::error_code(::GetLastError(), std::system_category()), "Error opening mutex");
+    }
+#else
+    m_shared_part = &shared_part;
+    static_cast<void>(path);
+    static_cast<void>(mutex_name);
+#endif
+}
+
+inline void InterprocessMutex::set_shared_part(SharedPart& shared_part, File&& lock_file)
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    static_cast<void>(shared_part);
+
+    free_lock_info();
+
+    std::lock_guard<Mutex> guard(*s_mutex);
+
+    m_fileuid = lock_file.get_unique_id();
+    auto result = s_info_map->find(m_fileuid);
+    if (result == s_info_map->end()) {
+        m_lock_info = std::make_shared<LockInfo>();
+        m_lock_info->m_file = std::move(lock_file);
+        (*s_info_map)[m_fileuid] = m_lock_info;
+    }
+    else {
+        // File exists and the lock info has been created in the map.
+        m_lock_info = result->second.lock();
+        lock_file.close();
+    }
+#else
+    m_shared_part = &shared_part;
+    static_cast<void>(lock_file);
+#endif
+}
+
+inline void InterprocessMutex::release_shared_part()
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    if (!m_filename.empty())
+        File::try_remove(m_filename);
+
+    free_lock_info();
+#else
+    m_shared_part = nullptr;
+#endif
+}
+
+inline void InterprocessMutex::lock()
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    std::unique_lock<Mutex> mutex_lock(m_lock_info->m_local_mutex);
+    m_lock_info->m_file.lock_exclusive();
+    mutex_lock.release();
+#else
+
+#ifdef _WIN32
+    DWORD d = WaitForSingleObject(m_handle, INFINITE);
+    REALM_ASSERT_RELEASE(d != WAIT_FAILED);
+#else
+    REALM_ASSERT(m_shared_part);
+    m_shared_part->lock([]() {});
+#endif
+#endif
+}
+
+inline bool InterprocessMutex::try_lock()
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    std::unique_lock<Mutex> mutex_lock(m_lock_info->m_local_mutex, std::try_to_lock_t());
+    if (!mutex_lock.owns_lock()) {
+        return false;
+    }
+    bool success = m_lock_info->m_file.try_lock_exclusive();
+    if (success) {
+        mutex_lock.release();
+        return true;
+    }
+    else {
+        return false;
+    }
+#else
+
+#ifdef _WIN32
+    DWORD ret = WaitForSingleObject(m_handle, 0);
+    REALM_ASSERT_RELEASE(ret != WAIT_FAILED);
+
+    if (ret == WAIT_OBJECT_0) {
+        return true;
+    }
+    else {
+        return false;
+    }
+#else
+    REALM_ASSERT(m_shared_part);
+    return m_shared_part->try_lock([]() {});
+#endif
+#endif
+}
+
+
+inline void InterprocessMutex::unlock()
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    m_lock_info->m_file.unlock();
+    m_lock_info->m_local_mutex.unlock();
+#else
+#ifdef _WIN32
+    bool b = ReleaseMutex(m_handle);
+    REALM_ASSERT_RELEASE(b);
+#else
+    REALM_ASSERT(m_shared_part);
+    m_shared_part->unlock();
+#endif
+#endif
+}
+
+
+inline bool InterprocessMutex::is_valid() noexcept
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    return true;
+#elif defined(_WIN32)
+    // There is no safe way of testing if the m_handle mutex handle is valid on Windows, without having bad side effects
+    // for the cases where it is indeed invalid. If m_handle contains an arbitrary value, it might by coincidence be equal
+    // to a real live handle of another kind. This excludes a try_lock implementation and many other ideas.
+    return true;
+#else
+    REALM_ASSERT(m_shared_part);
+    return m_shared_part->is_valid();
+#endif
+}
+
+
+} // namespace util
+} // namespace realm
+
+#endif // #ifndef REALM_UTIL_INTERPROCESS_MUTEX
diff --git a/iOS/Pods/Realm/include/core/realm/util/json_parser.hpp b/iOS/Pods/Realm/include/core/realm/util/json_parser.hpp
new file mode 100644 (file)
index 0000000..bc6664a
--- /dev/null
@@ -0,0 +1,545 @@
+/*************************************************************************
+ * 
+ * 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_UTIL_JSON_PARSER_HPP
+#define REALM_UTIL_JSON_PARSER_HPP
+
+#include <system_error>
+#include <algorithm>
+#include <cstdlib>
+#include <cctype>
+
+#include <realm/string_data.hpp>
+
+namespace realm {
+namespace util {
+
+/// A JSON parser that neither allocates heap memory nor throws exceptions.
+///
+/// The parser takes as input a range of characters, and emits a stream of events
+/// representing the structure of the JSON document.
+///
+/// Parser errors are represented as `std::error_condition`s.
+class JSONParser {
+public:
+    using InputIterator = const char*;
+
+    enum class EventType {
+        number,
+        string,
+        boolean,
+        null,
+        array_begin,
+        array_end,
+        object_begin,
+        object_end
+    };
+
+    using Range = StringData;
+
+    struct Event {
+        EventType type;
+        Range range;
+        Event(EventType type): type(type) {}
+
+        union {
+            bool boolean;
+            double number;
+        };
+
+        StringData escaped_string_value() const noexcept;
+
+        /// Unescape the string value into \a buffer.
+        /// The type of this event must be EventType::string.
+        ///
+        /// \param buffer is a pointer to a buffer big enough to hold the
+        /// unescaped string value. The unescaped string is guaranteed to be
+        /// shorter than the escaped string, so escaped_string_value().size() can
+        /// be used as an upper bound. Unicode sequences of the form "\uXXXX"
+        /// will be converted to UTF-8 sequences. Note that the escaped form of
+        /// a unicode point takes exactly 6 bytes, which is also the maximum
+        /// possible length of a UTF-8 encoded codepoint.
+        StringData unescape_string(char* buffer) const noexcept;
+    };
+
+    enum class Error {
+        unexpected_token = 1,
+        unexpected_end_of_stream = 2
+    };
+
+    JSONParser(StringData);
+
+    /// Parse the input data, and call f repeatedly with an argument of type Event
+    /// representing the token that the parser encountered.
+    ///
+    /// The stream of events is "flat", which is to say that it is the responsibility
+    /// of the function f to keep track of any nested object structures as it deems
+    /// appropriate.
+    ///
+    /// This function is guaranteed to never throw, as long as f never throws.
+    template<class F>
+    std::error_condition parse(F&& f) noexcept(noexcept(f(std::declval<Event>())));
+
+    class ErrorCategory: public std::error_category {
+    public:
+        const char* name() const noexcept final;
+        std::string message(int) const final;
+    };
+    static const ErrorCategory error_category;
+private:
+    enum Token: char {
+        object_begin = '{',
+        object_end   = '}',
+        array_begin  = '[',
+        array_end    = ']',
+        colon        = ':',
+        comma        = ',',
+        dquote       = '"',
+        escape       = '\\',
+        minus        = '-',
+        space        = ' ',
+        tab          = '\t',
+        cr           = '\r',
+        lf           = '\n',
+    };
+
+    InputIterator m_current;
+    InputIterator m_end;
+
+    template<class F>
+    std::error_condition parse_object(F&& f) noexcept(noexcept(f(std::declval<Event>())));
+    template<class F>
+    std::error_condition parse_pair(F&& f) noexcept(noexcept(f(std::declval<Event>())));
+    template<class F>
+    std::error_condition parse_array(F&& f) noexcept(noexcept(f(std::declval<Event>())));
+    template<class F>
+    std::error_condition parse_number(F&& f) noexcept(noexcept(f(std::declval<Event>())));
+    template<class F>
+    std::error_condition parse_string(F&& f) noexcept(noexcept(f(std::declval<Event>())));
+    template<class F>
+    std::error_condition parse_value(F&& f) noexcept(noexcept(f(std::declval<Event>())));
+    template<class F>
+    std::error_condition parse_boolean(F&& f) noexcept(noexcept(f(std::declval<Event>())));
+    template<class F>
+    std::error_condition parse_null(F&& f) noexcept(noexcept(f(std::declval<Event>())));
+
+    std::error_condition expect_token(char, Range& out_range) noexcept;
+    std::error_condition expect_token(Token, Range& out_range) noexcept;
+
+    // Returns true unless EOF was reached.
+    bool peek_char(char& out_c) noexcept;
+    bool peek_token(Token& out_t) noexcept;
+    bool is_whitespace(Token t) noexcept;
+    void skip_whitespace() noexcept;
+};
+
+std::error_condition make_error_condition(JSONParser::Error e);
+
+} // namespace util
+} // namespace realm
+
+namespace std {
+template<>
+struct is_error_condition_enum<realm::util::JSONParser::Error> {
+    static const bool value = true;
+};
+}
+
+namespace realm {
+namespace util {
+
+/// Implementation:
+
+
+inline JSONParser::JSONParser(StringData input):
+    m_current(input.data()), m_end(input.data() + input.size())
+{
+}
+
+template<class F>
+std::error_condition JSONParser::parse(F&& f) noexcept(noexcept(f(std::declval<Event>())))
+{
+    return parse_value(f);
+}
+
+template<class F>
+std::error_condition JSONParser::parse_object(F&& f) noexcept(noexcept(f(std::declval<Event>())))
+{
+    Event event{EventType::object_begin};
+    auto ec = expect_token(Token::object_begin, event.range);
+    if (ec)
+        return ec;
+    ec = f(event);
+    if (ec)
+        return ec;
+
+    while (true) {
+        ec = expect_token(Token::object_end, event.range);
+        if (!ec) {
+            // End of object
+            event.type = EventType::object_end;
+            ec = f(event);
+            if (ec)
+                return ec;
+            break;
+        }
+
+        if (ec != Error::unexpected_token)
+            return ec;
+
+        ec = parse_pair(f);
+        if (ec)
+            return ec;
+
+        skip_whitespace();
+
+        Token t;
+        if (peek_token(t)) {
+            if (t == Token::object_end) {
+                // Fine, will terminate on next iteration
+            }
+            else if (t == Token::comma)
+                ++m_current; // OK, because peek_char returned true
+            else
+                return Error::unexpected_token;
+        }
+        else {
+            return Error::unexpected_end_of_stream;
+        }
+    }
+
+    return std::error_condition{};
+}
+
+template<class F>
+std::error_condition JSONParser::parse_pair(F&& f) noexcept(noexcept(f(std::declval<Event>())))
+{
+    skip_whitespace();
+
+    auto ec = parse_string(f);
+    if (ec)
+        return ec;
+
+    skip_whitespace();
+
+    Token t;
+    if (peek_token(t)) {
+        if (t == Token::colon) {
+            ++m_current;
+        }
+        else {
+            return Error::unexpected_token;
+        }
+    }
+
+    return parse_value(f);
+}
+
+template<class F>
+std::error_condition JSONParser::parse_array(F&& f) noexcept(noexcept(f(std::declval<Event>())))
+{
+    Event event{EventType::array_begin};
+    auto ec = expect_token(Token::array_begin, event.range);
+    if (ec)
+        return ec;
+    ec = f(event);
+    if (ec)
+        return ec;
+
+    while (true) {
+        ec = expect_token(Token::array_end, event.range);
+        if (!ec) {
+            // End of array
+            event.type = EventType::array_end;
+            ec = f(event);
+            if (ec)
+                return ec;
+            break;
+        }
+
+        if (ec != Error::unexpected_token)
+            return ec;
+
+        ec = parse_value(f);
+        if (ec)
+            return ec;
+
+        skip_whitespace();
+
+        Token t;
+        if (peek_token(t)) {
+            if (t == Token::array_end) {
+                // Fine, will terminate next iteration.
+            }
+            else if (t == Token::comma)
+                ++m_current; // OK, because peek_char returned true
+            else
+                return Error::unexpected_token;
+        }
+        else {
+            return Error::unexpected_end_of_stream;
+        }
+    }
+
+    return std::error_condition{};
+}
+
+template<class F>
+std::error_condition JSONParser::parse_number(F&& f) noexcept(noexcept(f(std::declval<Event>())))
+{
+    static const size_t buffer_size = 64;
+    char buffer[buffer_size] = {0};
+    size_t bytes_to_copy = std::min<size_t>(m_end - m_current, buffer_size - 1);
+    if (bytes_to_copy == 0)
+        return Error::unexpected_end_of_stream;
+
+    if (std::isspace(*m_current)) {
+        // JSON has a different idea of what constitutes whitespace than isspace(),
+        // but strtod() uses isspace() to skip initial whitespace. We have already
+        // skipped whitespace that JSON considers valid, so if there is any whitespace
+        // at m_current now, it is invalid according to JSON, and so is an error.
+        return Error::unexpected_token;
+    }
+
+    switch (m_current[0]) {
+        case 'N':
+            // strtod() parses "NAN", JSON does not.
+        case 'I':
+            // strtod() parses "INF", JSON does not.
+        case 'p':
+        case 'P':
+            // strtod() may parse exponent notation, JSON does not.
+            return Error::unexpected_token;
+        case '0':
+            if (bytes_to_copy > 2 && (m_current[1] == 'x' || m_current[1] == 'X')) {
+                // strtod() parses hexadecimal, JSON does not.
+                return Error::unexpected_token;
+            }
+    }
+
+    std::copy(m_current, m_current + bytes_to_copy, buffer);
+
+    char* endp = nullptr;
+    Event event{EventType::number};
+    event.number = std::strtod(buffer, &endp);
+
+    if (endp == buffer) {
+        return Error::unexpected_token;
+    }
+    size_t num_bytes_consumed = endp - buffer;
+    m_current += num_bytes_consumed;
+    return f(event);
+}
+
+template<class F>
+std::error_condition JSONParser::parse_string(F&& f) noexcept(noexcept(f(std::declval<Event>())))
+{
+    InputIterator p = m_current;
+    if (p >= m_end)
+        return Error::unexpected_end_of_stream;
+
+    auto count_num_escapes_backwards = [](const char* p, const char* begin) -> size_t {
+        size_t result = 0;
+        for (; p > begin && *p == Token::escape; ++p)
+            ++result;
+        return result;
+    };
+
+    Token t = static_cast<Token>(*p);
+    InputIterator inner_end;
+    if (t == Token::dquote) {
+        inner_end = m_current;
+        do {
+            inner_end = std::find(inner_end + 1, m_end, Token::dquote);
+            if (inner_end == m_end)
+                return Error::unexpected_end_of_stream;
+        } while (count_num_escapes_backwards(inner_end - 1, m_current) % 2 == 1);
+
+        Event event{EventType::string};
+        event.range = Range(m_current, inner_end - m_current + 1);
+        m_current = inner_end + 1;
+        return f(event);
+    }
+    return Error::unexpected_token;
+}
+
+template<class F>
+std::error_condition JSONParser::parse_boolean(F&& f) noexcept(noexcept(f(std::declval<Event>())))
+{
+    auto first_nonalpha = std::find_if_not(m_current, m_end, [](auto c) { return std::isalpha(c); });
+
+    Event event{EventType::boolean};
+    event.range = Range(m_current, first_nonalpha - m_current);
+    if (event.range == "true") {
+        event.boolean = true;
+        m_current += 4;
+        return f(event);
+    }
+    else if (event.range == "false") {
+        event.boolean = false;
+        m_current += 5;
+        return f(event);
+    }
+
+    return Error::unexpected_token;
+}
+
+template<class F>
+std::error_condition JSONParser::parse_null(F&& f) noexcept(noexcept(f(std::declval<Event>())))
+{
+    auto first_nonalpha = std::find_if_not(m_current, m_end, [](auto c) { return std::isalpha(c); });
+
+    Event event{EventType::null};
+    event.range = Range(m_current, first_nonalpha - m_current);
+    if (event.range == "null") {
+        m_current += 4;
+        return f(event);
+    }
+
+    return Error::unexpected_token;
+}
+
+template<class F>
+std::error_condition JSONParser::parse_value(F&& f) noexcept(noexcept(f(std::declval<Event>())))
+{
+    skip_whitespace();
+
+    if (m_current >= m_end)
+        return Error::unexpected_end_of_stream;
+
+    if (*m_current == Token::object_begin)
+        return parse_object(f);
+
+    if (*m_current == Token::array_begin)
+        return parse_array(f);
+
+    if (*m_current == 't' || *m_current == 'f')
+        return parse_boolean(f);
+
+    if (*m_current == 'n')
+        return parse_null(f);
+
+    if (*m_current == Token::dquote)
+        return parse_string(f);
+
+    return parse_number(f);
+}
+
+inline
+bool JSONParser::is_whitespace(Token t) noexcept
+{
+    switch (t) {
+        case Token::space:
+        case Token::tab:
+        case Token::cr:
+        case Token::lf:
+            return true;
+        default:
+            return false;
+    }
+}
+
+inline
+void JSONParser::skip_whitespace() noexcept
+{
+    while (m_current < m_end && is_whitespace(static_cast<Token>(*m_current)))
+        ++m_current;
+}
+
+inline
+std::error_condition JSONParser::expect_token(char c, Range& out_range) noexcept
+{
+    skip_whitespace();
+    if (m_current == m_end)
+        return Error::unexpected_end_of_stream;
+    if (*m_current == c) {
+        out_range = Range(m_current, 1);
+        ++m_current;
+        return std::error_condition{};
+    }
+    return Error::unexpected_token;
+}
+
+inline
+std::error_condition JSONParser::expect_token(Token t, Range& out_range) noexcept
+{
+    return expect_token(static_cast<char>(t), out_range);
+}
+
+inline
+bool JSONParser::peek_char(char& out_c) noexcept
+{
+    if (m_current < m_end) {
+        out_c = *m_current;
+        return true;
+    }
+    return false;
+}
+
+inline
+bool JSONParser::peek_token(Token& out_t) noexcept
+{
+    if (m_current < m_end) {
+        out_t = static_cast<Token>(*m_current);
+        return true;
+    }
+    return false;
+}
+
+inline
+StringData JSONParser::Event::escaped_string_value() const noexcept
+{
+    REALM_ASSERT(type == EventType::string);
+    REALM_ASSERT(range.size() >= 2);
+    return StringData(range.data() + 1, range.size() - 2);
+}
+
+template<class OS>
+OS& operator<<(OS& os, JSONParser::EventType type)
+{
+    switch (type) {
+        case JSONParser::EventType::number:       os << "number"; return os;
+        case JSONParser::EventType::string:       os << "string"; return os;
+        case JSONParser::EventType::boolean:      os << "boolean"; return os;
+        case JSONParser::EventType::null:         os << "null"; return os;
+        case JSONParser::EventType::array_begin:  os << "["; return os;
+        case JSONParser::EventType::array_end:    os << "]"; return os;
+        case JSONParser::EventType::object_begin: os << "{"; return os;
+        case JSONParser::EventType::object_end:   os << "}"; return os;
+    }
+    REALM_UNREACHABLE();
+}
+
+template<class OS>
+OS& operator<<(OS& os, const JSONParser::Event& e) {
+    os << e.type;
+    switch (e.type) {
+        case JSONParser::EventType::number:       return os << "(" << e.number << ")";
+        case JSONParser::EventType::string:       return os << "(" << e.range << ")";
+        case JSONParser::EventType::boolean:      return os << "(" << e.boolean << ")"; 
+        default: return os;
+    }
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_JSON_PARSER_HPP
+
diff --git a/iOS/Pods/Realm/include/core/realm/util/logger.hpp b/iOS/Pods/Realm/include/core/realm/util/logger.hpp
new file mode 100644 (file)
index 0000000..0946208
--- /dev/null
@@ -0,0 +1,511 @@
+/*************************************************************************
+ *
+ * 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_UTIL_LOGGER_HPP
+#define REALM_UTIL_LOGGER_HPP
+
+#include <cstring>
+#include <utility>
+#include <string>
+#include <locale>
+#include <sstream>
+#include <iostream>
+
+#include <realm/util/features.h>
+#include <realm/util/thread.hpp>
+#include <realm/util/file.hpp>
+
+namespace realm {
+namespace util {
+
+
+/// All Logger objects store a reference to a LevelThreshold object which it
+/// uses to efficiently query about the current log level threshold
+/// (`level_threshold.get()`). All messages logged with a level that is lower
+/// than the current threshold will be dropped. For the sake of efficiency, this
+/// test happens before the message is formatted.
+///
+/// A logger is not inherently thread-safe, but specific implementations can be
+/// (see ThreadSafeLogger). For a logger to be thread-safe, the implementation
+/// of do_log() must be thread-safe and the referenced LevelThreshold object
+/// must have a thread-safe get() method.
+///
+/// Examples:
+///
+///    logger.error("Overlong message from master coordinator");
+///    logger.info("Listening for peers on %1:%2", listen_address, listen_port);
+class Logger {
+public:
+    template <class... Params>
+    void trace(const char* message, Params&&...);
+    template <class... Params>
+    void debug(const char* message, Params&&...);
+    template <class... Params>
+    void detail(const char* message, Params&&...);
+    template <class... Params>
+    void info(const char* message, Params&&...);
+    template <class... Params>
+    void warn(const char* message, Params&&...);
+    template <class... Params>
+    void error(const char* message, Params&&...);
+    template <class... Params>
+    void fatal(const char* message, Params&&...);
+
+    /// Specifies criticality when passed to log(). Functions as a criticality
+    /// threshold when returned from LevelThreshold::get().
+    ///
+    ///     error   Be silent unless when there is an error.
+    ///     warn    Be silent unless when there is an error or a warning.
+    ///     info    Reveal information about what is going on, but in a
+    ///             minimalistic fashion to avoid general overhead from logging
+    ///             and to keep volume down.
+    ///     detail  Same as 'info', but prioritize completeness over minimalism.
+    ///     debug   Reveal information that can aid debugging, no longer paying
+    ///             attention to efficiency.
+    ///     trace   A version of 'debug' that allows for very high volume
+    ///             output.
+    enum class Level { all, trace, debug, detail, info, warn, error, fatal, off };
+
+    template <class... Params>
+    void log(Level, const char* message, Params&&...);
+
+    /// Shorthand for `int(level) >= int(level_threshold.get())`.
+    bool would_log(Level level) const noexcept;
+
+    class LevelThreshold;
+
+    const LevelThreshold& level_threshold;
+
+    virtual ~Logger() noexcept;
+
+protected:
+    Logger(const LevelThreshold&) noexcept;
+
+    static void do_log(Logger&, Level, std::string message);
+
+    virtual void do_log(Level, std::string message) = 0;
+
+    static const char* get_level_prefix(Level) noexcept;
+
+private:
+    struct State;
+
+    template <class... Params>
+    REALM_NOINLINE void do_log(Level, const char* message, Params&&...);
+    void log_impl(State&);
+    template <class Param, class... Params>
+    void log_impl(State&, Param&&, Params&&...);
+    template <class Param>
+    static void subst(State&, Param&&);
+};
+
+template <class C, class T>
+std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>&, Logger::Level);
+
+template <class C, class T>
+std::basic_istream<C, T>& operator>>(std::basic_istream<C, T>&, Logger::Level&);
+
+class Logger::LevelThreshold {
+public:
+    virtual Level get() const noexcept = 0;
+};
+
+
+/// A root logger that is not thread-safe and allows for the log level threshold
+/// to be changed over time. The initial log level threshold is
+/// Logger::Level::info.
+class RootLogger : private Logger::LevelThreshold, public Logger {
+public:
+    void set_level_threshold(Level) noexcept;
+
+protected:
+    RootLogger();
+
+private:
+    Level m_level_threshold = Level::info;
+    Level get() const noexcept override final;
+};
+
+
+/// A logger that writes to STDERR. This logger is not thread-safe.
+///
+/// Since this class is a RootLogger, it contains modifiable a log level
+/// threshold.
+class StderrLogger : public RootLogger {
+protected:
+    void do_log(Level, std::string) override final;
+};
+
+
+/// A logger that writes to a stream. This logger is not thread-safe.
+///
+/// Since this class is a RootLogger, it contains modifiable a log level
+/// threshold.
+class StreamLogger : public RootLogger {
+public:
+    explicit StreamLogger(std::ostream&) noexcept;
+
+protected:
+    void do_log(Level, std::string) override final;
+
+private:
+    std::ostream& m_out;
+};
+
+
+/// A logger that writes to a file. This logger is not thread-safe.
+///
+/// Since this class is a RootLogger, it contains modifiable a log level
+/// threshold.
+class FileLogger : public StreamLogger {
+public:
+    explicit FileLogger(std::string path);
+    explicit FileLogger(util::File);
+
+private:
+    util::File m_file;
+    util::File::Streambuf m_streambuf;
+    std::ostream m_out;
+};
+
+
+/// A thread-safe logger. This logger ignores the level threshold of the base
+/// logger. Instead, it introduces new a LevelThreshold object with a fixed
+/// value to achieve thread safety.
+class ThreadSafeLogger : private Logger::LevelThreshold, public Logger {
+public:
+    explicit ThreadSafeLogger(Logger& base_logger, Level = Level::info);
+
+protected:
+    void do_log(Level, std::string) override final;
+
+private:
+    const Level m_level_threshold; // Immutable for thread safety
+    Logger& m_base_logger;
+    Mutex m_mutex;
+    Level get() const noexcept override final;
+};
+
+
+/// A logger that adds a fixed prefix to each message. This logger inherits the
+/// LevelThreshold object of the specified base logger. This logger is
+/// thread-safe if, and only if the base logger is thread-safe.
+class PrefixLogger : public Logger {
+public:
+    PrefixLogger(std::string prefix, Logger& base_logger) noexcept;
+
+protected:
+    void do_log(Level, std::string) override final;
+
+private:
+    const std::string m_prefix;
+    Logger& m_base_logger;
+};
+
+
+// Implementation
+
+struct Logger::State {
+    Logger::Level m_level;
+    std::string m_message;
+    std::string m_search;
+    int m_param_num = 1;
+    std::ostringstream m_formatter;
+    std::locale m_locale = std::locale::classic();
+    State(Logger::Level level, const char* s)
+        : m_level(level)
+        , m_message(s)
+        , m_search(m_message)
+    {
+        m_formatter.imbue(m_locale);
+    }
+};
+
+template <class... Params>
+inline void Logger::trace(const char* message, Params&&... params)
+{
+    log(Level::trace, message, std::forward<Params>(params)...); // Throws
+}
+
+template <class... Params>
+inline void Logger::debug(const char* message, Params&&... params)
+{
+    log(Level::debug, message, std::forward<Params>(params)...); // Throws
+}
+
+template <class... Params>
+inline void Logger::detail(const char* message, Params&&... params)
+{
+    log(Level::detail, message, std::forward<Params>(params)...); // Throws
+}
+
+template <class... Params>
+inline void Logger::info(const char* message, Params&&... params)
+{
+    log(Level::info, message, std::forward<Params>(params)...); // Throws
+}
+
+template <class... Params>
+inline void Logger::warn(const char* message, Params&&... params)
+{
+    log(Level::warn, message, std::forward<Params>(params)...); // Throws
+}
+
+template <class... Params>
+inline void Logger::error(const char* message, Params&&... params)
+{
+    log(Level::error, message, std::forward<Params>(params)...); // Throws
+}
+
+template <class... Params>
+inline void Logger::fatal(const char* message, Params&&... params)
+{
+    log(Level::fatal, message, std::forward<Params>(params)...); // Throws
+}
+
+template <class... Params>
+inline void Logger::log(Level level, const char* message, Params&&... params)
+{
+    if (would_log(level))
+        do_log(level, message, std::forward<Params>(params)...); // Throws
+}
+
+inline bool Logger::would_log(Level level) const noexcept
+{
+    return int(level) >= int(level_threshold.get());
+}
+
+inline Logger::~Logger() noexcept
+{
+}
+
+inline Logger::Logger(const LevelThreshold& lt) noexcept
+    : level_threshold(lt)
+{
+}
+
+inline void Logger::do_log(Logger& logger, Level level, std::string message)
+{
+    logger.do_log(level, std::move(message)); // Throws
+}
+
+template <class... Params>
+void Logger::do_log(Level level, const char* message, Params&&... params)
+{
+    State state(level, message);
+    log_impl(state, std::forward<Params>(params)...); // Throws
+}
+
+inline void Logger::log_impl(State& state)
+{
+    do_log(state.m_level, std::move(state.m_message)); // Throws
+}
+
+template <class Param, class... Params>
+inline void Logger::log_impl(State& state, Param&& param, Params&&... params)
+{
+    subst(state, std::forward<Param>(param));         // Throws
+    log_impl(state, std::forward<Params>(params)...); // Throws
+}
+
+template <class Param>
+void Logger::subst(State& state, Param&& param)
+{
+    state.m_formatter << "%" << state.m_param_num;
+    std::string key = state.m_formatter.str();
+    state.m_formatter.str(std::string());
+    std::string::size_type j = state.m_search.find(key);
+    if (j != std::string::npos) {
+        state.m_formatter << std::forward<Param>(param);
+        std::string str = state.m_formatter.str();
+        state.m_formatter.str(std::string());
+        state.m_message.replace(j, key.size(), str);
+        state.m_search.replace(j, key.size(), std::string(str.size(), '\0'));
+    }
+    ++state.m_param_num;
+}
+
+template <class C, class T>
+std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& out, Logger::Level level)
+{
+    switch (level) {
+        case Logger::Level::all:
+            out << "all";
+            return out;
+        case Logger::Level::trace:
+            out << "trace";
+            return out;
+        case Logger::Level::debug:
+            out << "debug";
+            return out;
+        case Logger::Level::detail:
+            out << "detail";
+            return out;
+        case Logger::Level::info:
+            out << "info";
+            return out;
+        case Logger::Level::warn:
+            out << "warn";
+            return out;
+        case Logger::Level::error:
+            out << "error";
+            return out;
+        case Logger::Level::fatal:
+            out << "fatal";
+            return out;
+        case Logger::Level::off:
+            out << "off";
+            return out;
+    }
+    REALM_ASSERT(false);
+    return out;
+}
+
+template <class C, class T>
+std::basic_istream<C, T>& operator>>(std::basic_istream<C, T>& in, Logger::Level& level)
+{
+    std::basic_string<C, T> str;
+    auto check = [&](const char* name) {
+        size_t n = strlen(name);
+        if (n != str.size())
+            return false;
+        for (size_t i = 0; i < n; ++i) {
+            if (in.widen(name[i]) != str[i])
+                return false;
+        }
+        return true;
+    };
+    if (in >> str) {
+        if (check("all")) {
+            level = Logger::Level::all;
+        }
+        else if (check("trace")) {
+            level = Logger::Level::trace;
+        }
+        else if (check("debug")) {
+            level = Logger::Level::debug;
+        }
+        else if (check("detail")) {
+            level = Logger::Level::detail;
+        }
+        else if (check("info")) {
+            level = Logger::Level::info;
+        }
+        else if (check("warn")) {
+            level = Logger::Level::warn;
+        }
+        else if (check("error")) {
+            level = Logger::Level::error;
+        }
+        else if (check("fatal")) {
+            level = Logger::Level::fatal;
+        }
+        else if (check("off")) {
+            level = Logger::Level::off;
+        }
+        else {
+            in.setstate(std::ios_base::failbit);
+        }
+    }
+    return in;
+}
+
+inline void RootLogger::set_level_threshold(Level new_level_threshold) noexcept
+{
+    m_level_threshold = new_level_threshold;
+}
+
+inline RootLogger::RootLogger()
+    : Logger::LevelThreshold()
+    , Logger(static_cast<Logger::LevelThreshold&>(*this))
+{
+}
+
+inline Logger::Level RootLogger::get() const noexcept
+{
+    return m_level_threshold;
+}
+
+inline void StderrLogger::do_log(Level level, std::string message)
+{
+    std::cerr << get_level_prefix(level) << message << '\n'; // Throws
+    std::cerr.flush();                                       // Throws
+}
+
+inline StreamLogger::StreamLogger(std::ostream& out) noexcept
+    : m_out(out)
+{
+}
+
+inline void StreamLogger::do_log(Level level, std::string message)
+{
+    m_out << get_level_prefix(level) << message << '\n'; // Throws
+    m_out.flush();                                       // Throws
+}
+
+inline FileLogger::FileLogger(std::string path)
+    : StreamLogger(m_out)
+    , m_file(path, util::File::mode_Write) // Throws
+    , m_streambuf(&m_file)                 // Throws
+    , m_out(&m_streambuf)                  // Throws
+{
+}
+
+inline FileLogger::FileLogger(util::File file)
+    : StreamLogger(m_out)
+    , m_file(std::move(file))
+    , m_streambuf(&m_file) // Throws
+    , m_out(&m_streambuf)  // Throws
+{
+}
+
+inline ThreadSafeLogger::ThreadSafeLogger(Logger& base_logger, Level threshold)
+    : Logger::LevelThreshold()
+    , Logger(static_cast<Logger::LevelThreshold&>(*this))
+    , m_level_threshold(threshold)
+    , m_base_logger(base_logger)
+{
+}
+
+inline void ThreadSafeLogger::do_log(Level level, std::string message)
+{
+    LockGuard l(m_mutex);
+    Logger::do_log(m_base_logger, level, message); // Throws
+}
+
+inline Logger::Level ThreadSafeLogger::get() const noexcept
+{
+    return m_level_threshold;
+}
+
+inline PrefixLogger::PrefixLogger(std::string prefix, Logger& base_logger) noexcept
+    : Logger(base_logger.level_threshold)
+    , m_prefix(std::move(prefix))
+    , m_base_logger(base_logger)
+{
+}
+
+inline void PrefixLogger::do_log(Level level, std::string message)
+{
+    Logger::do_log(m_base_logger, level, m_prefix + message); // Throws
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_LOGGER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/memory_stream.hpp b/iOS/Pods/Realm/include/core/realm/util/memory_stream.hpp
new file mode 100644 (file)
index 0000000..51584e8
--- /dev/null
@@ -0,0 +1,212 @@
+/*************************************************************************
+ *
+ * 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_UTIL_MEMORY_STREAM_HPP
+#define REALM_UTIL_MEMORY_STREAM_HPP
+
+#include <cstddef>
+#include <string>
+#include <istream>
+#include <ostream>
+
+namespace realm {
+namespace util {
+
+class MemoryInputStreambuf : public std::streambuf {
+public:
+    MemoryInputStreambuf();
+    ~MemoryInputStreambuf() noexcept;
+
+    /// Behavior is undefined if the size of the specified buffer exceeds
+    /// PTRDIFF_MAX.
+    void set_buffer(const char* begin, const char* end) noexcept;
+
+private:
+    const char* m_begin;
+    const char* m_end;
+    const char* m_curr;
+
+    int_type underflow() override;
+    int_type uflow() override;
+    int_type pbackfail(int_type) override;
+    std::streamsize showmanyc() override;
+    pos_type seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode) override;
+    pos_type seekpos(pos_type, std::ios_base::openmode) override;
+
+    pos_type do_seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode);
+};
+
+
+class MemoryOutputStreambuf : public std::streambuf {
+public:
+    MemoryOutputStreambuf();
+    ~MemoryOutputStreambuf() noexcept;
+
+    /// Behavior is undefined if the size of the specified buffer exceeds
+    /// PTRDIFF_MAX.
+    void set_buffer(char* begin, char* end) noexcept;
+
+    /// Returns the amount of data written to the buffer.
+    size_t size() const noexcept;
+};
+
+
+class MemoryInputStream : public std::istream {
+public:
+    MemoryInputStream();
+    ~MemoryInputStream() noexcept;
+
+    /// \{ Behavior is undefined if the size of the specified buffer exceeds
+    /// PTRDIFF_MAX.
+    void set_buffer(const char* begin, const char* end) noexcept;
+    template <size_t N> void set_buffer(const char (&buffer)[N]) noexcept;
+    void set_string(const std::string&) noexcept;
+    void set_c_string(const char* c_str) noexcept;
+    /// \}
+
+private:
+    MemoryInputStreambuf m_streambuf;
+};
+
+
+class MemoryOutputStream : public std::ostream {
+public:
+    MemoryOutputStream();
+    ~MemoryOutputStream() noexcept;
+
+    /// \{ Behavior is undefined if the size of the specified buffer exceeds
+    /// PTRDIFF_MAX.
+    void set_buffer(char* begin, char* end) noexcept;
+    template <size_t N> void set_buffer(char (&buffer)[N]) noexcept;
+    /// \}
+
+    /// Returns the amount of data written to the underlying buffer.
+    size_t size() const noexcept;
+
+private:
+    MemoryOutputStreambuf m_streambuf;
+};
+
+
+// Implementation
+
+inline MemoryInputStreambuf::MemoryInputStreambuf()
+    : m_begin(nullptr)
+    , m_end(nullptr)
+    , m_curr(nullptr)
+{
+}
+
+inline MemoryInputStreambuf::~MemoryInputStreambuf() noexcept
+{
+}
+
+inline void MemoryInputStreambuf::set_buffer(const char* begin, const char* end) noexcept
+{
+    m_begin = begin;
+    m_end = end;
+    m_curr = begin;
+}
+
+
+inline MemoryOutputStreambuf::MemoryOutputStreambuf()
+{
+}
+
+inline MemoryOutputStreambuf::~MemoryOutputStreambuf() noexcept
+{
+}
+
+inline void MemoryOutputStreambuf::set_buffer(char* begin, char* end) noexcept
+{
+    setp(begin, end);
+}
+
+inline size_t MemoryOutputStreambuf::size() const noexcept
+{
+    return pptr() - pbase();
+}
+
+
+inline MemoryInputStream::MemoryInputStream()
+    : std::istream(&m_streambuf)
+{
+}
+
+inline MemoryInputStream::~MemoryInputStream() noexcept
+{
+}
+
+inline void MemoryInputStream::set_buffer(const char* begin, const char* end) noexcept
+{
+    m_streambuf.set_buffer(begin, end);
+    clear();
+}
+
+template <size_t N> inline void MemoryInputStream::set_buffer(const char (&buffer)[N]) noexcept
+{
+    const char* begin = buffer;
+    const char* end = begin + N;
+    set_buffer(begin, end);
+}
+
+inline void MemoryInputStream::set_string(const std::string& str) noexcept
+{
+    const char* begin = str.data();
+    const char* end = begin + str.size();
+    set_buffer(begin, end);
+}
+
+inline void MemoryInputStream::set_c_string(const char* c_str) noexcept
+{
+    const char* begin = c_str;
+    const char* end = begin + traits_type::length(c_str);
+    set_buffer(begin, end);
+}
+
+
+inline MemoryOutputStream::MemoryOutputStream()
+    : std::ostream(&m_streambuf)
+{
+}
+
+inline MemoryOutputStream::~MemoryOutputStream() noexcept
+{
+}
+
+inline void MemoryOutputStream::set_buffer(char* begin, char* end) noexcept
+{
+    m_streambuf.set_buffer(begin, end);
+    clear();
+}
+
+template <size_t N>
+inline void MemoryOutputStream::set_buffer(char (&buffer)[N]) noexcept
+{
+    set_buffer(buffer, buffer + N);
+}
+
+inline size_t MemoryOutputStream::size() const noexcept
+{
+    return m_streambuf.size();
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_MEMORY_STREAM_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/misc_errors.hpp b/iOS/Pods/Realm/include/core/realm/util/misc_errors.hpp
new file mode 100644 (file)
index 0000000..9335ba9
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_UTIL_MISC_ERRORS_HPP
+#define REALM_UTIL_MISC_ERRORS_HPP
+
+#include <system_error>
+
+
+namespace realm {
+namespace util {
+namespace error {
+
+enum misc_errors {
+    unknown = 1,
+};
+
+std::error_code make_error_code(misc_errors);
+
+} // namespace error
+} // namespace util
+} // namespace realm
+
+namespace std {
+
+template <>
+class is_error_code_enum<realm::util::error::misc_errors> {
+public:
+    static const bool value = true;
+};
+
+} // namespace std
+
+#endif // REALM_UTIL_MISC_ERRORS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/miscellaneous.hpp b/iOS/Pods/Realm/include/core/realm/util/miscellaneous.hpp
new file mode 100644 (file)
index 0000000..c45e4f3
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_UTIL_MISCELLANEOUS_HPP
+#define REALM_UTIL_MISCELLANEOUS_HPP
+
+#include <type_traits>
+
+namespace realm {
+namespace util {
+
+// FIXME: Replace this with std::add_const_t when we switch over to C++14 by
+// default.
+/// \brief Adds const qualifier, unless T already has the const qualifier
+template <class T>
+using add_const_t = typename std::add_const<T>::type;
+
+// FIXME: Replace this with std::as_const when we switch over to C++17 by
+// default.
+/// \brief Forms an lvalue reference to const T
+template <class T>
+constexpr add_const_t<T>& as_const(T& v) noexcept
+{
+    return v;
+}
+
+/// \brief Disallows rvalue arguments
+template <class T>
+add_const_t<T>& as_const(const T&&) = delete;
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_MISCELLANEOUS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/network.hpp b/iOS/Pods/Realm/include/core/realm/util/network.hpp
new file mode 100644 (file)
index 0000000..39a10d6
--- /dev/null
@@ -0,0 +1,3657 @@
+/*************************************************************************
+ *
+ * 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_UTIL_NETWORK_HPP
+#define REALM_UTIL_NETWORK_HPP
+
+#include <cstddef>
+#include <memory>
+#include <chrono>
+#include <string>
+#include <system_error>
+#include <ostream>
+
+#include <sys/types.h>
+
+#ifdef _WIN32
+#  include <winsock2.h>
+#  include <ws2tcpip.h>
+#  include <stdio.h>
+#  include <Ws2def.h>
+#  pragma comment(lib, "Ws2_32.lib")
+#else
+#  include <sys/socket.h>
+#  include <arpa/inet.h>
+#  include <netdb.h>
+#endif
+
+#include <realm/util/features.h>
+#include <realm/util/assert.hpp>
+#include <realm/util/bind_ptr.hpp>
+#include <realm/util/buffer.hpp>
+#include <realm/util/basic_system_errors.hpp>
+
+// Linux epoll
+//
+// Require Linux kernel version >= 2.6.27 such that we have epoll_create1(),
+// `O_CLOEXEC`, and `EPOLLRDHUP`.
+#if defined(__linux__)
+#  include <linux/version.h>
+#  if !defined(REALM_HAVE_EPOLL)
+#    if !defined(REALM_DISABLE_UTIL_NETWORK_EPOLL)
+#      if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#        define REALM_HAVE_EPOLL 1
+#      endif
+#    endif
+#  endif
+#endif
+#if !defined(REALM_HAVE_EPOLL)
+#  define REALM_HAVE_EPOLL 0
+#endif
+
+// FreeBSD Kqueue.
+//
+// Available on Mac OS X, FreeBSD, NetBSD, OpenBSD
+#if (defined(__MACH__) && defined(__APPLE__)) || defined(__FreeBSD__) || \
+    defined(__NetBSD__) || defined(__OpenBSD__)
+#  if !defined(REALM_HAVE_KQUEUE)
+#    if !defined(REALM_DISABLE_UTIL_NETWORK_KQUEUE)
+#      define REALM_HAVE_KQUEUE 1
+#    endif
+#  endif
+#endif
+#if !defined(REALM_HAVE_KQUEUE)
+#  define REALM_HAVE_KQUEUE 0
+#endif
+
+
+
+// FIXME: Unfinished business around `Address::m_ip_v6_scope_id`.
+
+
+namespace realm {
+namespace util {
+
+/// \brief TCP/IP networking API.
+///
+/// The design of this networking API is heavily inspired by the ASIO C++
+/// library (http://think-async.com).
+///
+///
+/// ### Thread safety
+///
+/// A *service context* is a set of objects consisting of an instance of
+/// Service, and all the objects that are associated with that instance (\ref
+/// Resolver, \ref Socket`, \ref Acceptor`, \ref DeadlineTimer, \ref Trigger,
+/// and \ref ssl::Stream).
+///
+/// In general, it is unsafe for two threads to call functions on the same
+/// object, or on different objects in the same service context. This also
+/// applies to destructors. Notable exceptions are the fully thread-safe
+/// functions, such as Service::post(), Service::stop(), and Service::reset().
+///
+/// On the other hand, it is always safe for two threads to call functions on
+/// objects belonging to different service contexts.
+///
+/// One implication of these rules is that at most one thread must execute run()
+/// at any given time, and if one thread is executing run(), then no other
+/// thread is allowed to access objects in the same service context (with the
+/// mentioned exceptions).
+///
+/// Unless otherwise specified, free-standing objects, such as \ref
+/// StreamProtocol, \ref Address, \ref Endpoint, and \ref Endpoint::List are
+/// fully thread-safe as long as they are not mutated. If one thread is mutating
+/// such an object, no other thread may access it. Note that these free-standing
+/// objects are not associcated with an instance of Service, and are therefore
+/// not part of a service context.
+///
+///
+/// ### Comparison with ASIO
+///
+/// There is a crucial difference between the two libraries in regards to the
+/// guarantees that are provided about the cancelability of asynchronous
+/// operations. The Realm networking library (this library) considers an
+/// asynchronous operation to be complete precisely when the completion handler
+/// starts to execute, and it guarantees that such an operation is cancelable up
+/// until that point in time. In particular, if `cancel()` is called on a socket
+/// or a deadline timer object before the completion handler starts to execute,
+/// then that operation will be canceled, and will receive
+/// `error::operation_aborted`. This guarantee is possible to provide (and free
+/// of ambiguities) precisely because this library prohibits multiple threads
+/// from executing the event loop concurrently, and because `cancel()` is
+/// allowed to be called only from a completion handler (executed by the event
+/// loop thread) or while no thread is executing the event loop. This guarantee
+/// allows for safe destruction of sockets and deadline timers as long as the
+/// completion handlers react appropriately to `error::operation_aborted`, in
+/// particular, that they do not attempt to access the socket or deadline timer
+/// object in such cases.
+///
+/// ASIO, on the other hand, allows for an asynchronous operation to complete
+/// and become **uncancellable** before the completion handler starts to
+/// execute. For this reason, it is possible with ASIO to get the completion
+/// handler of an asynchronous wait operation to start executing and receive an
+/// error code other than "operation aborted" at a point in time where
+/// `cancel()` has already been called on the deadline timer object, or even at
+/// a point in timer where the deadline timer has been destroyed. This seems
+/// like an inevitable consequence of the fact that ASIO allows for multiple
+/// threads to execute the event loop concurrently. This generally forces ASIO
+/// applications to invent ways of extending the lifetime of deadline timer and
+/// socket objects until the completion handler starts executing.
+///
+/// IMPORTANT: Even if ASIO is used in a way where at most one thread executes
+/// the event loop, there is still no guarantee that an asynchronous operation
+/// remains cancelable up until the point in time where the completion handler
+/// starts to execute.
+namespace network {
+
+std::string host_name();
+
+
+class StreamProtocol;
+class Address;
+class Endpoint;
+class Service;
+class Resolver;
+class SocketBase;
+class Socket;
+class Acceptor;
+class DeadlineTimer;
+class Trigger;
+class ReadAheadBuffer;
+namespace ssl {
+class Stream;
+} // namespace ssl
+
+
+/// \brief An IP protocol descriptor.
+class StreamProtocol {
+public:
+    static StreamProtocol ip_v4();
+    static StreamProtocol ip_v6();
+
+    bool is_ip_v4() const;
+    bool is_ip_v6() const;
+
+    int protocol() const;
+    int family() const;
+
+    StreamProtocol();
+    ~StreamProtocol() noexcept {}
+
+private:
+    int m_family;
+    int m_socktype;
+    int m_protocol;
+
+    friend class Resolver;
+    friend class SocketBase;
+};
+
+
+/// \brief An IP address (IPv4 or IPv6).
+class Address {
+public:
+    bool is_ip_v4() const;
+    bool is_ip_v6() const;
+
+    template<class C, class T>
+    friend std::basic_ostream<C,T>& operator<<(std::basic_ostream<C,T>&, const Address&);
+
+    Address();
+    ~Address() noexcept {}
+
+private:
+    using ip_v4_type = in_addr;
+    using ip_v6_type = in6_addr;
+    union union_type {
+        ip_v4_type m_ip_v4;
+        ip_v6_type m_ip_v6;
+    };
+    union_type m_union;
+    std::uint_least32_t m_ip_v6_scope_id = 0;
+    bool m_is_ip_v6 = false;
+
+    friend Address make_address(const char*, std::error_code&) noexcept;
+    friend class Endpoint;
+};
+
+Address make_address(const char* c_str);
+Address make_address(const char* c_str, std::error_code& ec) noexcept;
+Address make_address(const std::string&);
+Address make_address(const std::string&, std::error_code& ec) noexcept;
+
+
+/// \brief An IP endpoint.
+///
+/// An IP endpoint is a triplet (`protocol`, `address`, `port`).
+class Endpoint {
+public:
+    using port_type = std::uint_fast16_t;
+    class List;
+
+    StreamProtocol protocol() const;
+    Address address() const;
+    port_type port() const;
+
+    Endpoint();
+    Endpoint(const StreamProtocol&, port_type);
+    Endpoint(const Address&, port_type);
+    ~Endpoint() noexcept {}
+
+    using data_type = sockaddr;
+    data_type* data();
+    const data_type* data() const;
+
+private:
+    StreamProtocol m_protocol;
+
+    using sockaddr_base_type  = sockaddr;
+    using sockaddr_ip_v4_type = sockaddr_in;
+    using sockaddr_ip_v6_type = sockaddr_in6;
+    union sockaddr_union_type {
+        sockaddr_base_type  m_base;
+        sockaddr_ip_v4_type m_ip_v4;
+        sockaddr_ip_v6_type m_ip_v6;
+    };
+    sockaddr_union_type m_sockaddr_union;
+
+    friend class Service;
+    friend class Resolver;
+    friend class SocketBase;
+    friend class Socket;
+};
+
+
+/// \brief A list of IP endpoints.
+class Endpoint::List {
+public:
+    using iterator = const Endpoint*;
+
+    iterator begin() const noexcept;
+    iterator end() const noexcept;
+    std::size_t size() const noexcept;
+    bool empty() const noexcept;
+
+    List() noexcept = default;
+    List(List&&) noexcept = default;
+    ~List() noexcept = default;
+
+    List& operator=(List&&) noexcept = default;
+
+private:
+    Buffer<Endpoint> m_endpoints;
+
+    friend class Resolver;
+};
+
+
+/// \brief TCP/IP networking service.
+class Service {
+public:
+    Service();
+    ~Service() noexcept;
+
+    /// \brief Execute the event loop.
+    ///
+    /// Execute completion handlers of completed asynchronous operations, or
+    /// wait for more completion handlers to become ready for
+    /// execution. Handlers submitted via post() are considered immeditely
+    /// ready. If there are no completion handlers ready for execution, and
+    /// there are no asynchronous operations in progress, run() returns.
+    ///
+    /// All completion handlers, including handlers submitted via post() will be
+    /// executed from run(), that is, by the thread that executes run(). If no
+    /// thread executes run(), then the completion handlers will not be
+    /// executed.
+    ///
+    /// Exceptions thrown by completion handlers will always propagate back
+    /// through run().
+    ///
+    /// Syncronous operations (e.g., Socket::connect()) execute independently of
+    /// the event loop, and do not require that any thread calls run().
+    void run();
+
+    /// @{ \brief Stop event loop execution.
+    ///
+    /// stop() puts the event loop into the stopped mode. If a thread is
+    /// currently executing run(), it will be made to return in a timely
+    /// fashion, that is, without further blocking. If a thread is currently
+    /// blocked in run(), it will be unblocked. Handlers that can be executed
+    /// immediately, may, or may not be executed before run() returns, but new
+    /// handlers submitted by these, will not be executed before run()
+    /// returns. Also, if a handler is submitted by a call to post, and that
+    /// call happens after stop() returns, then that handler is guaranteed to
+    /// not be executed before run() returns (assuming that reset() is not called
+    /// before run() returns).
+    ///
+    /// The event loop will remain in the stopped mode until reset() is
+    /// called. If reset() is called before run() returns, it may, or may not
+    /// cause run() to resume normal operation without returning.
+    ///
+    /// Both stop() and reset() are thread-safe, that is, they may be called by
+    /// any thread. Also, both of these function may be called from completion
+    /// handlers (including posted handlers).
+    void stop() noexcept;
+    void reset() noexcept;
+    /// @}
+
+    /// \brief Submit a handler to be executed by the event loop thread.
+    ///
+    /// Register the sepcified completion handler for immediate asynchronous
+    /// execution. The specified handler will be executed by an expression on
+    /// the form `handler()`. If the the handler object is movable, it will
+    /// never be copied. Otherwise, it will be copied as necessary.
+    ///
+    /// This function is thread-safe, that is, it may be called by any
+    /// thread. It may also be called from other completion handlers.
+    ///
+    /// The handler will never be called as part of the execution of post(). It
+    /// will always be called by a thread that is executing run(). If no thread
+    /// is currently executing run(), the handler will not be executed until a
+    /// thread starts executing run(). If post() is called while another thread
+    /// is executing run(), the handler may be called before post() returns. If
+    /// post() is called from another completion handler, the submitted handler
+    /// is guaranteed to not be called during the execution of post().
+    ///
+    /// Completion handlers added through post() will be executed in the order
+    /// that they are added. More precisely, if post() is called twice to add
+    /// two handlers, A and B, and the execution of post(A) ends before the
+    /// beginning of the execution of post(B), then A is guaranteed to execute
+    /// before B.
+    template<class H> void post(H handler);
+
+private:
+    enum class Want { nothing = 0, read, write };
+
+    template<class Oper> class OperQueue;
+    class Descriptor;
+    class AsyncOper;
+    class WaitOperBase;
+    class TriggerExecOperBase;
+    class PostOperBase;
+    template<class H> class PostOper;
+    class IoOper;
+    class UnusedOper; // Allocated, but currently unused memory
+
+    template<class S> class BasicStreamOps;
+
+    struct OwnersOperDeleter {
+        void operator()(AsyncOper*) const noexcept;
+    };
+    struct LendersOperDeleter {
+        void operator()(AsyncOper*) const noexcept;
+    };
+    using OwnersOperPtr      = std::unique_ptr<AsyncOper,    OwnersOperDeleter>;
+    using LendersOperPtr     = std::unique_ptr<AsyncOper,    LendersOperDeleter>;
+    using LendersWaitOperPtr = std::unique_ptr<WaitOperBase, LendersOperDeleter>;
+    using LendersIoOperPtr   = std::unique_ptr<IoOper,       LendersOperDeleter>;
+
+    class IoReactor;
+    class Impl;
+    const std::unique_ptr<Impl> m_impl;
+
+    template<class Oper, class... Args>
+    static std::unique_ptr<Oper, LendersOperDeleter> alloc(OwnersOperPtr&, Args&&...);
+
+    template<class Oper> static void execute(std::unique_ptr<Oper, LendersOperDeleter>&);
+
+    using PostOperConstr = PostOperBase*(void* addr, std::size_t size, Impl&, void* cookie);
+    void do_post(PostOperConstr, std::size_t size, void* cookie);
+    template<class H>
+    static PostOperBase* post_oper_constr(void* addr, std::size_t size, Impl&, void* cookie);
+    static void recycle_post_oper(Impl&, PostOperBase*) noexcept;
+    static void trigger_exec(Impl&, TriggerExecOperBase&) noexcept;
+    static void reset_trigger_exec(Impl&, TriggerExecOperBase&) noexcept;
+
+    using clock = std::chrono::steady_clock;
+
+    friend class Resolver;
+    friend class SocketBase;
+    friend class Socket;
+    friend class Acceptor;
+    friend class DeadlineTimer;
+    friend class Trigger;
+    friend class ReadAheadBuffer;
+    friend class ssl::Stream;
+};
+
+
+template<class Oper> class Service::OperQueue {
+public:
+    using LendersOperPtr = std::unique_ptr<Oper, LendersOperDeleter>;
+    bool empty() const noexcept;
+    void push_back(LendersOperPtr) noexcept;
+    template<class Oper2> void push_back(OperQueue<Oper2>&) noexcept;
+    LendersOperPtr pop_front() noexcept;
+    void clear() noexcept;
+    OperQueue() noexcept = default;
+    OperQueue(OperQueue&&) noexcept;
+    ~OperQueue() noexcept;
+private:
+    Oper* m_back = nullptr;
+    template<class> friend class OperQueue;
+};
+
+
+class Service::Descriptor {
+public:
+#ifdef _WIN32
+    using native_handle_type = SOCKET;
+#else
+    using native_handle_type = int;
+#endif
+
+    Impl& service_impl;
+
+    Descriptor(Impl& service) noexcept;
+    ~Descriptor() noexcept;
+
+    /// \param in_blocking_mode Must be true if, and only if the passed file
+    /// descriptor refers to a file description in which the file status flag
+    /// O_NONBLOCK is not set.
+    ///
+    /// The passed file descriptor must have the file descriptor flag FD_CLOEXEC
+    /// set.
+    void assign(native_handle_type fd, bool in_blocking_mode) noexcept;
+    void close() noexcept;
+
+    bool is_open() const noexcept;
+
+    native_handle_type native_handle() const noexcept;
+    bool in_blocking_mode() const noexcept;
+
+    void accept(Descriptor&, StreamProtocol, Endpoint*, std::error_code&) noexcept;
+    std::size_t read_some(char* buffer, std::size_t size, std::error_code&) noexcept;
+    std::size_t write_some(const char* data, std::size_t size, std::error_code&) noexcept;
+
+    /// \tparam Oper An operation type inherited from IoOper with an initate()
+    /// function that initiates the operation and figures out whether it needs
+    /// to read from, or write to the underlying descriptor to
+    /// proceed. `initiate()` must return Want::read if the operation needs to
+    /// read, or Want::write if the operation needs to write. If the operation
+    /// completes immediately (e.g. due to a failure during initialization),
+    /// `initiate()` must return Want::nothing.
+    template<class Oper, class... Args>
+    void initiate_oper(std::unique_ptr<Oper, LendersOperDeleter>, Args&&...);
+
+    void ensure_blocking_mode();
+    void ensure_nonblocking_mode();
+
+private:
+    native_handle_type m_fd = -1;
+    bool m_in_blocking_mode; // Not in nonblocking mode
+
+#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE
+    bool m_read_ready;
+    bool m_write_ready;
+    bool m_imminent_end_of_input; // Kernel has seen the end of input
+    bool m_is_registered;
+    OperQueue<IoOper> m_suspended_read_ops, m_suspended_write_ops;
+
+    void deregister_for_async() noexcept;
+#endif
+
+    bool assume_read_would_block() const noexcept;
+    bool assume_write_would_block() const noexcept;
+
+    void set_read_ready(bool) noexcept;
+    void set_write_ready(bool) noexcept;
+
+    void set_nonblock_flag(bool value);
+    void add_initiated_oper(LendersIoOperPtr, Want);
+
+    void do_close() noexcept;
+
+    friend class IoReactor;
+};
+
+
+class Resolver {
+public:
+    class Query;
+
+    Resolver(Service&);
+    ~Resolver() noexcept;
+
+    /// Thread-safe.
+    Service& get_service() noexcept;
+
+    /// @{ \brief Resolve the specified query to one or more endpoints.
+    Endpoint::List resolve(const Query&);
+    Endpoint::List resolve(const Query&, std::error_code&);
+    /// @}
+
+    /// \brief Perform an asynchronous resolve operation.
+    ///
+    /// Initiate an asynchronous resolve operation. The completion handler will
+    /// be called when the operation completes. The operation completes when it
+    /// succeeds, or an error occurs.
+    ///
+    /// The completion handler is always executed by the event loop thread,
+    /// i.e., by a thread that is executing Service::run(). Conversely, the
+    /// completion handler is guaranteed to not be called while no thread is
+    /// executing Service::run(). The execution of the completion handler is
+    /// always deferred to the event loop, meaning that it never happens as a
+    /// synchronous side effect of the execution of async_resolve(), even when
+    /// async_resolve() is executed by the event loop thread. The completion
+    /// handler is guaranteed to be called eventually, as long as there is time
+    /// enough for the operation to complete or fail, and a thread is executing
+    /// Service::run() for long enough.
+    ///
+    /// The operation can be canceled by calling cancel(), and will be
+    /// automatically canceled if the resolver object is destroyed. If the
+    /// operation is canceled, it will fail with `error::operation_aborted`. The
+    /// operation remains cancelable up until the point in time where the
+    /// completion handler starts to execute. This means that if cancel() is
+    /// called before the completion handler starts to execute, then the
+    /// completion handler is guaranteed to have `error::operation_aborted`
+    /// passed to it. This is true regardless of whether cancel() is called
+    /// explicitly or implicitly, such as when the resolver is destroyed.
+    ///
+    /// The specified handler will be executed by an expression on the form
+    /// `handler(ec, endpoints)` where `ec` is the error code and `endpoints` is
+    /// an object of type `Endpoint::List`. If the the handler object is
+    /// movable, it will never be copied. Otherwise, it will be copied as
+    /// necessary.
+    ///
+    /// It is an error to start a new resolve operation (synchronous or
+    /// asynchronous) while an asynchronous resolve operation is in progress via
+    /// the same resolver object. An asynchronous resolve operation is
+    /// considered complete as soon as the completion handler starts to
+    /// execute. This means that a new resolve operation can be started from the
+    /// completion handler.
+    template<class H> void async_resolve(Query, H handler);
+
+    /// \brief Cancel all asynchronous operations.
+    ///
+    /// Cause all incomplete asynchronous operations, that are associated with
+    /// this resolver (at most one), to fail with `error::operation_aborted`. An
+    /// asynchronous operation is complete precisely when its completion handler
+    /// starts executing.
+    ///
+    /// Completion handlers of canceled operations will become immediately ready
+    /// to execute, but will never be executed directly as part of the execution
+    /// of cancel().
+    ///
+    /// Cancellation happens automatically when the resolver object is destroyed.
+    void cancel() noexcept;
+
+private:
+    class ResolveOperBase;
+    template<class H> class ResolveOper;
+
+    using LendersResolveOperPtr = std::unique_ptr<ResolveOperBase, Service::LendersOperDeleter>;
+
+    Service::Impl& m_service_impl;
+
+    Service::OwnersOperPtr m_resolve_oper;
+
+    void initiate_oper(LendersResolveOperPtr);
+};
+
+
+class Resolver::Query {
+public:
+    enum {
+        /// Locally bound socket endpoint (server side)
+        passive = AI_PASSIVE,
+
+        /// Ignore families without a configured non-loopback address
+        address_configured = AI_ADDRCONFIG
+    };
+
+    Query(std::string service_port, int init_flags = passive|address_configured);
+    Query(const StreamProtocol&, std::string service_port,
+          int init_flags = passive|address_configured);
+    Query(std::string host_name, std::string service_port,
+          int init_flags = address_configured);
+    Query(const StreamProtocol&, std::string host_name, std::string service_port,
+          int init_flags = address_configured);
+
+    ~Query() noexcept;
+
+    int flags() const;
+    StreamProtocol protocol() const;
+    std::string host() const;
+    std::string service() const;
+
+private:
+    int m_flags;
+    StreamProtocol m_protocol;
+    std::string m_host;    // hostname
+    std::string m_service; // port
+
+    friend class Resolver;
+};
+
+
+class SocketBase {
+public:
+    using native_handle_type = Service::Descriptor::native_handle_type;
+
+    ~SocketBase() noexcept;
+
+    /// Thread-safe.
+    Service& get_service() noexcept;
+
+    bool is_open() const noexcept;
+    native_handle_type native_handle() const noexcept;
+
+    /// @{ \brief Open the socket for use with the specified protocol.
+    ///
+    /// It is an error to call open() on a socket that is already open.
+    void open(const StreamProtocol&);
+    std::error_code open(const StreamProtocol&, std::error_code&);
+    /// @}
+
+    /// \brief Close this socket.
+    ///
+    /// If the socket is open, it will be closed. If it is already closed (or
+    /// never opened), this function does nothing (idempotency).
+    ///
+    /// A socket is automatically closed when destroyed.
+    ///
+    /// When the socket is closed, any incomplete asynchronous operation will be
+    /// canceled (as if cancel() was called).
+    void close() noexcept;
+
+    /// \brief Cancel all asynchronous operations.
+    ///
+    /// Cause all incomplete asynchronous operations, that are associated with
+    /// this socket, to fail with `error::operation_aborted`. An asynchronous
+    /// operation is complete precisely when its completion handler starts
+    /// executing.
+    ///
+    /// Completion handlers of canceled operations will become immediately ready
+    /// to execute, but will never be executed directly as part of the execution
+    /// of cancel().
+    void cancel() noexcept;
+
+    template<class O>
+    void get_option(O& opt) const;
+
+    template<class O>
+    std::error_code get_option(O& opt, std::error_code&) const;
+
+    template<class O>
+    void set_option(const O& opt);
+
+    template<class O>
+    std::error_code set_option(const O& opt, std::error_code&);
+
+    void bind(const Endpoint&);
+    std::error_code bind(const Endpoint&, std::error_code&);
+
+    Endpoint local_endpoint() const;
+    Endpoint local_endpoint(std::error_code&) const;
+
+private:
+    enum opt_enum {
+        opt_ReuseAddr, ///< `SOL_SOCKET`, `SO_REUSEADDR`
+        opt_Linger,    ///< `SOL_SOCKET`, `SO_LINGER`
+        opt_NoDelay,   ///< `IPPROTO_TCP`, `TCP_NODELAY` (disable the Nagle algorithm)
+    };
+
+    template<class, int, class> class Option;
+
+public:
+    using reuse_address = Option<bool, opt_ReuseAddr, int>;
+    using no_delay      = Option<bool, opt_NoDelay,   int>;
+
+    // linger struct defined by POSIX sys/socket.h.
+    struct linger_opt;
+    using linger = Option<linger_opt, opt_Linger, struct linger>;
+
+protected:
+    Service::Descriptor m_desc;
+
+private:
+    StreamProtocol m_protocol;
+
+protected:
+    Service::OwnersOperPtr m_read_oper;  // Read or accept
+    Service::OwnersOperPtr m_write_oper; // Write or connect
+
+    SocketBase(Service&);
+
+    const StreamProtocol& get_protocol() const noexcept;
+    std::error_code do_assign(const StreamProtocol&, native_handle_type, std::error_code&);
+    void do_close() noexcept;
+
+    void get_option(opt_enum, void* value_data, std::size_t& value_size, std::error_code&) const;
+    void set_option(opt_enum, const void* value_data, std::size_t value_size, std::error_code&);
+    void map_option(opt_enum, int& level, int& option_name) const;
+
+    friend class Acceptor;
+};
+
+
+template<class T, int opt, class U> class SocketBase::Option {
+public:
+    Option(T value = T());
+    T value() const;
+
+private:
+    T m_value;
+
+    void get(const SocketBase&, std::error_code&);
+    void set(SocketBase&, std::error_code&) const;
+
+    friend class SocketBase;
+};
+
+struct SocketBase::linger_opt {
+    linger_opt(bool enable, int timeout_seconds = 0)
+    {
+        m_linger.l_onoff = enable ? 1 : 0;
+        m_linger.l_linger = timeout_seconds;
+    }
+
+    ::linger m_linger;
+
+    operator ::linger() const { return m_linger; }
+
+    bool enabled() const { return m_linger.l_onoff != 0; }
+    int  timeout() const { return m_linger.l_linger; }
+};
+
+
+/// Switching between synchronous and asynchronous operations is allowed, but
+/// only in a nonoverlapping fashion. That is, a synchronous operation is not
+/// allowed to run concurrently with an asynchronous one on the same
+/// socket. Note that an asynchronous operation is considered to be running
+/// until its completion handler starts executing.
+class Socket: public SocketBase {
+public:
+    Socket(Service&);
+
+    /// \brief Create a socket with an already-connected native socket handle.
+    ///
+    /// This constructor is shorthand for creating the socket with the
+    /// one-argument constructor, and then calling the two-argument assign()
+    /// with the specified protocol and native handle.
+    Socket(Service&, const StreamProtocol&, native_handle_type);
+
+    ~Socket() noexcept;
+
+    void connect(const Endpoint&);
+    std::error_code connect(const Endpoint&, std::error_code&);
+
+    /// @{ \brief Perform a synchronous read operation.
+    ///
+    /// read() will not return until the specified buffer is full, or an error
+    /// occurs. Reaching the end of input before the buffer is filled, is
+    /// considered an error, and will cause the operation to fail with
+    /// `network::end_of_input`.
+    ///
+    /// read_until() will not return until the specified buffer contains the
+    /// specified delimiter, or an error occurs. If the buffer is filled before
+    /// the delimiter is found, the operation fails with
+    /// `network::delim_not_found`. Otherwise, if the end of input is reached
+    /// before the delimiter is found, the operation fails with
+    /// `network::end_of_input`. If the operation succeeds, the last byte placed
+    /// in the buffer is the delimiter.
+    ///
+    /// The versions that take a ReadAheadBuffer argument will read through that
+    /// buffer. This allows for fewer larger reads on the underlying
+    /// socket. Since unconsumed data may be left in the read-ahead buffer after
+    /// a read operation returns, it is important that the same read-ahead
+    /// buffer is passed to the next read operation.
+    ///
+    /// The versions of read() and read_until() that do not take an
+    /// `std::error_code&` argument will throw std::system_error on failure.
+    ///
+    /// The versions that do take an `std::error_code&` argument will set \a ec
+    /// to `std::error_code()` on success, and to something else on failure. On
+    /// failure they will return the number of bytes placed in the specified
+    /// buffer before the error occured.
+    ///
+    /// \return The number of bytes places in the specified buffer upon return.
+    std::size_t read(char* buffer, std::size_t size);
+    std::size_t read(char* buffer, std::size_t size, std::error_code& ec);
+    std::size_t read(char* buffer, std::size_t size, ReadAheadBuffer&);
+    std::size_t read(char* buffer, std::size_t size, ReadAheadBuffer&, std::error_code& ec);
+    std::size_t read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&);
+    std::size_t read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&,
+                           std::error_code& ec);
+    /// @}
+
+    /// @{ \brief Perform a synchronous write operation.
+    ///
+    /// write() will not return until all the specified bytes have been written
+    /// to the socket, or an error occurs.
+    ///
+    /// The versions of write() that does not take an `std::error_code&`
+    /// argument will throw std::system_error on failure. When it succeeds, it
+    /// always returns \a size.
+    ///
+    /// The versions that does take an `std::error_code&` argument will set \a
+    /// ec to `std::error_code()` on success, and to something else on
+    /// failure. On success it returns \a size. On faulure it returns the number
+    /// of bytes written before the failure occured.
+    std::size_t write(const char* data, std::size_t size);
+    std::size_t write(const char* data, std::size_t size, std::error_code& ec);
+    /// @}
+
+    /// @{ \brief Read at least one byte from this socket.
+    ///
+    /// If \a size is zero, both versions of read_some() will return zero
+    /// without blocking. Read errors may or may not be detected in this case.
+    ///
+    /// Otherwise, if \a size is greater than zero, and at least one byte is
+    /// immediately available, that is, without blocking, then both versions
+    /// will read at least one byte (but generally as many immediately available
+    /// bytes as will fit into the specified buffer), and return without
+    /// blocking.
+    ///
+    /// Otherwise, both versions will block the calling thread until at least one
+    /// byte becomes available, or an error occurs.
+    ///
+    /// In this context, it counts as an error, if the end of input is reached
+    /// before at least one byte becomes available (see
+    /// `network::end_of_input`).
+    ///
+    /// If no error occurs, both versions will return the number of bytes placed
+    /// in the specified buffer, which is generally as many as are immediately
+    /// available at the time when the first byte becomes available, although
+    /// never more than \a size.
+    ///
+    /// If no error occurs, the three-argument version will set \a ec to
+    /// indicate success.
+    ///
+    /// If an error occurs, the two-argument version will throw
+    /// `std::system_error`, while the three-argument version will set \a ec to
+    /// indicate the error, and return zero.
+    ///
+    /// As long as \a size is greater than zero, the two argument version will
+    /// always return a value that is greater than zero, while the three
+    /// argument version will return a value greater than zero when, and only
+    /// when \a ec is set to indicate success (no error, and no end of input).
+    std::size_t read_some(char* buffer, std::size_t size);
+    std::size_t read_some(char* buffer, std::size_t size, std::error_code& ec);
+    /// @}
+
+    /// @{ \brief Write at least one byte to this socket.
+    ///
+    /// If \a size is zero, both versions of write_some() will return zero
+    /// without blocking. Write errors may or may not be detected in this case.
+    ///
+    /// Otherwise, if \a size is greater than zero, and at least one byte can be
+    /// written immediately, that is, without blocking, then both versions will
+    /// write at least one byte (but generally as many as can be written
+    /// immediately), and return without blocking.
+    ///
+    /// Otherwise, both versions will block the calling thread until at least one
+    /// byte can be written, or an error occurs.
+    ///
+    /// If no error occurs, both versions will return the number of bytes
+    /// written, which is generally as many as can be written immediately at the
+    /// time when the first byte can be written.
+    ///
+    /// If no error occurs, the three-argument version will set \a ec to
+    /// indicate success.
+    ///
+    /// If an error occurs, the two-argument version will throw
+    /// `std::system_error`, while the three-argument version will set \a ec to
+    /// indicate the error, and return zero.
+    ///
+    /// As long as \a size is greater than zero, the two argument version will
+    /// always return a value that is greater than zero, while the three
+    /// argument version will return a value greater than zero when, and only
+    /// when \a ec is set to indicate success.
+    std::size_t write_some(const char* data, std::size_t size);
+    std::size_t write_some(const char* data, std::size_t size, std::error_code&);
+    /// @}
+
+    /// \brief Perform an asynchronous connect operation.
+    ///
+    /// Initiate an asynchronous connect operation. The completion handler is
+    /// called when the operation completes. The operation completes when the
+    /// connection is established, or an error occurs.
+    ///
+    /// The completion handler is always executed by the event loop thread,
+    /// i.e., by a thread that is executing Service::run(). Conversely, the
+    /// completion handler is guaranteed to not be called while no thread is
+    /// executing Service::run(). The execution of the completion handler is
+    /// always deferred to the event loop, meaning that it never happens as a
+    /// synchronous side effect of the execution of async_connect(), even when
+    /// async_connect() is executed by the event loop thread. The completion
+    /// handler is guaranteed to be called eventually, as long as there is time
+    /// enough for the operation to complete or fail, and a thread is executing
+    /// Service::run() for long enough.
+    ///
+    /// The operation can be canceled by calling cancel(), and will be
+    /// automatically canceled if the socket is closed. If the operation is
+    /// canceled, it will fail with `error::operation_aborted`. The operation
+    /// remains cancelable up until the point in time where the completion
+    /// handler starts to execute. This means that if cancel() is called before
+    /// the completion handler starts to execute, then the completion handler is
+    /// guaranteed to have `error::operation_aborted` passed to it. This is true
+    /// regardless of whether cancel() is called explicitly or implicitly, such
+    /// as when the socket is destroyed.
+    ///
+    /// If the socket is not already open, it will be opened as part of the
+    /// connect operation as if by calling `open(ep.protocol())`. If the opening
+    /// operation succeeds, but the connect operation fails, the socket will be
+    /// left in the opened state.
+    ///
+    /// The specified handler will be executed by an expression on the form
+    /// `handler(ec)` where `ec` is the error code. If the the handler object is
+    /// movable, it will never be copied. Otherwise, it will be copied as
+    /// necessary.
+    ///
+    /// It is an error to start a new connect operation (synchronous or
+    /// asynchronous) while an asynchronous connect operation is in progress. An
+    /// asynchronous connect operation is considered complete as soon as the
+    /// completion handler starts to execute.
+    ///
+    /// \param ep The remote endpoint of the connection to be established.
+    template<class H> void async_connect(const Endpoint& ep, H handler);
+
+    /// @{ \brief Perform an asynchronous read operation.
+    ///
+    /// Initiate an asynchronous buffered read operation on the associated
+    /// socket. The completion handler will be called when the operation
+    /// completes, or an error occurs.
+    ///
+    /// async_read() will continue reading until the specified buffer is full,
+    /// or an error occurs. If the end of input is reached before the buffer is
+    /// filled, the operation fails with `network::end_of_input`.
+    ///
+    /// async_read_until() will continue reading until the specified buffer
+    /// contains the specified delimiter, or an error occurs. If the buffer is
+    /// filled before a delimiter is found, the operation fails with
+    /// `network::delim_not_found`. Otherwise, if the end of input is reached
+    /// before a delimiter is found, the operation fails with
+    /// `network::end_of_input`. Otherwise, if the operation succeeds, the last
+    /// byte placed in the buffer is the delimiter.
+    ///
+    /// The versions that take a ReadAheadBuffer argument will read through that
+    /// buffer. This allows for fewer larger reads on the underlying
+    /// socket. Since unconsumed data may be left in the read-ahead buffer after
+    /// a read operation completes, it is important that the same read-ahead
+    /// buffer is passed to the next read operation.
+    ///
+    /// The completion handler is always executed by the event loop thread,
+    /// i.e., by a thread that is executing Service::run(). Conversely, the
+    /// completion handler is guaranteed to not be called while no thread is
+    /// executing Service::run(). The execution of the completion handler is
+    /// always deferred to the event loop, meaning that it never happens as a
+    /// synchronous side effect of the execution of async_read() or
+    /// async_read_until(), even when async_read() or async_read_until() is
+    /// executed by the event loop thread. The completion handler is guaranteed
+    /// to be called eventually, as long as there is time enough for the
+    /// operation to complete or fail, and a thread is executing Service::run()
+    /// for long enough.
+    ///
+    /// The operation can be canceled by calling cancel() on the associated
+    /// socket, and will be automatically canceled if the associated socket is
+    /// closed. If the operation is canceled, it will fail with
+    /// `error::operation_aborted`. The operation remains cancelable up until
+    /// the point in time where the completion handler starts to execute. This
+    /// means that if cancel() is called before the completion handler starts to
+    /// execute, then the completion handler is guaranteed to have
+    /// `error::operation_aborted` passed to it. This is true regardless of
+    /// whether cancel() is called explicitly or implicitly, such as when the
+    /// socket is destroyed.
+    ///
+    /// The specified handler will be executed by an expression on the form
+    /// `handler(ec, n)` where `ec` is the error code, and `n` is the number of
+    /// bytes placed in the buffer (of type `std::size_t`). `n` is guaranteed to
+    /// be less than, or equal to \a size. If the the handler object is movable,
+    /// it will never be copied. Otherwise, it will be copied as necessary.
+    ///
+    /// It is an error to start a read operation before the associated socket is
+    /// connected.
+    ///
+    /// It is an error to start a new read operation (synchronous or
+    /// asynchronous) while an asynchronous read operation is in progress. An
+    /// asynchronous read operation is considered complete as soon as the
+    /// completion handler starts executing. This means that a new read
+    /// operation can be started from the completion handler of another
+    /// asynchronous buffered read operation.
+    template<class H> void async_read(char* buffer, std::size_t size, H handler);
+    template<class H> void async_read(char* buffer, std::size_t size, ReadAheadBuffer&, H handler);
+    template<class H> void async_read_until(char* buffer, std::size_t size, char delim,
+                                            ReadAheadBuffer&, H handler);
+    /// @}
+
+    /// \brief Perform an asynchronous write operation.
+    ///
+    /// Initiate an asynchronous write operation. The completion handler is
+    /// called when the operation completes. The operation completes when all
+    /// the specified bytes have been written to the socket, or an error occurs.
+    ///
+    /// The completion handler is always executed by the event loop thread,
+    /// i.e., by a thread that is executing Service::run(). Conversely, the
+    /// completion handler is guaranteed to not be called while no thread is
+    /// executing Service::run(). The execution of the completion handler is
+    /// always deferred to the event loop, meaning that it never happens as a
+    /// synchronous side effect of the execution of async_write(), even when
+    /// async_write() is executed by the event loop thread. The completion
+    /// handler is guaranteed to be called eventually, as long as there is time
+    /// enough for the operation to complete or fail, and a thread is executing
+    /// Service::run() for long enough.
+    ///
+    /// The operation can be canceled by calling cancel(), and will be
+    /// automatically canceled if the socket is closed. If the operation is
+    /// canceled, it will fail with `error::operation_aborted`. The operation
+    /// remains cancelable up until the point in time where the completion
+    /// handler starts to execute. This means that if cancel() is called before
+    /// the completion handler starts to execute, then the completion handler is
+    /// guaranteed to have `error::operation_aborted` passed to it. This is true
+    /// regardless of whether cancel() is called explicitly or implicitly, such
+    /// as when the socket is destroyed.
+    ///
+    /// The specified handler will be executed by an expression on the form
+    /// `handler(ec, n)` where `ec` is the error code, and `n` is the number of
+    /// bytes written (of type `std::size_t`). If the the handler object is
+    /// movable, it will never be copied. Otherwise, it will be copied as
+    /// necessary.
+    ///
+    /// It is an error to start an asynchronous write operation before the
+    /// socket is connected.
+    ///
+    /// It is an error to start a new write operation (synchronous or
+    /// asynchronous) while an asynchronous write operation is in progress. An
+    /// asynchronous write operation is considered complete as soon as the
+    /// completion handler starts to execute. This means that a new write
+    /// operation can be started from the completion handler of another
+    /// asynchronous write operation.
+    template<class H> void async_write(const char* data, std::size_t size, H handler);
+
+    template<class H> void async_read_some(char* buffer, std::size_t size, H handler);
+    template<class H> void async_write_some(const char* data, std::size_t size, H handler);
+
+    enum shutdown_type {
+#ifdef _WIN32
+        /// Shutdown the receiving side of the socket.
+        shutdown_receive = SD_RECEIVE,
+
+        /// Shutdown the sending side of the socket.
+        shutdown_send = SD_SEND,
+
+        /// Shutdown both sending and receiving side of the socket.
+        shutdown_both = SD_BOTH
+#else
+        shutdown_receive = SHUT_RD,
+        shutdown_send = SHUT_WR,
+        shutdown_both = SHUT_RDWR
+#endif
+    };
+
+    /// @{ \brief Shut down the connected sockets sending and/or receiving
+    /// side.
+    ///
+    /// It is an error to call this function when the socket is not both open
+    /// and connected.
+    void shutdown(shutdown_type);
+    std::error_code shutdown(shutdown_type, std::error_code&);
+    /// @}
+
+    /// @{ \brief Initialize socket with an already-connected native socket
+    /// handle.
+    ///
+    /// The specified native handle must refer to a socket that is already fully
+    /// open and connected.
+    ///
+    /// If the assignment operation succeeds, this socket object has taken
+    /// ownership of the specified native handle, and the handle will be closed
+    /// when the socket object is destroyed, (or when close() is called). If the
+    /// operation fails, the caller still owns the specified native handle.
+    ///
+    /// It is an error to call connect() or async_connect() on a socket object
+    /// that is initialized this way (unless it is first closed).
+    ///
+    /// It is an error to call this function on a socket object that is already
+    /// open.
+    void assign(const StreamProtocol&, native_handle_type);
+    std::error_code assign(const StreamProtocol&, native_handle_type, std::error_code&);
+    /// @}
+
+    /// Returns a reference to this socket, as this socket is the lowest layer
+    /// of a stream.
+    Socket& lowest_layer() noexcept;
+
+private:
+    using Want = Service::Want;
+    using StreamOps = Service::BasicStreamOps<Socket>;
+
+    class ConnectOperBase;
+    template<class H> class ConnectOper;
+
+    using LendersConnectOperPtr = std::unique_ptr<ConnectOperBase, Service::LendersOperDeleter>;
+
+    // `ec` untouched on success, but no immediate completion
+    bool initiate_async_connect(const Endpoint&, std::error_code& ec);
+    // `ec` untouched on success
+    std::error_code finalize_async_connect(std::error_code& ec) noexcept;
+
+    // See Service::BasicStreamOps for details on these these 6 functions.
+    void do_init_read_async(std::error_code&, Want&) noexcept;
+    void do_init_write_async(std::error_code&, Want&) noexcept;
+    std::size_t do_read_some_sync(char* buffer, std::size_t size,
+                                  std::error_code&) noexcept;
+    std::size_t do_write_some_sync(const char* data, std::size_t size,
+                                   std::error_code&) noexcept;
+    std::size_t do_read_some_async(char* buffer, std::size_t size,
+                                   std::error_code&, Want&) noexcept;
+    std::size_t do_write_some_async(const char* data, std::size_t size,
+                                    std::error_code&, Want&) noexcept;
+
+    friend class Service::BasicStreamOps<Socket>;
+    friend class Service::BasicStreamOps<ssl::Stream>;
+    friend class ReadAheadBuffer;
+    friend class ssl::Stream;
+};
+
+
+/// Switching between synchronous and asynchronous operations is allowed, but
+/// only in a nonoverlapping fashion. That is, a synchronous operation is not
+/// allowed to run concurrently with an asynchronous one on the same
+/// acceptor. Note that an asynchronous operation is considered to be running
+/// until its completion handler starts executing.
+class Acceptor: public SocketBase {
+public:
+    Acceptor(Service&);
+    ~Acceptor() noexcept;
+
+    static constexpr int max_connections = SOMAXCONN;
+
+    void listen(int backlog = max_connections);
+    std::error_code listen(int backlog, std::error_code&);
+
+    void accept(Socket&);
+    void accept(Socket&, Endpoint&);
+    std::error_code accept(Socket&, std::error_code&);
+    std::error_code accept(Socket&, Endpoint&, std::error_code&);
+
+    /// @{ \brief Perform an asynchronous accept operation.
+    ///
+    /// Initiate an asynchronous accept operation. The completion handler will
+    /// be called when the operation completes. The operation completes when the
+    /// connection is accepted, or an error occurs. If the operation succeeds,
+    /// the specified local socket will have become connected to a remote
+    /// socket.
+    ///
+    /// The completion handler is always executed by the event loop thread,
+    /// i.e., by a thread that is executing Service::run(). Conversely, the
+    /// completion handler is guaranteed to not be called while no thread is
+    /// executing Service::run(). The execution of the completion handler is
+    /// always deferred to the event loop, meaning that it never happens as a
+    /// synchronous side effect of the execution of async_accept(), even when
+    /// async_accept() is executed by the event loop thread. The completion
+    /// handler is guaranteed to be called eventually, as long as there is time
+    /// enough for the operation to complete or fail, and a thread is executing
+    /// Service::run() for long enough.
+    ///
+    /// The operation can be canceled by calling cancel(), and will be
+    /// automatically canceled if the acceptor is closed. If the operation is
+    /// canceled, it will fail with `error::operation_aborted`. The operation
+    /// remains cancelable up until the point in time where the completion
+    /// handler starts to execute. This means that if cancel() is called before
+    /// the completion handler starts to execute, then the completion handler is
+    /// guaranteed to have `error::operation_aborted` passed to it. This is true
+    /// regardless of whether cancel() is called explicitly or implicitly, such
+    /// as when the acceptor is destroyed.
+    ///
+    /// The specified handler will be executed by an expression on the form
+    /// `handler(ec)` where `ec` is the error code. If the the handler object is
+    /// movable, it will never be copied. Otherwise, it will be copied as
+    /// necessary.
+    ///
+    /// It is an error to start a new accept operation (synchronous or
+    /// asynchronous) while an asynchronous accept operation is in progress. An
+    /// asynchronous accept operation is considered complete as soon as the
+    /// completion handler starts executing. This means that a new accept
+    /// operation can be started from the completion handler.
+    ///
+    /// \param sock This is the local socket, that upon successful completion
+    /// will have become connected to the remote socket. It must be in the
+    /// closed state (Socket::is_open()) when async_accept() is called.
+    ///
+    /// \param ep Upon completion, the remote peer endpoint will have been
+    /// assigned to this variable.
+    template<class H> void async_accept(Socket& sock, H handler);
+    template<class H> void async_accept(Socket& sock, Endpoint& ep, H handler);
+    /// @}
+
+private:
+    using Want = Service::Want;
+
+    class AcceptOperBase;
+    template<class H> class AcceptOper;
+
+    using LendersAcceptOperPtr = std::unique_ptr<AcceptOperBase, Service::LendersOperDeleter>;
+
+    std::error_code accept(Socket&, Endpoint*, std::error_code&);
+    Want do_accept_async(Socket&, Endpoint*, std::error_code&) noexcept;
+
+    template<class H> void async_accept(Socket&, Endpoint*, H);
+};
+
+
+/// \brief A timer object supporting asynchronous wait operations.
+class DeadlineTimer {
+public:
+    DeadlineTimer(Service&);
+    ~DeadlineTimer() noexcept;
+
+    /// Thread-safe.
+    Service& get_service() noexcept;
+
+    /// \brief Perform an asynchronous wait operation.
+    ///
+    /// Initiate an asynchronous wait operation. The completion handler becomes
+    /// ready to execute when the expiration time is reached, or an error occurs
+    /// (cancellation counts as an error here). The expiration time is the time
+    /// of initiation plus the specified delay. The error code passed to the
+    /// complition handler will **never** indicate success, unless the
+    /// expiration time was reached.
+    ///
+    /// The completion handler is always executed by the event loop thread,
+    /// i.e., by a thread that is executing Service::run(). Conversely, the
+    /// completion handler is guaranteed to not be called while no thread is
+    /// executing Service::run(). The execution of the completion handler is
+    /// always deferred to the event loop, meaning that it never happens as a
+    /// synchronous side effect of the execution of async_wait(), even when
+    /// async_wait() is executed by the event loop thread. The completion
+    /// handler is guaranteed to be called eventually, as long as there is time
+    /// enough for the operation to complete or fail, and a thread is executing
+    /// Service::run() for long enough.
+    ///
+    /// The operation can be canceled by calling cancel(), and will be
+    /// automatically canceled if the timer is destroyed. If the operation is
+    /// canceled, it will fail with `error::operation_aborted`. The operation
+    /// remains cancelable up until the point in time where the completion
+    /// handler starts to execute. This means that if cancel() is called before
+    /// the completion handler starts to execute, then the completion handler is
+    /// guaranteed to have `error::operation_aborted` passed to it. This is true
+    /// regardless of whether cancel() is called explicitly or implicitly, such
+    /// as when the timer is destroyed.
+    ///
+    /// The specified handler will be executed by an expression on the form
+    /// `handler(ec)` where `ec` is the error code. If the the handler object is
+    /// movable, it will never be copied. Otherwise, it will be copied as
+    /// necessary.
+    ///
+    /// It is an error to start a new asynchronous wait operation while an
+    /// another one is in progress. An asynchronous wait operation is in
+    /// progress until its completion handler starts executing.
+    template<class R, class P, class H>
+    void async_wait(std::chrono::duration<R,P> delay, H handler);
+
+    /// \brief Cancel an asynchronous wait operation.
+    ///
+    /// If an asynchronous wait operation, that is associated with this deadline
+    /// timer, is in progress, cause it to fail with
+    /// `error::operation_aborted`. An asynchronous wait operation is in
+    /// progress until its completion handler starts executing.
+    ///
+    /// Completion handlers of canceled operations will become immediately ready
+    /// to execute, but will never be executed directly as part of the execution
+    /// of cancel().
+    ///
+    /// Cancellation happens automatically when the timer object is destroyed.
+    void cancel() noexcept;
+
+private:
+    template<class H> class WaitOper;
+
+    using clock = Service::clock;
+
+    Service::Impl& m_service_impl;
+    Service::OwnersOperPtr m_wait_oper;
+
+    void add_oper(Service::LendersWaitOperPtr);
+};
+
+
+/// \brief Register a function whose invocation can be triggered repeatedly.
+///
+/// While the function is always executed by the event loop thread, the
+/// triggering of its execution can be done by any thread, and the triggering
+/// operation is guaranteed to never throw.
+///
+/// The function is guaranteed to not be called after the Trigger object is
+/// destroyed. It is safe, though, to destroy the Trigger object during the
+/// execution of the function.
+///
+/// Note that even though the trigger() function is thread-safe, the Trigger
+/// object, as a whole, is not. In particular, construction and destruction must
+/// not be considered thread-safe.
+///
+/// ### Relation to post()
+///
+/// For a particular execution of trigger() and a particular invocation of
+/// Service::post(), if the execution of trigger() ends before the execution of
+/// Service::post() begins, then it is guaranteed that the function associated
+/// with the trigger gets to execute at least once after the execution of
+/// trigger() begins, and before the post handler gets to execute.
+class Trigger {
+public:
+    template<class F> Trigger(Service&, F func);
+    ~Trigger() noexcept;
+
+    Trigger() noexcept = default;
+    Trigger(Trigger&&) noexcept = default;
+    Trigger& operator=(Trigger&&) noexcept = default;
+
+    /// \brief Trigger another invocation of the associated function.
+    ///
+    /// An invocation of trigger() puts the Trigger object into the triggered
+    /// state. It remains in the triggered state until shortly before the
+    /// function starts to execute. While the Trigger object is in the triggered
+    /// state, trigger() has no effect. This means that the number of executions
+    /// of the function will generally be less that the number of times
+    /// trigger() is invoked().
+    ///
+    /// A particular invocation of trigger() ensures that there will be at least
+    /// one invocation of the associated function whose execution begins after
+    /// the beginning of the execution of trigger(), so long as the event loop
+    /// thread does not exit prematurely from run().
+    ///
+    /// If trigger() is invoked from the event loop thread, the next execution
+    /// of the associated function will not begin until after trigger returns(),
+    /// effectively preventing reentrancy for the associated function.
+    ///
+    /// If trigger() is invoked from another thread, the associated function may
+    /// start to execute before trigger() returns.
+    ///
+    /// Note that the associated function can retrigger itself, i.e., if the
+    /// associated function calls trigger(), then that will lead to another
+    /// invocation of the associated function, but not until the first
+    /// invocation ends (no reentrance).
+    ///
+    /// This function is thread-safe.
+    void trigger() noexcept;
+
+private:
+    template<class H> class ExecOper;
+
+    util::bind_ptr<Service::TriggerExecOperBase> m_exec_oper;
+};
+
+
+class ReadAheadBuffer {
+public:
+    ReadAheadBuffer();
+
+    /// Discard any buffered data.
+    void clear() noexcept;
+
+private:
+    using Want = Service::Want;
+
+    char* m_begin = nullptr;
+    char* m_end   = nullptr;
+    static constexpr std::size_t s_size = 1024;
+    const std::unique_ptr<char[]> m_buffer;
+
+    bool empty() const noexcept;
+    bool read(char*& begin, char* end, int delim, std::error_code&) noexcept;
+    template<class S> void refill_sync(S& stream, std::error_code&) noexcept;
+    template<class S> bool refill_async(S& stream, std::error_code&, Want&) noexcept;
+
+    template<class> friend class Service::BasicStreamOps;
+};
+
+
+enum errors {
+    /// End of input.
+    end_of_input = 1,
+
+    /// Delimiter not found.
+    delim_not_found,
+
+    /// Host not found (authoritative).
+    host_not_found,
+
+    /// Host not found (non-authoritative).
+    host_not_found_try_again,
+
+    /// The query is valid but does not have associated address data.
+    no_data,
+
+    /// A non-recoverable error occurred.
+    no_recovery,
+
+    /// The service is not supported for the given socket type.
+    service_not_found,
+
+    /// The socket type is not supported.
+    socket_type_not_supported,
+
+    /// Premature end of input (e.g., end of input before reception of SSL
+    /// shutdown alert).
+    premature_end_of_input
+};
+
+std::error_code make_error_code(errors);
+
+} // namespace network
+} // namespace util
+} // namespace realm
+
+namespace std {
+
+template<> class is_error_code_enum<realm::util::network::errors> {
+public:
+    static const bool value = true;
+};
+
+} // namespace std
+
+namespace realm {
+namespace util {
+namespace network {
+
+
+
+
+
+// Implementation
+
+// ---------------- StreamProtocol ----------------
+
+inline StreamProtocol StreamProtocol::ip_v4()
+{
+    StreamProtocol prot;
+    prot.m_family = AF_INET;
+    return prot;
+}
+
+inline StreamProtocol StreamProtocol::ip_v6()
+{
+    StreamProtocol prot;
+    prot.m_family = AF_INET6;
+    return prot;
+}
+
+inline bool StreamProtocol::is_ip_v4() const
+{
+    return m_family == AF_INET;
+}
+
+inline bool StreamProtocol::is_ip_v6() const
+{
+    return m_family == AF_INET6;
+}
+
+inline int StreamProtocol::family() const
+{
+    return m_family;
+}
+
+inline int StreamProtocol::protocol() const
+{
+    return m_protocol;
+}
+
+inline StreamProtocol::StreamProtocol():
+    m_family{AF_UNSPEC},     // Allow both IPv4 and IPv6
+    m_socktype{SOCK_STREAM}, // Or SOCK_DGRAM for UDP
+    m_protocol{0}            // Any protocol
+{
+}
+
+// ---------------- Address ----------------
+
+inline bool Address::is_ip_v4() const
+{
+    return !m_is_ip_v6;
+}
+
+inline bool Address::is_ip_v6() const
+{
+    return m_is_ip_v6;
+}
+
+template<class C, class T>
+inline std::basic_ostream<C,T>& operator<<(std::basic_ostream<C,T>& out, const Address& addr)
+{
+    // FIXME: Not taking `addr.m_ip_v6_scope_id` into account. What does ASIO
+    // do?
+    union buffer_union {
+        char ip_v4[INET_ADDRSTRLEN];
+        char ip_v6[INET6_ADDRSTRLEN];
+    };
+    char buffer[sizeof (buffer_union)];
+    int af = addr.m_is_ip_v6 ? AF_INET6 : AF_INET;
+#ifdef _WIN32
+    void* src = const_cast<void*>(reinterpret_cast<const void*>(&addr.m_union));
+#else
+    const void* src = &addr.m_union;
+#endif
+    const char* ret = ::inet_ntop(af, src, buffer, sizeof buffer);
+    if (ret == 0) {
+        std::error_code ec = make_basic_system_error_code(errno);
+        throw std::system_error(ec);
+    }
+    out << ret;
+    return out;
+}
+
+inline Address::Address()
+{
+    m_union.m_ip_v4 = ip_v4_type();
+}
+
+inline Address make_address(const char* c_str)
+{
+    std::error_code ec;
+    Address addr = make_address(c_str, ec);
+    if (ec)
+        throw std::system_error(ec);
+    return addr;
+}
+
+inline Address make_address(const std::string& str)
+{
+    std::error_code ec;
+    Address addr = make_address(str, ec);
+    if (ec)
+        throw std::system_error(ec);
+    return addr;
+}
+
+inline Address make_address(const std::string& str, std::error_code& ec) noexcept
+{
+    return make_address(str.c_str(), ec);
+}
+
+// ---------------- Endpoint ----------------
+
+inline StreamProtocol Endpoint::protocol() const
+{
+    return m_protocol;
+}
+
+inline Address Endpoint::address() const
+{
+    Address addr;
+    if (m_protocol.is_ip_v4()) {
+        addr.m_union.m_ip_v4 = m_sockaddr_union.m_ip_v4.sin_addr;
+    }
+    else {
+        addr.m_union.m_ip_v6 = m_sockaddr_union.m_ip_v6.sin6_addr;
+        addr.m_ip_v6_scope_id = m_sockaddr_union.m_ip_v6.sin6_scope_id;
+        addr.m_is_ip_v6 = true;
+    }
+    return addr;
+}
+
+inline Endpoint::port_type Endpoint::port() const
+{
+    return ntohs(m_protocol.is_ip_v4() ? m_sockaddr_union.m_ip_v4.sin_port :
+                 m_sockaddr_union.m_ip_v6.sin6_port);
+}
+
+inline Endpoint::data_type* Endpoint::data()
+{
+    return &m_sockaddr_union.m_base;
+}
+
+inline const Endpoint::data_type* Endpoint::data() const
+{
+    return &m_sockaddr_union.m_base;
+}
+
+inline Endpoint::Endpoint():
+    Endpoint{StreamProtocol::ip_v4(), 0}
+{
+}
+
+inline Endpoint::Endpoint(const StreamProtocol& protocol, port_type port):
+    m_protocol{protocol}
+{
+    int family = m_protocol.family();
+    if (family == AF_INET) {
+        m_sockaddr_union.m_ip_v4 = sockaddr_ip_v4_type(); // Clear
+        m_sockaddr_union.m_ip_v4.sin_family = AF_INET;
+        m_sockaddr_union.m_ip_v4.sin_port = htons(port);
+        m_sockaddr_union.m_ip_v4.sin_addr.s_addr = INADDR_ANY;
+    }
+    else if (family == AF_INET6) {
+        m_sockaddr_union.m_ip_v6 = sockaddr_ip_v6_type(); // Clear
+        m_sockaddr_union.m_ip_v6.sin6_family = AF_INET6;
+        m_sockaddr_union.m_ip_v6.sin6_port = htons(port);
+    }
+    else {
+        m_sockaddr_union.m_ip_v4 = sockaddr_ip_v4_type(); // Clear
+        m_sockaddr_union.m_ip_v4.sin_family = AF_UNSPEC;
+        m_sockaddr_union.m_ip_v4.sin_port = htons(port);
+        m_sockaddr_union.m_ip_v4.sin_addr.s_addr = INADDR_ANY;
+    }
+}
+
+inline Endpoint::Endpoint(const Address& addr, port_type port)
+{
+    if (addr.m_is_ip_v6) {
+        m_protocol = StreamProtocol::ip_v6();
+        m_sockaddr_union.m_ip_v6.sin6_family = AF_INET6;
+        m_sockaddr_union.m_ip_v6.sin6_port = htons(port);
+        m_sockaddr_union.m_ip_v6.sin6_flowinfo = 0;
+        m_sockaddr_union.m_ip_v6.sin6_addr = addr.m_union.m_ip_v6;
+        m_sockaddr_union.m_ip_v6.sin6_scope_id = addr.m_ip_v6_scope_id;
+    }
+    else {
+        m_protocol = StreamProtocol::ip_v4();
+        m_sockaddr_union.m_ip_v4.sin_family = AF_INET;
+        m_sockaddr_union.m_ip_v4.sin_port = htons(port);
+        m_sockaddr_union.m_ip_v4.sin_addr = addr.m_union.m_ip_v4;
+    }
+}
+
+inline Endpoint::List::iterator Endpoint::List::begin() const noexcept
+{
+    return m_endpoints.data();
+}
+
+inline Endpoint::List::iterator Endpoint::List::end() const noexcept
+{
+    return m_endpoints.data() + m_endpoints.size();
+}
+
+inline std::size_t Endpoint::List::size() const noexcept
+{
+    return m_endpoints.size();
+}
+
+inline bool Endpoint::List::empty() const noexcept
+{
+    return m_endpoints.size() == 0;
+}
+
+// ---------------- Service::OperQueue ----------------
+
+template<class Oper> inline bool Service::OperQueue<Oper>::empty() const noexcept
+{
+    return !m_back;
+}
+
+template<class Oper> inline void Service::OperQueue<Oper>::push_back(LendersOperPtr op) noexcept
+{
+    REALM_ASSERT(!op->m_next);
+    if (m_back) {
+        op->m_next = m_back->m_next;
+        m_back->m_next = op.get();
+    }
+    else {
+        op->m_next = op.get();
+    }
+    m_back = op.release();
+}
+
+template<class Oper> template<class Oper2>
+inline void Service::OperQueue<Oper>::push_back(OperQueue<Oper2>& q) noexcept
+{
+    if (!q.m_back)
+        return;
+    if (m_back)
+        std::swap(m_back->m_next, q.m_back->m_next);
+    m_back = q.m_back;
+    q.m_back = nullptr;
+}
+
+template<class Oper> inline auto Service::OperQueue<Oper>::pop_front() noexcept -> LendersOperPtr
+{
+    Oper* op = nullptr;
+    if (m_back) {
+        op = static_cast<Oper*>(m_back->m_next);
+        if (op != m_back) {
+            m_back->m_next = op->m_next;
+        }
+        else {
+            m_back = nullptr;
+        }
+        op->m_next = nullptr;
+    }
+    return LendersOperPtr(op);
+}
+
+template<class Oper> inline void Service::OperQueue<Oper>::clear() noexcept
+{
+    if (m_back) {
+        LendersOperPtr op(m_back);
+        while (op->m_next != m_back)
+            op.reset(static_cast<Oper*>(op->m_next));
+        m_back = nullptr;
+    }
+}
+
+template<class Oper> inline Service::OperQueue<Oper>::OperQueue(OperQueue&& q) noexcept:
+    m_back{q.m_back}
+{
+    q.m_back = nullptr;
+}
+
+template<class Oper> inline Service::OperQueue<Oper>::~OperQueue() noexcept
+{
+    clear();
+}
+
+// ---------------- Service::Descriptor ----------------
+
+inline Service::Descriptor::Descriptor(Impl& s) noexcept:
+    service_impl{s}
+{
+}
+
+inline Service::Descriptor::~Descriptor() noexcept
+{
+    if (is_open())
+        close();
+}
+
+inline void Service::Descriptor::assign(native_handle_type fd, bool in_blocking_mode) noexcept
+{
+    REALM_ASSERT(!is_open());
+    m_fd = fd;
+    m_in_blocking_mode = in_blocking_mode;
+#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE
+    m_read_ready  = false;
+    m_write_ready = false;
+    m_imminent_end_of_input = false;
+    m_is_registered = false;
+#endif
+}
+
+inline void Service::Descriptor::close() noexcept
+{
+    REALM_ASSERT(is_open());
+#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE
+    if (m_is_registered)
+        deregister_for_async();
+    m_is_registered = false;
+#endif
+    do_close();
+}
+
+inline bool Service::Descriptor::is_open() const noexcept
+{
+    return (m_fd != -1);
+}
+
+inline auto Service::Descriptor::native_handle() const noexcept -> native_handle_type
+{
+    return m_fd;
+}
+
+inline bool Service::Descriptor::in_blocking_mode() const noexcept
+{
+    return m_in_blocking_mode;
+}
+
+template<class Oper, class... Args>
+inline void Service::Descriptor::initiate_oper(std::unique_ptr<Oper, LendersOperDeleter> op,
+                                               Args&&... args)
+{
+    Service::Want want = op->initiate(std::forward<Args>(args)...); // Throws
+    add_initiated_oper(std::move(op), want); // Throws
+}
+
+inline void Service::Descriptor::ensure_blocking_mode()
+{
+    // Assuming that descriptors are either used mostly in blocking mode, or
+    // mostly in nonblocking mode.
+    if (REALM_UNLIKELY(!m_in_blocking_mode)) {
+        bool value = false;
+        set_nonblock_flag(value); // Throws
+        m_in_blocking_mode = true;
+    }
+}
+
+inline void Service::Descriptor::ensure_nonblocking_mode()
+{
+    // Assuming that descriptors are either used mostly in blocking mode, or
+    // mostly in nonblocking mode.
+    if (REALM_UNLIKELY(m_in_blocking_mode)) {
+        bool value = true;
+        set_nonblock_flag(value); // Throws
+        m_in_blocking_mode = false;
+    }
+}
+
+inline bool Service::Descriptor::assume_read_would_block() const noexcept
+{
+#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE
+    return !m_in_blocking_mode && !m_read_ready;
+#else
+    return false;
+#endif
+}
+
+inline bool Service::Descriptor::assume_write_would_block() const noexcept
+{
+#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE
+    return !m_in_blocking_mode && !m_write_ready;
+#else
+    return false;
+#endif
+}
+
+inline void Service::Descriptor::set_read_ready(bool value) noexcept
+{
+#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE
+    m_read_ready = value;
+#else
+    // No-op
+    static_cast<void>(value);
+#endif
+}
+
+inline void Service::Descriptor::set_write_ready(bool value) noexcept
+{
+#if REALM_HAVE_EPOLL || REALM_HAVE_KQUEUE
+    m_write_ready = value;
+#else
+    // No-op
+    static_cast<void>(value);
+#endif
+}
+
+// ---------------- Service ----------------
+
+class Service::AsyncOper {
+public:
+    bool in_use() const noexcept;
+    bool is_complete() const noexcept;
+    bool is_canceled() const noexcept;
+    void cancel() noexcept;
+    /// Every object of type \ref AsyncOper must be destroyed either by a call
+    /// to this function or to recycle(). This function recycles the operation
+    /// object (commits suicide), even if it throws.
+    virtual void recycle_and_execute() = 0;
+    /// Every object of type \ref AsyncOper must be destroyed either by a call
+    /// to recycle_and_execute() or to this function. This function destroys the
+    /// object (commits suicide).
+    virtual void recycle() noexcept = 0;
+    /// Must be called when the owner dies, and the object is in use (not an
+    /// instance of UnusedOper).
+    virtual void orphan()  noexcept = 0;
+protected:
+    AsyncOper(std::size_t size, bool in_use) noexcept;
+    virtual ~AsyncOper() noexcept {}
+    void set_is_complete(bool value) noexcept;
+    template<class H, class... Args>
+    void do_recycle_and_execute(bool orphaned, H& handler, Args&&...);
+    void do_recycle(bool orphaned) noexcept;
+private:
+    std::size_t m_size; // Allocated number of bytes
+    bool m_in_use = false;
+    // Set to true when the operation completes successfully or fails. If the
+    // operation is canceled before this happens, it will never be set to
+    // true. Always false when not in use
+    bool m_complete = false;
+    // Set to true when the operation is canceled. Always false when not in use.
+    bool m_canceled = false;
+    AsyncOper* m_next = nullptr; // Always null when not in use
+    template<class H, class... Args>
+    void do_recycle_and_execute_helper(bool orphaned, bool& was_recycled, H handler, Args...);
+    friend class Service;
+};
+
+class Service::WaitOperBase: public AsyncOper {
+public:
+    WaitOperBase(std::size_t size, DeadlineTimer& timer,
+                 clock::time_point expiration_time) noexcept:
+        AsyncOper{size, true}, // Second argument is `in_use`
+        m_timer{&timer},
+        m_expiration_time{expiration_time}
+    {
+    }
+    void expired() noexcept
+    {
+        set_is_complete(true);
+    }
+    void recycle() noexcept override final
+    {
+        bool orphaned = !m_timer;
+        REALM_ASSERT(orphaned);
+        // Note: do_recycle() commits suicide.
+        do_recycle(orphaned);
+    }
+    void orphan() noexcept override final
+    {
+        m_timer = nullptr;
+    }
+protected:
+    DeadlineTimer* m_timer;
+    clock::time_point m_expiration_time;
+    friend class Service;
+};
+
+class Service::TriggerExecOperBase: public AsyncOper, public AtomicRefCountBase {
+public:
+    TriggerExecOperBase(Impl& service) noexcept:
+        AsyncOper{0, false}, // First arg is `size` (unused), second arg is `in_use`
+        m_service{&service}
+    {
+    }
+    void recycle() noexcept override final
+    {
+        REALM_ASSERT(in_use());
+        REALM_ASSERT(!m_service);
+        // Note: Potential suicide when `self` goes out of scope
+        util::bind_ptr<TriggerExecOperBase> self{this, bind_ptr_base::adopt_tag{}};
+    }
+    void orphan() noexcept override final
+    {
+        REALM_ASSERT(m_service);
+        m_service = nullptr;
+    }
+    void trigger() noexcept
+    {
+        REALM_ASSERT(m_service);
+        Service::trigger_exec(*m_service, *this);
+    }
+protected:
+    Impl* m_service;
+};
+
+class Service::PostOperBase: public AsyncOper {
+public:
+    PostOperBase(std::size_t size, Impl& service) noexcept:
+        AsyncOper{size, true}, // Second argument is `in_use`
+        m_service{service}
+    {
+    }
+    void recycle() noexcept override final
+    {
+        // Service::recycle_post_oper() destroys this operation object
+        Service::recycle_post_oper(m_service, this);
+    }
+    void orphan() noexcept override final
+    {
+        REALM_ASSERT(false); // Never called
+    }
+protected:
+    Impl& m_service;
+};
+
+template<class H> class Service::PostOper: public PostOperBase {
+public:
+    PostOper(std::size_t size, Impl& service, H handler):
+        PostOperBase{size, service},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        // Recycle the operation object before the handler is exceuted, such
+        // that the memory is available for a new post operation that might be
+        // initiated during the execution of the handler.
+        bool was_recycled = false;
+        try {
+            H handler = std::move(m_handler); // Throws
+            // Service::recycle_post_oper() destroys this operation object
+            Service::recycle_post_oper(m_service, this);
+            was_recycled = true;
+            handler(); // Throws
+        }
+        catch (...) {
+            if (!was_recycled) {
+                // Service::recycle_post_oper() destroys this operation object
+                Service::recycle_post_oper(m_service, this);
+            }
+            throw;
+        }
+    }
+private:
+    H m_handler;
+};
+
+class Service::IoOper: public AsyncOper {
+public:
+    IoOper(std::size_t size) noexcept:
+        AsyncOper{size, true} // Second argument is `in_use`
+    {
+    }
+    virtual Descriptor& descriptor() noexcept = 0;
+    /// Advance this operation and figure out out whether it needs to read from,
+    /// or write to the underlying descriptor to advance further. This function
+    /// must return Want::read if the operation needs to read, or Want::write if
+    /// the operation needs to write to advance further. If the operation
+    /// completes (due to success or failure), this function must return
+    /// Want::nothing.
+    virtual Want advance() noexcept = 0;
+};
+
+class Service::UnusedOper: public AsyncOper {
+public:
+    UnusedOper(std::size_t size) noexcept:
+        AsyncOper{size, false} // Second argument is `in_use`
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        // Must never be called
+        REALM_ASSERT(false);
+    }
+    void recycle() noexcept override final
+    {
+        // Must never be called
+        REALM_ASSERT(false);
+    }
+    void orphan() noexcept override final
+    {
+        // Must never be called
+        REALM_ASSERT(false);
+    }
+};
+
+// `S` must be a stream class with the following member functions:
+//
+//    Socket& lowest_layer() noexcept;
+//
+//    void do_init_read_async(std::error_code& ec, Want& want) noexcept;
+//    void do_init_write_async(std::error_code& ec, Want& want) noexcept;
+//
+//    std::size_t do_read_some_sync(char* buffer, std::size_t size,
+//                                  std::error_code& ec) noexcept;
+//    std::size_t do_write_some_sync(const char* data, std::size_t size,
+//                                   std::error_code& ec) noexcept;
+//    std::size_t do_read_some_async(char* buffer, std::size_t size,
+//                                   std::error_code& ec, Want& want) noexcept;
+//    std::size_t do_write_some_async(const char* data, std::size_t size,
+//                                    std::error_code& ec, Want& want) noexcept;
+//
+// If an error occurs during any of these 6 functions, the `ec` argument must be
+// set accordingly. Otherwise the `ec` argument must be set to
+// `std::error_code()`.
+//
+// The do_init_*_async() functions must update the `want` argument to indicate
+// how the operation must be initiated:
+//
+//    Want::read      Wait for read readiness, then call do_*_some_async().
+//    Want::write     Wait for write readiness, then call do_*_some_async().
+//    Want::nothing   Call do_*_some_async() immediately without waiting for
+//                    read or write readiness.
+//
+// If end-of-input occurs while reading, do_read_some_*() must fail, set `ec` to
+// `network::end_of_input`, and return zero.
+//
+// If an error occurs during reading or writing, do_*_some_sync() must set `ec`
+// accordingly (to something other than `std::system_error()`) and return
+// zero. Otherwise they must set `ec` to `std::system_error()` and return the
+// number of bytes read or written, which **must** be at least 1. If the
+// underlying socket is in nonblocking mode, and no bytes could be immediately
+// read or written these functions must fail with
+// `error::resource_unavailable_try_again`.
+//
+// If an error occurs during reading or writing, do_*_some_async() must set `ec`
+// accordingly (to something other than `std::system_error()`), `want` to
+// `Want::nothing`, and return zero. Otherwise they must set `ec` to
+// `std::system_error()` and return the number of bytes read or written, which
+// must be zero if no bytes could be immediately read or written. Note, in this
+// case it is not an error if the underlying socket is in nonblocking mode, and
+// no bytes could be immediately read or written. When these functions succeed,
+// but return zero because no bytes could be immediately read or written, they
+// must set `want` to something other than `Want::nothing`.
+//
+// If no error occurs, do_*_some_async() must set `want` to indicate how the
+// operation should proceed if additional data needs to be read or written, or
+// if no bytes were transferred:
+//
+//    Want::read      Wait for read readiness, then call do_*_some_async() again.
+//    Want::write     Wait for write readiness, then call do_*_some_async() again.
+//    Want::nothing   Call do_*_some_async() again without waiting for read or
+//                    write readiness.
+//
+// NOTE: If, for example, do_read_some_async() sets `want` to `Want::write`, it
+// means that the stream needs to write data to the underlying TCP socket before
+// it is able to deliver any additional data to the caller. While such a
+// situation will never occur on a raw TCP socket, it can occur on an SSL stream
+// (Secure Socket Layer).
+//
+// When do_*_some_async() returns `n`, at least one of the following conditions
+// must be true:
+//
+//    n > 0                     Bytes were transferred.
+//    ec != std::error_code()   An error occured.
+//    want != Want::nothing     Wait for read/write readiness.
+//
+// This is of critical importance, as it is the only way we can avoid falling
+// into a busy loop of repeated invocations of do_*_some_async().
+//
+// NOTE: do_*_some_async() are allowed to set `want` to `Want::read` or
+// `Want::write`, even when they succesfully transfer a nonzero number of bytes.
+template<class S> class Service::BasicStreamOps {
+public:
+    class StreamOper;
+    class ReadOperBase;
+    class WriteOperBase;
+    class BufferedReadOperBase;
+    template<class H> class ReadOper;
+    template<class H> class WriteOper;
+    template<class H> class BufferedReadOper;
+
+    using LendersReadOperPtr          = std::unique_ptr<ReadOperBase,         LendersOperDeleter>;
+    using LendersWriteOperPtr         = std::unique_ptr<WriteOperBase,        LendersOperDeleter>;
+    using LendersBufferedReadOperPtr  = std::unique_ptr<BufferedReadOperBase, LendersOperDeleter>;
+
+    // Synchronous read
+    static std::size_t read(S& stream, char* buffer, std::size_t size,
+                            std::error_code& ec)
+    {
+        REALM_ASSERT(!stream.lowest_layer().m_read_oper ||
+                     !stream.lowest_layer().m_read_oper->in_use());
+        stream.lowest_layer().m_desc.ensure_blocking_mode(); // Throws
+        char* begin = buffer;
+        char* end   = buffer + size;
+        char* curr  = begin;
+        for (;;) {
+            if (curr == end) {
+                ec = std::error_code(); // Success
+                break;
+            }
+            char* buffer_2 = curr;
+            std::size_t size_2 = std::size_t(end - curr);
+            std::size_t n = stream.do_read_some_sync(buffer_2, size_2, ec);
+            if (REALM_UNLIKELY(ec))
+                break;
+            REALM_ASSERT(n > 0);
+            REALM_ASSERT(n <= size_2);
+            curr += n;
+        }
+        std::size_t n = std::size_t(curr - begin);
+        return n;
+    }
+
+    // Synchronous write
+    static std::size_t write(S& stream, const char* data, std::size_t size,
+                             std::error_code& ec)
+    {
+        REALM_ASSERT(!stream.lowest_layer().m_write_oper ||
+                     !stream.lowest_layer().m_write_oper->in_use());
+        stream.lowest_layer().m_desc.ensure_blocking_mode(); // Throws
+        const char* begin = data;
+        const char* end   = data + size;
+        const char* curr  = begin;
+        for (;;) {
+            if (curr == end) {
+                ec = std::error_code(); // Success
+                break;
+            }
+            const char* data_2 = curr;
+            std::size_t size_2 = std::size_t(end - curr);
+            std::size_t n = stream.do_write_some_sync(data_2, size_2, ec);
+            if (REALM_UNLIKELY(ec))
+                break;
+            REALM_ASSERT(n > 0);
+            REALM_ASSERT(n <= size_2);
+            curr += n;
+        }
+        std::size_t n = std::size_t(curr - begin);
+        return n;
+    }
+
+    // Synchronous read
+    static std::size_t buffered_read(S& stream, char* buffer, std::size_t size, int delim,
+                                     ReadAheadBuffer& rab, std::error_code& ec)
+    {
+        REALM_ASSERT(!stream.lowest_layer().m_read_oper ||
+                     !stream.lowest_layer().m_read_oper->in_use());
+        stream.lowest_layer().m_desc.ensure_blocking_mode(); // Throws
+        char* begin = buffer;
+        char* end   = buffer + size;
+        char* curr  = begin;
+        for (;;) {
+            bool complete = rab.read(curr, end, delim, ec);
+            if (complete)
+                break;
+
+            rab.refill_sync(stream, ec);
+            if (REALM_UNLIKELY(ec))
+                break;
+        }
+        std::size_t n = (curr - begin);
+        return n;
+    }
+
+    // Synchronous read
+    static std::size_t read_some(S& stream, char* buffer, std::size_t size,
+                                 std::error_code& ec)
+    {
+        REALM_ASSERT(!stream.lowest_layer().m_read_oper ||
+                     !stream.lowest_layer().m_read_oper->in_use());
+        stream.lowest_layer().m_desc.ensure_blocking_mode(); // Throws
+        return stream.do_read_some_sync(buffer, size, ec);
+    }
+
+    // Synchronous write
+    static std::size_t write_some(S& stream, const char* data, std::size_t size,
+                                  std::error_code& ec)
+    {
+        REALM_ASSERT(!stream.lowest_layer().m_write_oper ||
+                     !stream.lowest_layer().m_write_oper->in_use());
+        stream.lowest_layer().m_desc.ensure_blocking_mode(); // Throws
+        return stream.do_write_some_sync(data, size, ec);
+    }
+
+    template<class H>
+    static void async_read(S& stream, char* buffer, std::size_t size, bool is_read_some, H handler)
+    {
+        char* begin = buffer;
+        char* end   = buffer + size;
+        LendersReadOperPtr op =
+            Service::alloc<ReadOper<H>>(stream.lowest_layer().m_read_oper, stream, is_read_some,
+                                        begin, end, std::move(handler)); // Throws
+        stream.lowest_layer().m_desc.initiate_oper(std::move(op)); // Throws
+    }
+
+    template<class H>
+    static void async_write(S& stream, const char* data, std::size_t size, bool is_write_some,
+                            H handler)
+    {
+        const char* begin = data;
+        const char* end   = data + size;
+        LendersWriteOperPtr op =
+            Service::alloc<WriteOper<H>>(stream.lowest_layer().m_write_oper, stream, is_write_some,
+                                         begin, end, std::move(handler)); // Throws
+        stream.lowest_layer().m_desc.initiate_oper(std::move(op)); // Throws
+    }
+
+    template<class H>
+    static void async_buffered_read(S& stream, char* buffer, std::size_t size, int delim,
+                                    ReadAheadBuffer& rab, H handler)
+    {
+        char* begin = buffer;
+        char* end   = buffer + size;
+        LendersBufferedReadOperPtr op =
+            Service::alloc<BufferedReadOper<H>>(stream.lowest_layer().m_read_oper, stream,
+                                                begin, end, delim, rab,
+                                                std::move(handler)); // Throws
+        stream.lowest_layer().m_desc.initiate_oper(std::move(op)); // Throws
+    }
+};
+
+template<class S> class Service::BasicStreamOps<S>::StreamOper: public IoOper {
+public:
+    StreamOper(std::size_t size, S& stream) noexcept:
+        IoOper{size},
+        m_stream{&stream}
+    {
+    }
+    void recycle() noexcept override final
+    {
+        bool orphaned = !m_stream;
+        REALM_ASSERT(orphaned);
+        // Note: do_recycle() commits suicide.
+        do_recycle(orphaned);
+    }
+    void orphan() noexcept override final
+    {
+        m_stream = nullptr;
+    }
+    Descriptor& descriptor() noexcept override final
+    {
+        return m_stream->lowest_layer().m_desc;
+    }
+protected:
+    S* m_stream;
+    std::error_code m_error_code;
+};
+
+template<class S> class Service::BasicStreamOps<S>::ReadOperBase: public StreamOper {
+public:
+    ReadOperBase(std::size_t size, S& stream, bool is_read_some, char* begin, char* end) noexcept:
+        StreamOper{size, stream},
+        m_is_read_some{is_read_some},
+        m_begin{begin},
+        m_end{end}
+    {
+    }
+    Want initiate()
+    {
+        auto& s = *this;
+        REALM_ASSERT(this == s.m_stream->lowest_layer().m_read_oper.get());
+        REALM_ASSERT(!s.is_complete());
+        REALM_ASSERT(s.m_curr <= s.m_end);
+        Want want = Want::nothing;
+        if (REALM_UNLIKELY(s.m_curr == s.m_end)) {
+            s.set_is_complete(true); // Success
+        }
+        else {
+            s.m_stream->lowest_layer().m_desc.ensure_nonblocking_mode(); // Throws
+            s.m_stream->do_init_read_async(s.m_error_code, want);
+            if (want == Want::nothing) {
+                if (REALM_UNLIKELY(s.m_error_code)) {
+                    s.set_is_complete(true); // Failure
+                }
+                else {
+                    want = advance();
+                }
+            }
+        }
+        return want;
+    }
+    Want advance() noexcept override final
+    {
+        auto& s = *this;
+        REALM_ASSERT(!s.is_complete());
+        REALM_ASSERT(!s.is_canceled());
+        REALM_ASSERT(!s.m_error_code);
+        REALM_ASSERT(s.m_curr < s.m_end);
+        REALM_ASSERT(!s.m_is_read_some || s.m_curr == m_begin);
+        for (;;) {
+            // Read into callers buffer
+            char* buffer = s.m_curr;
+            std::size_t size = std::size_t(s.m_end - s.m_curr);
+            Want want = Want::nothing;
+            std::size_t n = s.m_stream->do_read_some_async(buffer, size, s.m_error_code, want);
+            REALM_ASSERT(n > 0 || s.m_error_code || want != Want::nothing); // No busy loop, please
+            bool got_nothing = (n == 0);
+            if (got_nothing) {
+                if (REALM_UNLIKELY(s.m_error_code)) {
+                    s.set_is_complete(true); // Failure
+                    return Want::nothing;
+                }
+                // Got nothing, but want something
+                return want;
+            }
+            REALM_ASSERT(!s.m_error_code);
+            // Check for completion
+            REALM_ASSERT(n <= size);
+            s.m_curr += n;
+            if (s.m_is_read_some || s.m_curr == s.m_end) {
+                s.set_is_complete(true); // Success
+                return Want::nothing;
+            }
+            if (want != Want::nothing)
+                return want;
+            REALM_ASSERT(n < size);
+        }
+    }
+protected:
+    const bool m_is_read_some;
+    char* const m_begin;    // May be dangling after cancellation
+    char* const m_end;      // May be dangling after cancellation
+    char* m_curr = m_begin; // May be dangling after cancellation
+};
+
+template<class S> class Service::BasicStreamOps<S>::WriteOperBase: public StreamOper {
+public:
+    WriteOperBase(std::size_t size, S& stream, bool is_write_some,
+                  const char* begin, const char* end) noexcept:
+        StreamOper{size, stream},
+        m_is_write_some{is_write_some},
+        m_begin{begin},
+        m_end{end}
+    {
+    }
+    Want initiate()
+    {
+        auto& s = *this;
+        REALM_ASSERT(this == s.m_stream->lowest_layer().m_write_oper.get());
+        REALM_ASSERT(!s.is_complete());
+        REALM_ASSERT(s.m_curr <= s.m_end);
+        Want want = Want::nothing;
+        if (REALM_UNLIKELY(s.m_curr == s.m_end)) {
+            s.set_is_complete(true); // Success
+        }
+        else {
+            s.m_stream->lowest_layer().m_desc.ensure_nonblocking_mode(); // Throws
+            s.m_stream->do_init_write_async(s.m_error_code, want);
+            if (want == Want::nothing) {
+                if (REALM_UNLIKELY(s.m_error_code)) {
+                    s.set_is_complete(true); // Failure
+                }
+                else {
+                    want = advance();
+                }
+            }
+        }
+        return want;
+    }
+    Want advance() noexcept override final
+    {
+        auto& s = *this;
+        REALM_ASSERT(!s.is_complete());
+        REALM_ASSERT(!s.is_canceled());
+        REALM_ASSERT(!s.m_error_code);
+        REALM_ASSERT(s.m_curr < s.m_end);
+        REALM_ASSERT(!s.m_is_write_some || s.m_curr == s.m_begin);
+        for (;;) {
+            // Write from callers buffer
+            const char* data = s.m_curr;
+            std::size_t size = std::size_t(s.m_end - s.m_curr);
+            Want want = Want::nothing;
+            std::size_t n = s.m_stream->do_write_some_async(data, size, s.m_error_code, want);
+            REALM_ASSERT(n > 0 || s.m_error_code || want != Want::nothing); // No busy loop, please
+            bool wrote_nothing = (n == 0);
+            if (wrote_nothing) {
+                if (REALM_UNLIKELY(s.m_error_code)) {
+                    s.set_is_complete(true); // Failure
+                    return Want::nothing;
+                }
+                // Wrote nothing, but want something written
+                return want;
+            }
+            REALM_ASSERT(!s.m_error_code);
+            // Check for completion
+            REALM_ASSERT(n <= size);
+            s.m_curr += n;
+            if (s.m_is_write_some || s.m_curr == s.m_end) {
+                s.set_is_complete(true); // Success
+                return Want::nothing;
+            }
+            if (want != Want::nothing)
+                return want;
+            REALM_ASSERT(n < size);
+        }
+    }
+protected:
+    const bool m_is_write_some;
+    const char* const m_begin;    // May be dangling after cancellation
+    const char* const m_end;      // May be dangling after cancellation
+    const char* m_curr = m_begin; // May be dangling after cancellation
+};
+
+template<class S> class Service::BasicStreamOps<S>::BufferedReadOperBase: public StreamOper {
+public:
+    BufferedReadOperBase(std::size_t size, S& stream, char* begin, char* end, int delim,
+                         ReadAheadBuffer& rab) noexcept:
+        StreamOper{size, stream},
+        m_read_ahead_buffer{rab},
+        m_begin{begin},
+        m_end{end},
+        m_delim{delim}
+    {
+    }
+    Want initiate()
+    {
+        auto& s = *this;
+        REALM_ASSERT(this == s.m_stream->lowest_layer().m_read_oper.get());
+        REALM_ASSERT(!s.is_complete());
+        Want want = Want::nothing;
+        bool complete = s.m_read_ahead_buffer.read(s.m_curr, s.m_end, s.m_delim, s.m_error_code);
+        if (complete) {
+            s.set_is_complete(true); // Success or failure
+        }
+        else {
+            s.m_stream->lowest_layer().m_desc.ensure_nonblocking_mode(); // Throws
+            s.m_stream->do_init_read_async(s.m_error_code, want);
+            if (want == Want::nothing) {
+                if (REALM_UNLIKELY(s.m_error_code)) {
+                    s.set_is_complete(true); // Failure
+                }
+                else {
+                    want = advance();
+                }
+            }
+        }
+        return want;
+    }
+    Want advance() noexcept override final
+    {
+        auto& s = *this;
+        REALM_ASSERT(!s.is_complete());
+        REALM_ASSERT(!s.is_canceled());
+        REALM_ASSERT(!s.m_error_code);
+        REALM_ASSERT(s.m_read_ahead_buffer.empty());
+        REALM_ASSERT(s.m_curr < s.m_end);
+        for (;;) {
+            // Fill read-ahead buffer from stream (is empty now)
+            Want want = Want::nothing;
+            bool nonempty = s.m_read_ahead_buffer.refill_async(*s.m_stream, s.m_error_code, want);
+            REALM_ASSERT(nonempty || s.m_error_code ||
+                         want != Want::nothing); // No busy loop, please
+            bool got_nothing = !nonempty;
+            if (got_nothing) {
+                if (REALM_UNLIKELY(s.m_error_code)) {
+                    s.set_is_complete(true); // Failure
+                    return Want::nothing;
+                }
+                // Got nothing, but want something
+                return want;
+            }
+            // Transfer buffered data to callers buffer
+            bool complete =
+                s.m_read_ahead_buffer.read(s.m_curr, s.m_end, s.m_delim, s.m_error_code);
+            if (complete) {
+                s.set_is_complete(true); // Success or failure (delim_not_found)
+                return Want::nothing;
+            }
+            if (want != Want::nothing)
+                return want;
+        }
+    }
+protected:
+    ReadAheadBuffer& m_read_ahead_buffer; // May be dangling after cancellation
+    char* const m_begin;                  // May be dangling after cancellation
+    char* const m_end;                    // May be dangling after cancellation
+    char* m_curr = m_begin;               // May be dangling after cancellation
+    const int m_delim;
+};
+
+template<class S> template<class H>
+class Service::BasicStreamOps<S>::ReadOper: public ReadOperBase {
+public:
+    ReadOper(std::size_t size, S& stream, bool is_read_some, char* begin, char* end, H handler):
+        ReadOperBase{size, stream, is_read_some, begin, end},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        auto& s = *this;
+        REALM_ASSERT(s.is_complete() || s.is_canceled());
+        REALM_ASSERT(s.is_complete() == (s.m_error_code || s.m_curr == s.m_end ||
+                                         (s.m_is_read_some && s.m_curr != s.m_begin)));
+        REALM_ASSERT(s.m_curr >= s.m_begin);
+        bool orphaned = !s.m_stream;
+        std::error_code ec = s.m_error_code;
+        if (s.is_canceled())
+            ec = error::operation_aborted;
+        std::size_t num_bytes_transferred = std::size_t(s.m_curr - s.m_begin);
+        // Note: do_recycle_and_execute() commits suicide.
+        s.template do_recycle_and_execute<H>(orphaned, s.m_handler, ec,
+                                             num_bytes_transferred); // Throws
+    }
+private:
+    H m_handler;
+};
+
+template<class S> template<class H>
+class Service::BasicStreamOps<S>::WriteOper: public WriteOperBase {
+public:
+    WriteOper(std::size_t size, S& stream, bool is_write_some,
+              const char* begin, const char* end, H handler):
+        WriteOperBase{size, stream, is_write_some, begin, end},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        auto& s = *this;
+        REALM_ASSERT(s.is_complete() || s.is_canceled());
+        REALM_ASSERT(s.is_complete() == (s.m_error_code || s.m_curr == s.m_end ||
+                                         (s.m_is_write_some && s.m_curr != s.m_begin)));
+        REALM_ASSERT(s.m_curr >= s.m_begin);
+        bool orphaned = !s.m_stream;
+        std::error_code ec = s.m_error_code;
+        if (s.is_canceled())
+            ec = error::operation_aborted;
+        std::size_t num_bytes_transferred = std::size_t(s.m_curr - s.m_begin);
+        // Note: do_recycle_and_execute() commits suicide.
+        s.template do_recycle_and_execute<H>(orphaned, s.m_handler, ec,
+                                             num_bytes_transferred); // Throws
+    }
+private:
+    H m_handler;
+};
+
+template<class S> template<class H>
+class Service::BasicStreamOps<S>::BufferedReadOper: public BufferedReadOperBase {
+public:
+    BufferedReadOper(std::size_t size, S& stream, char* begin, char* end, int delim,
+                     ReadAheadBuffer& rab, H handler):
+        BufferedReadOperBase{size, stream, begin, end, delim, rab},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        auto& s = *this;
+        REALM_ASSERT(s.is_complete() || (s.is_canceled() && !s.m_error_code));
+        REALM_ASSERT(s.is_canceled() || s.m_error_code ||
+                     (s.m_delim != std::char_traits<char>::eof() ?
+                      s.m_curr > s.m_begin && s.m_curr[-1] ==
+                      std::char_traits<char>::to_char_type(s.m_delim) :
+                      s.m_curr == s.m_end));
+        REALM_ASSERT(s.m_curr >= s.m_begin);
+        bool orphaned = !s.m_stream;
+        std::error_code ec = s.m_error_code;
+        if (s.is_canceled())
+            ec = error::operation_aborted;
+        std::size_t num_bytes_transferred = std::size_t(s.m_curr - s.m_begin);
+        // Note: do_recycle_and_execute() commits suicide.
+        s.template do_recycle_and_execute<H>(orphaned, s.m_handler, ec,
+                                             num_bytes_transferred); // Throws
+    }
+private:
+    H m_handler;
+};
+
+template<class H> inline void Service::post(H handler)
+{
+    do_post(&Service::post_oper_constr<H>, sizeof (PostOper<H>), &handler);
+}
+
+inline void Service::OwnersOperDeleter::operator()(AsyncOper* op) const noexcept
+{
+    if (op->in_use()) {
+        op->orphan();
+    }
+    else {
+        void* addr = op;
+        op->~AsyncOper();
+        delete[] static_cast<char*>(addr);
+    }
+}
+
+inline void Service::LendersOperDeleter::operator()(AsyncOper* op) const noexcept
+{
+    op->recycle(); // Suicide
+}
+
+template<class Oper, class... Args> std::unique_ptr<Oper, Service::LendersOperDeleter>
+Service::alloc(OwnersOperPtr& owners_ptr, Args&&... args)
+{
+    void* addr = owners_ptr.get();
+    std::size_t size;
+    if (REALM_LIKELY(addr)) {
+        REALM_ASSERT(!owners_ptr->in_use());
+        size = owners_ptr->m_size;
+        // We can use static dispatch in the destructor call here, since an
+        // object, that is not in use, is always an instance of UnusedOper.
+        REALM_ASSERT(dynamic_cast<UnusedOper*>(owners_ptr.get()));
+        static_cast<UnusedOper*>(owners_ptr.get())->UnusedOper::~UnusedOper();
+        if (REALM_UNLIKELY(size < sizeof (Oper))) {
+            owners_ptr.release();
+            delete[] static_cast<char*>(addr);
+            goto no_object;
+        }
+    }
+    else {
+      no_object:
+        addr = new char[sizeof (Oper)]; // Throws
+        size = sizeof (Oper);
+        owners_ptr.reset(static_cast<AsyncOper*>(addr));
+    }
+    std::unique_ptr<Oper, LendersOperDeleter> lenders_ptr;
+    try {
+        lenders_ptr.reset(new (addr) Oper(size, std::forward<Args>(args)...)); // Throws
+    }
+    catch (...) {
+        new (addr) UnusedOper(size); // Does not throw
+        throw;
+    }
+    return lenders_ptr;
+}
+
+template<class Oper>
+inline void Service::execute(std::unique_ptr<Oper, LendersOperDeleter>& lenders_ptr)
+{
+    lenders_ptr.release()->recycle_and_execute(); // Throws
+}
+
+template<class H> inline Service::PostOperBase*
+Service::post_oper_constr(void* addr, std::size_t size, Impl& service, void* cookie)
+{
+    H& handler = *static_cast<H*>(cookie);
+    return new (addr) PostOper<H>(size, service, std::move(handler)); // Throws
+}
+
+inline bool Service::AsyncOper::in_use() const noexcept
+{
+    return m_in_use;
+}
+
+inline bool Service::AsyncOper::is_complete() const noexcept
+{
+    return m_complete;
+}
+
+inline void Service::AsyncOper::cancel() noexcept
+{
+    REALM_ASSERT(m_in_use);
+    REALM_ASSERT(!m_canceled);
+    m_canceled = true;
+}
+
+inline Service::AsyncOper::AsyncOper(std::size_t size, bool is_in_use) noexcept:
+    m_size{size},
+    m_in_use{is_in_use}
+{
+}
+
+inline bool Service::AsyncOper::is_canceled() const noexcept
+{
+    return m_canceled;
+}
+
+inline void Service::AsyncOper::set_is_complete(bool value) noexcept
+{
+    REALM_ASSERT(!m_complete);
+    REALM_ASSERT(!value || m_in_use);
+    m_complete = value;
+}
+
+template<class H, class... Args>
+inline void Service::AsyncOper::do_recycle_and_execute(bool orphaned, H& handler, Args&&... args)
+{
+    // Recycle the operation object before the handler is exceuted, such that
+    // the memory is available for a new post operation that might be initiated
+    // during the execution of the handler.
+    bool was_recycled = false;
+    try {
+        // We need to copy or move all arguments to be passed to the handler,
+        // such that there is no risk of references to the recycled operation
+        // object being passed to the handler (the passed arguments may be
+        // references to members of the recycled operation object). The easiest
+        // way to achive this, is by forwarding the reference arguments (passed
+        // to this function) to a helper function whose arguments have
+        // nonreference type (`Args...` rather than `Args&&...`).
+        //
+        // Note that the copying and moving of arguments may throw, and it is
+        // important that the operation is still recycled even if that
+        // happens. For that reason, copying and moving of arguments must not
+        // happen until we are in a scope (this scope) that catches and deals
+        // correctly with such exceptions.
+        do_recycle_and_execute_helper(orphaned, was_recycled, std::move(handler),
+                                      std::forward<Args>(args)...); // Throws
+    }
+    catch (...) {
+        if (!was_recycled)
+            do_recycle(orphaned);
+        throw;
+    }
+}
+
+template<class H, class... Args>
+inline void Service::AsyncOper::do_recycle_and_execute_helper(bool orphaned, bool& was_recycled,
+                                                              H handler, Args... args)
+{
+    do_recycle(orphaned);
+    was_recycled = true;
+    handler(std::move(args)...); // Throws
+}
+
+inline void Service::AsyncOper::do_recycle(bool orphaned) noexcept
+{
+    REALM_ASSERT(in_use());
+    void* addr = this;
+    std::size_t size = m_size;
+    this->~AsyncOper(); // Suicide
+    if (orphaned) {
+        delete[] static_cast<char*>(addr);
+    }
+    else {
+        new (addr) UnusedOper(size);
+    }
+}
+
+// ---------------- Resolver ----------------
+
+class Resolver::ResolveOperBase: public Service::AsyncOper {
+public:
+    ResolveOperBase(std::size_t size, Resolver& r, Query q) noexcept:
+        AsyncOper{size, true},
+        m_resolver{&r},
+        m_query{std::move(q)}
+    {
+    }
+    void perform()
+    {
+        // FIXME: Temporary hack until we get a true asynchronous resolver
+        m_endpoints = m_resolver->resolve(std::move(m_query), m_error_code); // Throws
+        set_is_complete(true);
+    }
+    void recycle() noexcept override final
+    {
+        bool orphaned = !m_resolver;
+        REALM_ASSERT(orphaned);
+        // Note: do_recycle() commits suicide.
+        do_recycle(orphaned);
+    }
+    void orphan() noexcept override final
+    {
+        m_resolver = nullptr;
+    }
+protected:
+    Resolver* m_resolver;
+    Query m_query;
+    Endpoint::List m_endpoints;
+    std::error_code m_error_code;
+};
+
+template<class H> class Resolver::ResolveOper: public ResolveOperBase {
+public:
+    ResolveOper(std::size_t size, Resolver& r, Query q, H handler):
+        ResolveOperBase{size, r, std::move(q)},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        REALM_ASSERT(is_complete() || (is_canceled() && !m_error_code));
+        REALM_ASSERT(is_canceled() || m_error_code || !m_endpoints.empty());
+        bool orphaned = !m_resolver;
+        std::error_code ec = m_error_code;
+        if (is_canceled())
+            ec = error::operation_aborted;
+        // Note: do_recycle_and_execute() commits suicide.
+        do_recycle_and_execute<H>(orphaned, m_handler, ec, std::move(m_endpoints)); // Throws
+    }
+private:
+    H m_handler;
+};
+
+inline Resolver::Resolver(Service& service):
+    m_service_impl{*service.m_impl}
+{
+}
+
+inline Resolver::~Resolver() noexcept
+{
+    cancel();
+}
+
+inline Endpoint::List Resolver::resolve(const Query& q)
+{
+    std::error_code ec;
+    Endpoint::List list = resolve(q, ec);
+    if (REALM_UNLIKELY(ec))
+        throw std::system_error(ec);
+    return list;
+}
+
+template<class H> void Resolver::async_resolve(Query query, H handler)
+{
+    LendersResolveOperPtr op = Service::alloc<ResolveOper<H>>(m_resolve_oper, *this,
+                                                              std::move(query),
+                                                              std::move(handler)); // Throws
+    initiate_oper(std::move(op)); // Throws
+}
+
+inline Resolver::Query::Query(std::string service_port, int init_flags):
+    m_flags{init_flags},
+    m_service{service_port}
+{
+}
+
+inline Resolver::Query::Query(const StreamProtocol& prot, std::string service_port,
+                              int init_flags):
+    m_flags{init_flags},
+    m_protocol{prot},
+    m_service{service_port}
+{
+}
+
+inline Resolver::Query::Query(std::string host_name, std::string service_port, int init_flags):
+    m_flags{init_flags},
+    m_host{host_name},
+    m_service{service_port}
+{
+}
+
+inline Resolver::Query::Query(const StreamProtocol& prot, std::string host_name,
+                              std::string service_port, int init_flags):
+    m_flags{init_flags},
+    m_protocol{prot},
+    m_host{host_name},
+    m_service{service_port}
+{
+}
+
+inline Resolver::Query::~Query() noexcept
+{
+}
+
+inline int Resolver::Query::flags() const
+{
+    return m_flags;
+}
+
+inline StreamProtocol Resolver::Query::protocol() const
+{
+    return m_protocol;
+}
+
+inline std::string Resolver::Query::host() const
+{
+    return m_host;
+}
+
+inline std::string Resolver::Query::service() const
+{
+    return m_service;
+}
+
+// ---------------- SocketBase ----------------
+
+inline SocketBase::SocketBase(Service& service):
+    m_desc{*service.m_impl}
+{
+}
+
+inline SocketBase::~SocketBase() noexcept
+{
+    close();
+}
+
+inline bool SocketBase::is_open() const noexcept
+{
+    return m_desc.is_open();
+}
+
+inline auto SocketBase::native_handle() const noexcept -> native_handle_type
+{
+    return m_desc.native_handle();
+}
+
+inline void SocketBase::open(const StreamProtocol& prot)
+{
+    std::error_code ec;
+    if (open(prot, ec))
+        throw std::system_error(ec);
+}
+
+inline void SocketBase::close() noexcept
+{
+    if (!is_open())
+        return;
+    cancel();
+    m_desc.close();
+}
+
+template<class O>
+inline void SocketBase::get_option(O& opt) const
+{
+    std::error_code ec;
+    if (get_option(opt, ec))
+        throw std::system_error(ec);
+}
+
+template<class O>
+inline std::error_code SocketBase::get_option(O& opt, std::error_code& ec) const
+{
+    opt.get(*this, ec);
+    return ec;
+}
+
+template<class O>
+inline void SocketBase::set_option(const O& opt)
+{
+    std::error_code ec;
+    if (set_option(opt, ec))
+        throw std::system_error(ec);
+}
+
+template<class O>
+inline std::error_code SocketBase::set_option(const O& opt, std::error_code& ec)
+{
+    opt.set(*this, ec);
+    return ec;
+}
+
+inline void SocketBase::bind(const Endpoint& ep)
+{
+    std::error_code ec;
+    if (bind(ep, ec))
+        throw std::system_error(ec);
+}
+
+inline Endpoint SocketBase::local_endpoint() const
+{
+    std::error_code ec;
+    Endpoint ep = local_endpoint(ec);
+    if (ec)
+        throw std::system_error(ec);
+    return ep;
+}
+
+inline const StreamProtocol& SocketBase::get_protocol() const noexcept
+{
+    return m_protocol;
+}
+
+template<class T, int opt, class U>
+inline SocketBase::Option<T, opt, U>::Option(T init_value):
+    m_value{init_value}
+{
+}
+
+template<class T, int opt, class U>
+inline T SocketBase::Option<T, opt, U>::value() const
+{
+    return m_value;
+}
+
+template<class T, int opt, class U>
+inline void SocketBase::Option<T, opt, U>::get(const SocketBase& sock, std::error_code& ec)
+{
+    union {
+        U value;
+        char strut[sizeof (U) + 1];
+    };
+    std::size_t value_size = sizeof strut;
+    sock.get_option(opt_enum(opt), &value, value_size, ec);
+    if (!ec) {
+        REALM_ASSERT(value_size == sizeof value);
+        m_value = T(value);
+    }
+}
+
+template<class T, int opt, class U>
+inline void SocketBase::Option<T, opt, U>::set(SocketBase& sock, std::error_code& ec) const
+{
+    U value_to_set = U(m_value);
+    sock.set_option(opt_enum(opt), &value_to_set, sizeof value_to_set, ec);
+}
+
+// ---------------- Socket ----------------
+
+class Socket::ConnectOperBase: public Service::IoOper {
+public:
+    ConnectOperBase(std::size_t size, Socket& sock) noexcept:
+        IoOper{size},
+        m_socket{&sock}
+    {
+    }
+    Want initiate(const Endpoint& ep)
+    {
+        REALM_ASSERT(this == m_socket->m_write_oper.get());
+        if (m_socket->initiate_async_connect(ep, m_error_code)) { // Throws
+            set_is_complete(true); // Failure, or immediate completion
+            return Want::nothing;
+        }
+        return Want::write;
+    }
+    Want advance() noexcept override final
+    {
+        REALM_ASSERT(!is_complete());
+        REALM_ASSERT(!is_canceled());
+        REALM_ASSERT(!m_error_code);
+        m_socket->finalize_async_connect(m_error_code);
+        set_is_complete(true);
+        return Want::nothing;
+    }
+    void recycle() noexcept override final
+    {
+        bool orphaned = !m_socket;
+        REALM_ASSERT(orphaned);
+        // Note: do_recycle() commits suicide.
+        do_recycle(orphaned);
+    }
+    void orphan() noexcept override final
+    {
+        m_socket = nullptr;
+    }
+    Service::Descriptor& descriptor() noexcept override final
+    {
+        return m_socket->m_desc;
+    }
+protected:
+    Socket* m_socket;
+    std::error_code m_error_code;
+};
+
+template<class H> class Socket::ConnectOper: public ConnectOperBase {
+public:
+    ConnectOper(std::size_t size, Socket& sock, H handler):
+        ConnectOperBase{size, sock},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        REALM_ASSERT(is_complete() || (is_canceled() && !m_error_code));
+        bool orphaned = !m_socket;
+        std::error_code ec = m_error_code;
+        if (is_canceled())
+            ec = error::operation_aborted;
+        // Note: do_recycle_and_execute() commits suicide.
+        do_recycle_and_execute<H>(orphaned, m_handler, ec); // Throws
+    }
+private:
+    H m_handler;
+};
+
+inline Socket::Socket(Service& service):
+    SocketBase{service}
+{
+}
+
+inline Socket::Socket(Service& service, const StreamProtocol& prot,
+                      native_handle_type native_socket):
+    SocketBase{service}
+{
+    assign(prot, native_socket); // Throws
+}
+
+inline Socket::~Socket() noexcept
+{
+}
+
+inline void Socket::connect(const Endpoint& ep)
+{
+    std::error_code ec;
+    if (connect(ep, ec)) // Throws
+        throw std::system_error(ec);
+}
+
+inline std::size_t Socket::read(char* buffer, std::size_t size)
+{
+    std::error_code ec;
+    read(buffer, size, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return size;
+}
+
+inline std::size_t Socket::read(char* buffer, std::size_t size, std::error_code& ec)
+{
+    return StreamOps::read(*this, buffer, size, ec); // Throws
+}
+
+inline std::size_t Socket::read(char* buffer, std::size_t size, ReadAheadBuffer& rab)
+{
+    std::error_code ec;
+    read(buffer, size, rab, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return size;
+}
+
+inline std::size_t Socket::read(char* buffer, std::size_t size, ReadAheadBuffer& rab,
+                                std::error_code& ec)
+{
+    int delim = std::char_traits<char>::eof();
+    return StreamOps::buffered_read(*this, buffer, size, delim, rab, ec); // Throws
+}
+
+inline std::size_t Socket::read_until(char* buffer, std::size_t size, char delim,
+                                      ReadAheadBuffer& rab)
+{
+    std::error_code ec;
+    std::size_t n = read_until(buffer, size, delim, rab, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return n;
+}
+
+inline std::size_t Socket::read_until(char* buffer, std::size_t size, char delim,
+                                      ReadAheadBuffer& rab, std::error_code& ec)
+{
+    int delim_2 = std::char_traits<char>::to_int_type(delim);
+    return StreamOps::buffered_read(*this, buffer, size, delim_2, rab, ec); // Throws
+}
+
+inline std::size_t Socket::write(const char* data, std::size_t size)
+{
+    std::error_code ec;
+    write(data, size, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return size;
+}
+
+inline std::size_t Socket::write(const char* data, std::size_t size, std::error_code& ec)
+{
+    return StreamOps::write(*this, data, size, ec); // Throws
+}
+
+inline std::size_t Socket::read_some(char* buffer, std::size_t size)
+{
+    std::error_code ec;
+    std::size_t n = read_some(buffer, size, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return n;
+}
+
+inline std::size_t Socket::read_some(char* buffer, std::size_t size, std::error_code& ec)
+{
+    return StreamOps::read_some(*this, buffer, size, ec); // Throws
+}
+
+inline std::size_t Socket::write_some(const char* data, std::size_t size)
+{
+    std::error_code ec;
+    std::size_t n = write_some(data, size, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return n;
+}
+
+inline std::size_t Socket::write_some(const char* data, std::size_t size, std::error_code& ec)
+{
+    return StreamOps::write_some(*this, data, size, ec); // Throws
+}
+
+template<class H> inline void Socket::async_connect(const Endpoint& ep, H handler)
+{
+    LendersConnectOperPtr op =
+        Service::alloc<ConnectOper<H>>(m_write_oper, *this, std::move(handler)); // Throws
+    m_desc.initiate_oper(std::move(op), ep); // Throws
+}
+
+template<class H> inline void Socket::async_read(char* buffer, std::size_t size, H handler)
+{
+    bool is_read_some = false;
+    StreamOps::async_read(*this, buffer, size, is_read_some, std::move(handler)); // Throws
+}
+
+template<class H>
+inline void Socket::async_read(char* buffer, std::size_t size, ReadAheadBuffer& rab, H handler)
+{
+    int delim = std::char_traits<char>::eof();
+    StreamOps::async_buffered_read(*this, buffer, size, delim, rab, std::move(handler)); // Throws
+}
+
+template<class H>
+inline void Socket::async_read_until(char* buffer, std::size_t size, char delim,
+                                     ReadAheadBuffer& rab, H handler)
+{
+    int delim_2 = std::char_traits<char>::to_int_type(delim);
+    StreamOps::async_buffered_read(*this, buffer, size, delim_2, rab, std::move(handler)); // Throws
+}
+
+template<class H> inline void Socket::async_write(const char* data, std::size_t size, H handler)
+{
+    bool is_write_some = false;
+    StreamOps::async_write(*this, data, size, is_write_some, std::move(handler)); // Throws
+}
+
+template<class H> inline void Socket::async_read_some(char* buffer, std::size_t size, H handler)
+{
+    bool is_read_some = true;
+    StreamOps::async_read(*this, buffer, size, is_read_some, std::move(handler)); // Throws
+}
+
+template<class H>
+inline void Socket::async_write_some(const char* data, std::size_t size, H handler)
+{
+    bool is_write_some = true;
+    StreamOps::async_write(*this, data, size, is_write_some, std::move(handler)); // Throws
+}
+
+inline void Socket::shutdown(shutdown_type what)
+{
+    std::error_code ec;
+    if (shutdown(what, ec)) // Throws
+        throw std::system_error(ec);
+}
+
+inline void Socket::assign(const StreamProtocol& prot, native_handle_type native_socket)
+{
+    std::error_code ec;
+    if (assign(prot, native_socket, ec)) // Throws
+        throw std::system_error(ec);
+}
+
+inline std::error_code Socket::assign(const StreamProtocol& prot,
+                                      native_handle_type native_socket, std::error_code& ec)
+{
+    return do_assign(prot, native_socket, ec); // Throws
+}
+
+inline Socket& Socket::lowest_layer() noexcept
+{
+    return *this;
+}
+
+inline void Socket::do_init_read_async(std::error_code&, Want& want) noexcept
+{
+    want = Want::read; // Wait for read readiness before proceeding
+}
+
+inline void Socket::do_init_write_async(std::error_code&, Want& want) noexcept
+{
+    want = Want::write; // Wait for write readiness before proceeding
+}
+
+inline std::size_t Socket::do_read_some_sync(char* buffer, std::size_t size,
+                                             std::error_code& ec) noexcept
+{
+    return m_desc.read_some(buffer, size, ec);
+}
+
+inline std::size_t Socket::do_write_some_sync(const char* data, std::size_t size,
+                                              std::error_code& ec) noexcept
+{
+    return m_desc.write_some(data, size, ec);
+}
+
+inline std::size_t Socket::do_read_some_async(char* buffer, std::size_t size,
+                                              std::error_code& ec, Want& want) noexcept
+{
+    std::error_code ec_2;
+    std::size_t n = m_desc.read_some(buffer, size, ec_2);
+    bool success = (!ec_2 || ec_2 == error::resource_unavailable_try_again);
+    if (REALM_UNLIKELY(!success)) {
+        ec = ec_2;
+        want = Want::nothing; // Failure
+        return 0;
+    }
+    ec = std::error_code();
+    want = Want::read; // Success
+    return n;
+}
+
+inline std::size_t Socket::do_write_some_async(const char* data, std::size_t size,
+                                               std::error_code& ec, Want& want) noexcept
+{
+    std::error_code ec_2;
+    std::size_t n = m_desc.write_some(data, size, ec_2);
+    bool success = (!ec_2 || ec_2 == error::resource_unavailable_try_again);
+    if (REALM_UNLIKELY(!success)) {
+        ec = ec_2;
+        want = Want::nothing; // Failure
+        return 0;
+    }
+    ec = std::error_code();
+    want = Want::write; // Success
+    return n;
+}
+
+// ---------------- Acceptor ----------------
+
+class Acceptor::AcceptOperBase: public Service::IoOper {
+public:
+    AcceptOperBase(std::size_t size, Acceptor& a, Socket& s, Endpoint* e):
+        IoOper{size},
+        m_acceptor{&a},
+        m_socket{s},
+        m_endpoint{e}
+    {
+    }
+    Want initiate()
+    {
+        REALM_ASSERT(this == m_acceptor->m_read_oper.get());
+        REALM_ASSERT(!is_complete());
+        m_acceptor->m_desc.ensure_nonblocking_mode(); // Throws
+        return Want::read;
+    }
+    Want advance() noexcept override final
+    {
+        REALM_ASSERT(!is_complete());
+        REALM_ASSERT(!is_canceled());
+        REALM_ASSERT(!m_error_code);
+        REALM_ASSERT(!m_socket.is_open());
+        Want want = m_acceptor->do_accept_async(m_socket, m_endpoint, m_error_code);
+        if (want == Want::nothing)
+            set_is_complete(true); // Success or failure
+        return want;
+    }
+    void recycle() noexcept override final
+    {
+        bool orphaned = !m_acceptor;
+        REALM_ASSERT(orphaned);
+        // Note: do_recycle() commits suicide.
+        do_recycle(orphaned);
+    }
+    void orphan() noexcept override final
+    {
+        m_acceptor = nullptr;
+    }
+    Service::Descriptor& descriptor() noexcept override final
+    {
+        return m_acceptor->m_desc;
+    }
+protected:
+    Acceptor* m_acceptor;
+    Socket& m_socket;           // May be dangling after cancellation
+    Endpoint* const m_endpoint; // May be dangling after cancellation
+    std::error_code m_error_code;
+};
+
+template<class H> class Acceptor::AcceptOper: public AcceptOperBase {
+public:
+    AcceptOper(std::size_t size, Acceptor& a, Socket& s, Endpoint* e, H handler):
+        AcceptOperBase{size, a, s, e},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        REALM_ASSERT(is_complete() || (is_canceled() && !m_error_code));
+        REALM_ASSERT(is_canceled() || m_error_code || m_socket.is_open());
+        bool orphaned = !m_acceptor;
+        std::error_code ec = m_error_code;
+        if (is_canceled())
+            ec = error::operation_aborted;
+        // Note: do_recycle_and_execute() commits suicide.
+        do_recycle_and_execute<H>(orphaned, m_handler, ec); // Throws
+    }
+private:
+    H m_handler;
+};
+
+inline Acceptor::Acceptor(Service& service):
+    SocketBase{service}
+{
+}
+
+inline Acceptor::~Acceptor() noexcept
+{
+}
+
+inline void Acceptor::listen(int backlog)
+{
+    std::error_code ec;
+    if (listen(backlog, ec)) // Throws
+        throw std::system_error(ec);
+}
+
+inline void Acceptor::accept(Socket& sock)
+{
+    std::error_code ec;
+    if (accept(sock, ec)) // Throws
+        throw std::system_error(ec);
+}
+
+inline void Acceptor::accept(Socket& sock, Endpoint& ep)
+{
+    std::error_code ec;
+    if (accept(sock, ep, ec)) // Throws
+        throw std::system_error(ec);
+}
+
+inline std::error_code Acceptor::accept(Socket& sock, std::error_code& ec)
+{
+    Endpoint* ep = nullptr;
+    return accept(sock, ep, ec); // Throws
+}
+
+inline std::error_code Acceptor::accept(Socket& sock, Endpoint& ep, std::error_code& ec)
+{
+    return accept(sock, &ep, ec); // Throws
+}
+
+template<class H> inline void Acceptor::async_accept(Socket& sock, H handler)
+{
+    Endpoint* ep = nullptr;
+    async_accept(sock, ep, std::move(handler)); // Throws
+}
+
+template<class H> inline void Acceptor::async_accept(Socket& sock, Endpoint& ep, H handler)
+{
+    async_accept(sock, &ep, std::move(handler)); // Throws
+}
+
+inline std::error_code Acceptor::accept(Socket& socket, Endpoint* ep, std::error_code& ec)
+{
+    REALM_ASSERT(!m_read_oper || !m_read_oper->in_use());
+    if (REALM_UNLIKELY(socket.is_open()))
+        throw std::runtime_error("Socket is already open");
+    m_desc.ensure_blocking_mode(); // Throws
+    m_desc.accept(socket.m_desc, m_protocol, ep, ec);
+    return ec;
+}
+
+inline Acceptor::Want Acceptor::do_accept_async(Socket& socket, Endpoint* ep,
+                                                std::error_code& ec) noexcept
+{
+    std::error_code ec_2;
+    m_desc.accept(socket.m_desc, m_protocol, ep, ec_2);
+    if (ec_2 == error::resource_unavailable_try_again)
+        return Want::read;
+    ec = ec_2;
+    return Want::nothing;
+}
+
+template<class H> inline void Acceptor::async_accept(Socket& sock, Endpoint* ep, H handler)
+{
+    if (REALM_UNLIKELY(sock.is_open()))
+        throw std::runtime_error("Socket is already open");
+    LendersAcceptOperPtr op = Service::alloc<AcceptOper<H>>(m_read_oper, *this, sock, ep,
+                                                            std::move(handler)); // Throws
+    m_desc.initiate_oper(std::move(op)); // Throws
+}
+
+// ---------------- DeadlineTimer ----------------
+
+template<class H>
+class DeadlineTimer::WaitOper: public Service::WaitOperBase {
+public:
+    WaitOper(std::size_t size, DeadlineTimer& timer, clock::time_point expiration_time, H handler):
+        Service::WaitOperBase{size, timer, expiration_time},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        bool orphaned = !m_timer;
+        std::error_code ec;
+        if (is_canceled())
+            ec = error::operation_aborted;
+        // Note: do_recycle_and_execute() commits suicide.
+        do_recycle_and_execute<H>(orphaned, m_handler, ec); // Throws
+    }
+private:
+    H m_handler;
+};
+
+inline DeadlineTimer::DeadlineTimer(Service& service):
+    m_service_impl{*service.m_impl}
+{
+}
+
+inline DeadlineTimer::~DeadlineTimer() noexcept
+{
+    cancel();
+}
+
+template<class R, class P, class H>
+inline void DeadlineTimer::async_wait(std::chrono::duration<R,P> delay, H handler)
+{
+    clock::time_point now = clock::now();
+    // FIXME: This method of detecting overflow does not work. Comparison
+    // between distinct duration types is not overflow safe. Overflow easily
+    // happens in the implied conversion of arguments to the common duration
+    // type (std::common_type<>).
+    auto max_add = clock::time_point::max() - now;
+    if (delay > max_add)
+        throw std::runtime_error("Expiration time overflow");
+    clock::time_point expiration_time = now + delay;
+    Service::LendersWaitOperPtr op =
+        Service::alloc<WaitOper<H>>(m_wait_oper, *this, expiration_time,
+                                    std::move(handler)); // Throws
+    add_oper(std::move(op)); // Throws
+}
+
+// ---------------- Trigger ----------------
+
+template<class H>
+class Trigger::ExecOper: public Service::TriggerExecOperBase {
+public:
+    ExecOper(Service::Impl& service_impl, H handler):
+        Service::TriggerExecOperBase{service_impl},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        REALM_ASSERT(in_use());
+        // Note: Potential suicide when `self` goes out of scope
+        util::bind_ptr<TriggerExecOperBase> self{this, bind_ptr_base::adopt_tag{}};
+        if (m_service) {
+            Service::reset_trigger_exec(*m_service, *this);
+            m_handler(); // Throws
+        }
+    }
+private:
+    H m_handler;
+};
+
+template<class H> inline Trigger::Trigger(Service& service, H handler) :
+    m_exec_oper{new ExecOper<H>{*service.m_impl, std::move(handler)}} // Throws
+{
+}
+
+inline Trigger::~Trigger() noexcept
+{
+    if (m_exec_oper)
+        m_exec_oper->orphan();
+}
+
+inline void Trigger::trigger() noexcept
+{
+    REALM_ASSERT(m_exec_oper);
+    m_exec_oper->trigger();
+}
+
+// ---------------- ReadAheadBuffer ----------------
+
+inline ReadAheadBuffer::ReadAheadBuffer():
+    m_buffer{new char[s_size]} // Throws
+{
+}
+
+inline void ReadAheadBuffer::clear() noexcept
+{
+    m_begin = nullptr;
+    m_end   = nullptr;
+}
+
+inline bool ReadAheadBuffer::empty() const noexcept
+{
+    return (m_begin == m_end);
+}
+
+template<class S> inline void ReadAheadBuffer::refill_sync(S& stream, std::error_code& ec) noexcept
+{
+    char* buffer = m_buffer.get();
+    std::size_t size = s_size;
+    static_assert(noexcept(stream.do_read_some_sync(buffer, size, ec)), "");
+    std::size_t n = stream.do_read_some_sync(buffer, size, ec);
+    if (REALM_UNLIKELY(n == 0))
+        return;
+    REALM_ASSERT(!ec);
+    REALM_ASSERT(n <= size);
+    m_begin = m_buffer.get();
+    m_end   = m_begin + n;
+}
+
+template<class S>
+inline bool ReadAheadBuffer::refill_async(S& stream, std::error_code& ec, Want& want) noexcept
+{
+    char* buffer = m_buffer.get();
+    std::size_t size = s_size;
+    static_assert(noexcept(stream.do_read_some_async(buffer, size, ec, want)), "");
+    std::size_t n = stream.do_read_some_async(buffer, size, ec, want);
+    if (n == 0)
+        return false;
+    REALM_ASSERT(!ec);
+    REALM_ASSERT(n <= size);
+    m_begin = m_buffer.get();
+    m_end   = m_begin + n;
+    return true;
+}
+
+} // namespace network
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_NETWORK_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/network_ssl.hpp b/iOS/Pods/Realm/include/core/realm/util/network_ssl.hpp
new file mode 100644 (file)
index 0000000..408087b
--- /dev/null
@@ -0,0 +1,1304 @@
+/*************************************************************************
+ *
+ * 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_UTIL_NETWORK_SSL_HPP
+#define REALM_UTIL_NETWORK_SSL_HPP
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <string>
+#include <exception>
+#include <system_error>
+
+#include <realm/util/features.h>
+#include <realm/util/assert.hpp>
+#include <realm/util/misc_errors.hpp>
+#include <realm/util/network.hpp>
+#include <realm/util/optional.hpp>
+
+#if REALM_HAVE_OPENSSL
+#  include <openssl/ssl.h>
+#  include <openssl/err.h>
+#elif REALM_HAVE_SECURE_TRANSPORT
+#  include <realm/util/cf_ptr.hpp>
+#  include <Security/Security.h>
+#  include <Security/SecureTransport.h>
+
+#define REALM_HAVE_KEYCHAIN_APIS (TARGET_OS_MAC && !TARGET_OS_IPHONE)
+
+#endif
+
+// FIXME: Add necessary support for customizing the SSL server and client
+// configurations.
+
+// FIXME: Currently, the synchronous SSL operations (handshake, read, write,
+// shutdown) do not automatically retry if the underlying SSL function returns
+// with SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. This normally never
+// happens, but it can happen according to the man pages, but in the case of
+// SSL_write(), only when a renegotiation has to take place. It is likely that
+// the solution is to to wrap the SSL calls inside a loop, such that they keep
+// retrying until they succeed, however, such a simple scheme will fail if the
+// synchronous operations were to be used with an underlying TCP socket in
+// nonblocking mode. Currently, the underlying TCP socket is always in blocking
+// mode when performing synchronous operations, but that may continue to be the
+// case in teh future.
+
+
+namespace realm {
+namespace util {
+namespace network {
+namespace ssl {
+
+enum class Error {
+    certificate_rejected = 1,
+};
+
+const std::error_category& ssl_error_category();
+
+std::error_condition make_error_condition(Error value);
+
+} // namespace ssl
+} // namespace network
+} // namespace util
+} // namespace realm
+
+namespace std {
+
+template <>
+struct is_error_condition_enum<realm::util::network::ssl::Error>: public true_type {};
+
+} // namespace std
+
+namespace realm {
+namespace util {
+namespace network {
+namespace ssl {
+
+#if REALM_HAVE_OPENSSL
+
+const std::error_category& openssl_error_category();
+
+#elif REALM_HAVE_SECURE_TRANSPORT
+
+const std::error_category& secure_transport_error_category();
+
+#endif
+
+
+class ProtocolNotSupported;
+
+
+/// `VerifyMode::none` corresponds to OpenSSL's `SSL_VERIFY_NONE`, and
+/// `VerifyMode::peer` to `SSL_VERIFY_PEER`.
+enum class VerifyMode { none, peer };
+
+
+class Context {
+public:
+    Context();
+    ~Context() noexcept;
+
+    /// File must be in PEM format. Corresponds to OpenSSL's
+    /// `SSL_CTX_use_certificate_chain_file()`.
+    void use_certificate_chain_file(const std::string& path);
+
+    /// File must be in PEM format. Corresponds to OpenSSL's
+    /// `SSL_CTX_use_PrivateKey_file()`.
+    void use_private_key_file(const std::string& path);
+
+    /// Calling use_default_verify() will make a client use the
+    /// device default certificates for server verification.
+    /// For OpenSSL, use_default_verify() corresponds to
+    /// SSL_CTX_set_default_verify_paths(SSL_CTX*);
+    void use_default_verify();
+
+    /// The verify file is a PEM file containing trust
+    /// certificates that the client will use to
+    /// verify the server crtificate. If use_verify_file()
+    /// is not called, the default device trust store will
+    /// be used.
+    /// Corresponds roughly to OpenSSL's
+    /// SSL_CTX_load_verify_locations().
+    void use_verify_file(const std::string& path);
+
+private:
+    void ssl_init();
+    void ssl_destroy() noexcept;
+    void ssl_use_certificate_chain_file(const std::string& path, std::error_code&);
+    void ssl_use_private_key_file(const std::string& path, std::error_code&);
+    void ssl_use_default_verify(std::error_code&);
+    void ssl_use_verify_file(const std::string& path, std::error_code&);
+
+#if REALM_HAVE_OPENSSL
+    SSL_CTX* m_ssl_ctx = nullptr;
+
+#elif REALM_HAVE_SECURE_TRANSPORT
+
+#if REALM_HAVE_KEYCHAIN_APIS
+    static util::CFPtr<CFArrayRef> load_pem_file(const std::string& path, SecKeychainRef, std::error_code&);
+
+    std::error_code open_temporary_keychain_if_needed();
+    std::error_code update_identity_if_needed();
+
+    util::CFPtr<SecKeychainRef> m_keychain;
+    std::string m_keychain_path;
+
+    util::CFPtr<SecCertificateRef> m_certificate;
+    util::CFPtr<SecKeyRef> m_private_key;
+    util::CFPtr<SecIdentityRef> m_identity;
+
+    util::CFPtr<CFArrayRef> m_certificate_chain;
+
+    util::CFPtr<CFArrayRef> m_trust_anchors;
+#endif // REALM_HAVE_KEYCHAIN_APIS
+
+#endif
+
+    friend class Stream;
+};
+
+
+/// Switching between synchronous and asynchronous operations is allowed, but
+/// only in a nonoverlapping fashion. That is, a synchronous operation is not
+/// allowed to run concurrently with an asynchronous one on the same
+/// stream. Note that an asynchronous operation is considered to be running
+/// until its completion handler starts executing.
+class Stream {
+public:
+    using port_type = util::network::Endpoint::port_type;
+    using SSLVerifyCallback = bool(const std::string& server_address,
+                                   port_type server_port,
+                                   const char* pem_data,
+                                   size_t pem_size,
+                                   int preverify_ok,
+                                   int depth);
+
+    enum HandshakeType { client, server };
+
+    Stream(Socket&, Context&, HandshakeType);
+    ~Stream() noexcept;
+
+    /// \brief Set the certificate verification mode for this SSL stream.
+    ///
+    /// Corresponds to OpenSSL's `SSL_set_verify()` with null passed as
+    /// `verify_callback`.
+    ///
+    /// Clients should always set it to `VerifyMode::peer`, such that the client
+    /// verifies the servers certificate. Servers should only set it to
+    /// `VerifyMode::peer` if they want to request a certificate from the
+    /// client. When testing with self-signed certificates, it is necessary to
+    /// set it to `VerifyMode::none` for clients too.
+    ///
+    /// It is an error if this function is called after the handshake operation
+    /// is initiated.
+    ///
+    /// The default verify mode is `VerifyMode::none`.
+    void set_verify_mode(VerifyMode);
+
+    /// \brief Check the certificate against a host_name.
+    ///
+    /// set_check_host() includes a host name check in the
+    /// certificate verification. It is typically used by clients
+    /// to secure that the received certificate has a common name
+    /// or subject alternative name that matches \param host_name.
+    ///
+    /// set_check_host() is only useful if verify_mode is
+    /// set to VerifyMode::peer.
+    void set_check_host(std::string host_name);
+    const std::string& get_host_name();
+
+    /// get_server_port() and set_server_port() are getter and setter for
+    /// the server port. They are only used by the verify callback function
+    /// below.
+    port_type get_server_port();
+    void set_server_port(port_type server_port);
+
+    /// If use_verify_callback() is called, the SSL certificate chain of
+    /// the server is presented to callback, one certificate at a time.
+    /// The SSL connection is accepted if and only if callback returns true
+    /// for all certificates.
+    /// The signature of \param callback is
+    ///
+    /// bool(const std::string& server_address,
+    ///      port_type server_port,
+    ///      const char* pem_data,
+    ///      size_t pem_size,
+    ///      int preverify_ok,
+    ///      int depth);
+    //
+    /// server address and server_port is the address and port of the server
+    /// that a SSL connection is being established to.
+    /// pem_data is the certificate of length pem_size in
+    /// the PEM format. preverify_ok is OpenSSL's preverification of the
+    /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1,
+    /// OpenSSL has accepted the certificate and it will generally be safe
+    /// to trust that certificate. depth represents the position of the
+    /// certificate in the certificate chain sent by the server. depth = 0
+    /// represents the actual server certificate that should contain the
+    /// host name(server address) of the server. The highest depth is the
+    /// root certificate.
+    /// The callback function will receive the certificates starting from
+    /// the root certificate and moving down the chain until it reaches the
+    /// server's own certificate with a host name. The depth of the last
+    /// certificate is 0. The depth of the first certificate is chain
+    /// length - 1.
+    ///
+    /// The return value of the callback function decides whether the
+    /// client accepts the certificate. If the return value is false, the
+    /// processing of the certificate chain is interrupted and the SSL
+    /// connection is rejected. If the return value is true, the verification
+    /// process continues. If the callback function returns true for all
+    /// presented certificates including the depth == 0 certificate, the
+    /// SSL connection is accepted.
+    ///
+    /// A recommended way of using the callback function is to return true
+    /// if preverify_ok = 1 and depth > 0,
+    /// always check the host name if depth = 0,
+    /// and use an independent verification step if preverify_ok = 0.
+    ///
+    /// Another possible way of using the callback is to collect all the
+    /// certificates until depth = 0, and present the entire chain for
+    /// independent verification.
+    void use_verify_callback(const std::function<SSLVerifyCallback>& callback);
+
+    /// @{
+    ///
+    /// Read and write operations behave the same way as they do on \ref
+    /// network::Socket, except that after cancellation of asynchronous
+    /// operations (`lowest_layer().cancel()`), the stream may be left in a bad
+    /// state (see below).
+    ///
+    /// The handshake operation must complete successfully before any read,
+    /// write, or shutdown operations are performed.
+    ///
+    /// The shutdown operation sends the shutdown alert to the peer, and
+    /// returns/completes as soon as the alert message has been written to the
+    /// underlying socket. It is an error if the shutdown operation is initiated
+    /// while there are read or write operations in progress. No read or write
+    /// operations are allowed to be initiated after the shutdown operation has
+    /// been initiated. When the shutdown operation has completed, it is safe to
+    /// close the underlying socket (`lowest_layer().close()`).
+    ///
+    /// If a write operation is executing while, or is initiated after a close
+    /// notify alert is received from the remote peer, the write operation will
+    /// fail with error::broken_pipe.
+    ///
+    /// Callback functions for async read and write operations must take two
+    /// arguments, an std::error_code(), and an integer of a type std::size_t
+    /// indicating the number of transferred bytes (other types are allowed as
+    /// long as implicit conversion can take place).
+    ///
+    /// Callback functions for async handshake and shutdown operations must take
+    /// a single argument of type std::error_code() (other types are allowed as
+    /// long as implicit conversion can take place).
+    ///
+    /// Resumption of stream operation after cancellation of asynchronous
+    /// operations is not supported (does not work). Since the shutdown
+    /// operation involves network communication, that operation is also not
+    /// allowed after cancellation. The only thing that is allowed, is to
+    /// destroy the stream object. Other stream objects are not affected.
+
+    void handshake();
+    std::error_code handshake(std::error_code&);
+
+    std::size_t read(char* buffer, std::size_t size);
+    std::size_t read(char* buffer, std::size_t size, std::error_code& ec);
+    std::size_t read(char* buffer, std::size_t size, ReadAheadBuffer&);
+    std::size_t read(char* buffer, std::size_t size, ReadAheadBuffer&, std::error_code& ec);
+    std::size_t read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&);
+    std::size_t read_until(char* buffer, std::size_t size, char delim, ReadAheadBuffer&,
+                           std::error_code& ec);
+
+    std::size_t write(const char* data, std::size_t size);
+    std::size_t write(const char* data, std::size_t size, std::error_code& ec);
+
+    std::size_t read_some(char* buffer, std::size_t size);
+    std::size_t read_some(char* buffer, std::size_t size, std::error_code&);
+
+    std::size_t write_some(const char* data, std::size_t size);
+    std::size_t write_some(const char* data, std::size_t size, std::error_code&);
+
+    void shutdown();
+    std::error_code shutdown(std::error_code&);
+
+    template<class H> void async_handshake(H handler);
+
+    template<class H> void async_read(char* buffer, std::size_t size, H handler);
+    template<class H> void async_read(char* buffer, std::size_t size, ReadAheadBuffer&, H handler);
+    template<class H> void async_read_until(char* buffer, std::size_t size, char delim,
+                                            ReadAheadBuffer&, H handler);
+
+    template<class H> void async_write(const char* data, std::size_t size, H handler);
+
+    template<class H> void async_read_some(char* buffer, std::size_t size, H handler);
+
+    template<class H> void async_write_some(const char* data, std::size_t size, H handler);
+
+    template<class H> void async_shutdown(H handler);
+
+    /// @}
+
+    /// Returns a reference to the underlying socket.
+    Socket& lowest_layer() noexcept;
+
+private:
+    using Want = Service::Want;
+    using StreamOps = Service::BasicStreamOps<Stream>;
+
+    class HandshakeOperBase;
+    template<class H> class HandshakeOper;
+    class ShutdownOperBase;
+    template<class H> class ShutdownOper;
+
+    using LendersHandshakeOperPtr = std::unique_ptr<HandshakeOperBase, Service::LendersOperDeleter>;
+    using LendersShutdownOperPtr  = std::unique_ptr<ShutdownOperBase,  Service::LendersOperDeleter>;
+
+    Socket& m_tcp_socket;
+    Context& m_ssl_context;
+    const HandshakeType m_handshake_type;
+
+    // The host name that the certificate should be checked against.
+    // The host name is called server address in the certificate verify
+    // callback function.
+    std::string m_host_name;
+
+    // The port of the server which is used in the certificate verify
+    // callback function.
+    port_type m_server_port;
+
+    // The callback for certificate verification and an
+    // opaque argument that will be supplied to the callback.
+    const std::function<SSLVerifyCallback>* m_ssl_verify_callback = nullptr;
+
+    // See Service::BasicStreamOps for details on these these 6 functions.
+    void do_init_read_async(std::error_code&, Want&) noexcept;
+    void do_init_write_async(std::error_code&, Want&) noexcept;
+    std::size_t do_read_some_sync(char* buffer, std::size_t size,
+                                  std::error_code&) noexcept;
+    std::size_t do_write_some_sync(const char* data, std::size_t size,
+                                   std::error_code&) noexcept;
+    std::size_t do_read_some_async(char* buffer, std::size_t size,
+                                   std::error_code&, Want&) noexcept;
+    std::size_t do_write_some_async(const char* data, std::size_t size,
+                                    std::error_code&, Want&) noexcept;
+
+    // The meaning of the arguments and return values of ssl_read() and
+    // ssl_write() are identical to do_read_some_async() and
+    // do_write_some_async() respectively, except that when the return value is
+    // nonzero, `want` is always `Want::nothing`, meaning that after bytes have
+    // been transferred, ssl_read() and ssl_write() must be called again to
+    // figure out whether it is necessary to wait for read or write readiness.
+    //
+    // The first invocation of ssl_shutdown() must send the shutdown alert to
+    // the peer. In blocking mode it must wait until the alert has been sent. In
+    // nonblocking mode, it must keep setting `want` to something other than
+    // `Want::nothing` until the alert has been sent. When the shutdown alert
+    // has been sent, it is safe to shut down the sending side of the underlying
+    // socket. On failure, ssl_shutdown() must set `ec` to something different
+    // than `std::error_code()` and return false. On success, it must set `ec`
+    // to `std::error_code()`, and return true if a shutdown alert from the peer
+    // has already been received, otherwise it must return false. When it sets
+    // `want` to something other than `Want::nothing`, it must set `ec` to
+    // `std::error_code()` and return false.
+    //
+    // The second invocation of ssl_shutdown() (after the first invocation
+    // completed) must wait for reception on the peers shutdown alert.
+    //
+    // Note: The semantics around the second invocation of shutdown is currently
+    // unused by the higher level API, because of a requirement of compatibility
+    // with Apple's Secure Transport API.
+    void ssl_init();
+    void ssl_destroy() noexcept;
+    void ssl_set_verify_mode(VerifyMode, std::error_code&);
+    void ssl_set_check_host(std::string, std::error_code&);
+    void ssl_use_verify_callback(const std::function<SSLVerifyCallback>&, std::error_code&);
+
+    void ssl_handshake(std::error_code&, Want& want) noexcept;
+    bool ssl_shutdown(std::error_code& ec, Want& want) noexcept;
+    std::size_t ssl_read(char* buffer, std::size_t size,
+                         std::error_code&, Want& want) noexcept;
+    std::size_t ssl_write(const char* data, std::size_t size,
+                          std::error_code&, Want& want) noexcept;
+
+#if REALM_HAVE_OPENSSL
+    class BioMethod;
+    static BioMethod s_bio_method;
+    SSL* m_ssl = nullptr;
+    std::error_code m_bio_error_code;
+
+    int m_ssl_index = -1;
+
+    template<class Oper>
+    std::size_t ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept;
+
+    int do_ssl_accept() noexcept;
+    int do_ssl_connect() noexcept;
+    int do_ssl_shutdown() noexcept;
+    int do_ssl_read(char* buffer, std::size_t size) noexcept;
+    int do_ssl_write(const char* data, std::size_t size) noexcept;
+
+    static int bio_write(BIO*, const char*, int) noexcept;
+    static int bio_read(BIO*, char*, int) noexcept;
+    static int bio_puts(BIO*, const char*) noexcept;
+    static long bio_ctrl(BIO*, int, long, void*) noexcept;
+    static int bio_create(BIO*) noexcept;
+    static int bio_destroy(BIO*) noexcept;
+
+    // verify_callback_using_hostname is used as an argument to OpenSSL's SSL_set_verify function.
+    // verify_callback_using_hostname verifies that the certificate is valid and contains
+    // m_host_name as a Common Name or Subject Alternative Name.
+    static int verify_callback_using_hostname(int preverify_ok, X509_STORE_CTX *ctx) noexcept;
+
+    // verify_callback_using_delegate() is also used as an argument to OpenSSL's set_verify_function.
+    // verify_callback_using_delegate() calls out to the user supplied verify callback.
+    static int verify_callback_using_delegate(int preverify_ok, X509_STORE_CTX *ctx) noexcept;
+#elif REALM_HAVE_SECURE_TRANSPORT
+    util::CFPtr<SSLContextRef> m_ssl;
+    VerifyMode m_verify_mode = VerifyMode::none;
+
+    enum class BlockingOperation {
+        read,
+        write,
+    };
+    util::Optional<BlockingOperation> m_last_operation;
+
+    // Details of the underlying I/O error that lead to errSecIO being returned
+    // from a SecureTransport function.
+    std::error_code m_last_error;
+
+    // The number of bytes accepted by SSWrite() but not yet confirmed to be
+    // written to the underlying socket.
+    std::size_t m_num_partially_written_bytes = 0;
+
+    template<class Oper>
+    std::size_t ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept;
+
+    std::pair<OSStatus, std::size_t> do_ssl_handshake() noexcept;
+    std::pair<OSStatus, std::size_t> do_ssl_shutdown() noexcept;
+    std::pair<OSStatus, std::size_t> do_ssl_read(char* buffer, std::size_t size) noexcept;
+    std::pair<OSStatus, std::size_t> do_ssl_write(const char* data, std::size_t size) noexcept;
+
+    static OSStatus tcp_read(SSLConnectionRef, void*, std::size_t* length) noexcept;
+    static OSStatus tcp_write(SSLConnectionRef, const void*, std::size_t* length) noexcept;
+
+    OSStatus tcp_read(void*, std::size_t* length) noexcept;
+    OSStatus tcp_write(const void*, std::size_t* length) noexcept;
+
+    OSStatus verify_peer() noexcept;
+#endif
+
+    friend class Service::BasicStreamOps<Stream>;
+    friend class network::ReadAheadBuffer;
+};
+
+
+// Implementation
+
+class ProtocolNotSupported: public std::exception {
+public:
+    const char* what() const noexcept override final;
+};
+
+inline Context::Context()
+{
+    ssl_init(); // Throws
+}
+
+inline Context::~Context() noexcept
+{
+    ssl_destroy();
+}
+
+inline void Context::use_certificate_chain_file(const std::string& path)
+{
+    std::error_code ec;
+    ssl_use_certificate_chain_file(path, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+}
+
+inline void Context::use_private_key_file(const std::string& path)
+{
+    std::error_code ec;
+    ssl_use_private_key_file(path, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+}
+
+inline void Context::use_default_verify()
+{
+    std::error_code ec;
+    ssl_use_default_verify(ec);
+    if (ec)
+        throw std::system_error(ec);
+}
+
+inline void Context::use_verify_file(const std::string& path)
+{
+    std::error_code ec;
+    ssl_use_verify_file(path, ec);
+    if (ec)
+        throw std::system_error(ec);
+}
+
+class Stream::HandshakeOperBase: public Service::IoOper {
+public:
+    HandshakeOperBase(std::size_t size, Stream& stream):
+        IoOper{size},
+        m_stream{&stream}
+    {
+    }
+    Want initiate()
+    {
+        REALM_ASSERT(this == m_stream->m_tcp_socket.m_read_oper.get());
+        REALM_ASSERT(!is_complete());
+        m_stream->m_tcp_socket.m_desc.ensure_nonblocking_mode(); // Throws
+        return advance();
+    }
+    Want advance() noexcept override final
+    {
+        REALM_ASSERT(!is_complete());
+        REALM_ASSERT(!is_canceled());
+        REALM_ASSERT(!m_error_code);
+        Want want = Want::nothing;
+        m_stream->ssl_handshake(m_error_code, want);
+        set_is_complete(want == Want::nothing);
+        return want;
+    }
+    void recycle() noexcept override final
+    {
+        bool orphaned = !m_stream;
+        REALM_ASSERT(orphaned);
+        // Note: do_recycle() commits suicide.
+        do_recycle(orphaned);
+    }
+    void orphan() noexcept override final
+    {
+        m_stream = nullptr;
+    }
+    Service::Descriptor& descriptor() noexcept override final
+    {
+        return m_stream->lowest_layer().m_desc;
+    }
+protected:
+    Stream* m_stream;
+    std::error_code m_error_code;
+};
+
+template<class H> class Stream::HandshakeOper: public HandshakeOperBase {
+public:
+    HandshakeOper(std::size_t size, Stream& stream, H handler):
+        HandshakeOperBase{size, stream},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        REALM_ASSERT(is_complete() || is_canceled());
+        bool orphaned = !m_stream;
+        std::error_code ec = m_error_code;
+        if (is_canceled())
+            ec = error::operation_aborted;
+        // Note: do_recycle_and_execute() commits suicide.
+        do_recycle_and_execute<H>(orphaned, m_handler, ec); // Throws
+    }
+private:
+    H m_handler;
+};
+
+class Stream::ShutdownOperBase: public Service::IoOper {
+public:
+    ShutdownOperBase(std::size_t size, Stream& stream):
+        IoOper{size},
+        m_stream{&stream}
+    {
+    }
+    Want initiate()
+    {
+        REALM_ASSERT(this == m_stream->m_tcp_socket.m_write_oper.get());
+        REALM_ASSERT(!is_complete());
+        m_stream->m_tcp_socket.m_desc.ensure_nonblocking_mode(); // Throws
+        return advance();
+    }
+    Want advance() noexcept override final
+    {
+        REALM_ASSERT(!is_complete());
+        REALM_ASSERT(!is_canceled());
+        REALM_ASSERT(!m_error_code);
+        Want want = Want::nothing;
+        m_stream->ssl_shutdown(m_error_code, want);
+        if (want == Want::nothing)
+            set_is_complete(true);
+        return want;
+    }
+    void recycle() noexcept override final
+    {
+        bool orphaned = !m_stream;
+        REALM_ASSERT(orphaned);
+        // Note: do_recycle() commits suicide.
+        do_recycle(orphaned);
+    }
+    void orphan() noexcept override final
+    {
+        m_stream = nullptr;
+    }
+    Service::Descriptor& descriptor() noexcept override final
+    {
+        return m_stream->lowest_layer().m_desc;
+    }
+protected:
+    Stream* m_stream;
+    std::error_code m_error_code;
+};
+
+template<class H> class Stream::ShutdownOper: public ShutdownOperBase {
+public:
+    ShutdownOper(std::size_t size, Stream& stream, H handler):
+        ShutdownOperBase{size, stream},
+        m_handler{std::move(handler)}
+    {
+    }
+    void recycle_and_execute() override final
+    {
+        REALM_ASSERT(is_complete() || is_canceled());
+        bool orphaned = !m_stream;
+        std::error_code ec = m_error_code;
+        if (is_canceled())
+            ec = error::operation_aborted;
+        // Note: do_recycle_and_execute() commits suicide.
+        do_recycle_and_execute<H>(orphaned, m_handler, ec); // Throws
+    }
+private:
+    H m_handler;
+};
+
+inline Stream::Stream(Socket& socket, Context& context, HandshakeType type):
+    m_tcp_socket{socket},
+    m_ssl_context{context},
+    m_handshake_type{type}
+{
+    ssl_init(); // Throws
+}
+
+inline Stream::~Stream() noexcept
+{
+    m_tcp_socket.cancel();
+    ssl_destroy();
+}
+
+inline void Stream::set_verify_mode(VerifyMode mode)
+{
+    std::error_code ec;
+    ssl_set_verify_mode(mode, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+}
+
+inline void Stream::set_check_host(std::string host_name)
+{
+    m_host_name = host_name;
+    std::error_code ec;
+    ssl_set_check_host(host_name, ec);
+    if (ec)
+        throw std::system_error(ec);
+}
+
+inline const std::string& Stream::get_host_name()
+{
+    return m_host_name;
+}
+
+inline Stream::port_type Stream::get_server_port()
+{
+    return m_server_port;
+}
+
+inline void Stream::set_server_port(port_type server_port)
+{
+    m_server_port = server_port;
+}
+
+inline void Stream::use_verify_callback(const std::function<SSLVerifyCallback>& callback)
+{
+    std::error_code ec;
+    ssl_use_verify_callback(callback, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+}
+inline void Stream::handshake()
+{
+    std::error_code ec;
+    if (handshake(ec)) // Throws
+        throw std::system_error(ec);
+}
+
+inline std::size_t Stream::read(char* buffer, std::size_t size)
+{
+    std::error_code ec;
+    read(buffer, size, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return size;
+}
+
+inline std::size_t Stream::read(char* buffer, std::size_t size, std::error_code& ec)
+{
+    return StreamOps::read(*this, buffer, size, ec); // Throws
+}
+
+inline std::size_t Stream::read(char* buffer, std::size_t size, ReadAheadBuffer& rab)
+{
+    std::error_code ec;
+    read(buffer, size, rab, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return size;
+}
+
+inline std::size_t Stream::read(char* buffer, std::size_t size, ReadAheadBuffer& rab,
+                                std::error_code& ec)
+{
+    int delim = std::char_traits<char>::eof();
+    return StreamOps::buffered_read(*this, buffer, size, delim, rab, ec); // Throws
+}
+
+inline std::size_t Stream::read_until(char* buffer, std::size_t size, char delim,
+                                      ReadAheadBuffer& rab)
+{
+    std::error_code ec;
+    std::size_t n = read_until(buffer, size, delim, rab, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return n;
+}
+
+inline std::size_t Stream::read_until(char* buffer, std::size_t size, char delim,
+                                      ReadAheadBuffer& rab, std::error_code& ec)
+{
+    int delim_2 = std::char_traits<char>::to_int_type(delim);
+    return StreamOps::buffered_read(*this, buffer, size, delim_2, rab, ec); // Throws
+}
+
+inline std::size_t Stream::write(const char* data, std::size_t size)
+{
+    std::error_code ec;
+    write(data, size, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return size;
+}
+
+inline std::size_t Stream::write(const char* data, std::size_t size, std::error_code& ec)
+{
+    return StreamOps::write(*this, data, size, ec); // Throws
+}
+
+inline std::size_t Stream::read_some(char* buffer, std::size_t size)
+{
+    std::error_code ec;
+    std::size_t n = read_some(buffer, size, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return n;
+}
+
+inline std::size_t Stream::read_some(char* buffer, std::size_t size, std::error_code& ec)
+{
+    return StreamOps::read_some(*this, buffer, size, ec); // Throws
+}
+
+inline std::size_t Stream::write_some(const char* data, std::size_t size)
+{
+    std::error_code ec;
+    std::size_t n = write_some(data, size, ec); // Throws
+    if (ec)
+        throw std::system_error(ec);
+    return n;
+}
+
+inline std::size_t Stream::write_some(const char* data, std::size_t size, std::error_code& ec)
+{
+    return StreamOps::write_some(*this, data, size, ec); // Throws
+}
+
+inline void Stream::shutdown()
+{
+    std::error_code ec;
+    if (shutdown(ec)) // Throws
+        throw std::system_error(ec);
+}
+
+template<class H> inline void Stream::async_handshake(H handler)
+{
+    LendersHandshakeOperPtr op =
+        Service::alloc<HandshakeOper<H>>(m_tcp_socket.m_read_oper, *this,
+                                         std::move(handler)); // Throws
+    m_tcp_socket.m_desc.initiate_oper(std::move(op)); // Throws
+}
+
+template<class H> inline void Stream::async_read(char* buffer, std::size_t size, H handler)
+{
+    bool is_read_some = false;
+    StreamOps::async_read(*this, buffer, size, is_read_some, std::move(handler)); // Throws
+}
+
+template<class H>
+inline void Stream::async_read(char* buffer, std::size_t size, ReadAheadBuffer& rab, H handler)
+{
+    int delim = std::char_traits<char>::eof();
+    StreamOps::async_buffered_read(*this, buffer, size, delim, rab, std::move(handler)); // Throws
+}
+
+template<class H>
+inline void Stream::async_read_until(char* buffer, std::size_t size, char delim,
+                                     ReadAheadBuffer& rab, H handler)
+{
+    int delim_2 = std::char_traits<char>::to_int_type(delim);
+    StreamOps::async_buffered_read(*this, buffer, size, delim_2, rab, std::move(handler)); // Throws
+}
+
+template<class H> inline void Stream::async_write(const char* data, std::size_t size, H handler)
+{
+    bool is_write_some = false;
+    StreamOps::async_write(*this, data, size, is_write_some, std::move(handler)); // Throws
+}
+
+template<class H> inline void Stream::async_read_some(char* buffer, std::size_t size, H handler)
+{
+    bool is_read_some = true;
+    StreamOps::async_read(*this, buffer, size, is_read_some, std::move(handler)); // Throws
+}
+
+template<class H> inline void Stream::async_write_some(const char* data, std::size_t size, H handler)
+{
+    bool is_write_some = true;
+    StreamOps::async_write(*this, data, size, is_write_some, std::move(handler)); // Throws
+}
+
+template<class H> inline void Stream::async_shutdown(H handler)
+{
+    LendersShutdownOperPtr op =
+        Service::alloc<ShutdownOper<H>>(m_tcp_socket.m_write_oper, *this,
+                                        std::move(handler)); // Throws
+    m_tcp_socket.m_desc.initiate_oper(std::move(op)); // Throws
+}
+
+inline void Stream::do_init_read_async(std::error_code&, Want& want) noexcept
+{
+    want = Want::nothing; // Proceed immediately unless there is an error
+}
+
+inline void Stream::do_init_write_async(std::error_code&, Want& want) noexcept
+{
+    want = Want::nothing; // Proceed immediately unless there is an error
+}
+
+inline std::size_t Stream::do_read_some_sync(char* buffer, std::size_t size,
+                                             std::error_code& ec) noexcept
+{
+    Want want = Want::nothing;
+    std::size_t n = do_read_some_async(buffer, size, ec, want);
+    if (n == 0 && want != Want::nothing)
+        ec = error::resource_unavailable_try_again;
+    return n;
+}
+
+inline std::size_t Stream::do_write_some_sync(const char* data, std::size_t size,
+                                              std::error_code& ec) noexcept
+{
+    Want want = Want::nothing;
+    std::size_t n = do_write_some_async(data, size, ec, want);
+    if (n == 0 && want != Want::nothing)
+        ec = error::resource_unavailable_try_again;
+    return n;
+}
+
+inline std::size_t Stream::do_read_some_async(char* buffer, std::size_t size,
+                                              std::error_code& ec, Want& want) noexcept
+{
+    return ssl_read(buffer, size, ec, want);
+}
+
+inline std::size_t Stream::do_write_some_async(const char* data, std::size_t size,
+                                               std::error_code& ec, Want& want) noexcept
+{
+    return ssl_write(data, size, ec, want);
+}
+
+inline Socket& Stream::lowest_layer() noexcept
+{
+    return m_tcp_socket;
+}
+
+
+#if REALM_HAVE_OPENSSL
+
+inline void Stream::ssl_handshake(std::error_code& ec, Want& want) noexcept
+{
+    auto perform = [this]() noexcept {
+        switch (m_handshake_type) {
+            case client:
+                return do_ssl_connect();
+            case server:
+                return do_ssl_accept();
+        }
+        REALM_ASSERT(false);
+        return 0;
+    };
+    std::size_t n = ssl_perform(std::move(perform), ec, want);
+    REALM_ASSERT(n == 0 || n == 1);
+    if (want == Want::nothing && n == 0 && !ec) {
+        // End of input on TCP socket
+        ec = network::premature_end_of_input;
+    }
+}
+
+inline std::size_t Stream::ssl_read(char* buffer, std::size_t size,
+                                    std::error_code& ec, Want& want) noexcept
+{
+    auto perform = [this, buffer, size]() noexcept {
+        return do_ssl_read(buffer, size);
+    };
+    std::size_t n = ssl_perform(std::move(perform), ec, want);
+    if (want == Want::nothing && n == 0 && !ec) {
+        // End of input on TCP socket
+        if (SSL_get_shutdown(m_ssl) & SSL_RECEIVED_SHUTDOWN) {
+            ec = network::end_of_input;
+        }
+        else {
+            ec = network::premature_end_of_input;
+        }
+    }
+    return n;
+}
+
+inline std::size_t Stream::ssl_write(const char* data, std::size_t size,
+                                     std::error_code& ec, Want& want) noexcept
+{
+    // While OpenSSL is able to continue writing after we have received the
+    // close notify alert fro the remote peer, Apple's Secure Transport API is
+    // not, so to achieve common behaviour, we make sure that any such attempt
+    // will result in an `error::broken_pipe` error.
+    if ((SSL_get_shutdown(m_ssl) & SSL_RECEIVED_SHUTDOWN) != 0) {
+        ec = error::broken_pipe;
+        want = Want::nothing;
+        return 0;
+    }
+    auto perform = [this, data, size]() noexcept {
+        return do_ssl_write(data, size);
+    };
+    std::size_t n = ssl_perform(std::move(perform), ec, want);
+    if (want == Want::nothing && n == 0 && !ec) {
+        // End of input on TCP socket
+        ec = network::premature_end_of_input;
+    }
+    return n;
+}
+
+inline bool Stream::ssl_shutdown(std::error_code& ec, Want& want) noexcept
+{
+    auto perform = [this]() noexcept {
+        return do_ssl_shutdown();
+    };
+    std::size_t n = ssl_perform(std::move(perform), ec, want);
+    REALM_ASSERT(n == 0 || n == 1);
+    if (want == Want::nothing && n == 0 && !ec) {
+        // The first invocation of SSL_shutdown() does not signal completion
+        // until the shutdown alert has been sent to the peer, or an error
+        // occurred (does not wait for acknowledgment).
+        //
+        // The second invocation (after a completed first invocation) does not
+        // signal completion until the peers shutdown alert has been received,
+        // or an error occurred.
+        //
+        // It is believed that:
+        //
+        // If this is the first time SSL_shutdown() is called, and
+        // `SSL_get_shutdown() & SSL_SENT_SHUTDOWN` evaluates to nonzero, then a
+        // zero return value means "partial success" (shutdown alert was sent,
+        // but the peers shutdown alert was not yet received), and 1 means "full
+        // success" (peers shutdown alert has already been received).
+        //
+        // If this is the first time SSL_shutdown() is called, and
+        // `SSL_get_shutdown() & SSL_SENT_SHUTDOWN` valuates to zero, then a
+        // zero return value means "premature end of input", and 1 is supposedly
+        // not a possibility.
+        //
+        // If this is the second time SSL_shutdown() is called (after the first
+        // call has returned zero), then a zero return value means "premature
+        // end of input", and 1 means "full success" (peers shutdown alert has
+        // now been received).
+        if ((SSL_get_shutdown(m_ssl) & SSL_SENT_SHUTDOWN) == 0)
+            ec = network::premature_end_of_input;
+    }
+    return (n > 0);
+}
+
+// Provides a homogeneous, and mostly quirks-free interface across the OpenSSL
+// operations (handshake, read, write, shutdown).
+//
+// First of all, if the operation remains incomplete (neither successfully
+// completed, nor failed), ssl_perform() will set `ec` to `std::system_error()`,
+// `want` to something other than `Want::nothing`, and return zero. Note that
+// read and write operations are partial in the sense that they do not need to
+// read or write everything before completing successfully. They only need to
+// read or write at least one byte to complete successfully.
+//
+// Such a situation will normally only happen when the underlying TCP socket is
+// in nonblocking mode, and the read/write requirements of the operation could
+// not be immediately accommodated. However, as is noted in the SSL_write() man
+// page, it can also happen in blocking mode (at least while writing).
+//
+// If an error occurred, ssl_perform() will set `ec` to something other than
+// `std::system_error()`, `want` to `Want::nothing`, and return 0.
+//
+// If no error occurred, and the operation completed (`!ec && want ==
+// Want::nothing`), then the return value indicates the outcome of the
+// operation.
+//
+// In general, a nonzero value means "full" success, and a zero value means
+// "partial" success, however, a zero result can also generally mean "premature
+// end of input" / "unclean protocol termination".
+//
+// Assuming there is no premature end of input, then for reads and writes, the
+// returned value is the number of transferred bytes. Zero for read on end of
+// input. Never zero for write. For handshake it is always 1. For shutdown it is
+// 1 if the peer shutdown alert was already received, otherwise it is zero.
+//
+// ssl_read() should use `SSL_get_shutdown() & SSL_RECEIVED_SHUTDOWN` to
+// distinguish between the two possible meanings of zero.
+//
+// ssl_shutdown() should use `SSL_get_shutdown() & SSL_SENT_SHUTDOWN` to
+// distinguish between the two possible meanings of zero.
+template<class Oper>
+std::size_t Stream::ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept
+{
+    ERR_clear_error();
+    m_bio_error_code = std::error_code(); // Success
+    int ret = oper();
+    int ssl_error = SSL_get_error(m_ssl, ret);
+    int sys_error = int(ERR_get_error());
+
+    // Guaranteed by the documentation of SSL_get_error()
+    REALM_ASSERT((ret > 0) == (ssl_error == SSL_ERROR_NONE));
+
+    REALM_ASSERT(!m_bio_error_code || ssl_error == SSL_ERROR_SYSCALL);
+
+    // Judging from various comments in the man pages, and from experience with
+    // the API, it seems that,
+    //
+    //   ret=0, ssl_error=SSL_ERROR_SYSCALL, sys_error=0
+    //
+    // is supposed to be an indicator of "premature end of input" / "unclean
+    // protocol termination", while
+    //
+    //   ret=0, ssl_error=SSL_ERROR_ZERO_RETURN
+    //
+    // is supposed to be an indicator of the following success conditions:
+    //
+    //   - Mature end of input / clean protocol termination.
+    //
+    //   - Successful transmission of the shutdown alert, but no prior reception
+    //     of shutdown alert from peer.
+    //
+    // Unfortunately, as is also remarked in various places in the man pages,
+    // those two success conditions may actually result in `ret=0,
+    // ssl_error=SSL_ERROR_SYSCALL, sys_error=0` too, and it seems that they
+    // almost always do.
+    //
+    // This means that we cannot properly discriminate between these conditions
+    // in ssl_perform(), and will have to defer to the caller to interpret the
+    // situation. Since thay cannot be properly told apart, we report all
+    // `ret=0, ssl_error=SSL_ERROR_SYSCALL, sys_error=0` and `ret=0,
+    // ssl_error=SSL_ERROR_ZERO_RETURN` cases as the latter.
+    switch (ssl_error) {
+        case SSL_ERROR_NONE:
+            ec = std::error_code(); // Success
+            want = Want::nothing;
+            return std::size_t(ret); // ret > 0
+        case SSL_ERROR_ZERO_RETURN:
+            ec = std::error_code(); // Success
+            want = Want::nothing;
+            return 0;
+        case SSL_ERROR_WANT_READ:
+            ec = std::error_code(); // Success
+            want = Want::read;
+            return 0;
+        case SSL_ERROR_WANT_WRITE:
+            ec = std::error_code(); // Success
+            want = Want::write;
+            return 0;
+        case SSL_ERROR_SYSCALL:
+            if (REALM_UNLIKELY(sys_error != 0)) {
+                ec = make_basic_system_error_code(sys_error);
+            }
+            else if (REALM_UNLIKELY(m_bio_error_code)) {
+                ec = m_bio_error_code;
+            }
+            else if (ret == 0) {
+                // ret = 0, ssl_eror = SSL_ERROR_SYSCALL, sys_error = 0
+                //
+                // See remarks above!
+                ec = std::error_code(); // Success
+            }
+            else {
+                // ret = -1, ssl_eror = SSL_ERROR_SYSCALL, sys_error = 0
+                //
+                // This situation arises in OpenSSL version >= 1.1.
+                // It has been observed in the SSL_connect call if the
+                // other endpoint terminates the connection during
+                // SSL_connect. The OpenSSL documentation states
+                // that ret = -1 implies an underlying BIO error and
+                // that errno should be consulted. However,
+                // errno = 0(Undefined error) in the observed case.
+                // At the moment. we will report
+                // premature_end_of_input.
+                // If we see this error case occurring in other situations in
+                // the future, we will have to update this case.
+                ec = network::premature_end_of_input;
+            }
+            want = Want::nothing;
+            return 0;
+        case SSL_ERROR_SSL:
+            ec = std::error_code(sys_error, openssl_error_category());
+            want = Want::nothing;
+            return 0;
+        default:
+            break;
+    }
+    // We are not supposed to ever get here
+    REALM_ASSERT(false);
+    return 0;
+}
+
+inline int Stream::do_ssl_accept() noexcept
+{
+    int ret = SSL_accept(m_ssl);
+    return ret;
+}
+
+inline int Stream::do_ssl_connect() noexcept
+{
+    int ret = SSL_connect(m_ssl);
+    return ret;
+}
+
+inline int Stream::do_ssl_read(char* buffer, std::size_t size) noexcept
+{
+    int size_2 = int(size);
+    if (size > unsigned(std::numeric_limits<int>::max()))
+        size_2 = std::size_t(std::numeric_limits<int>::max());
+    int ret = SSL_read(m_ssl, buffer, size_2);
+    return ret;
+}
+
+inline int Stream::do_ssl_write(const char* data, std::size_t size) noexcept
+{
+    int size_2 = int(size);
+    if (size > unsigned(std::numeric_limits<int>::max()))
+        size_2 = std::size_t(std::numeric_limits<int>::max());
+    int ret = SSL_write(m_ssl, data, size_2);
+    return ret;
+}
+
+inline int Stream::do_ssl_shutdown() noexcept
+{
+    int ret = SSL_shutdown(m_ssl);
+    return ret;
+}
+
+#elif REALM_HAVE_SECURE_TRANSPORT
+
+// Provides a homogeneous, and mostly quirks-free interface across the SecureTransport
+// operations (handshake, read, write, shutdown).
+//
+// First of all, if the operation remains incomplete (neither successfully
+// completed, nor failed), ssl_perform() will set `ec` to `std::system_error()`,
+// `want` to something other than `Want::nothing`, and return zero.
+//
+// If an error occurred, ssl_perform() will set `ec` to something other than
+// `std::system_error()`, `want` to `Want::nothing`, and return 0.
+//
+// If no error occurred, and the operation completed (`!ec && want ==
+// Want::nothing`), then the return value indicates the outcome of the
+// operation.
+//
+// In general, a nonzero value means "full" success, and a zero value means
+// "partial" success, however, a zero result can also generally mean "premature
+// end of input" / "unclean protocol termination".
+//
+// Assuming there is no premature end of input, then for reads and writes, the
+// returned value is the number of transferred bytes. Zero for read on end of
+// input. Never zero for write. For handshake it is always 1. For shutdown it is
+// 1 if the peer shutdown alert was already received, otherwise it is zero.
+template<class Oper>
+std::size_t Stream::ssl_perform(Oper oper, std::error_code& ec, Want& want) noexcept
+{
+    OSStatus result;
+    std::size_t n;
+    std::tie(result, n) = oper();
+
+    if (result == noErr) {
+        ec = std::error_code();
+        want = Want::nothing;
+        return n;
+    }
+
+    if (result == errSSLWouldBlock) {
+        REALM_ASSERT(m_last_operation);
+        ec = std::error_code();
+        want = m_last_operation == BlockingOperation::read ? Want::read : Want::write;
+        m_last_operation = {};
+        return n;
+    }
+
+    if (result == errSSLClosedGraceful) {
+        ec = network::end_of_input;
+        want = Want::nothing;
+        return n;
+    }
+
+    if (result == errSSLClosedAbort || result == errSSLClosedNoNotify) {
+        ec = network::premature_end_of_input;
+        want = Want::nothing;
+        return n;
+    }
+
+    if (result == errSecIO) {
+        // A generic I/O error means something went wrong at a lower level. Use the error
+        // code we smuggled out of our lower-level functions to provide a more specific error.
+        REALM_ASSERT(m_last_error);
+        ec = m_last_error;
+        want = Want::nothing;
+        return n;
+    }
+
+    ec = std::error_code(result, secure_transport_error_category());
+    want = Want::nothing;
+    return 0;
+}
+#endif // REALM_HAVE_OPENSSL / REALM_HAVE_SECURE_TRANSPORT
+
+} // namespace ssl
+} // namespace network
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_NETWORK_SSL_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/optional.hpp b/iOS/Pods/Realm/include/core/realm/util/optional.hpp
new file mode 100644 (file)
index 0000000..29a8be1
--- /dev/null
@@ -0,0 +1,728 @@
+/*************************************************************************
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#pragma once
+#ifndef REALM_UTIL_OPTIONAL_HPP
+#define REALM_UTIL_OPTIONAL_HPP
+
+#include <realm/util/features.h>
+
+#include <stdexcept>  // std::logic_error
+#include <functional> // std::less
+
+namespace realm {
+namespace util {
+
+template <class T>
+class Optional;
+
+// some() should be the equivalent of the proposed C++17 `make_optional`.
+template <class T, class... Args>
+Optional<T> some(Args&&...);
+template <class T>
+struct Some;
+
+// Note: Should conform with the future std::nullopt_t and std::in_place_t.
+struct None {
+    constexpr explicit None(int)
+    {
+    }
+};
+static constexpr None none{0};
+struct InPlace {
+    constexpr InPlace()
+    {
+    }
+};
+static constexpr InPlace in_place;
+
+// Note: Should conform with the future std::bad_optional_access.
+struct BadOptionalAccess : std::logic_error {
+    explicit BadOptionalAccess(const std::string& what_arg)
+        : std::logic_error(what_arg)
+    {
+    }
+    explicit BadOptionalAccess(const char* what_arg)
+        : std::logic_error(what_arg)
+    {
+    }
+};
+
+} // namespace util
+
+namespace _impl {
+
+template <class T, bool = std::is_trivially_destructible<T>::value>
+struct OptionalStorage;
+
+template <class T, class U>
+struct TypeIsAssignableToOptional {
+    // Constraints from [optional.object.assign.18]
+    static const bool value = (std::is_same<typename std::remove_reference<U>::type, T>::value &&
+                               std::is_constructible<T, U>::value && std::is_assignable<T&, U>::value);
+};
+
+} // namespace _impl
+
+namespace util {
+
+// Note: Should conform with the future std::optional.
+template <class T>
+class Optional : private _impl::OptionalStorage<T> {
+public:
+    using value_type = T;
+
+    constexpr Optional();
+    constexpr Optional(None);
+    Optional(Optional<T>&& other);
+    Optional(const Optional<T>& other);
+
+    constexpr Optional(T&& value);
+    constexpr Optional(const T& value);
+
+    template <class... Args>
+    constexpr Optional(InPlace tag, Args&&...);
+    // FIXME: std::optional specifies an std::initializer_list constructor overload as well.
+
+    Optional<T>& operator=(None);
+    Optional<T>& operator=(Optional<T>&& other);
+    Optional<T>& operator=(const Optional<T>& other);
+
+    template <class U, class = typename std::enable_if<_impl::TypeIsAssignableToOptional<T, U>::value>::type>
+    Optional<T>& operator=(U&& value);
+
+    explicit constexpr operator bool() const;
+    constexpr const T& value() const;      // Throws
+    T& value();                            // Throws, FIXME: Can be constexpr with C++14
+    constexpr const T& operator*() const;  // Throws
+    T& operator*();                        // Throws, FIXME: Can be constexpr with C++14
+    constexpr const T* operator->() const; // Throws
+    T* operator->();                       // Throws, FIXME: Can be constexpr with C++14
+
+    template <class U>
+    constexpr T value_or(U&& value) const &;
+
+    template <class U>
+    T value_or(U&& value) &&;
+
+    void swap(Optional<T>& other); // FIXME: Add noexcept() clause
+
+    template <class... Args>
+    void emplace(Args&&...);
+    // FIXME: std::optional specifies an std::initializer_list overload for `emplace` as well.
+private:
+    using Storage = _impl::OptionalStorage<T>;
+    using Storage::m_engaged;
+    using Storage::m_value;
+
+    constexpr bool is_engaged() const
+    {
+        return m_engaged;
+    }
+    void set_engaged(bool b)
+    {
+        m_engaged = b;
+    }
+    void clear();
+};
+
+/// An Optional<void> is functionally equivalent to a bool.
+/// Note: C++17 does not (yet) specify this specialization, but it is convenient
+/// as a "safer bool", especially in the presence of `fmap`.
+/// Disabled for compliance with std::optional.
+// template <>
+// class Optional<void> {
+// public:
+//     Optional() {}
+//     Optional(None) {}
+//     Optional(Optional<void>&&) = default;
+//     Optional(const Optional<void>&) = default;
+//     explicit operator bool() const { return m_engaged; }
+// private:
+//     bool m_engaged = false;
+//     friend struct Some<void>;
+// };
+
+/// An Optional<T&> is a non-owning nullable pointer that throws on dereference.
+// FIXME: Visual Studio 2015's constexpr support isn't sufficient to allow Optional<T&> to compile
+// in constexpr contexts.
+template <class T>
+class Optional<T&> {
+public:
+    using value_type = T&;
+    using target_type = typename std::decay<T>::type;
+
+    constexpr Optional()
+    {
+    }
+    constexpr Optional(None)
+    {
+    } // FIXME: Was a delegating constructor, but not fully supported in VS2015
+    Optional(const Optional<T&>& other) = default;
+    template <class U>
+    Optional(const Optional<U&>& other)
+        : m_ptr(other.m_ptr)
+    {
+    }
+    template <class U>
+    Optional(std::reference_wrapper<U> ref)
+        : m_ptr(&ref.get())
+    {
+    }
+
+    constexpr Optional(T& init_value)
+        : m_ptr(&init_value)
+    {
+    }
+    Optional(T&& value) = delete; // Catches accidental references to rvalue temporaries.
+
+    Optional<T&>& operator=(None)
+    {
+        m_ptr = nullptr;
+        return *this;
+    }
+    Optional<T&>& operator=(const Optional<T&>& other)
+    {
+        m_ptr = other.m_ptr;
+        return *this;
+    }
+
+    template <class U>
+    Optional<T&>& operator=(std::reference_wrapper<U> ref)
+    {
+        m_ptr = &ref.get();
+        return *this;
+    }
+
+    explicit constexpr operator bool() const
+    {
+        return m_ptr;
+    }
+    constexpr const target_type& value() const; // Throws
+    target_type& value();                       // Throws
+    constexpr const target_type& operator*() const
+    {
+        return value();
+    }
+    target_type& operator*()
+    {
+        return value();
+    }
+    constexpr const target_type* operator->() const
+    {
+        return &value();
+    }
+    target_type* operator->()
+    {
+        return &value();
+    }
+
+    void swap(Optional<T&> other); // FIXME: Add noexcept() clause
+private:
+    T* m_ptr = nullptr;
+
+    template <class U>
+    friend class Optional;
+};
+
+
+template <class T>
+struct RemoveOptional {
+    using type = T;
+};
+template <class T>
+struct RemoveOptional<Optional<T>> {
+    using type = typename RemoveOptional<T>::type; // Remove recursively
+};
+
+
+/// Implementation:
+
+template <class T>
+struct Some {
+    template <class... Args>
+    static Optional<T> some(Args&&... args)
+    {
+        return Optional<T>{std::forward<Args>(args)...};
+    }
+};
+
+/// Disabled for compliance with std::optional.
+// template <>
+// struct Some<void> {
+//     static Optional<void> some()
+//     {
+//         Optional<void> opt;
+//         opt.m_engaged = true;
+//         return opt;
+//     }
+// };
+
+template <class T, class... Args>
+Optional<T> some(Args&&... args)
+{
+    return Some<T>::some(std::forward<Args>(args)...);
+}
+
+
+template <class T>
+constexpr Optional<T>::Optional()
+    : Storage(none)
+{
+}
+
+template <class T>
+constexpr Optional<T>::Optional(None)
+    : Storage(none)
+{
+}
+
+template <class T>
+Optional<T>::Optional(Optional<T>&& other)
+    : Storage(none)
+{
+    if (other.m_engaged) {
+        new (&m_value) T(std::move(other.m_value));
+        m_engaged = true;
+    }
+}
+
+template <class T>
+Optional<T>::Optional(const Optional<T>& other)
+    : Storage(none)
+{
+    if (other.m_engaged) {
+        new (&m_value) T(other.m_value);
+        m_engaged = true;
+    }
+}
+
+template <class T>
+constexpr Optional<T>::Optional(T&& r_value)
+    : Storage(std::move(r_value))
+{
+}
+
+template <class T>
+constexpr Optional<T>::Optional(const T& l_value)
+    : Storage(l_value)
+{
+}
+
+template <class T>
+template <class... Args>
+constexpr Optional<T>::Optional(InPlace, Args&&... args)
+    : Storage(std::forward<Args>(args)...)
+{
+}
+
+template <class T>
+void Optional<T>::clear()
+{
+    if (m_engaged) {
+        m_value.~T();
+        m_engaged = false;
+    }
+}
+
+template <class T>
+Optional<T>& Optional<T>::operator=(None)
+{
+    clear();
+    return *this;
+}
+
+template <class T>
+Optional<T>& Optional<T>::operator=(Optional<T>&& other)
+{
+    if (m_engaged) {
+        if (other.m_engaged) {
+            m_value = std::move(other.m_value);
+        }
+        else {
+            clear();
+        }
+    }
+    else {
+        if (other.m_engaged) {
+            new (&m_value) T(std::move(other.m_value));
+            m_engaged = true;
+        }
+    }
+    return *this;
+}
+
+template <class T>
+Optional<T>& Optional<T>::operator=(const Optional<T>& other)
+{
+    if (m_engaged) {
+        if (other.m_engaged) {
+            m_value = other.m_value;
+        }
+        else {
+            clear();
+        }
+    }
+    else {
+        if (other.m_engaged) {
+            new (&m_value) T(other.m_value);
+            m_engaged = true;
+        }
+    }
+    return *this;
+}
+
+template <class T>
+template <class U, class>
+Optional<T>& Optional<T>::operator=(U&& r_value)
+{
+    if (m_engaged) {
+        m_value = std::forward<U>(r_value);
+    }
+    else {
+        new (&m_value) T(std::forward<U>(r_value));
+        m_engaged = true;
+    }
+    return *this;
+}
+
+template <class T>
+constexpr Optional<T>::operator bool() const
+{
+    return m_engaged;
+}
+
+template <class T>
+constexpr const T& Optional<T>::value() const
+{
+    return m_engaged ? m_value : (throw BadOptionalAccess{"bad optional access"}, m_value);
+}
+
+template <class T>
+T& Optional<T>::value()
+{
+    if (!m_engaged) {
+        throw BadOptionalAccess{"bad optional access"};
+    }
+    return m_value;
+}
+
+template <class T>
+constexpr const typename Optional<T&>::target_type& Optional<T&>::value() const
+{
+    return m_ptr ? *m_ptr : (throw BadOptionalAccess{"bad optional access"}, *m_ptr);
+}
+
+template <class T>
+typename Optional<T&>::target_type& Optional<T&>::value()
+{
+    if (!m_ptr) {
+        throw BadOptionalAccess{"bad optional access"};
+    }
+    return *m_ptr;
+}
+
+template <class T>
+constexpr const T& Optional<T>::operator*() const
+{
+    // Note: This differs from std::optional, which doesn't throw.
+    return value();
+}
+
+template <class T>
+T& Optional<T>::operator*()
+{
+    // Note: This differs from std::optional, which doesn't throw.
+    return value();
+}
+
+template <class T>
+constexpr const T* Optional<T>::operator->() const
+{
+    // Note: This differs from std::optional, which doesn't throw.
+    return &value();
+}
+
+template <class T>
+T* Optional<T>::operator->()
+{
+    // Note: This differs from std::optional, which doesn't throw.
+    return &value();
+}
+
+template <class T>
+template <class U>
+constexpr T Optional<T>::value_or(U&& otherwise) const &
+{
+    return m_engaged ? T{m_value} : T{std::forward<U>(otherwise)};
+}
+
+template <class T>
+template <class U>
+T Optional<T>::value_or(U&& otherwise) &&
+{
+    if (is_engaged()) {
+        return T(std::move(m_value));
+    }
+    else {
+        return T(std::forward<U>(otherwise));
+    }
+}
+
+template <class T>
+void Optional<T>::swap(Optional<T>& other)
+{
+    // FIXME: This might be optimizable.
+    Optional<T> tmp = std::move(other);
+    other = std::move(*this);
+    *this = std::move(tmp);
+}
+
+template <class T>
+template <class... Args>
+void Optional<T>::emplace(Args&&... args)
+{
+    clear();
+    new (&m_value) T(std::forward<Args>(args)...);
+    m_engaged = true;
+}
+
+
+template <class T>
+constexpr Optional<typename std::decay<T>::type> make_optional(T&& value)
+{
+    using Type = typename std::decay<T>::type;
+    return some<Type>(std::forward<T>(value));
+}
+
+template <class T>
+bool operator==(const Optional<T>& lhs, const Optional<T>& rhs)
+{
+    if (!lhs && !rhs) {
+        return true;
+    }
+    if (lhs && rhs) {
+        return *lhs == *rhs;
+    }
+    return false;
+}
+
+template <class T>
+bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs)
+{
+    return !(lhs == rhs);
+}
+
+template <class T>
+bool operator<(const Optional<T>& lhs, const Optional<T>& rhs)
+{
+    if (!rhs) {
+        return false;
+    }
+    if (!lhs) {
+        return true;
+    }
+    return std::less<T>{}(*lhs, *rhs);
+}
+
+template <class T>
+bool operator>(const util::Optional<T>& lhs, const util::Optional<T>& rhs)
+{
+    if (!lhs) {
+        return false;
+    }
+    if (!rhs) {
+        return true;
+    }
+    return std::greater<T>{}(*lhs, *rhs);
+}
+
+template <class T>
+bool operator==(const Optional<T>& lhs, None)
+{
+    return !bool(lhs);
+}
+
+template <class T>
+bool operator!=(const Optional<T>& lhs, None)
+{
+    return bool(lhs);
+}
+
+template <class T>
+bool operator<(const Optional<T>& lhs, None)
+{
+    static_cast<void>(lhs);
+    return false;
+}
+
+template <class T>
+bool operator==(None, const Optional<T>& rhs)
+{
+    return !bool(rhs);
+}
+
+template <class T>
+bool operator!=(None, const Optional<T>& rhs)
+{
+    return bool(rhs);
+}
+
+template <class T>
+bool operator<(None, const Optional<T>& rhs)
+{
+    return bool(rhs);
+}
+
+template <class T, class U>
+bool operator==(const Optional<T>& lhs, const U& rhs)
+{
+    return lhs ? *lhs == rhs : false;
+}
+
+template <class T>
+bool operator<(const Optional<T>& lhs, const T& rhs)
+{
+    return lhs ? std::less<T>{}(*lhs, rhs) : true;
+}
+
+template <class T, class U>
+bool operator==(const T& lhs, const Optional<U>& rhs)
+{
+    return rhs ? lhs == *rhs : false;
+}
+
+template <class T>
+bool operator<(const T& lhs, const Optional<T>& rhs)
+{
+    return rhs ? std::less<T>{}(lhs, *rhs) : false;
+}
+
+template <class T, class F>
+auto operator>>(Optional<T> lhs, F&& rhs) -> decltype(fmap(lhs, std::forward<F>(rhs)))
+{
+    return fmap(lhs, std::forward<F>(rhs));
+}
+
+template <class OS, class T>
+OS& operator<<(OS& os, const Optional<T>& rhs)
+{
+    if (rhs) {
+        os << "some(" << *rhs << ")";
+    }
+    else {
+        os << "none";
+    }
+    return os;
+}
+
+template <class T>
+T unwrap(T&& value)
+{
+    return value;
+}
+
+template <class T>
+T unwrap(util::Optional<T>&& value)
+{
+    return *value;
+}
+
+template <class T>
+T unwrap(const util::Optional<T>& value)
+{
+    return *value;
+}
+
+template <class T>
+T unwrap(util::Optional<T>& value)
+{
+    return *value;
+}
+
+} // namespace util
+
+namespace _impl {
+
+// T is trivially destructible.
+template <class T>
+struct OptionalStorage<T, true> {
+    union {
+        T m_value;
+        char m_null_state;
+    };
+    bool m_engaged = false;
+
+    constexpr OptionalStorage(realm::util::None)
+        : m_null_state()
+    {
+    }
+    constexpr OptionalStorage(T&& value)
+        : m_value(std::move(value))
+        , m_engaged(true)
+    {
+    }
+
+    template <class... Args>
+    constexpr OptionalStorage(Args&&... args)
+        : m_value(args...)
+        , m_engaged(true)
+    {
+    }
+};
+
+// T is not trivially destructible.
+template <class T>
+struct OptionalStorage<T, false> {
+    union {
+        T m_value;
+        char m_null_state;
+    };
+    bool m_engaged = false;
+
+    constexpr OptionalStorage(realm::util::None)
+        : m_null_state()
+    {
+    }
+    constexpr OptionalStorage(T&& value)
+        : m_value(std::move(value))
+        , m_engaged(true)
+    {
+    }
+
+    template <class... Args>
+    constexpr OptionalStorage(Args&&... args)
+        : m_value(args...)
+        , m_engaged(true)
+    {
+    }
+
+    ~OptionalStorage()
+    {
+        if (m_engaged)
+            m_value.~T();
+    }
+};
+
+} // namespace _impl
+
+using util::none;
+
+} // namespace realm
+
+#endif // REALM_UTIL_OPTIONAL_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/overload.hpp b/iOS/Pods/Realm/include/core/realm/util/overload.hpp
new file mode 100644 (file)
index 0000000..49188ae
--- /dev/null
@@ -0,0 +1,70 @@
+/*************************************************************************
+ *
+ * Copyright 2017 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_UTIL_OVERLOAD_HPP
+#define REALM_UTIL_OVERLOAD_HPP
+
+#include <utility>
+
+namespace realm {
+
+namespace _impl {
+
+template<typename Fn, typename... Fns>
+struct Overloaded;
+
+} // namespace _impl
+
+
+namespace util {
+
+// Declare an overload set using lambdas or other function objects.
+// A minimal version of C++ Library Evolution Working Group proposal P0051R2.
+
+template<typename... Fns>
+_impl::Overloaded<Fns...> overload(Fns&&... f)
+{
+    return _impl::Overloaded<Fns...>(std::forward<Fns>(f)...);
+}
+
+} // namespace util
+
+
+namespace _impl {
+
+template<typename Fn, typename... Fns>
+struct Overloaded : Fn, Overloaded<Fns...> {
+    template<typename U, typename... Rest>
+    Overloaded(U&& fn, Rest&&... rest) : Fn(std::forward<U>(fn)), Overloaded<Fns...>(std::forward<Rest>(rest)...) { }
+
+    using Fn::operator();
+    using Overloaded<Fns...>::operator();
+};
+
+template<typename Fn>
+struct Overloaded<Fn> : Fn {
+    template<typename U>
+    Overloaded(U&& fn) : Fn(std::forward<U>(fn)) { }
+
+    using Fn::operator();
+};
+
+} // namespace _impl
+} // namespace realm
+
+#endif // REALM_UTIL_OVERLOAD_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/priority_queue.hpp b/iOS/Pods/Realm/include/core/realm/util/priority_queue.hpp
new file mode 100644 (file)
index 0000000..a2a28c8
--- /dev/null
@@ -0,0 +1,304 @@
+/*************************************************************************
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+
+#pragma once
+#ifndef REALM_UTIL_PRIORITY_QUEUE_HPP
+#define REALM_UTIL_PRIORITY_QUEUE_HPP
+
+#include <vector>
+#include <functional>
+#include <algorithm>
+
+namespace realm {
+namespace util {
+
+
+/// PriorityQueue corresponds exactly to `std::priority_queue`, but has the extra feature
+/// of allowing iteration and erasure of elements in the queue.
+///
+/// PriorityQueue only allows const access to its elements, because non-const access
+/// would open up the risk of changing the ordering of the elements.
+///
+/// Note: As opposed to `std::priority_queue`, this does not store elements in a heap
+/// internally. Instead, elements are stored in sorted order. Users of this class are
+/// allowed to operate on this assumption.
+template <class T, class Container = std::vector<T>, class Compare = std::less<typename Container::value_type>>
+class PriorityQueue : private Compare {
+public:
+    using container_type = Container;
+    using value_type = typename Container::value_type;
+    using size_type = typename Container::size_type;
+    using reference = typename Container::reference;
+    using const_reference = typename Container::const_reference;
+    using const_reverse_iterator = typename Container::const_reverse_iterator;
+    using const_iterator = typename Container::const_iterator;
+
+    //@{
+    /// Construct a PriorityQueue, optionally providing a comparator object.
+    PriorityQueue(const Compare& comparator, const Container& cont);
+
+    explicit PriorityQueue(const Compare& comparator = Compare{}, Container&& cont = Container{});
+
+    template <class InputIt>
+    PriorityQueue(InputIt first, InputIt last, const Compare& comparator, const Container& cont);
+
+    template <class InputIt>
+    PriorityQueue(InputIt first, InputIt last, const Compare& comparator = Compare{}, Container&& cont = Container{});
+    //@}
+    // Skipping Allocator-specific template constructors.
+
+    PriorityQueue(const PriorityQueue&) = default;
+    PriorityQueue(PriorityQueue&&) = default;
+    PriorityQueue& operator=(const PriorityQueue&) = default;
+    PriorityQueue& operator=(PriorityQueue&&) = default;
+
+    bool empty() const;
+    size_type size() const;
+
+    //@{
+    /// Push an element to the priority queue.
+    ///
+    /// If insertion to the underlying `Container` invalidates
+    /// iterators and references, any iterators and references into this
+    /// priority queue are also invalidated. By default, this is the case.
+    void push(const T& value);
+    void push(T&& value);
+    //@}
+
+    /// Pop the largest element from the priority queue.
+    ///
+    /// If `pop_back` on the underlying `Container` invalidates
+    /// iterators and references, any iterators and reference into this
+    /// priority queue are also invalidated. By default, this is *NOT* the case.
+    ///
+    /// Calling `pop()` on an empty priority queue is undefined.
+    void pop();
+
+    /// Return a reference to the largest element of the priority queue.
+    ///
+    /// Calling `top()` on an empty priority queue is undefined.
+    const_reference top() const;
+
+    /// Pop the top of the queue and return it by moving it out of the queue.
+    ///
+    /// Note: This method does not exist in `std::priority_queue`.
+    ///
+    /// Calling `pop_top()` on an empty priorty queue is undefined.
+    value_type pop_top();
+
+    // FIXME: emplace() deliberately omitted for simplicity.
+
+    /// Swap the contents of this priority queue with the contents of \a other.
+    void swap(PriorityQueue& other);
+
+    // Not in std::priority_queue:
+
+    /// Return an iterator to the beginning of the queue (smallest element first).
+    const_iterator begin() const;
+
+    /// Return an iterator to the end of the queue (largest element last);
+    const_iterator end() const;
+
+    /// Return a reverse iterator into the priority queue (largest element first).
+    const_reverse_iterator rbegin() const;
+
+    /// Return a reverse iterator representing the end of the priority queue (smallest element last).
+    const_reverse_iterator rend() const;
+
+    /// Erase element pointed to by \a it.
+    ///
+    /// Note: This function differs from `std::priority_queue` by returning the erased
+    /// element using move semantics.
+    ///
+    /// Calling `erase()` with a beyond-end iterator (such as what is returned by `end()`)
+    /// is undefined.
+    value_type erase(const_iterator it);
+
+    /// Remove all elements from the priority queue.
+    void clear();
+
+    /// Calls `reserve()` on the underlying `Container`.
+    void reserve(size_type);
+
+private:
+    Container m_queue;
+
+    const Compare& compare() const;
+    Compare& compare();
+};
+
+
+/// Implementation
+
+template <class T, class Container, class Compare>
+PriorityQueue<T, Container, Compare>::PriorityQueue(const Compare& comparator, const Container& cont)
+    : Compare(comparator)
+    , m_queue(cont)
+{
+}
+
+template <class T, class Container, class Compare>
+PriorityQueue<T, Container, Compare>::PriorityQueue(const Compare& comparator, Container&& cont)
+    : Compare(comparator)
+    , m_queue(std::move(cont))
+{
+}
+
+template <class T, class Container, class Compare>
+template <class InputIt>
+PriorityQueue<T, Container, Compare>::PriorityQueue(InputIt first, InputIt last, const Compare& comparator,
+                                                    const Container& cont)
+    : Compare(comparator)
+    , m_queue(cont)
+{
+    for (auto it = first; it != last; ++it) {
+        push(*it);
+    }
+}
+
+template <class T, class Container, class Compare>
+template <class InputIt>
+PriorityQueue<T, Container, Compare>::PriorityQueue(InputIt first, InputIt last, const Compare& comparator,
+                                                    Container&& cont)
+    : Compare(comparator)
+    , m_queue(std::move(cont))
+{
+    for (auto it = first; it != last; ++it) {
+        push(*it);
+    }
+}
+
+template <class T, class Container, class Compare>
+typename PriorityQueue<T, Container, Compare>::size_type PriorityQueue<T, Container, Compare>::size() const
+{
+    return m_queue.size();
+}
+
+template <class T, class Container, class Compare>
+bool PriorityQueue<T, Container, Compare>::empty() const
+{
+    return m_queue.empty();
+}
+
+template <class T, class Container, class Compare>
+void PriorityQueue<T, Container, Compare>::push(const T& element)
+{
+    auto it = std::lower_bound(m_queue.begin(), m_queue.end(), element, compare());
+    m_queue.insert(it, element);
+}
+
+template <class T, class Container, class Compare>
+void PriorityQueue<T, Container, Compare>::push(T&& element)
+{
+    auto it = std::lower_bound(m_queue.begin(), m_queue.end(), element, compare());
+    m_queue.insert(it, std::move(element));
+}
+
+template <class T, class Container, class Compare>
+void PriorityQueue<T, Container, Compare>::pop()
+{
+    m_queue.pop_back();
+}
+
+template <class T, class Container, class Compare>
+typename PriorityQueue<T, Container, Compare>::const_reference PriorityQueue<T, Container, Compare>::top() const
+{
+    return m_queue.back();
+}
+
+template <class T, class Container, class Compare>
+typename PriorityQueue<T, Container, Compare>::value_type PriorityQueue<T, Container, Compare>::pop_top()
+{
+    value_type value = std::move(m_queue.back());
+    m_queue.pop_back();
+    return value;
+}
+
+template <class T, class Container, class Compare>
+Compare& PriorityQueue<T, Container, Compare>::compare()
+{
+    return *this;
+}
+
+template <class T, class Container, class Compare>
+const Compare& PriorityQueue<T, Container, Compare>::compare() const
+{
+    return *this;
+}
+
+template <class T, class Container, class Compare>
+typename PriorityQueue<T, Container, Compare>::const_iterator PriorityQueue<T, Container, Compare>::begin() const
+{
+    return m_queue.begin();
+}
+
+template <class T, class Container, class Compare>
+typename PriorityQueue<T, Container, Compare>::const_iterator PriorityQueue<T, Container, Compare>::end() const
+{
+    return m_queue.end();
+}
+
+template <class T, class Container, class Compare>
+typename PriorityQueue<T, Container, Compare>::const_reverse_iterator
+PriorityQueue<T, Container, Compare>::rbegin() const
+{
+    return m_queue.rbegin();
+}
+
+template <class T, class Container, class Compare>
+typename PriorityQueue<T, Container, Compare>::const_reverse_iterator
+PriorityQueue<T, Container, Compare>::rend() const
+{
+    return m_queue.rend();
+}
+
+template <class T, class Container, class Compare>
+typename PriorityQueue<T, Container, Compare>::value_type
+PriorityQueue<T, Container, Compare>::erase(const_iterator it)
+{
+    // Convert to non-const iterator:
+    auto non_const_iterator = m_queue.begin() + (it - m_queue.begin());
+    value_type value = std::move(*non_const_iterator);
+    m_queue.erase(non_const_iterator);
+    return value;
+}
+
+template <class T, class Container, class Compare>
+void PriorityQueue<T, Container, Compare>::clear()
+{
+    m_queue.clear();
+}
+
+template <class T, class Container, class Compare>
+void PriorityQueue<T, Container, Compare>::reserve(size_type sz)
+{
+    m_queue.reserve(sz);
+}
+
+template <class T, class Container, class Compare>
+void PriorityQueue<T, Container, Compare>::swap(PriorityQueue& other)
+{
+    using std::swap;
+    swap(m_queue, other.m_queue);
+    swap(compare(), other.compare());
+}
+}
+}
+
+#endif // REALM_UTIL_PRIORITY_QUEUE_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/random.hpp b/iOS/Pods/Realm/include/core/realm/util/random.hpp
new file mode 100644 (file)
index 0000000..f862345
--- /dev/null
@@ -0,0 +1,122 @@
+/*************************************************************************
+ *
+ * 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_UTIL_RANDOM_HPP
+#define REALM_UTIL_RANDOM_HPP
+
+#include <stddef.h>
+#include <limits>
+#include <array>
+#include <random>
+#include <algorithm>
+#include <functional>
+
+namespace realm {
+namespace util {
+
+/// Perform a nondeterministc seeding of the specified pseudo random number
+/// generator.
+///
+/// \tparam Engine A type that satisfies UniformRandomBitGenerator as defined by
+/// the C++ standard.
+///
+/// \tparam state_size The number of words of type Engine::result_type that make
+/// up the engine state.
+///
+/// Thread-safe.
+///
+/// FIXME: Move this to core repo, as it is generally useful.
+template<class Engine, size_t state_size = Engine::state_size>
+void seed_prng_nondeterministically(Engine&);
+
+
+
+
+// Implementation
+
+} // namespace util
+
+namespace _impl {
+
+void get_extra_seed_entropy(unsigned int& extra_entropy_1, unsigned int& extra_entropy_2,
+                            unsigned int& extra_entropy_3);
+
+} // namespace _impl
+
+namespace util {
+
+template<class Engine, size_t state_size> void seed_prng_nondeterministically(Engine& engine)
+{
+    // This implementation was informed and inspired by
+    // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html.
+    //
+    // The number of bits of entropy needed is `state_size *
+    // std::numeric_limits<typename Engine::result_type>::digits` (assuming that
+    // the engine uses all available bits in each word).
+    //
+    // Each invocation of `std::random_device::operator()` gives us
+    // `std::numeric_limits<unsigned int>::digits` bits (assuming maximum
+    // entropy). Note that `std::random_device::result_type` must be `unsigned
+    // int`, `std::random_device::min()` must return zero, and
+    // `std::random_device::max()` must return `std::numeric_limits<unsigned
+    // int>::max()`.
+    //
+    // Ideally, we could have used `std::random_device::entropy()` as the actual
+    // number of bits of entropy produced per invocation of
+    // `std::random_device::operator()`, however, it is incorrectly implemented
+    // on many platform. Also, it is supposed to return zero when
+    // `std::random_device` is just a PRNG, but that would leave us with no way
+    // to continue.
+    //
+    // When the actual entropy from `std::random_device` is less than maximum,
+    // the seeding will be less than optimal. For example, if the actual entropy
+    // is only half of the maximum, then the seeding will only produce half the
+    // entrpy that it ought to, but that will generally still be a good seeding.
+    //
+    // For the (assumed) rare cases where `std::random_device` is a PRGN that is
+    // not nondeterministically seeded, we include a bit of extra entropy taken
+    // from such places as the current time and the ID of the executing process
+    // (when available).
+
+    constexpr long seed_bits_needed = state_size *
+        long(std::numeric_limits<typename Engine::result_type>::digits);
+    constexpr int seed_bits_per_device_invocation =
+        std::numeric_limits<unsigned int>::digits;
+    constexpr size_t seed_words_needed =
+        size_t((seed_bits_needed + (seed_bits_per_device_invocation - 1)) /
+               seed_bits_per_device_invocation); // Rounding up
+    constexpr int num_extra = 3;
+    std::array<std::random_device::result_type, seed_words_needed+num_extra> seed_values;
+    std::random_device rnddev;
+    std::generate(seed_values.begin(), seed_values.end()-num_extra, std::ref(rnddev));
+
+    unsigned int extra_entropy[3];
+    _impl::get_extra_seed_entropy(extra_entropy[0], extra_entropy[1], extra_entropy[2]);
+    static_assert(num_extra == sizeof extra_entropy / sizeof extra_entropy[0], "Mismatch");
+    std::copy(extra_entropy, extra_entropy+num_extra, seed_values.end()-num_extra);
+
+    std::seed_seq seed_seq(seed_values.begin(), seed_values.end());
+    engine.seed(seed_seq);
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_RANDOM_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/safe_int_ops.hpp b/iOS/Pods/Realm/include/core/realm/util/safe_int_ops.hpp
new file mode 100644 (file)
index 0000000..15030ad
--- /dev/null
@@ -0,0 +1,623 @@
+/*************************************************************************
+ *
+ * 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_UTIL_SAFE_INT_OPS_HPP
+#define REALM_UTIL_SAFE_INT_OPS_HPP
+
+#ifdef _WIN32
+#undef max // collides with numeric_limits::max called later in this header file
+#undef min // collides with numeric_limits::min called later in this header file
+#endif
+
+#include <limits>
+
+#include <realm/util/features.h>
+#include <realm/util/assert.hpp>
+#include <realm/util/type_traits.hpp>
+
+namespace realm {
+namespace util {
+
+
+/// Perform integral or floating-point promotion on the argument. This
+/// is useful for example when printing a number of arbitrary numeric
+/// type to 'stdout', since it will convert values of character-like
+/// types to regular integer types, which will then be printed as
+/// numbers rather characters.
+template <class T>
+typename Promote<T>::type promote(T value) noexcept;
+
+
+/// This function allows you to test for a negative value in any
+/// numeric type, even when the type is unsigned. Normally, when the
+/// type is unsigned, such a test will produce a compiler warning.
+template <class T>
+bool is_negative(T value) noexcept;
+
+
+/// Cast the specified value to the specified unsigned type reducing
+/// the value (or in case of negative values, the two's complement
+/// representation) modulo `2**N` where `N` is the number of value
+/// bits (or digits) in the unsigned target type. This is usefull in
+/// cases where the target type may be `bool`, but need not be `bool`.
+template <class To, class From>
+To cast_to_unsigned(From) noexcept;
+
+
+//@{
+
+/// Compare two integers of the same, or of different type, and
+/// produce the expected result according to the natural
+/// interpretation of the operation.
+///
+/// Note that in general a standard comparison between a signed and an
+/// unsigned integer type is unsafe, and it often generates a compiler
+/// warning. An example is a 'less than' comparison between a negative
+/// value of type 'int' and a small positive value of type
+/// 'unsigned'. In this case the negative value will be converted to
+/// 'unsigned' producing a large positive value which, in turn, will
+/// lead to the counter intuitive result of 'false'.
+///
+/// Please note that these operation incur absolutely no overhead when
+/// the two types have the same signedness.
+///
+/// These functions check at compile time that both types have valid
+/// specializations of std::numeric_limits<> and that both are indeed
+/// integers.
+///
+/// These functions make absolutely no assumptions about the platform
+/// except that it complies with at least C++03.
+
+template <class A, class B>
+inline bool int_equal_to(A, B) noexcept;
+template <class A, class B>
+inline bool int_not_equal_to(A, B) noexcept;
+template <class A, class B>
+inline bool int_less_than(A, B) noexcept;
+template <class A, class B>
+inline bool int_less_than_or_equal(A, B) noexcept;
+template <class A, class B>
+inline bool int_greater_than(A, B) noexcept;
+template <class A, class B>
+inline bool int_greater_than_or_equal(A, B) noexcept;
+
+//@}
+
+
+//@{
+
+/// Check for overflow in integer variable `lval` while adding integer
+/// `rval` to it, or while subtracting integer `rval` from it. Returns
+/// true on positive or negative overflow.
+///
+/// Both `lval` and `rval` must be of an integer type for which a
+/// specialization of std::numeric_limits<> exists. The two types need
+/// not be the same, in particular, one can be signed and the other
+/// one can be unsigned.
+///
+/// These functions are especially well suited for cases where \a rval
+/// is a compile-time constant.
+///
+/// These functions check at compile time that both types have valid
+/// specializations of std::numeric_limits<> and that both are indeed
+/// integers.
+///
+/// These functions make absolutely no assumptions about the platform
+/// except that it complies with at least C++03.
+
+template <class L, class R>
+inline bool int_add_with_overflow_detect(L& lval, R rval) noexcept;
+
+template <class L, class R>
+inline bool int_subtract_with_overflow_detect(L& lval, R rval) noexcept;
+
+//@}
+
+
+/// Check for positive overflow when multiplying two positive integers
+/// of the same, or of different type. Returns true on overflow.
+///
+/// \param lval Must not be negative. Both signed and unsigned types
+/// can be used.
+///
+/// \param rval Must be stricly greater than zero. Both signed and
+/// unsigned types can be used.
+///
+/// This function is especially well suited for cases where \a rval is
+/// a compile-time constant.
+///
+/// This function checks at compile time that both types have valid
+/// specializations of std::numeric_limits<> and that both are indeed
+/// integers.
+///
+/// This function makes absolutely no assumptions about the platform
+/// except that it complies with at least C++03.
+template <class L, class R>
+inline bool int_multiply_with_overflow_detect(L& lval, R rval) noexcept;
+
+
+/// Checks for positive overflow when performing a bitwise shift to
+/// the left on a non-negative value of arbitrary integer
+/// type. Returns true on overflow.
+///
+/// \param lval Must not be negative. Both signed and unsigned types
+/// can be used.
+///
+/// \param i Must be non-negative and such that <tt>L(1)>>i</tt> has a
+/// value that is defined by the C++03 standard. In particular, the
+/// value of i must not exceed the number of bits of storage type T as
+/// shifting by this amount is not defined by the standard.
+///
+/// This function makes absolutely no assumptions about the platform
+/// except that it complies with at least C++03.
+template <class T>
+inline bool int_shift_left_with_overflow_detect(T& lval, int i) noexcept;
+
+
+//@{
+
+/// Check for overflow when casting an integer value from one type to
+/// another. While the first function is a mere check, the second one
+/// also carries out the cast, but only when there is no
+/// overflow. Both return true on overflow.
+///
+/// These functions check at compile time that both types have valid
+/// specializations of std::numeric_limits<> and that both are indeed
+/// integers.
+///
+/// These functions make absolutely no assumptions about the platform
+/// except that it complies with at least C++03.
+
+template <class To, class From>
+bool int_cast_has_overflow(From from) noexcept;
+
+template <class To, class From>
+bool int_cast_with_overflow_detect(From from, To& to) noexcept;
+
+//@}
+
+
+/// Convert negative values from two's complement representation to the
+/// platforms native representation.
+///
+/// If `To` is an unsigned type, this function does nothing beyond casting the
+/// specified value to `To`. Otherwise, `To` is a signed type, and negative
+/// values will be converted from two's complement representation in unsigned
+/// `From` to the platforms native representation in `To`.
+///
+/// For signed `To` the result is well-defined if, and only if the value with
+/// the specified two's complement representation is representable in the
+/// specified signed type. While this is generally the case when using
+/// corresponding signed/unsigned type pairs, it is not guaranteed by the
+/// standard. However, if you know that the signed type has at least as many
+/// value bits as the unsigned type, then the result is always
+/// well-defined. Note that a 'value bit' in this context is the same as a
+/// 'digit' from the point of view of `std::numeric_limits`.
+///
+/// On platforms that use two's complement representation of negative values,
+/// this function is expected to be completely optimized away. This has been
+/// observed to be true with both GCC 4.8 and Clang 3.2.
+///
+/// Note that the **opposite** direction (from the platforms native
+/// representation to two's complement) is trivially handled by casting the
+/// signed value to a value of a sufficiently wide unsigned integer type. An
+/// unsigned type will be sufficiently wide if it has at least one more value
+/// bit than the signed type.
+///
+/// Interestingly, the C++ language offers no direct way of doing what this
+/// function does, yet, this function is implemented in a way that makes no
+/// assumption about the underlying platform except what is guaranteed by C++11.
+///
+/// \tparam From The unsigned type used to store the two's complement
+/// representation.
+///
+/// \tparam To A signed or unsigned integer type.
+template <class To, class From>
+To from_twos_compl(From twos_compl) noexcept;
+
+
+// Implementation:
+
+template <class T>
+inline typename Promote<T>::type promote(T value) noexcept
+{
+    typedef typename Promote<T>::type promoted_type;
+    promoted_type value_2 = promoted_type(value);
+    return value_2;
+}
+
+} // namespace util
+
+namespace _impl {
+
+template <class T, bool is_signed>
+struct IsNegative {
+    static bool test(T value) noexcept
+    {
+        return value < 0;
+    }
+};
+template <class T>
+struct IsNegative<T, false> {
+    static bool test(T) noexcept
+    {
+        return false;
+    }
+};
+
+template <class To>
+struct CastToUnsigned {
+    template <class From>
+    static To cast(From value) noexcept
+    {
+        return To(value);
+    }
+};
+template <>
+struct CastToUnsigned<bool> {
+    template <class From>
+    static bool cast(From value) noexcept
+    {
+        return bool(unsigned(value) & 1);
+    }
+};
+
+template <class L, class R, bool l_signed, bool r_signed>
+struct SafeIntBinopsImpl {
+};
+
+// (unsigned, unsigned) (all size combinations)
+//
+// This implementation utilizes the fact that overflow in unsigned
+// arithmetic is guaranteed to be handled by reduction modulo 2**N
+// where N is the number of bits in the unsigned type. The purpose of
+// the bitwise 'and' with lim_l::max() is to make a cast to bool
+// behave the same way as casts to other unsigned integer types.
+// Finally, this implementation uses the fact that if modular addition
+// overflows, then the result must be a value that is less than both
+// operands. Also, if modular subtraction overflows, then the result
+// must be a value that is greater than the first operand.
+template <class L, class R>
+struct SafeIntBinopsImpl<L, R, false, false> {
+    typedef std::numeric_limits<L> lim_l;
+    typedef std::numeric_limits<R> lim_r;
+    static const int needed_bits_l = lim_l::digits;
+    static const int needed_bits_r = lim_r::digits;
+    static const int needed_bits = needed_bits_l >= needed_bits_r ? needed_bits_l : needed_bits_r;
+    typedef typename util::FastestUnsigned<needed_bits>::type common_unsigned;
+    static bool equal(L l, R r) noexcept
+    {
+        return common_unsigned(l) == common_unsigned(r);
+    }
+    static bool less(L l, R r) noexcept
+    {
+        return common_unsigned(l) < common_unsigned(r);
+    }
+    static bool add(L& lval, R rval) noexcept
+    {
+        L lval_2 = util::cast_to_unsigned<L>(lval + rval);
+        bool overflow = common_unsigned(lval_2) < common_unsigned(rval);
+        if (REALM_UNLIKELY(overflow))
+            return true;
+        lval = lval_2;
+        return false;
+    }
+    static bool sub(L& lval, R rval) noexcept
+    {
+        common_unsigned lval_2 = common_unsigned(lval) - common_unsigned(rval);
+        bool overflow = lval_2 > common_unsigned(lval);
+        if (REALM_UNLIKELY(overflow))
+            return true;
+        lval = util::cast_to_unsigned<L>(lval_2);
+        return false;
+    }
+};
+
+// (unsigned, signed) (all size combinations)
+template <class L, class R>
+struct SafeIntBinopsImpl<L, R, false, true> {
+    typedef std::numeric_limits<L> lim_l;
+    typedef std::numeric_limits<R> lim_r;
+    static const int needed_bits_l = lim_l::digits;
+    static const int needed_bits_r = lim_r::digits + 1;
+    static const int needed_bits = needed_bits_l >= needed_bits_r ? needed_bits_l : needed_bits_r;
+    typedef typename util::FastestUnsigned<needed_bits>::type common_unsigned;
+    typedef std::numeric_limits<common_unsigned> lim_cu;
+    static bool equal(L l, R r) noexcept
+    {
+        return (lim_l::digits > lim_r::digits) ? r >= 0 && l == util::cast_to_unsigned<L>(r) : R(l) == r;
+    }
+    static bool less(L l, R r) noexcept
+    {
+        return (lim_l::digits > lim_r::digits) ? r >= 0 && l < util::cast_to_unsigned<L>(r) : R(l) < r;
+    }
+    static bool add(L& lval, R rval) noexcept
+    {
+        common_unsigned lval_2 = lval + common_unsigned(rval);
+        bool overflow;
+        if (lim_l::digits < lim_cu::digits) {
+            overflow = common_unsigned(lval_2) > common_unsigned(lim_l::max());
+        }
+        else {
+            overflow = (lval_2 < common_unsigned(lval)) == (rval >= 0);
+        }
+        if (REALM_UNLIKELY(overflow))
+            return true;
+        lval = util::cast_to_unsigned<L>(lval_2);
+        return false;
+    }
+    static bool sub(L& lval, R rval) noexcept
+    {
+        common_unsigned lval_2 = lval - common_unsigned(rval);
+        bool overflow;
+        if (lim_l::digits < lim_cu::digits) {
+            overflow = common_unsigned(lval_2) > common_unsigned(lim_l::max());
+        }
+        else {
+            overflow = (common_unsigned(lval_2) > common_unsigned(lval)) == (rval >= 0);
+        }
+        if (REALM_UNLIKELY(overflow))
+            return true;
+        lval = util::cast_to_unsigned<L>(lval_2);
+        return false;
+    }
+};
+
+// (signed, unsigned) (all size combinations)
+template <class L, class R>
+struct SafeIntBinopsImpl<L, R, true, false> {
+    typedef std::numeric_limits<L> lim_l;
+    typedef std::numeric_limits<R> lim_r;
+    static const int needed_bits_l = lim_l::digits + 1;
+    static const int needed_bits_r = lim_r::digits;
+    static const int needed_bits = needed_bits_l >= needed_bits_r ? needed_bits_l : needed_bits_r;
+    typedef typename util::FastestUnsigned<needed_bits>::type common_unsigned;
+    static bool equal(L l, R r) noexcept
+    {
+        return (lim_l::digits < lim_r::digits) ? l >= 0 && util::cast_to_unsigned<R>(l) == r : l == L(r);
+    }
+    static bool less(L l, R r) noexcept
+    {
+        return (lim_l::digits < lim_r::digits) ? l < 0 || util::cast_to_unsigned<R>(l) < r : l < L(r);
+    }
+    static bool add(L& lval, R rval) noexcept
+    {
+        common_unsigned max_add = common_unsigned(lim_l::max()) - common_unsigned(lval);
+        bool overflow = common_unsigned(rval) > max_add;
+        if (REALM_UNLIKELY(overflow))
+            return true;
+        lval = util::from_twos_compl<L>(common_unsigned(lval) + rval);
+        return false;
+    }
+    static bool sub(L& lval, R rval) noexcept
+    {
+        common_unsigned max_sub = common_unsigned(lval) - common_unsigned(lim_l::min());
+        bool overflow = common_unsigned(rval) > max_sub;
+        if (REALM_UNLIKELY(overflow))
+            return true;
+        lval = util::from_twos_compl<L>(common_unsigned(lval) - rval);
+        return false;
+    }
+};
+
+// (signed, signed) (all size combinations)
+template <class L, class R>
+struct SafeIntBinopsImpl<L, R, true, true> {
+    typedef std::numeric_limits<L> lim_l;
+    static bool equal(L l, R r) noexcept
+    {
+        return l == r;
+    }
+    static bool less(L l, R r) noexcept
+    {
+        return l < r;
+    }
+    static bool add(L& lval, R rval) noexcept
+    {
+        // Note that both subtractions below occur in a signed type
+        // that is at least as wide as both of the two types. Note
+        // also that any signed type guarantees that there is no
+        // overflow when subtracting two negative values or two
+        // non-negative value. See C99 (adopted as subset of C++11)
+        // section 6.2.6.2 "Integer types" paragraph 2.
+        if (rval < 0) {
+            if (REALM_UNLIKELY(lval < lim_l::min() - rval))
+                return true;
+        }
+        else {
+            if (REALM_UNLIKELY(lval > lim_l::max() - rval))
+                return true;
+        }
+        // The following statement has exactly the same effect as
+        // `lval += rval`.
+        lval = L(lval + rval);
+        return false;
+    }
+    static bool sub(L& lval, R rval) noexcept
+    {
+        // Note that both subtractions below occur in a signed type
+        // that is at least as wide as both of the two types. Note
+        // also that there can be no overflow when adding a negative
+        // value to a non-negative value, or when adding a
+        // non-negative value to a negative one.
+        if (rval < 0) {
+            if (REALM_UNLIKELY(lval > lim_l::max() + rval))
+                return true;
+        }
+        else {
+            if (REALM_UNLIKELY(lval < lim_l::min() + rval))
+                return true;
+        }
+        // The following statement has exactly the same effect as
+        // `lval += rval`.
+        lval = L(lval - rval);
+        return false;
+    }
+};
+
+template <class L, class R>
+struct SafeIntBinops : SafeIntBinopsImpl<L, R, std::numeric_limits<L>::is_signed, std::numeric_limits<R>::is_signed> {
+    typedef std::numeric_limits<L> lim_l;
+    typedef std::numeric_limits<R> lim_r;
+    static_assert(lim_l::is_specialized && lim_r::is_specialized,
+                  "std::numeric_limits<> must be specialized for both types");
+    static_assert(lim_l::is_integer && lim_r::is_integer, "Both types must be integers");
+};
+
+} // namespace _impl
+
+namespace util {
+
+template <class T>
+inline bool is_negative(T value) noexcept
+{
+    return _impl::IsNegative<T, std::numeric_limits<T>::is_signed>::test(value);
+}
+
+template <class To, class From>
+inline To cast_to_unsigned(From value) noexcept
+{
+    return _impl::CastToUnsigned<To>::cast(value);
+}
+
+template <class A, class B>
+inline bool int_equal_to(A a, B b) noexcept
+{
+    return _impl::SafeIntBinops<A, B>::equal(a, b);
+}
+
+template <class A, class B>
+inline bool int_not_equal_to(A a, B b) noexcept
+{
+    return !_impl::SafeIntBinops<A, B>::equal(a, b);
+}
+
+template <class A, class B>
+inline bool int_less_than(A a, B b) noexcept
+{
+    return _impl::SafeIntBinops<A, B>::less(a, b);
+}
+
+template <class A, class B>
+inline bool int_less_than_or_equal(A a, B b) noexcept
+{
+    return !_impl::SafeIntBinops<B, A>::less(b, a); // Not greater than
+}
+
+template <class A, class B>
+inline bool int_greater_than(A a, B b) noexcept
+{
+    return _impl::SafeIntBinops<B, A>::less(b, a);
+}
+
+template <class A, class B>
+inline bool int_greater_than_or_equal(A a, B b) noexcept
+{
+    return !_impl::SafeIntBinops<A, B>::less(a, b); // Not less than
+}
+
+template <class L, class R>
+inline bool int_add_with_overflow_detect(L& lval, R rval) noexcept
+{
+    return _impl::SafeIntBinops<L, R>::add(lval, rval);
+}
+
+template <class L, class R>
+inline bool int_subtract_with_overflow_detect(L& lval, R rval) noexcept
+{
+    return _impl::SafeIntBinops<L, R>::sub(lval, rval);
+}
+
+template <class L, class R>
+inline bool int_multiply_with_overflow_detect(L& lval, R rval) noexcept
+{
+    // FIXME: Check if the following optimizes better (if it works at all):
+    // L lval_2 = L(lval * rval);
+    // bool overflow  =  rval != 0  &&  (lval_2 / rval) != lval;
+    typedef std::numeric_limits<L> lim_l;
+    typedef std::numeric_limits<R> lim_r;
+    static_assert(lim_l::is_specialized && lim_r::is_specialized,
+                  "std::numeric_limits<> must be specialized for both types");
+    static_assert(lim_l::is_integer && lim_r::is_integer, "Both types must be integers");
+    REALM_ASSERT(int_greater_than_or_equal(lval, 0));
+    REALM_ASSERT(int_greater_than(rval, 0));
+    if (int_less_than(lim_l::max() / rval, lval))
+        return true;
+    lval = L(lval * rval);
+    return false;
+}
+
+template <class T>
+inline bool int_shift_left_with_overflow_detect(T& lval, int i) noexcept
+{
+    typedef std::numeric_limits<T> lim;
+    static_assert(lim::is_specialized, "std::numeric_limits<> must be specialized for T");
+    static_assert(lim::is_integer, "T must be an integer type");
+    REALM_ASSERT(int_greater_than_or_equal(lval, 0));
+    if ((lim::max() >> i) < lval)
+        return true;
+    lval <<= i;
+    return false;
+}
+
+template <class To, class From>
+inline bool int_cast_has_overflow(From from) noexcept
+{
+    typedef std::numeric_limits<To> lim_to;
+    return int_less_than(from, lim_to::min()) || int_less_than(lim_to::max(), from);
+}
+
+template <class To, class From>
+inline bool int_cast_with_overflow_detect(From from, To& to) noexcept
+{
+    if (REALM_LIKELY(!int_cast_has_overflow<To>(from))) {
+        to = To(from);
+        return false;
+    }
+    return true;
+}
+
+template <class To, class From>
+inline To from_twos_compl(From twos_compl) noexcept
+{
+    typedef std::numeric_limits<From> lim_f;
+    typedef std::numeric_limits<To> lim_t;
+    static_assert(lim_f::is_specialized && lim_t::is_specialized,
+                  "std::numeric_limits<> must be specialized for both types");
+    static_assert(lim_f::is_integer && lim_t::is_integer, "Both types must be integers");
+    static_assert(!lim_f::is_signed, "`From` must be unsigned");
+    To native;
+    int sign_bit_pos = lim_f::digits - 1;
+    From sign_bit = From(1) << sign_bit_pos;
+    bool non_negative = !lim_t::is_signed || (twos_compl & sign_bit) == 0;
+    if (non_negative) {
+        // Non-negative value
+        native = To(twos_compl);
+    }
+    else {
+        // Negative value
+        native = To(-1 - To(From(-1) - twos_compl));
+    }
+    return native;
+}
+
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_SAFE_INT_OPS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/scope_exit.hpp b/iOS/Pods/Realm/include/core/realm/util/scope_exit.hpp
new file mode 100644 (file)
index 0000000..5410d19
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_UTIL_SCOPE_EXIT_HPP
+#define REALM_UTIL_SCOPE_EXIT_HPP
+
+#include <type_traits>
+#include <utility>
+
+#include <realm/util/optional.hpp>
+
+namespace realm {
+namespace util {
+
+template <class H>
+class ScopeExit {
+public:
+    explicit ScopeExit(const H& handler) noexcept(std::is_nothrow_copy_constructible<H>::value)
+        : m_handler(handler)
+    {
+    }
+
+    explicit ScopeExit(H&& handler) noexcept(std::is_nothrow_move_constructible<H>::value)
+        : m_handler(std::move(handler))
+    {
+    }
+
+    ScopeExit(ScopeExit&& se) noexcept(std::is_nothrow_move_constructible<H>::value)
+        : m_handler(std::move(se.m_handler))
+    {
+        se.m_handler = none;
+    }
+
+    ~ScopeExit() noexcept
+    {
+        if (m_handler)
+            (*m_handler)();
+    }
+
+    static_assert(noexcept(std::declval<H>()()), "Handler must be nothrow executable");
+    static_assert(std::is_nothrow_destructible<H>::value, "Handler must be nothrow destructible");
+
+private:
+    util::Optional<H> m_handler;
+};
+
+template <class H>
+ScopeExit<typename std::remove_reference<H>::type> make_scope_exit(H&& handler) noexcept(
+    noexcept(ScopeExit<typename std::remove_reference<H>::type>(std::forward<H>(handler))))
+{
+    return ScopeExit<typename std::remove_reference<H>::type>(std::forward<H>(handler));
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_SCOPE_EXIT_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/serializer.hpp b/iOS/Pods/Realm/include/core/realm/util/serializer.hpp
new file mode 100644 (file)
index 0000000..a9441df
--- /dev/null
@@ -0,0 +1,77 @@
+/*************************************************************************
+ *
+ * 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_UTIL_SERIALIZER_HPP
+#define REALM_UTIL_SERIALIZER_HPP
+
+#include <realm/util/optional.hpp>
+
+#include <string>
+#include <sstream>
+
+namespace realm {
+
+class BinaryData;
+struct null;
+class StringData;
+class Timestamp;
+
+namespace util {
+namespace serializer {
+
+
+// Definitions
+template <typename T>
+std::string print_value(T value);
+
+template <typename T>
+std::string print_value(Optional<T> value);
+
+const static std::string value_separator = ".";
+
+// Specializations declared here to be defined in the cpp file
+template <> std::string print_value<>(BinaryData);
+template <> std::string print_value<>(bool);
+template <> std::string print_value<>(realm::null);
+template <> std::string print_value<>(StringData);
+template <> std::string print_value<>(realm::Timestamp);
+
+// General implementation for most types
+template <typename T>
+std::string print_value(T value)
+{
+    std::stringstream ss;
+    ss << value;
+    return ss.str();
+}
+
+template <typename T>
+std::string print_value(Optional<T> value)
+{
+    if (bool(value)) {
+        return print_value(*value);
+    } else {
+        return "NULL";
+    }
+}
+
+} // namespace serializer
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_SERIALIZER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/shared_ptr.hpp b/iOS/Pods/Realm/include/core/realm/util/shared_ptr.hpp
new file mode 100644 (file)
index 0000000..f89b24f
--- /dev/null
@@ -0,0 +1,130 @@
+/*************************************************************************
+ *
+ * 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_SHARED_PTR_HPP
+#define REALM_SHARED_PTR_HPP
+
+#include <cstdlib> // size_t
+
+namespace realm {
+namespace util {
+
+template <class T>
+class SharedPtr {
+public:
+    SharedPtr(T* p)
+    {
+        init(p);
+    }
+
+    SharedPtr()
+    {
+        init(0);
+    }
+
+    ~SharedPtr()
+    {
+        decref();
+    }
+
+    SharedPtr(const SharedPtr<T>& o)
+        : m_ptr(o.m_ptr)
+        , m_count(o.m_count)
+    {
+        incref();
+    }
+
+    SharedPtr<T>& operator=(const SharedPtr<T>& o)
+    {
+        if (m_ptr == o.m_ptr)
+            return *this;
+        decref();
+        m_ptr = o.m_ptr;
+        m_count = o.m_count;
+        incref();
+        return *this;
+    }
+
+    T* operator->() const
+    {
+        return m_ptr;
+    }
+
+    T& operator*() const
+    {
+        return *m_ptr;
+    }
+
+    T* get() const
+    {
+        return m_ptr;
+    }
+
+    bool operator==(const SharedPtr<T>& o) const
+    {
+        return m_ptr == o.m_ptr;
+    }
+
+    bool operator!=(const SharedPtr<T>& o) const
+    {
+        return m_ptr != o.m_ptr;
+    }
+
+    bool operator<(const SharedPtr<T>& o) const
+    {
+        return m_ptr < o.m_ptr;
+    }
+
+    size_t ref_count() const
+    {
+        return *m_count;
+    }
+
+private:
+    void init(T* p)
+    {
+        m_ptr = p;
+        try {
+            m_count = new size_t(1);
+        }
+        catch (...) {
+            delete p;
+            throw;
+        }
+    }
+
+    void decref()
+    {
+        if (--(*m_count) == 0) {
+            delete m_ptr;
+            delete m_count;
+        }
+    }
+
+    void incref()
+    {
+        ++(*m_count);
+    }
+
+    T* m_ptr;
+    size_t* m_count;
+};
+}
+}
+
+#endif
diff --git a/iOS/Pods/Realm/include/core/realm/util/string_buffer.hpp b/iOS/Pods/Realm/include/core/realm/util/string_buffer.hpp
new file mode 100644 (file)
index 0000000..9d85043
--- /dev/null
@@ -0,0 +1,169 @@
+/*************************************************************************
+ *
+ * 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_UTIL_STRING_BUFFER_HPP
+#define REALM_UTIL_STRING_BUFFER_HPP
+
+#include <cstddef>
+#include <cstring>
+#include <string>
+
+#include <realm/util/features.h>
+#include <realm/util/buffer.hpp>
+
+namespace realm {
+namespace util {
+
+
+// FIXME: Check whether this class provides anything that a C++03
+// std::string does not already provide. In particular, can a C++03
+// std::string be used as a contiguous mutable buffer?
+class StringBuffer {
+public:
+    StringBuffer() noexcept;
+
+    std::string str() const;
+
+    /// Returns the current size of the string in this buffer. This
+    /// size does not include the terminating zero.
+    size_t size() const noexcept;
+
+    /// Gives read and write access to the bytes of this buffer. The
+    /// caller may read and write from *c_str() up to, but not
+    /// including, *(c_str()+size()).
+    char* data() noexcept;
+
+    /// Gives read access to the bytes of this buffer. The caller may
+    /// read from *c_str() up to, but not including,
+    /// *(c_str()+size()).
+    const char* data() const noexcept;
+
+    /// Guarantees that the returned string is zero terminated, that
+    /// is, *(c_str()+size()) is zero. The caller may read from
+    /// *c_str() up to and including *(c_str()+size()).
+    const char* c_str() const noexcept;
+
+    void append(const std::string&);
+
+    void append(const char* append_data, size_t append_size);
+
+    /// Append a zero-terminated string to this buffer.
+    void append_c_str(const char* c_string);
+
+    /// The specified size is understood as not including the
+    /// terminating zero. If the specified size is less than the
+    /// current size, then the string is truncated accordingly. If the
+    /// specified size is greater than the current size, then the
+    /// extra characters will have undefined values, however, there
+    /// will be a terminating zero at *(c_str()+size()), and the
+    /// original terminating zero will also be left in place such that
+    /// from the point of view of c_str(), the size of the string is
+    /// unchanged.
+    void resize(size_t new_size);
+
+    /// The specified minimum capacity is understood as not including
+    /// the terminating zero. This operation does not change the size
+    /// of the string in the buffer as returned by size(). If the
+    /// specified capacity is less than the current capacity, this
+    /// operation has no effect.
+    void reserve(size_t min_capacity);
+
+    /// Set size to zero. The capacity remains unchanged.
+    void clear() noexcept;
+
+private:
+    util::Buffer<char> m_buffer;
+    size_t m_size; // Excluding the terminating zero
+    void reallocate(size_t min_capacity);
+};
+
+
+// Implementation:
+
+inline StringBuffer::StringBuffer() noexcept
+    : m_size(0)
+{
+}
+
+inline std::string StringBuffer::str() const
+{
+    return std::string(m_buffer.data(), m_size);
+}
+
+inline size_t StringBuffer::size() const noexcept
+{
+    return m_size;
+}
+
+inline char* StringBuffer::data() noexcept
+{
+    return m_buffer.data();
+}
+
+inline const char* StringBuffer::data() const noexcept
+{
+    return m_buffer.data();
+}
+
+inline const char* StringBuffer::c_str() const noexcept
+{
+    static const char zero = 0;
+    const char* d = data();
+    return d ? d : &zero;
+}
+
+inline void StringBuffer::append(const std::string& s)
+{
+    return append(s.data(), s.size());
+}
+
+inline void StringBuffer::append_c_str(const char* c_string)
+{
+    append(c_string, std::strlen(c_string));
+}
+
+inline void StringBuffer::reserve(size_t min_capacity)
+{
+    size_t capacity = m_buffer.size();
+    if (capacity == 0 || capacity - 1 < min_capacity)
+        reallocate(min_capacity);
+}
+
+inline void StringBuffer::resize(size_t new_size)
+{
+    reserve(new_size);
+    // Note that even reserve(0) will attempt to allocate a
+    // buffer, so we can safely write the truncating zero at this
+    // time.
+    m_size = new_size;
+    m_buffer[new_size] = 0;
+}
+
+inline void StringBuffer::clear() noexcept
+{
+    if (m_buffer.size() == 0)
+        return;
+    m_size = 0;
+    m_buffer[0] = 0;
+}
+
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_STRING_BUFFER_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/terminate.hpp b/iOS/Pods/Realm/include/core/realm/util/terminate.hpp
new file mode 100644 (file)
index 0000000..4e6034e
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_UTIL_TERMINATE_HPP
+#define REALM_UTIL_TERMINATE_HPP
+
+#include <cstdlib>
+
+#include <realm/util/features.h>
+#include <realm/util/to_string.hpp>
+#include <realm/version.hpp>
+
+#define REALM_TERMINATE(msg) realm::util::terminate((msg), __FILE__, __LINE__)
+
+namespace realm {
+namespace util {
+
+REALM_NORETURN void terminate(const char* message, const char* file, long line,
+                              std::initializer_list<Printable>&& = {}) noexcept;
+REALM_NORETURN void terminate_with_info(const char* message, const char* file, long line,
+                                        const char* interesting_names,
+                                        std::initializer_list<Printable>&& = {}) noexcept;
+
+// LCOV_EXCL_START
+template <class... Ts>
+REALM_NORETURN void terminate(const char* message, const char* file, long line, Ts... infos) noexcept
+{
+    static_assert(sizeof...(infos) == 2 || sizeof...(infos) == 4 || sizeof...(infos) == 6,
+                  "Called realm::util::terminate() with wrong number of arguments");
+    terminate(message, file, line, {Printable(infos)...});
+}
+
+template <class... Args>
+REALM_NORETURN void terminate_with_info(const char* assert_message, int line, const char* file,
+                                        const char* interesting_names, Args&&... interesting_values) noexcept
+{
+    terminate_with_info(assert_message, file, line, interesting_names, {Printable(interesting_values)...});
+}
+// LCOV_EXCL_STOP
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_TERMINATE_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/thread.hpp b/iOS/Pods/Realm/include/core/realm/util/thread.hpp
new file mode 100644 (file)
index 0000000..8ca5e38
--- /dev/null
@@ -0,0 +1,759 @@
+/*************************************************************************
+ *
+ * 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_UTIL_THREAD_HPP
+#define REALM_UTIL_THREAD_HPP
+
+#include <exception>
+
+#ifdef _WIN32
+#include <thread>
+#include <condition_variable> // for windows non-interprocess condvars we use std::condition_variable
+#include <Windows.h>
+#include <process.h> // _getpid()
+#else
+#include <pthread.h>
+#endif
+
+// Use below line to enable a thread bug detection tool. Note: Will make program execution slower.
+// #include <../test/pthread_test.hpp>
+
+#include <cerrno>
+#include <cstddef>
+#include <string>
+
+#include <realm/util/features.h>
+#include <realm/util/assert.hpp>
+#include <realm/util/terminate.hpp>
+#include <memory>
+
+#include <atomic>
+
+namespace realm {
+namespace util {
+
+
+/// A separate thread of execution.
+///
+/// This class is a C++03 compatible reproduction of a subset of std::thread
+/// from C++11 (when discounting Thread::start(), Thread::set_name(), and
+/// Thread::get_name()).
+class Thread {
+public:
+    Thread();
+    ~Thread() noexcept;
+
+    template <class F>
+    explicit Thread(F func);
+
+    // Disable copying. It is an error to copy this Thread class.
+    Thread(const Thread&) = delete;
+    Thread& operator=(const Thread&) = delete;
+
+    Thread(Thread&&);
+
+    /// This method is an extension of the API provided by
+    /// std::thread. This method exists because proper move semantics
+    /// is unavailable in C++03. If move semantics had been available,
+    /// calling `start(func)` would have been equivalent to `*this =
+    /// Thread(func)`. Please see std::thread::operator=() for
+    /// details.
+    template <class F>
+    void start(F func);
+
+    bool joinable() noexcept;
+
+    void join();
+
+    // If supported by the platform, set the name of the calling thread (mainly
+    // for debugging purposes). The name will be silently clamped to whatever
+    // limit the platform places on these names. Linux places a limit of 15
+    // characters for these names.
+    static void set_name(const std::string&);
+
+    // If supported by the platform, this function assigns the name of the
+    // calling thread to \a name, and returns true, otherwise it does nothing
+    // and returns false.
+    static bool get_name(std::string& name);
+
+private:
+
+#ifdef _WIN32
+    std::thread m_std_thread;
+#else    
+    pthread_t m_id;
+#endif
+    bool m_joinable;
+    typedef void* (*entry_func_type)(void*);
+
+    void start(entry_func_type, void* arg);
+
+    template <class>
+    static void* entry_point(void*) noexcept;
+
+    REALM_NORETURN static void create_failed(int);
+    REALM_NORETURN static void join_failed(int);
+};
+
+
+/// Low-level mutual exclusion device.
+class Mutex {
+public:
+    Mutex();
+    ~Mutex() noexcept;
+
+    struct process_shared_tag {
+    };
+    /// Initialize this mutex for use across multiple processes. When
+    /// constructed this way, the instance may be placed in memory
+    /// shared by multiple processes, as well as in a memory mapped
+    /// file. Such a mutex remains valid even after the constructing
+    /// process terminates. Deleting the instance (freeing the memory
+    /// or deleting the file) without first calling the destructor is
+    /// legal and will not cause any system resources to be leaked.
+    Mutex(process_shared_tag);
+
+    // Disable copying.
+    Mutex(const Mutex&) = delete;
+    Mutex& operator=(const Mutex&) = delete;
+
+    friend class LockGuard;
+    friend class UniqueLock;
+    friend class InterprocessCondVar;
+
+    void lock() noexcept;
+    bool try_lock() noexcept;
+    void unlock() noexcept;
+
+protected:
+#ifdef _WIN32
+    // Used for non-process-shared mutex. We only know at runtime whether or not to use it, depending on if we call
+    // Mutex::Mutex(process_shared_tag)
+    CRITICAL_SECTION m_critical_section;
+#else
+    pthread_mutex_t m_impl = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+    struct no_init_tag {
+    };
+    Mutex(no_init_tag)
+    {
+    }
+
+    void init_as_regular();
+    void init_as_process_shared(bool robust_if_available);
+
+    REALM_NORETURN static void init_failed(int);
+    REALM_NORETURN static void attr_init_failed(int);
+    REALM_NORETURN static void destroy_failed(int) noexcept;
+    REALM_NORETURN static void lock_failed(int) noexcept;
+
+private:
+    friend class CondVar;
+    friend class RobustMutex;
+};
+
+
+/// A simple mutex ownership wrapper.
+class LockGuard {
+public:
+    LockGuard(Mutex&) noexcept;
+    ~LockGuard() noexcept;
+
+private:
+    Mutex& m_mutex;
+    friend class CondVar;
+};
+
+
+/// See UniqueLock.
+struct defer_lock_tag {
+};
+
+/// A general-purpose mutex ownership wrapper supporting deferred
+/// locking as well as repeated unlocking and relocking.
+class UniqueLock {
+public:
+    UniqueLock(Mutex&) noexcept;
+    UniqueLock(Mutex&, defer_lock_tag) noexcept;
+    ~UniqueLock() noexcept;
+
+    void lock() noexcept;
+    void unlock() noexcept;
+    bool holds_lock() noexcept;
+
+private:
+    Mutex* m_mutex;
+    bool m_is_locked;
+};
+
+
+/// A robust version of a process-shared mutex.
+///
+/// A robust mutex is one that detects whether a thread (or process)
+/// has died while holding a lock on the mutex.
+///
+/// When the present platform does not offer support for robust
+/// mutexes, this mutex class behaves as a regular process-shared
+/// mutex, which means that if a thread dies while holding a lock, any
+/// future attempt at locking will block indefinitely.
+class RobustMutex : private Mutex {
+public:
+    RobustMutex();
+    ~RobustMutex() noexcept;
+
+    static bool is_robust_on_this_platform() noexcept;
+
+    class NotRecoverable;
+
+    /// \param recover_func If the present platform does not support
+    /// robust mutexes, this function is never called. Otherwise it is
+    /// called if, and only if a thread has died while holding a
+    /// lock. The purpose of the function is to reestablish a
+    /// consistent shared state. If it fails to do this by throwing an
+    /// exception, the mutex enters the 'unrecoverable' state where
+    /// any future attempt at locking it will fail and cause
+    /// NotRecoverable to be thrown. This function is advised to throw
+    /// NotRecoverable when it fails, but it may throw any exception.
+    ///
+    /// \throw NotRecoverable If thrown by the specified recover
+    /// function, or if the mutex has entered the 'unrecoverable'
+    /// state due to a different thread throwing from its recover
+    /// function.
+    template <class Func>
+    void lock(Func recover_func);
+
+    template <class Func>
+    bool try_lock(Func recover_func);
+
+    void unlock() noexcept;
+
+    /// Low-level locking of robust mutex.
+    ///
+    /// If the present platform does not support robust mutexes, this
+    /// function always returns true. Otherwise it returns false if,
+    /// and only if a thread has died while holding a lock.
+    ///
+    /// \note Most application should never call this function
+    /// directly. It is called automatically when using the ordinary
+    /// lock() function.
+    ///
+    /// \throw NotRecoverable If this mutex has entered the "not
+    /// recoverable" state. It enters this state if
+    /// mark_as_consistent() is not called between a call to
+    /// robust_lock() that returns false and the corresponding call to
+    /// unlock().
+    bool low_level_lock();
+
+    /// Low-level try-lock of robust mutex
+    ///
+    /// If the present platform does not support robust mutexes, this
+    /// function always returns 0 or 1. Otherwise it returns -1 if,
+    /// and only if a thread has died while holding a lock.
+    ///
+    /// Returns 1 if the lock is succesfully obtained.
+    /// Returns 0 if the lock is held by somebody else (not obtained)
+    /// Returns -1 if a thread has died while holding a lock.
+    ///
+    /// \note Most application should never call this function
+    /// directly. It is called automatically when using the ordinary
+    /// lock() function.
+    ///
+    /// \throw NotRecoverable If this mutex has entered the "not
+    /// recoverable" state. It enters this state if
+    /// mark_as_consistent() is not called between a call to
+    /// robust_lock() that returns false and the corresponding call to
+    /// unlock().
+    int try_low_level_lock();
+
+    /// Pull this mutex out of the 'inconsistent' state.
+    ///
+    /// Must be called only after low_level_lock() has returned false.
+    ///
+    /// \note Most application should never call this function
+    /// directly. It is called automatically when using the ordinary
+    /// lock() function.
+    void mark_as_consistent() noexcept;
+
+    /// Attempt to check if this mutex is a valid object.
+    ///
+    /// This attempts to trylock() the mutex, and if that fails returns false if
+    /// the return value indicates that the low-level mutex is invalid (which is
+    /// distinct from 'inconsistent'). Although pthread_mutex_trylock() may
+    /// return EINVAL if the argument is not an initialized mutex object, merely
+    /// attempting to check if an arbitrary blob of memory is a mutex object may
+    /// involve undefined behavior, so it is only safe to assume that this
+    /// function will run correctly when it is known that the mutex object is
+    /// valid.
+    bool is_valid() noexcept;
+
+    friend class CondVar;
+};
+
+class RobustMutex::NotRecoverable : public std::exception {
+public:
+    const char* what() const noexcept override
+    {
+        return "Failed to recover consistent state of shared memory";
+    }
+};
+
+
+/// A simple robust mutex ownership wrapper.
+class RobustLockGuard {
+public:
+    /// \param m the mutex to guard
+    /// \param func See RobustMutex::lock().
+    template <class TFunc>
+    RobustLockGuard(RobustMutex& m, TFunc func);
+    ~RobustLockGuard() noexcept;
+
+private:
+    RobustMutex& m_mutex;
+    friend class CondVar;
+};
+
+
+/// Condition variable for use in synchronization monitors.
+class CondVar {
+public:
+    CondVar();
+    ~CondVar() noexcept;
+
+    struct process_shared_tag {
+    };
+
+    /// Initialize this condition variable for use across multiple
+    /// processes. When constructed this way, the instance may be
+    /// placed in memory shared by multimple processes, as well as in
+    /// a memory mapped file. Such a condition variable remains valid
+    /// even after the constructing process terminates. Deleting the
+    /// instance (freeing the memory or deleting the file) without
+    /// first calling the destructor is legal and will not cause any
+    /// system resources to be leaked.
+    CondVar(process_shared_tag);
+
+    /// Wait for another thread to call notify() or notify_all().
+    void wait(LockGuard& l) noexcept;
+    template <class Func>
+    void wait(RobustMutex& m, Func recover_func, const struct timespec* tp = nullptr);
+
+    /// If any threads are wating for this condition, wake up at least
+    /// one.
+    void notify() noexcept;
+
+    /// Wake up every thread that is currently wating on this
+    /// condition.
+    void notify_all() noexcept;
+
+private:
+#ifdef _WIN32
+    CONDITION_VARIABLE m_condvar = CONDITION_VARIABLE_INIT;
+#else
+    pthread_cond_t m_impl;
+#endif
+
+    REALM_NORETURN static void init_failed(int);
+    REALM_NORETURN static void attr_init_failed(int);
+    REALM_NORETURN static void destroy_failed(int) noexcept;
+    void handle_wait_error(int error);
+};
+
+
+// Implementation:
+
+inline Thread::Thread()
+    : m_joinable(false)
+{
+}
+
+template <class F>
+inline Thread::Thread(F func)
+    : m_joinable(true)
+{
+    std::unique_ptr<F> func2(new F(func));       // Throws
+    start(&Thread::entry_point<F>, func2.get()); // Throws
+    func2.release();
+}
+
+inline Thread::Thread(Thread&& thread)
+{
+#ifndef _WIN32
+    m_id = thread.m_id;
+    m_joinable = thread.m_joinable;
+    thread.m_joinable = false;
+#endif
+}
+
+template <class F>
+inline void Thread::start(F func)
+{
+    if (m_joinable)
+        std::terminate();
+    std::unique_ptr<F> func2(new F(func));       // Throws
+    start(&Thread::entry_point<F>, func2.get()); // Throws
+    func2.release();
+    m_joinable = true;
+}
+
+inline Thread::~Thread() noexcept
+{
+    if (m_joinable)
+        REALM_TERMINATE("Destruction of joinable thread");
+}
+
+inline bool Thread::joinable() noexcept
+{
+    return m_joinable;
+}
+
+inline void Thread::start(entry_func_type entry_func, void* arg)
+{
+#ifdef _WIN32
+    m_std_thread = std::thread(entry_func, arg);
+#else
+    const pthread_attr_t* attr = nullptr; // Use default thread attributes
+    int r = pthread_create(&m_id, attr, entry_func, arg);
+    if (REALM_UNLIKELY(r != 0))
+        create_failed(r); // Throws
+#endif
+}
+
+template <class F>
+inline void* Thread::entry_point(void* cookie) noexcept
+{
+    std::unique_ptr<F> func(static_cast<F*>(cookie));
+    try {
+        (*func)();
+    }
+    catch (...) {
+        std::terminate();
+    }
+    return 0;
+}
+
+
+inline Mutex::Mutex()
+{
+    init_as_regular();
+}
+
+inline Mutex::Mutex(process_shared_tag)
+{
+    bool robust_if_available = false;
+    init_as_process_shared(robust_if_available);
+}
+
+inline Mutex::~Mutex() noexcept
+{
+#ifndef _WIN32
+    int r = pthread_mutex_destroy(&m_impl);
+    if (REALM_UNLIKELY(r != 0))
+        destroy_failed(r);
+#else
+    DeleteCriticalSection(&m_critical_section);
+#endif
+}
+
+inline void Mutex::init_as_regular()
+{
+#ifndef _WIN32
+    int r = pthread_mutex_init(&m_impl, 0);
+    if (REALM_UNLIKELY(r != 0))
+        init_failed(r);
+#else
+    InitializeCriticalSection(&m_critical_section);
+#endif
+}
+
+inline void Mutex::lock() noexcept
+{
+#ifdef _WIN32
+    EnterCriticalSection(&m_critical_section);
+#else
+    int r = pthread_mutex_lock(&m_impl);
+    if (REALM_LIKELY(r == 0))
+        return;
+    lock_failed(r);
+#endif
+}
+
+inline bool Mutex::try_lock() noexcept
+{
+#ifdef _WIN32
+    return TryEnterCriticalSection(&m_critical_section);
+#else
+    int r = pthread_mutex_trylock(&m_impl);
+    if (r == EBUSY) {
+        return false;
+    }
+    else if (r == 0) {
+        return true;
+    }
+    lock_failed(r);
+#endif
+}
+
+inline void Mutex::unlock() noexcept
+{
+#ifdef _WIN32
+    LeaveCriticalSection(&m_critical_section);
+#else
+    int r = pthread_mutex_unlock(&m_impl);
+    REALM_ASSERT(r == 0);
+#endif
+}
+
+
+inline LockGuard::LockGuard(Mutex& m) noexcept
+    : m_mutex(m)
+{
+    m_mutex.lock();
+}
+
+inline LockGuard::~LockGuard() noexcept
+{
+    m_mutex.unlock();
+}
+
+
+inline UniqueLock::UniqueLock(Mutex& m) noexcept
+    : m_mutex(&m)
+{
+    m_mutex->lock();
+    m_is_locked = true;
+}
+
+inline UniqueLock::UniqueLock(Mutex& m, defer_lock_tag) noexcept
+    : m_mutex(&m)
+{
+    m_is_locked = false;
+}
+
+inline UniqueLock::~UniqueLock() noexcept
+{
+    if (m_is_locked)
+        m_mutex->unlock();
+}
+
+inline bool UniqueLock::holds_lock() noexcept
+{
+    return m_is_locked;
+}
+
+inline void UniqueLock::lock() noexcept
+{
+    m_mutex->lock();
+    m_is_locked = true;
+}
+
+inline void UniqueLock::unlock() noexcept
+{
+    m_mutex->unlock();
+    m_is_locked = false;
+}
+
+template <typename TFunc>
+inline RobustLockGuard::RobustLockGuard(RobustMutex& m, TFunc func)
+    : m_mutex(m)
+{
+    m_mutex.lock(func);
+}
+
+inline RobustLockGuard::~RobustLockGuard() noexcept
+{
+    m_mutex.unlock();
+}
+
+
+inline RobustMutex::RobustMutex()
+    : Mutex(no_init_tag())
+{
+    bool robust_if_available = true;
+    init_as_process_shared(robust_if_available);
+}
+
+inline RobustMutex::~RobustMutex() noexcept
+{
+}
+
+template <class Func>
+inline void RobustMutex::lock(Func recover_func)
+{
+    bool no_thread_has_died = low_level_lock(); // Throws
+    if (REALM_LIKELY(no_thread_has_died))
+        return;
+    try {
+        recover_func(); // Throws
+        mark_as_consistent();
+        // If we get this far, the protected memory has been
+        // brought back into a consistent state, and the mutex has
+        // been notified about this. This means that we can safely
+        // enter the applications critical section.
+    }
+    catch (...) {
+        // Unlocking without first calling mark_as_consistent()
+        // means that the mutex enters the "not recoverable"
+        // state, which will cause all future attempts at locking
+        // to fail.
+        unlock();
+        throw;
+    }
+}
+
+template <class Func>
+inline bool RobustMutex::try_lock(Func recover_func)
+{
+    int lock_result = try_low_level_lock(); // Throws
+    if (lock_result == 0) return false;
+    bool no_thread_has_died = lock_result == 1;
+    if (REALM_LIKELY(no_thread_has_died))
+        return true;
+    try {
+        recover_func(); // Throws
+        mark_as_consistent();
+        // If we get this far, the protected memory has been
+        // brought back into a consistent state, and the mutex has
+        // been notified aboit this. This means that we can safely
+        // enter the applications critical section.
+    }
+    catch (...) {
+        // Unlocking without first calling mark_as_consistent()
+        // means that the mutex enters the "not recoverable"
+        // state, which will cause all future attempts at locking
+        // to fail.
+        unlock();
+        throw;
+    }
+    return true;
+}
+
+inline void RobustMutex::unlock() noexcept
+{
+    Mutex::unlock();
+}
+
+
+inline CondVar::CondVar()
+{
+#ifndef _WIN32
+    int r = pthread_cond_init(&m_impl, 0);
+    if (REALM_UNLIKELY(r != 0))
+        init_failed(r);
+#endif
+}
+
+inline CondVar::~CondVar() noexcept
+{
+#ifndef _WIN32
+    int r = pthread_cond_destroy(&m_impl);
+    if (REALM_UNLIKELY(r != 0))
+        destroy_failed(r);
+#endif
+}
+
+inline void CondVar::wait(LockGuard& l) noexcept
+{
+#ifdef _WIN32
+    SleepConditionVariableCS(&m_condvar, &l.m_mutex.m_critical_section, INFINITE);
+#else
+    int r = pthread_cond_wait(&m_impl, &l.m_mutex.m_impl);
+    if (REALM_UNLIKELY(r != 0))
+        REALM_TERMINATE("pthread_cond_wait() failed");
+#endif
+}
+
+template <class Func>
+inline void CondVar::wait(RobustMutex& m, Func recover_func, const struct timespec* tp)
+{
+    int r;
+
+    if (!tp) {
+#ifdef _WIN32
+        if (!SleepConditionVariableCS(&m_condvar, &m.m_critical_section, INFINITE))
+            r = GetLastError();
+        else
+            r = 0;
+#else
+        r = pthread_cond_wait(&m_impl, &m.m_impl);
+#endif
+    }
+    else {
+#ifdef _WIN32
+        if (!SleepConditionVariableCS(&m_condvar, &m.m_critical_section, tp->tv_sec / 1000)) {
+            r = GetLastError();
+            if (r == ERROR_TIMEOUT)
+                return;
+        } else {
+            r = 0
+        }
+#else
+        r = pthread_cond_timedwait(&m_impl, &m.m_impl, tp);
+        if (r == ETIMEDOUT)
+            return;
+#endif
+    }
+
+    if (REALM_LIKELY(r == 0))
+        return;
+
+    handle_wait_error(r);
+
+    try {
+        recover_func(); // Throws
+        m.mark_as_consistent();
+        // If we get this far, the protected memory has been
+        // brought back into a consistent state, and the mutex has
+        // been notified aboit this. This means that we can safely
+        // enter the applications critical section.
+    }
+    catch (...) {
+        // Unlocking without first calling mark_as_consistent()
+        // means that the mutex enters the "not recoverable"
+        // state, which will cause all future attempts at locking
+        // to fail.
+        m.unlock();
+        throw;
+    }
+}
+
+inline void CondVar::notify() noexcept
+{
+#ifdef _WIN32
+    WakeConditionVariable(&m_condvar);
+#else
+    int r = pthread_cond_signal(&m_impl);
+    REALM_ASSERT(r == 0);
+#endif
+}
+
+inline void CondVar::notify_all() noexcept
+{
+#ifdef _WIN32
+    WakeAllConditionVariable(&m_condvar);
+#else
+    int r = pthread_cond_broadcast(&m_impl);
+    REALM_ASSERT(r == 0);
+#endif
+}
+
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_THREAD_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/time.hpp b/iOS/Pods/Realm/include/core/realm/util/time.hpp
new file mode 100644 (file)
index 0000000..3a3d845
--- /dev/null
@@ -0,0 +1,88 @@
+/*************************************************************************
+ *
+ * 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_UTIL_TIME_HPP
+#define REALM_UTIL_TIME_HPP
+
+#include <cstring>
+#include <ctime>
+#include <iterator>
+#include <locale>
+#include <ostream>
+#include <sstream>
+
+
+namespace realm {
+namespace util {
+
+/// Thread safe version of std::localtime(). Uses localtime_r() on POSIX.
+std::tm localtime(std::time_t);
+
+/// Thread safe version of std::gmtime(). Uses gmtime_r() on POSIX.
+std::tm gmtime(std::time_t);
+
+/// Similar to std::put_time() from <iomanip>. See std::put_time() for
+/// information about the format string. This function is provided because
+/// std::put_time() is unavailable in GCC 4. This function is thread safe.
+///
+/// The default format is ISO 8601 date and time.
+template<class C, class T>
+void put_time(std::basic_ostream<C,T>&, const std::tm&, const C* format = "%FT%T%z");
+
+/// @{ These functions combine localtime() or gmtime() with put_time() and
+/// std::ostringstream. For detals on the format string, see
+/// std::put_time(). These function are thread safe.
+std::string format_local_time(std::time_t, const char* format = "%FT%T%z");
+std::string format_utc_time(std::time_t, const char* format = "%FT%T%z");
+/// @}
+
+
+
+
+// Implementation
+
+template<class C, class T>
+inline void put_time(std::basic_ostream<C,T>& out, const std::tm& tm, const C* format)
+{
+    const auto& facet = std::use_facet<std::time_put<C>>(out.getloc()); // Throws
+    facet.put(std::ostreambuf_iterator<C>(out), out, ' ', &tm,
+              format, format + T::length(format)); // Throws
+}
+
+inline std::string format_local_time(std::time_t time, const char* format)
+{
+    std::tm tm = util::localtime(time);
+    std::ostringstream out;
+    util::put_time(out, tm, format); // Throws
+    return out.str(); // Throws
+}
+
+inline std::string format_utc_time(std::time_t time, const char* format)
+{
+    std::tm tm = util::gmtime(time);
+    std::ostringstream out;
+    util::put_time(out, tm, format); // Throws
+    return out.str(); // Throws
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_TIME_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/to_string.hpp b/iOS/Pods/Realm/include/core/realm/util/to_string.hpp
new file mode 100644 (file)
index 0000000..c3fac65
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_UTIL_TO_STRING_HPP
+#define REALM_UTIL_TO_STRING_HPP
+
+#include <iosfwd>
+#include <string>
+
+namespace realm {
+namespace util {
+
+class Printable {
+public:
+    Printable(bool value)
+        : m_type(Type::Bool)
+        , m_uint(value)
+    {
+    }
+    Printable(unsigned char value)
+        : m_type(Type::Uint)
+        , m_uint(value)
+    {
+    }
+    Printable(unsigned int value)
+        : m_type(Type::Uint)
+        , m_uint(value)
+    {
+    }
+    Printable(unsigned long value)
+        : m_type(Type::Uint)
+        , m_uint(value)
+    {
+    }
+    Printable(unsigned long long value)
+        : m_type(Type::Uint)
+        , m_uint(value)
+    {
+    }
+    Printable(char value)
+        : m_type(Type::Int)
+        , m_int(value)
+    {
+    }
+    Printable(int value)
+        : m_type(Type::Int)
+        , m_int(value)
+    {
+    }
+    Printable(long value)
+        : m_type(Type::Int)
+        , m_int(value)
+    {
+    }
+    Printable(long long value)
+        : m_type(Type::Int)
+        , m_int(value)
+    {
+    }
+    Printable(const char* value)
+        : m_type(Type::String)
+        , m_string(value)
+    {
+    }
+    Printable(std::string const& value)
+        : m_type(Type::String)
+        , m_string(value.c_str())
+    {
+    }
+
+
+    void print(std::ostream& out, bool quote) const;
+    std::string str() const;
+
+    static void print_all(std::ostream& out, const std::initializer_list<Printable>& values, bool quote);
+
+private:
+    enum class Type {
+        Bool,
+        Int,
+        Uint,
+        String,
+    } m_type;
+
+    union {
+        uintmax_t m_uint;
+        intmax_t m_int;
+        const char* m_string;
+    };
+};
+
+
+template <class T>
+std::string to_string(const T& v)
+{
+    return Printable(v).str();
+}
+
+std::string format(const char* fmt, std::initializer_list<Printable>);
+
+template<typename... Args>
+std::string format(const char* fmt, Args&&... args)
+{
+    return format(fmt, {Printable(args)...});
+}
+
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_TO_STRING_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/type_list.hpp b/iOS/Pods/Realm/include/core/realm/util/type_list.hpp
new file mode 100644 (file)
index 0000000..da847c7
--- /dev/null
@@ -0,0 +1,244 @@
+/*************************************************************************
+ *
+ * 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_UTIL_TYPE_LIST_HPP
+#define REALM_UTIL_TYPE_LIST_HPP
+
+namespace realm {
+namespace util {
+
+
+/// The 'cons' operator for building lists of types.
+///
+/// \tparam H The head of the list, that is, the first type in the
+/// list.
+///
+/// \tparam T The tail of the list, that is, the list of types
+/// following the head. It is 'void' if nothing follows the head,
+/// otherwise it matches TypeCons<H2,T2>.
+///
+/// Note that 'void' is interpreted as a zero-length list.
+template <class H, class T>
+struct TypeCons {
+    typedef H head;
+    typedef T tail;
+};
+
+
+/// Append a type the the end of a type list. The resulting type list
+/// is available as TypeAppend<List, T>::type.
+///
+/// \tparam List A list of types constructed using TypeCons<>. Note
+/// that 'void' is interpreted as a zero-length list.
+///
+/// \tparam T The new type to be appended.
+template <class List, class T>
+struct TypeAppend {
+    typedef TypeCons<typename List::head, typename TypeAppend<typename List::tail, T>::type> type;
+};
+/// Base case for empty type list.
+template <class T>
+struct TypeAppend<void, T> {
+    typedef TypeCons<T, void> type;
+};
+
+
+/// Get an element from the specified list of types. The result is
+/// available as TypeAt<List, i>::type.
+///
+/// \tparam List A list of types constructed using TypeCons<>. Note
+/// that 'void' is interpreted as a zero-length list.
+///
+/// \tparam i The index of the list element to get.
+template <class List, int i>
+struct TypeAt {
+    typedef typename TypeAt<typename List::tail, i - 1>::type type;
+};
+/// Base case for empty type list.
+template <class List>
+struct TypeAt<List, 0> {
+    typedef typename List::head type;
+};
+
+
+/// Count the number of elements in the specified list of types. The
+/// result is available as TypeCount<List>::value.
+///
+/// \tparam List The list of types, constructed using TypeCons<>. Note
+/// that 'void' is interpreted as a zero-length list.
+template <class List>
+struct TypeCount {
+    static const int value = 1 + TypeCount<typename List::tail>::value;
+};
+/// Base case for empty type list.
+template <>
+struct TypeCount<void> {
+    static const int value = 0;
+};
+
+
+/// Find the first type in the specified list that satisfies the
+/// specified predicate.
+///
+/// \tparam List The list of types, constructed using TypeCons<>. Note
+/// that 'void' is interpreted as a zero-length list.
+///
+/// \tparam Pred Must be such that `Pred<T>::%value` is true if, and
+/// only if the predicate is satisfied for `T`.
+template <class List, template <class> class Pred>
+struct FindType {
+private:
+    typedef typename List::head type_1;
+    typedef typename FindType<typename List::tail, Pred>::type type_2;
+
+public:
+    typedef typename std::conditional<Pred<type_1>::value, type_1, type_2>::type type;
+};
+/// Base case for empty type list.
+template <template <class> class Pred>
+struct FindType<void, Pred> {
+    typedef void type;
+};
+
+
+/// Execute an action for each element in the specified list of types.
+///
+/// \tparam List The list of types, constructed using TypeCons<>. Note
+/// that 'void' is interpreted as a zero-length list.
+template <class List, template <class T, int i> class Op, int i = 0>
+struct ForEachType {
+    /// Execute the `Op<T,i>::%exec()` for each type `T` at index `i`
+    /// in `List`.
+    static void exec()
+    {
+        Op<typename List::head, i>::exec();
+        ForEachType<typename List::tail, Op, i + 1>::exec();
+    }
+    /// Execute the `Op<T,i>::%exec(a)` for each type `T` at index `i`
+    /// in `List`.
+    template <class A>
+    static void exec(const A& a)
+    {
+        Op<typename List::head, i>::exec(a);
+        ForEachType<typename List::tail, Op, i + 1>::exec(a);
+    }
+    /// Execute the `Op<T,i>::%exec(a,b)` for each type `T` at index
+    /// `i` in `List`.
+    template <class A, class B>
+    static void exec(const A& a, const B& b)
+    {
+        Op<typename List::head, i>::exec(a, b);
+        ForEachType<typename List::tail, Op, i + 1>::exec(a, b);
+    }
+    /// Execute the `Op<T,i>::%exec(a,b,c)` for each type `T` at index
+    /// `i` in `List`.
+    template <class A, class B, class C>
+    static void exec(const A& a, const B& b, const C& c)
+    {
+        Op<typename List::head, i>::exec(a, b, c);
+        ForEachType<typename List::tail, Op, i + 1>::exec(a, b, c);
+    }
+};
+/// Base case for empty type list.
+template <template <class T, int i> class Op, int i>
+struct ForEachType<void, Op, i> {
+    static void exec()
+    {
+    }
+    template <class A>
+    static void exec(const A&)
+    {
+    }
+    template <class A, class B>
+    static void exec(const A&, const B&)
+    {
+    }
+    template <class A, class B, class C>
+    static void exec(const A&, const B&, const C&)
+    {
+    }
+};
+
+
+/// Execute a predicate for each element in the specified list of
+/// types, and return true if, and only if the predicate returns true
+/// for at least one of those elements. Iteration over the type list
+/// is terminated as soon as a predicate returns true.
+///
+/// \tparam List The list of types, constructed using TypeCons<>. Note
+/// that 'void' is interpreted as a zero-length list.
+template <class List, template <class T, int i> class Pred, int i = 0>
+struct HasType {
+    /// Execute the `Op<T,i>::%exec()` for each type `T` at index `i`
+    /// in `List`.
+    static bool exec()
+    {
+        return Pred<typename List::head, i>::exec() || HasType<typename List::tail, Pred, i + 1>::exec();
+    }
+    /// Execute the `Op<T,i>::%exec(a)` for each type `T` at index `i`
+    /// in `List`.
+    template <class A>
+    static bool exec(const A& a)
+    {
+        return Pred<typename List::head, i>::exec(a) || HasType<typename List::tail, Pred, i + 1>::exec(a);
+    }
+    /// Execute the `Op<T,i>::%exec(a,b)` for each type `T` at index
+    /// `i` in `List`.
+    template <class A, class B>
+    static bool exec(const A& a, const B& b)
+    {
+        return Pred<typename List::head, i>::exec(a, b) || HasType<typename List::tail, Pred, i + 1>::exec(a, b);
+    }
+    /// Execute the `Op<T,i>::%exec(a,b,c)` for each type `T` at index
+    /// `i` in `List`.
+    template <class A, class B, class C>
+    static bool exec(const A& a, const B& b, const C& c)
+    {
+        return Pred<typename List::head, i>::exec(a, b, c) ||
+               HasType<typename List::tail, Pred, i + 1>::exec(a, b, c);
+    }
+};
+/// Base case for empty type list.
+template <template <class T, int i> class Pred, int i>
+struct HasType<void, Pred, i> {
+    static bool exec()
+    {
+        return false;
+    }
+    template <class A>
+    static bool exec(const A&)
+    {
+        return false;
+    }
+    template <class A, class B>
+    static bool exec(const A&, const B&)
+    {
+        return false;
+    }
+    template <class A, class B, class C>
+    static bool exec(const A&, const B&, const C&)
+    {
+        return false;
+    }
+};
+
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_TYPE_LIST_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/type_traits.hpp b/iOS/Pods/Realm/include/core/realm/util/type_traits.hpp
new file mode 100644 (file)
index 0000000..cedbe05
--- /dev/null
@@ -0,0 +1,161 @@
+/*************************************************************************
+ *
+ * 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_UTIL_TYPE_TRAITS_HPP
+#define REALM_UTIL_TYPE_TRAITS_HPP
+
+#include <cstdint>
+#include <climits>
+#include <cwchar>
+#include <limits>
+#include <type_traits>
+
+#include <realm/util/features.h>
+#include <realm/util/assert.hpp>
+#include <realm/util/type_list.hpp>
+
+namespace realm {
+namespace util {
+
+template <class From, class To>
+struct CopyConst {
+private:
+    typedef typename std::remove_const<To>::type type_1;
+
+public:
+    typedef typename std::conditional<std::is_const<From>::value, const type_1, type_1>::type type;
+};
+
+
+/// Member `type` is the type resulting from integral or
+/// floating-point promotion of a value of type `T`.
+///
+/// \note Enum types are supported only when the compiler supports the
+/// C++11 'decltype' feature.
+template <class T>
+struct Promote;
+
+
+/// Member `type` is the type of the result of a binary arithmetic (or
+/// bitwise) operation (+, -, *, /, %, |, &, ^) when applied to
+/// operands of type `A` and `B` respectively. The type of the result
+/// of a shift operation (<<, >>) can instead be found as the type
+/// resulting from integral promotion of the left operand. The type of
+/// the result of a unary arithmetic (or bitwise) operation can be
+/// found as the type resulting from integral promotion of the
+/// operand.
+///
+/// \note Enum types are supported only when the compiler supports the
+/// C++11 'decltype' feature.
+template <class A, class B>
+struct ArithBinOpType;
+
+
+/// Member `type` is `B` if `B` has more value bits than `A`,
+/// otherwise is is `A`.
+template <class A, class B>
+struct ChooseWidestInt;
+
+
+/// Member `type` is the first of `unsigned char`, `unsigned short`,
+/// `unsigned int`, `unsigned long`, and `unsigned long long` that has
+/// at least `bits` value bits.
+template <int bits>
+struct LeastUnsigned;
+
+
+/// Member `type` is `unsigned` if `unsigned` has at least `bits`
+/// value bits, otherwise it is the same as
+/// `LeastUnsigned<bits>::%type`.
+template <int bits>
+struct FastestUnsigned;
+
+
+// Implementation
+
+
+template <class T>
+struct Promote {
+    typedef decltype(+T()) type; // FIXME: This is not performing floating-point promotion.
+};
+
+
+template <class A, class B>
+struct ArithBinOpType {
+    typedef decltype(A() + B()) type;
+};
+
+
+template <class A, class B>
+struct ChooseWidestInt {
+private:
+    typedef std::numeric_limits<A> lim_a;
+    typedef std::numeric_limits<B> lim_b;
+    static_assert(lim_a::is_specialized && lim_b::is_specialized,
+                  "std::numeric_limits<> must be specialized for both types");
+    static_assert(lim_a::is_integer && lim_b::is_integer, "Both types must be integers");
+
+public:
+    typedef typename std::conditional<(lim_a::digits >= lim_b::digits), A, B>::type type;
+};
+
+
+template <int bits>
+struct LeastUnsigned {
+private:
+    typedef void types_0;
+    typedef TypeAppend<types_0, unsigned char>::type types_1;
+    typedef TypeAppend<types_1, unsigned short>::type types_2;
+    typedef TypeAppend<types_2, unsigned int>::type types_3;
+    typedef TypeAppend<types_3, unsigned long>::type types_4;
+    typedef TypeAppend<types_4, unsigned long long>::type types_5;
+    typedef types_5 types;
+    // The `dummy<>` template is there to work around a bug in
+    // VisualStudio (seen in versions 2010 and 2012). Without the
+    // `dummy<>` template, The C++ compiler in Visual Studio would
+    // attempt to instantiate `FindType<type, pred>` before the
+    // instantiation of `LeastUnsigned<>` which obviously fails
+    // because `pred` depends on `bits`.
+    template <int>
+    struct dummy {
+        template <class T>
+        struct pred {
+            static const bool value = std::numeric_limits<T>::digits >= bits;
+        };
+    };
+
+public:
+    typedef typename FindType<types, dummy<bits>::template pred>::type type;
+    static_assert(!(std::is_same<type, void>::value), "No unsigned type is that wide");
+};
+
+
+template <int bits>
+struct FastestUnsigned {
+private:
+    typedef typename util::LeastUnsigned<bits>::type least_unsigned;
+
+public:
+    typedef typename util::ChooseWidestInt<unsigned, least_unsigned>::type type;
+};
+
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_TYPE_TRAITS_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/uri.hpp b/iOS/Pods/Realm/include/core/realm/util/uri.hpp
new file mode 100644 (file)
index 0000000..2b39da5
--- /dev/null
@@ -0,0 +1,251 @@
+/*************************************************************************
+ *
+ * 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_UTIL_URI_HPP
+#define REALM_UTIL_URI_HPP
+
+#include <string>
+
+namespace realm {
+namespace util {
+
+
+/// \brief A decomposed URI reference.
+///
+/// A Uri object contains a URI reference decomposed into its 5 main component
+/// parts (scheme, authority, path, query, and fragment identifier).
+///
+/// The decomposition process (as carried out by the constructor) performs a
+/// maximally lenient parsing of the specified URI reference. It does that
+/// according to the following regular expression (copied verbatimly from
+/// http://tools.ietf.org/html/rfc3986#appendix-B):
+///
+///     ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+///      12            3  4          5       6  7        8 9
+///
+///     Group
+///     ------------------------
+///     1       Scheme part
+///     3       Authority part
+///     5       Path part
+///     6       Query part
+///     8       Fragment identifier part
+///
+/// NOTE: Since this regular expression maches every string, every string is
+/// decomposable.
+///
+/// NOTE: This class does not attempt to perform any level of validation of URI
+/// references against the grammer specified in the RFC. Such validation could
+/// be added later, for example through a new `Uri::validate()`.
+///
+/// For example, the decomposition of
+/// "http://www.ietf.org/rfc/rfc2396.txt?foo=bar#chp3" is:
+///
+/// <pre>
+///
+///   scheme -> "http:"
+///   auth   -> "//www.ietf.org"
+///   path   -> "/rfc/rfc2396.txt"
+///   query  -> "?foo=bar"
+///   frag   -> "#chp3"
+///
+/// </pre>
+///
+/// This class also provides recomposition of a URI references from their
+/// component parts, where the parts can be specified individually, or be a
+/// result of URI resoultion.
+///
+/// It is important to understand, however, that certain restrictions need to
+/// apply to each component part in order that the URI reference as a whole is
+/// self consistent. More concretely, it is necessary to require that the
+/// component parts at any time must have values that will be preserved across a
+/// recomposition -> decomposition cycle.
+///
+/// The actual restrictions on each component part is specified for the
+/// corresponding setter-method (e.g., set_scheme()).
+///
+/// Note that component parts resulting from decomposition, canonicalize, or
+/// from resolution (resolve()) will automatically (by design of the underlying
+/// algorithm) adhere to these rules.
+///
+/// Decomposition, recomposition, conanonicalization, and resolution algorithms
+/// are taken from RFC 3986.
+///
+/// \sa http://tools.ietf.org/html/rfc3986
+class Uri {
+public:
+    Uri();
+
+    /// Decompose the specified URI reference into its five main parts.
+    Uri(const std::string&);
+
+    /// Reconstruct a URI reference from its 5 components.
+    std::string recompose() const;
+
+/*
+    /// Resolve this URI reference against the specified base URI reference
+    /// according to the rules described in section 5.2 of RFC 3986.
+    ///
+    /// Be aware that a fragment identifier on the base URI reference is never
+    /// carried over to the result. This is in accordance with the RFC.
+    void resolve(const Uri& base, bool strict = true);
+*/
+
+    /// Remove empty URI components. Also, for URI references having either a
+    /// scheme part or an authority part, replace an absent path with "/".
+    void canonicalize();
+
+    /// Get the scheme part of this URI reference including the trailing ":", or
+    /// the empty tring if there is no scheme part.
+    const std::string& get_scheme() const;
+
+    /// Get the authority part of this URI reference including the leading "//",
+    /// or the empty tring if there is no authority part.
+    const std::string& get_auth() const;
+
+    /// Same as get_auth() (with no arguments), but parse the authority component
+    /// into userinfo, host, and port subcomponents.
+    ///
+    /// \return True if, and only if the authority component was present (i.e.,
+    /// not the empty string). When false is returned, none of the specified
+    /// strings will have been modified.
+    bool get_auth(std::string& userinfo, std::string& host, std::string& port) const;
+
+    /// Get the path part of this URI reference, or the empty tring if there is
+    /// no path part.
+    const std::string& get_path() const;
+
+    /// Get the query part of this URI reference including the leading "?", or
+    /// the empty tring if there is no query part.
+    const std::string& get_query() const;
+
+    /// Get the fragment identifier of this URI reference including the leading
+    /// "#", or the empty tring if there is no fragment identifier.
+    const std::string& get_frag() const;
+
+    /// The specified string must either be empty or have a final ":". Also, it
+    /// must not contain "/", "?", or "#", nor may it contain more than one ":".
+    ///
+    /// \throw std::invalid_argument If the specified string is not valid
+    /// according to the specified rules.
+    void set_scheme(const std::string&);
+
+    /// The specified string must either be empty or have "//" as a
+    /// prefix. Also, it must not contain "?" or "#", nor may it contain "/"
+    /// beyond the first two.
+    ///
+    /// \throw std::invalid_argument If the specified string is not valid
+    /// according to the specified rules.
+    void set_auth(const std::string&);
+
+    /// The specified string must not contain "?" or "#".
+    ///
+    /// \throw std::invalid_argument If the specified string is not valid
+    /// according to the specified rules.
+    void set_path(const std::string&);
+
+    /// The specified string must either be empty or have a leading "?". Also,
+    /// it must not contain "#".
+    ///
+    /// \throw std::invalid_argument If the specified string is not valid
+    /// according to the specified rules.
+    void set_query(const std::string&);
+
+/*
+    /// Set the query string to the serialized form of the specified set of
+    /// query parameters. This is slightly faster than set_query(q.encode())
+    /// because it avoids the validity check on the string.
+    void set_query(const Params&);
+*/
+
+    /// The specified string must either be empty or have a leading "#".
+    ///
+    /// \throw std::invalid_argument If the specified string is not valid
+    /// according to the specified rules.
+    void set_frag(const std::string&);
+
+    bool is_absolute() const;
+
+private:
+    std::string m_scheme, m_auth, m_path, m_query, m_frag;
+};
+
+
+/// uri_percent_encode() uri encodes a string as defined in according to
+/// https://tools.ietf.org/html/rfc3986#section-2.1
+/// The unescaped input must be UTF-8 encoded. uri_percent_encode() works
+/// by replacing each UTF-8 character by three charatcers.
+/// pct-encoded = "%" HEXDIG HEXDIG
+/// where HEXDIG HEXDIG is the hexadecimal value of the character.
+/// HEXDIG is a capital letter for A - F.
+/// Unreserved chracters are not encoded.
+/// unreseved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+///
+/// uri_percent_decode() is the inverse of uri_percent_encode().
+/// uri_percent_decode() throws std::runtime_error if the input
+/// is invalid and cannot be decoded.
+std::string uri_percent_encode(const std::string& unescaped);
+std::string uri_percent_decode(const std::string& escaped);
+
+
+// Implementation
+
+inline Uri::Uri()
+{
+}
+
+inline std::string Uri::recompose() const
+{
+    return m_scheme + m_auth + m_path + m_query + m_frag;
+}
+
+inline const std::string& Uri::get_scheme() const
+{
+    return m_scheme;
+}
+
+inline const std::string& Uri::get_auth() const
+{
+    return m_auth;
+}
+
+inline const std::string& Uri::get_path() const
+{
+    return m_path;
+}
+
+inline const std::string& Uri::get_query() const
+{
+    return m_query;
+}
+
+inline const std::string& Uri::get_frag() const
+{
+    return m_frag;
+}
+
+inline bool Uri::is_absolute() const
+{
+    return !m_scheme.empty();
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_URI_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/utf8.hpp b/iOS/Pods/Realm/include/core/realm/util/utf8.hpp
new file mode 100644 (file)
index 0000000..02be1d5
--- /dev/null
@@ -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_UTIL_UTF8_HPP
+#define REALM_UTIL_UTF8_HPP
+
+#include <cstdint>
+#include <string>
+
+#include <realm/util/safe_int_ops.hpp>
+#include <realm/string_data.hpp>
+#include <realm/util/features.h>
+#include <realm/utilities.hpp>
+
+namespace realm {
+namespace util {
+
+
+/// Transcode between UTF-8 and UTF-16.
+///
+/// \tparam Char16 Must be an integral type with at least 16 bits.
+///
+/// \tparam Traits16 Must define to_int_type() and to_char_type() for
+/// \a Char16.
+template <class Char16, class Traits16 = std::char_traits<Char16>>
+struct Utf8x16 {
+    /// Transcode as much as possible of the specified UTF-8 input, to
+    /// UTF-16. Returns true if all input characters were transcoded, or
+    /// transcoding stopped because the next character did not fit into the
+    /// output buffer. Returns false if transcoding stopped due to invalid
+    /// input. It is not specified whether this function returns true or false
+    /// if invalid input occurs at the same time as the output buffer runs
+    /// full. In any case, upon return, \a in_begin and \a out_begin are
+    /// advanced to the position where transcoding stopped.
+    ///
+    /// Throws only if Traits16::to_char_type() throws.
+    static bool to_utf16(const char*& in_begin, const char* in_end, Char16*& out_begin, Char16* out_end);
+
+    /// Same as to_utf16(), but in reverse.
+    ///
+    /// Throws only if Traits16::to_int_type() throws.
+    static bool to_utf8(const Char16*& in_begin, const Char16* in_end, char*& out_begin, char* out_end);
+
+    /// Summarize the number of UTF-16 elements needed to hold the result of
+    /// transcoding the specified UTF-8 string. Upon return, if \a in_begin !=
+    /// \a in_end, then the summation stopped due to invalid UTF-8 input. The
+    /// returned size then reflects the number of UTF-16 elements needed to hold
+    /// the result of transcoding the part of the input that was examined. This
+    /// function will only detect a few UTF-8 validity issues, and can therefore
+    /// not be used for general UTF-8 validation.
+    static size_t find_utf16_buf_size(const char*& in_begin, const char* in_end);
+
+    /// Summarize the number of UTF-8 bytes needed to hold the result of
+    /// transcoding the specified UTF-16 string. Upon return, if \a in_begin !=
+    /// \a in_end, then the summation stopped due to invalid UTF-16 input, or to
+    /// prevent the returned \c size_t value from overflowing. The returned size
+    /// then reflects the number of UTF-8 bytes needed to hold the result of
+    /// transcoding the part of the input that was examined. This function will
+    /// only detect a few UTF-16 validity issues, and can therefore not be used
+    /// for general UTF-16 validation.
+    static size_t find_utf8_buf_size(const Char16*& in_begin, const Char16* in_end);
+};
+
+
+// Implementation:
+
+// Adapted from reference implementation.
+// http://www.unicode.org/resources/utf8.html
+// http://www.bsdua.org/files/unicode.tar.gz
+template <class Char16, class Traits16>
+inline bool Utf8x16<Char16, Traits16>::to_utf16(const char*& in_begin, const char* const in_end, Char16*& out_begin,
+                                                Char16* const out_end)
+{
+    typedef std::char_traits<char> traits8;
+    bool invalid = false;
+    const char* in = in_begin;
+    Char16* out = out_begin;
+    while (in != in_end) {
+        if (REALM_UNLIKELY(out == out_end)) {
+            break; // Need space in output buffer
+        }
+        REALM_ASSERT(&in[0] >= in_begin && &in[0] < in_end);
+        uint_fast16_t v1 = uint_fast16_t(traits8::to_int_type(in[0]));
+        if (REALM_LIKELY(v1 < 0x80)) { // One byte
+            // UTF-8 layout: 0xxxxxxx
+            *out++ = Traits16::to_char_type(v1);
+            in += 1;
+            continue;
+        }
+        if (REALM_UNLIKELY(v1 < 0xC0)) {
+            invalid = true;
+            break; // Invalid first byte of UTF-8 sequence
+        }
+        if (REALM_LIKELY(v1 < 0xE0)) { // Two bytes
+            if (REALM_UNLIKELY(in_end - in < 2)) {
+                invalid = true;
+                break; // Incomplete UTF-8 sequence
+            }
+            REALM_ASSERT(&in[1] >= in_begin && &in[1] < in_end);
+            uint_fast16_t v2 = uint_fast16_t(traits8::to_int_type(in[1]));
+            // UTF-8 layout: 110xxxxx 10xxxxxx
+            if (REALM_UNLIKELY((v2 & 0xC0) != 0x80)) {
+                invalid = true;
+                break; // Invalid continuation byte
+            }
+            uint_fast16_t v = uint_fast16_t(((v1 & 0x1F) << 6) | ((v2 & 0x3F) << 0));
+            if (REALM_UNLIKELY(v < 0x80)) {
+                invalid = true;
+                break; // Overlong encoding is invalid
+            }
+            *out++ = Traits16::to_char_type(v);
+            in += 2;
+            continue;
+        }
+        if (REALM_LIKELY(v1 < 0xF0)) { // Three bytes
+            if (REALM_UNLIKELY(in_end - in < 3)) {
+                invalid = true;
+                break; // Incomplete UTF-8 sequence
+            }
+            REALM_ASSERT(&in[1] >= in_begin && &in[2] < in_end);
+            uint_fast16_t v2 = uint_fast16_t(traits8::to_int_type(in[1]));
+            uint_fast16_t v3 = uint_fast16_t(traits8::to_int_type(in[2]));
+            // UTF-8 layout: 1110xxxx 10xxxxxx 10xxxxxx
+            if (REALM_UNLIKELY((v2 & 0xC0) != 0x80 || (v3 & 0xC0) != 0x80)) {
+                invalid = true;
+                break; // Invalid continuation byte
+            }
+            uint_fast16_t v = uint_fast16_t(((v1 & 0x0F) << 12) | ((v2 & 0x3F) << 6) | ((v3 & 0x3F) << 0));
+            if (REALM_UNLIKELY(v < 0x800)) {
+                invalid = true;
+                break; // Overlong encoding is invalid
+            }
+            if (REALM_UNLIKELY(0xD800 <= v && v < 0xE000)) {
+                invalid = true;
+                break; // Illegal code point range (reserved for UTF-16 surrogate pairs)
+            }
+            *out++ = Traits16::to_char_type(v);
+            in += 3;
+            continue;
+        }
+        if (REALM_UNLIKELY(out + 1 == out_end)) {
+            break; // Need space in output buffer for surrogate pair
+        }
+        if (REALM_LIKELY(v1 < 0xF8)) { // Four bytes
+            if (REALM_UNLIKELY(in_end - in < 4)) {
+                invalid = true;
+                break; // Incomplete UTF-8 sequence
+            }
+            uint_fast32_t w1 = uint_fast32_t(v1); // 16 bit -> 32 bit
+            REALM_ASSERT(&in[1] >= in_begin && &in[3] < in_end);
+            uint_fast32_t v2 = uint_fast32_t(traits8::to_int_type(in[1])); // 32 bit intended
+            uint_fast16_t v3 = uint_fast16_t(traits8::to_int_type(in[2])); // 16 bit intended
+            uint_fast16_t v4 = uint_fast16_t(traits8::to_int_type(in[3])); // 16 bit intended
+            // UTF-8 layout: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+            if (REALM_UNLIKELY((v2 & 0xC0) != 0x80 || (v3 & 0xC0) != 0x80 || (v4 & 0xC0) != 0x80)) {
+                invalid = true;
+                break; // Invalid continuation byte
+            }
+            uint_fast32_t v = uint_fast32_t(((w1 & 0x07) << 18) | // Parenthesis is 32 bit partial result
+                                            ((v2 & 0x3F) << 12) | // Parenthesis is 32 bit partial result
+                                            ((v3 & 0x3F) << 6) |  // Parenthesis is 16 bit partial result
+                                            ((v4 & 0x3F) << 0));  // Parenthesis is 16 bit partial result
+            if (REALM_UNLIKELY(v < 0x10000)) {
+                invalid = true;
+                break; // Overlong encoding is invalid
+            }
+            if (REALM_UNLIKELY(0x110000 <= v)) {
+                invalid = true;
+                break; // Code point too big for UTF-16
+            }
+            v -= 0x10000l;
+            *out++ = Traits16::to_char_type(0xD800 + (v / 0x400));
+            *out++ = Traits16::to_char_type(0xDC00 + (v % 0x400));
+            in += 4;
+            continue;
+        }
+        // Invalid first byte of UTF-8 sequence, or code point too big for UTF-16
+        invalid = true;
+        break;
+    }
+
+    REALM_ASSERT(in >= in_begin && in <= in_end);
+    REALM_ASSERT(out >= out_begin && out <= out_end);
+    in_begin = in;
+    out_begin = out;
+    return !invalid;
+}
+
+
+template <class Char16, class Traits16>
+inline size_t Utf8x16<Char16, Traits16>::find_utf16_buf_size(const char*& in_begin, const char* const in_end)
+{
+    typedef std::char_traits<char> traits8;
+    size_t num_out = 0;
+    const char* in = in_begin;
+    while (in != in_end) {
+        REALM_ASSERT(&in[0] >= in_begin && &in[0] < in_end);
+        uint_fast16_t v1 = uint_fast16_t(traits8::to_int_type(in[0]));
+        if (REALM_LIKELY(v1 < 0x80)) { // One byte
+            num_out += 1;
+            in += 1;
+            continue;
+        }
+        if (REALM_UNLIKELY(v1 < 0xC0)) {
+            break; // Invalid first byte of UTF-8 sequence
+        }
+        if (REALM_LIKELY(v1 < 0xE0)) { // Two bytes
+            if (REALM_UNLIKELY(in_end - in < 2)) {
+                break; // Incomplete UTF-8 sequence
+            }
+            num_out += 1;
+            in += 2;
+            continue;
+        }
+        if (REALM_LIKELY(v1 < 0xF0)) { // Three bytes
+            if (REALM_UNLIKELY(in_end - in < 3)) {
+                break; // Incomplete UTF-8 sequence
+            }
+            num_out += 1;
+            in += 3;
+            continue;
+        }
+        if (REALM_LIKELY(v1 < 0xF8)) { // Four bytes
+            if (REALM_UNLIKELY(in_end - in < 4)) {
+                break; // Incomplete UTF-8 sequence
+            }
+            num_out += 2; // Surrogate pair
+            in += 4;
+            continue;
+        }
+        // Invalid first byte of UTF-8 sequence, or code point too big for UTF-16
+        break;
+    }
+
+    REALM_ASSERT(in >= in_begin && in <= in_end);
+    in_begin = in;
+    return num_out;
+}
+
+
+// Adapted from reference implementation.
+// http://www.unicode.org/resources/utf8.html
+// http://www.bsdua.org/files/unicode.tar.gz
+template <class Char16, class Traits16>
+inline bool Utf8x16<Char16, Traits16>::to_utf8(const Char16*& in_begin, const Char16* const in_end, char*& out_begin,
+                                               char* const out_end)
+{
+    typedef std::char_traits<char> traits8;
+    typedef typename traits8::int_type traits8_int_type;
+    bool invalid = false;
+    const Char16* in = in_begin;
+    char* out = out_begin;
+    while (in != in_end) {
+        REALM_ASSERT(&in[0] >= in_begin && &in[0] < in_end);
+        uint_fast16_t v1 = uint_fast16_t(Traits16::to_int_type(in[0]));
+        if (REALM_LIKELY(v1 < 0x80)) {
+            if (REALM_UNLIKELY(out == out_end)) {
+                break; // Not enough output buffer space
+            }
+            // UTF-8 layout: 0xxxxxxx
+            REALM_ASSERT(out >= out_begin && out < out_end);
+            *out++ = traits8::to_char_type(traits8_int_type(v1));
+            in += 1;
+            continue;
+        }
+        if (REALM_LIKELY(v1 < 0x800)) {
+            if (REALM_UNLIKELY(out_end - out < 2)) {
+                break; // Not enough output buffer space
+            }
+            // UTF-8 layout: 110xxxxx 10xxxxxx
+            *out++ = traits8::to_char_type(traits8_int_type(0xC0 + v1 / 0x40));
+            REALM_ASSERT(out >= out_begin && out < out_end);
+            *out++ = traits8::to_char_type(traits8_int_type(0x80 + v1 % 0x40));
+            in += 1;
+            continue;
+        }
+        if (REALM_LIKELY(v1 < 0xD800 || 0xE000 <= v1)) {
+            if (REALM_UNLIKELY(out_end - out < 3)) {
+                break; // Not enough output buffer space
+            }
+            // UTF-8 layout: 1110xxxx 10xxxxxx 10xxxxxx
+            REALM_ASSERT(out >= out_begin && out + 2 < out_end);
+            *out++ = traits8::to_char_type(traits8_int_type(0xE0 + v1 / 0x1000));
+            *out++ = traits8::to_char_type(traits8_int_type(0x80 + v1 / 0x40 % 0x40));
+            *out++ = traits8::to_char_type(traits8_int_type(0x80 + v1 % 0x40));
+            in += 1;
+            continue;
+        }
+
+        // Surrogate pair
+        if (REALM_UNLIKELY(out_end - out < 4)) {
+            break; // Not enough output buffer space
+        }
+        if (REALM_UNLIKELY(0xDC00 <= v1)) {
+            invalid = true;
+            break; // Invalid first half of surrogate pair
+        }
+        if (REALM_UNLIKELY(in + 1 == in_end)) {
+            invalid = true;
+            break; // Incomplete surrogate pair
+        }
+        REALM_ASSERT(&in[1] >= in_begin && &in[1] < in_end);
+        uint_fast16_t v2 = uint_fast16_t(Traits16::to_int_type(in[1]));
+        if (REALM_UNLIKELY(v2 < 0xDC00 || 0xE000 <= v2)) {
+            invalid = true;
+            break; // Invalid second half of surrogate pair
+        }
+        uint_fast32_t v = 0x10000l + (uint_fast32_t(v1 - 0xD800) * 0x400 + (v2 - 0xDC00));
+        // UTF-8 layout: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+        REALM_ASSERT(out >= out_begin && out + 3 < out_end);
+        *out++ = traits8::to_char_type(traits8_int_type(0xF0 + v / 0x40000));
+        *out++ = traits8::to_char_type(traits8_int_type(0x80 + v / 0x1000 % 0x40));
+        *out++ = traits8::to_char_type(traits8_int_type(0x80 + v / 0x40 % 0x40));
+        *out++ = traits8::to_char_type(traits8_int_type(0x80 + v % 0x40));
+        in += 2;
+    }
+
+    REALM_ASSERT(in >= in_begin && in <= in_end);
+    REALM_ASSERT(out >= out_begin && out <= out_end);
+    in_begin = in;
+    out_begin = out;
+    return !invalid;
+}
+
+
+template <class Char16, class Traits16>
+inline size_t Utf8x16<Char16, Traits16>::find_utf8_buf_size(const Char16*& in_begin, const Char16* const in_end)
+{
+    size_t num_out = 0;
+    const Char16* in = in_begin;
+    while (in != in_end) {
+        REALM_ASSERT(&in[0] >= in_begin && &in[0] < in_end);
+        uint_fast16_t v = uint_fast16_t(Traits16::to_int_type(in[0]));
+        if (REALM_LIKELY(v < 0x80)) {
+            if (REALM_UNLIKELY(int_add_with_overflow_detect(num_out, 1)))
+                break; // Avoid overflow
+            in += 1;
+        }
+        else if (REALM_LIKELY(v < 0x800)) {
+            if (REALM_UNLIKELY(int_add_with_overflow_detect(num_out, 2)))
+                break; // Avoid overflow
+            in += 1;
+        }
+        else if (REALM_LIKELY(v < 0xD800 || 0xE000 <= v)) {
+            if (REALM_UNLIKELY(int_add_with_overflow_detect(num_out, 3)))
+                break; // Avoid overflow
+            in += 1;
+        }
+        else {
+            if (REALM_UNLIKELY(in + 1 == in_end)) {
+                break; // Incomplete surrogate pair
+            }
+            if (REALM_UNLIKELY(int_add_with_overflow_detect(num_out, 4)))
+                break; // Avoid overflow
+            in += 2;
+        }
+    }
+    REALM_ASSERT(in >= in_begin && in <= in_end);
+    in_begin = in;
+    return num_out;
+}
+} // namespace util
+} // namespace realm
+
+#endif // REALM_UTIL_UTF8_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/util/websocket.hpp b/iOS/Pods/Realm/include/core/realm/util/websocket.hpp
new file mode 100644 (file)
index 0000000..9e869c3
--- /dev/null
@@ -0,0 +1,240 @@
+/*************************************************************************
+ *
+ * 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_UTIL_WEBSOCKET_HPP
+#define REALM_UTIL_WEBSOCKET_HPP
+
+#include <random>
+#include <system_error>
+#include <map>
+
+#include <realm/util/logger.hpp>
+#include <realm/util/http.hpp>
+
+
+namespace realm {
+namespace util {
+namespace websocket {
+
+using WriteCompletionHandler =
+    std::function<void(std::error_code, size_t num_bytes_transferred)>;
+using ReadCompletionHandler =
+    std::function<void(std::error_code, size_t num_bytes_transferred)>;
+
+class Config {
+public:
+    /// The Socket uses the caller supplied logger for logging.
+    virtual util::Logger& websocket_get_logger() noexcept = 0;
+
+    /// The Socket needs random numbers to satisfy the Websocket protocol.
+    /// The caller must supply a random number generator.
+    virtual std::mt19937_64& websocket_get_random() noexcept = 0;
+
+    //@{
+    /// The three functions below are used by the Socket to read and write to the underlying
+    /// stream. The functions will typically be implemented as wrappers to a TCP/TLS stream,
+    /// but could also map to pure memory streams. These functions abstract away the details of
+    /// the underlying sockets.
+    /// The functions have the same semantics as util::Socket.
+    ///
+    /// FIXME: Require that implementations ensure no callback reentrance, i.e.,
+    /// that the completion handler is never called from within the execution of
+    /// async_write(), async_read(), or async_read_until(). This guarantee is
+    /// provided by both network::Socket and network::ssl::Stream.
+    virtual void async_write(const char* data, size_t size, WriteCompletionHandler handler) = 0;
+    virtual void async_read(char* buffer, size_t size, ReadCompletionHandler handler) = 0;
+    virtual void async_read_until(char* buffer, size_t size, char delim, ReadCompletionHandler handler) = 0;
+    //@}
+
+    /// websocket_handshake_completion_handler() is called when the websocket is connected, .i.e.
+    /// after the handshake is done. It is not allowed to send messages on the socket before the
+    /// handshake is done. No message_received callbacks will be called before the handshake is done.
+    virtual void websocket_handshake_completion_handler(const HTTPHeaders&) = 0;
+
+    //@{
+    /// websocket_read_error_handler() and websocket_write_error_handler() are called when an
+    /// error occurs on the underlying stream given by the async_read and async_write functions above.
+    /// The error_code is passed through.
+    /// websocket_handshake_error_handler() will be called when there is an error in the handshake
+    /// such as "404 Not found".
+    /// websocket_protocol_error_handler() is called when there is an protocol error in the incoming
+    /// websocket messages.
+    /// After calling any of these error callbacks, the Socket will move into the stopped state, and
+    /// no more messages should be sent, or will be received.
+    /// It is safe to destroy the WebSocket object in these handlers.
+    virtual void websocket_read_error_handler(std::error_code) = 0;
+    virtual void websocket_write_error_handler(std::error_code) = 0;
+    virtual void websocket_handshake_error_handler(std::error_code, const HTTPHeaders&) = 0;
+    virtual void websocket_protocol_error_handler(std::error_code) = 0;
+    //@}
+
+    //@{
+    /// The five callback functions below are called whenever a full message has arrived.
+    /// The Socket defragments fragmented messages internally and delivers a full message.
+    /// The message is delivered in the buffer \param data of size \param size.
+    /// The buffer is only valid until the function returns.
+    /// The return value designates whether the WebSocket object should continue
+    /// processing messages. The normal return value is true. False must be returned if the
+    /// websocket object is destroyed during execution of the function.
+    virtual bool websocket_text_message_received(const char* data, size_t size);
+    virtual bool websocket_binary_message_received(const char* data, size_t size);
+    virtual bool websocket_close_message_received(const char* data, size_t size);
+    virtual bool websocket_ping_message_received(const char* data, size_t size);
+    virtual bool websocket_pong_message_received(const char* data, size_t size);
+    //@}
+};
+
+
+enum class Opcode {
+    continuation =  0,
+    text         =  1,
+    binary       =  2,
+    close        =  8,
+    ping         =  9,
+    pong         = 10
+};
+
+
+class Socket {
+public:
+    Socket(Config&);
+    Socket(Socket&&) noexcept;
+    ~Socket() noexcept;
+
+    /// initiate_client_handshake() starts the Socket in client mode. The Socket
+    /// will send the HTTP request that initiates the WebSocket protocol and
+    /// wait for the HTTP response from the server. The HTTP request will
+    /// contain the \param request_uri in the HTTP request line. The \param host
+    /// will be sent as the value in a HTTP Host header line.
+    /// \param sec_websocket_protocol will be set as header value for
+    /// Sec-WebSocket-Protocol. Extra HTTP headers can be provided in \a headers.
+    ///
+    /// When the server responds with a valid HTTP response, the callback
+    /// function websocket_handshake_completion_handler() is called. Messages
+    /// can only be sent and received after the handshake has completed.
+    void initiate_client_handshake(const std::string& request_uri,
+                                   const std::string& host,
+                                   const std::string& sec_websocket_protocol,
+                                   HTTPHeaders headers = HTTPHeaders{});
+
+    /// initiate_server_handshake() starts the Socket in server mode. It will
+    /// wait for a HTTP request from a client and respond with a HTTP response.
+    /// After sending a HTTP response, websocket_handshake_completion_handler()
+    /// is called. Messages can only be sent and received after the handshake
+    /// has completed.
+    void initiate_server_handshake();
+
+    /// initiate_server_websocket_after_handshake() starts the Socket in a state
+    /// where it will read and write WebSocket messages but it will expect the
+    /// handshake to have been completed by the caller. The use of this
+    /// function is to perform HTTP routing externally and then start the
+    /// WebSocket in case the HTTP request is an Upgrade to WebSocket.
+    /// Typically, the caller will have used make_http_response() to send the
+    /// HTTP response itself.
+    void initiate_server_websocket_after_handshake();
+
+    /// The async_write_* functions send frames. Only one frame should be sent at a time,
+    /// meaning that the user must wait for the handler to be called before sending the next frame.
+    /// The handler is type std::function<void()> and is called when the frame has been successfully
+    /// sent. In case of errors, the Config::websocket_write_error_handler() is called.
+
+    /// async_write_frame() sends a single frame with the fin bit set to 0 or 1 from \param fin, and the opcode
+    /// set by \param opcode. The frame payload is taken from \param data of size \param size. \param handler is
+    /// called when the frame has been successfully sent. Error s are reported through
+    /// websocket_write_error_handler() in Config.
+    /// This function is rather low level and should only be used with knowledge of the WebSocket protocol.
+    /// The five utility functions below are recommended for message sending.
+    ///
+    /// FIXME: Guarantee no callback reentrance, i.e., that the completion
+    /// handler, or the error handler in case an error occurs, is never called
+    /// from within the execution of async_write_frame().
+    void async_write_frame(bool fin, Opcode opcode, const char* data, size_t size, std::function<void()> handler);
+
+    //@{
+    /// Five utility functions used to send whole messages. These five functions are implemented in terms of
+    /// async_write_frame(). These functions send whole unfragmented messages. These functions should be
+    /// preferred over async_write_frame() for most use cases.
+    ///
+    /// FIXME: Guarantee no callback reentrance, i.e., that the completion
+    /// handler, or the error handler in case an error occurs, is never called
+    /// from within the execution of async_write_text(), and its friends. This
+    /// is already assumed by the client and server implementations of the sync
+    /// protocol.
+    void async_write_text(const char* data, size_t size, std::function<void()> handler);
+    void async_write_binary(const char* data, size_t size, std::function<void()> handler);
+    void async_write_close(const char* data, size_t size, std::function<void()> handler);
+    void async_write_ping(const char* data, size_t size, std::function<void()> handler);
+    void async_write_pong(const char* data, size_t size, std::function<void()> handler);
+    //@}
+
+    /// stop() stops the socket. The socket will stop processing incoming data, sending data, and calling callbacks.
+    /// It is an error to attempt to send a message after stop() has been called. stop() will typically be called
+    /// before the underlying TCP/TLS connection is closed. The Socket can be restarted with
+    /// initiate_client_handshake() and initiate_server_handshake().
+    void stop() noexcept;
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> m_impl;
+};
+
+
+/// read_sec_websocket_protocol() returns the value of the
+/// header Sec-WebSocket-Protocol in the http request \a request.
+/// None is returned if the header Sec-WebSocket-Protocol is absent
+/// in the request.
+util::Optional<std::string> read_sec_websocket_protocol(const HTTPRequest& request);
+
+/// make_http_response() takes \a request as a WebSocket handshake request,
+/// validates it, and makes a HTTP response. If the request is invalid, the
+/// return value is None, and ec is set to Error::bad_handshake_request.
+util::Optional<HTTPResponse> make_http_response(const HTTPRequest& request,
+                                                const std::string& sec_websocket_protocol,
+                                                std::error_code& ec);
+
+enum class Error {
+    bad_request_header_upgrade            = 1,
+    bad_request_header_connection         = 2,
+    bad_request_header_websocket_version  = 3,
+    bad_request_header_websocket_key      = 4,
+    bad_handshake_request                 = 5,
+    bad_handshake_response                = 6,
+    bad_handshake_response_404_not_found  = 7,
+    bad_handshake_response_50x_temporary  = 8,
+    bad_message                           = 9
+};
+
+const std::error_category& error_category() noexcept;
+
+std::error_code make_error_code(Error) noexcept;
+
+} // namespace websocket
+} // namespace util
+} // namespace realm
+
+namespace std {
+
+template<> struct is_error_code_enum<realm::util::websocket::Error> {
+    static const bool value = true;
+};
+
+} // namespace std
+
+#endif // REALM_UTIL_WEBSOCKET_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/utilities.hpp b/iOS/Pods/Realm/include/core/realm/utilities.hpp
new file mode 100644 (file)
index 0000000..cd05aea
--- /dev/null
@@ -0,0 +1,312 @@
+/*************************************************************************
+ *
+ * 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_UTILITIES_HPP
+#define REALM_UTILITIES_HPP
+
+#include <cstdint>
+#include <cstdlib>
+#include <cstdlib> // size_t
+#include <cstdio>
+#include <algorithm>
+#include <functional>
+#include <time.h>
+
+#ifdef _WIN32
+
+#include <WinSock2.h>
+#include <intrin.h>
+#include <BaseTsd.h>
+
+#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
+typedef SSIZE_T ssize_t;
+#define _SSIZE_T_
+#define _SSIZE_T_DEFINED
+#endif
+
+#endif // _WIN32
+
+#include <realm/util/features.h>
+#include <realm/util/assert.hpp>
+#include <realm/util/safe_int_ops.hpp>
+
+// GCC defines __i386__ and __x86_64__
+#if (defined(__X86__) || defined(__i386__) || defined(i386) || defined(_M_IX86) || defined(__386__) ||               \
+     defined(__x86_64__) || defined(_M_X64))
+#define REALM_X86_OR_X64
+#define REALM_X86_OR_X64_TRUE true
+#else
+#define REALM_X86_OR_X64_TRUE false
+#endif
+
+// GCC defines __arm__
+#ifdef __arm__
+#define REALM_ARCH_ARM
+#endif
+
+#if defined _LP64 || defined __LP64__ || defined __64BIT__ || defined _ADDR64 || defined _WIN64 ||                   \
+    defined __arch64__ || (defined(__WORDSIZE) && __WORDSIZE == 64) || (defined __sparc && defined __sparcv9) ||     \
+    defined __x86_64 || defined __amd64 || defined __x86_64__ || defined _M_X64 || defined _M_IA64 ||                \
+    defined __ia64 || defined __IA64__
+#define REALM_PTR_64
+#endif
+
+
+#if defined(REALM_PTR_64) && defined(REALM_X86_OR_X64)
+#define REALM_COMPILER_SSE // Compiler supports SSE 4.2 through __builtin_ accessors or back-end assembler
+#define REALM_COMPILER_AVX
+#endif
+
+namespace realm {
+
+using StringCompareCallback = std::function<bool(const char* string1, const char* string2)>;
+
+extern signed char sse_support;
+extern signed char avx_support;
+
+template <int version>
+REALM_FORCEINLINE bool sseavx()
+{
+    /*
+    Return whether or not SSE 3.0 (if version = 30) or 4.2 (for version = 42) is supported. Return value
+    is based on the CPUID instruction.
+
+    sse_support = -1: No SSE support
+    sse_support = 0: SSE3
+    sse_support = 1: SSE42
+
+    avx_support = -1: No AVX support
+    avx_support = 0: AVX1 supported
+    sse_support = 1: AVX2 supported (not yet implemented for detection in our cpuid_init(), todo)
+
+    This lets us test very rapidly at runtime because we just need 1 compare instruction (with 0) to test both for
+    SSE 3 and 4.2 by caller (compiler optimizes if calls are concecutive), and can decide branch with ja/jl/je because
+    sse_support is signed type. Also, 0 requires no immediate operand. Same for AVX.
+
+    We runtime-initialize sse_support in a constructor of a static variable which is not guaranteed to be called
+    prior to cpu_sse(). So we compile-time initialize sse_support to -2 as fallback.
+    */
+    static_assert(version == 1 || version == 2 || version == 30 || version == 42,
+                  "Only version == 1 (AVX), 2 (AVX2), 30 (SSE 3) and 42 (SSE 4.2) are supported for detection");
+#ifdef REALM_COMPILER_SSE
+    if (version == 30)
+        return (sse_support >= 0);
+    else if (version == 42)
+        return (sse_support > 0); // faster than == 1 (0 requres no immediate operand)
+    else if (version == 1)        // avx
+        return (avx_support >= 0);
+    else if (version == 2) // avx2
+        return (avx_support > 0);
+    else
+        return false;
+#else
+    return false;
+#endif
+}
+
+void cpuid_init();
+void* round_up(void* p, size_t align);
+void* round_down(void* p, size_t align);
+size_t round_up(size_t p, size_t align);
+size_t round_down(size_t p, size_t align);
+void millisleep(unsigned long milliseconds);
+
+#ifdef _WIN32
+int gettimeofday(struct timeval * tp, struct timezone * tzp);
+#endif
+
+int64_t platform_timegm(tm time);
+
+#ifdef REALM_SLAB_ALLOC_TUNE
+void process_mem_usage(double& vm_usage, double& resident_set);
+#endif
+// popcount
+int fast_popcount32(int32_t x);
+int fast_popcount64(int64_t x);
+uint64_t fastrand(uint64_t max = 0xffffffffffffffffULL, bool is_seed = false);
+
+// log2 - returns -1 if x==0, otherwise log2(x)
+inline int log2(size_t x)
+{
+    if (x == 0)
+        return -1;
+#if defined(__GNUC__)
+#ifdef REALM_PTR_64
+    return 63 - __builtin_clzll(x); // returns int
+#else
+    return 31 - __builtin_clz(x); // returns int
+#endif
+#elif defined(_WIN32)
+    unsigned long index = 0;
+#ifdef REALM_PTR_64
+    unsigned char c = _BitScanReverse64(&index, x); // outputs unsigned long
+#else
+    unsigned char c = _BitScanReverse(&index, x); // outputs unsigned long
+#endif
+    return static_cast<int>(index);
+#else // not __GNUC__ and not _WIN32
+    int r = 0;
+    while (x >>= 1) {
+        r++;
+    }
+    return r;
+#endif
+}
+
+// Implementation:
+
+// Safe cast from 64 to 32 bits on 32 bit architecture. Differs from to_ref() by not testing alignment and
+// REF-bitflag.
+inline size_t to_size_t(int_fast64_t v) noexcept
+{
+    REALM_ASSERT_DEBUG(!util::int_cast_has_overflow<size_t>(v));
+    return size_t(v);
+}
+
+
+template <typename ReturnType, typename OriginalType>
+ReturnType type_punning(OriginalType variable) noexcept
+{
+    union Both {
+        OriginalType in;
+        ReturnType out;
+    };
+    Both both;
+    both.out = ReturnType(); // Clear all bits in case ReturnType is larger than OriginalType
+    both.in = variable;
+    return both.out;
+}
+
+// Also see the comments in Array::index_string()
+enum FindRes {
+    // Indicate that no results were found in the search
+    FindRes_not_found,
+    // Indicates a single result is found
+    FindRes_single,
+    // Indicates more than one result is found and they are stored in a column
+    FindRes_column
+};
+
+enum IndexMethod {
+    index_FindFirst,
+    index_FindAll_nocopy,
+    index_Count,
+};
+
+// Combined result of the index_FindAll_nocopy operation. The column returned
+// can contain results that are not matches but all matches are within the
+// returned start_ndx and end_ndx.
+struct InternalFindResult {
+    // Reference to a IntegerColumn containing result rows, or a single row
+    // value if the result is FindRes_single.
+    size_t payload;
+    // Offset into the result column to start at.
+    size_t start_ndx;
+    // Offset index in the result column to end at.
+    size_t end_ndx;
+};
+
+
+// realm::is_any<T, U1, U2, U3, ...> ==
+// std::is_same<T, U1>::value || std::is_same<T, U2>::value || std::is_same<T, U3>::value ...
+template <typename... T>
+struct is_any : std::false_type {
+};
+
+template <typename T, typename... Ts>
+struct is_any<T, T, Ts...> : std::true_type {
+};
+
+template <typename T, typename U, typename... Ts>
+struct is_any<T, U, Ts...> : is_any<T, Ts...> {
+};
+
+
+// Use realm::safe_equal() instead of std::equal() if one of the parameters can be a null pointer.
+template <class InputIterator1, class InputIterator2>
+bool safe_equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2)
+{
+#if defined(_MSC_VER)
+    // VS has a special check in debug mode against passing a null pointer std::equal(); it will give a warning
+    // at runtime if this is observed.
+    // It's uncertain if this is allowed by the C++ standard. For details, see
+    // http://stackoverflow.com/questions/19120779/is-char-p-0-stdequalp-p-p-well-defined-according-to-the-c-standard.
+    // So we use a safe C++14 method instead that takes two range pairs.
+    size_t len = last1 - first1;
+    return std::equal(first1, last1, first2, first2 + len);
+#else
+    return std::equal(first1, last1, first2);
+#endif
+}
+
+// Use realm::safe_copy_n() instead of std::copy_n() if one of the parameters can be a null pointer. See the
+// explanation of safe_equal() above; same things apply.
+template< class InputIt, class Size, class OutputIt>
+OutputIt safe_copy_n(InputIt first, Size count, OutputIt result)
+{
+#if defined(_MSC_VER)
+    // This loop and the method prototype is copy pasted
+    // from "Possible implementation" on http://en.cppreference.com/w/cpp/algorithm/copy_n
+    if (count > 0) {
+        *result++ = *first;
+        for (Size i = 1; i < count; ++i) {
+            *result++ = *++first;
+        }
+    }
+    return result;
+#else
+    return std::copy_n(first, count, result);
+#endif
+}
+
+
+template <class T>
+struct Wrap {
+    Wrap(const T& v)
+        : m_value(v)
+    {
+    }
+    operator T() const
+    {
+        return m_value;
+    }
+
+private:
+    T m_value;
+};
+
+// PlacementDelete is intended for use with std::unique_ptr when it holds an object allocated with
+// placement new. It simply calls the object's destructor without freeing the memory.
+struct PlacementDelete {
+    template <class T>
+    void operator()(T* v) const
+    {
+        v->~T();
+    }
+};
+
+#ifdef _WIN32
+typedef void* FileDesc;
+#else
+typedef int FileDesc;
+#endif
+
+
+} // namespace realm
+
+#endif // REALM_UTILITIES_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/version.hpp b/iOS/Pods/Realm/include/core/realm/version.hpp
new file mode 100644 (file)
index 0000000..a235e16
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_VERSION_HPP
+#define REALM_VERSION_HPP
+
+#include <string>
+
+// Do not use `cmakedefine` here, as certain versions can be 0, which CMake
+// interprets as being undefined.
+#define REALM_VERSION_MAJOR 5
+#define REALM_VERSION_MINOR 1
+#define REALM_VERSION_PATCH 2
+#define REALM_VERSION_EXTRA ""
+#define REALM_VERSION_STRING "5.1.2"
+
+#define REALM_PRODUCT_NAME "realm-core"
+#define REALM_VER_CHUNK "[" REALM_PRODUCT_NAME "-" REALM_VERSION_STRING "]"
+
+namespace realm {
+
+enum Feature {
+    feature_Debug,
+    feature_Replication,
+};
+
+class StringData;
+
+class Version {
+public:
+    static int get_major() { return REALM_VERSION_MAJOR; }
+    static int get_minor() { return REALM_VERSION_MINOR; }
+    static int get_patch() { return REALM_VERSION_PATCH; }
+    static StringData get_extra();
+    static std::string get_version();
+    static bool is_at_least(int major, int minor, int patch, StringData extra);
+    static bool is_at_least(int major, int minor, int patch);
+    static bool has_feature(Feature feature);
+};
+
+
+} // namespace realm
+
+#endif // REALM_VERSION_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/version_id.hpp b/iOS/Pods/Realm/include/core/realm/version_id.hpp
new file mode 100644 (file)
index 0000000..069b2c7
--- /dev/null
@@ -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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_VERSION_ID_HPP
+#define REALM_VERSION_ID_HPP
+
+#if defined(_WIN32) && !defined(__STDC_LIMIT_MACROS)
+#define __STDC_LIMIT_MACROS
+#endif
+
+#include <limits>
+
+namespace realm {
+
+struct VersionID {
+    using version_type = uint_fast64_t;
+    version_type version = std::numeric_limits<version_type>::max();
+    uint_fast32_t index = 0;
+
+    VersionID()
+    {
+    }
+    VersionID(version_type initial_version, uint_fast32_t initial_index)
+    {
+        version = initial_version;
+        index = initial_index;
+    }
+
+    bool operator==(const VersionID& other)
+    {
+        return version == other.version;
+    }
+    bool operator!=(const VersionID& other)
+    {
+        return version != other.version;
+    }
+    bool operator<(const VersionID& other)
+    {
+        return version < other.version;
+    }
+    bool operator<=(const VersionID& other)
+    {
+        return version <= other.version;
+    }
+    bool operator>(const VersionID& other)
+    {
+        return version > other.version;
+    }
+    bool operator>=(const VersionID& other)
+    {
+        return version >= other.version;
+    }
+};
+
+} // namespace realm
+
+#endif // REALM_VERSION_ID_HPP
diff --git a/iOS/Pods/Realm/include/core/realm/views.hpp b/iOS/Pods/Realm/include/core/realm/views.hpp
new file mode 100644 (file)
index 0000000..de6f3e6
--- /dev/null
@@ -0,0 +1,181 @@
+/*************************************************************************
+ *
+ * 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_VIEWS_HPP
+#define REALM_VIEWS_HPP
+
+#include <realm/column.hpp>
+#include <realm/handover_defs.hpp>
+
+#include <vector>
+
+namespace realm {
+
+const int64_t detached_ref = -1;
+
+class RowIndexes;
+
+// Forward declaration needed for deleted CommonDescriptor constructor
+class SortDescriptor;
+
+// CommonDescriptor encapsulates a reference to a set of columns (possibly over
+// links), which is used to indicate the criteria columns for sort and distinct.
+// Although the input is column indices, it does not rely on those indices
+// remaining stable as long as the columns continue to exist.
+class CommonDescriptor {
+public:
+    CommonDescriptor() = default;
+    // Enforce type saftey to prevent automatic conversion of derived class
+    // SortDescriptor into CommonDescriptor at compile time.
+    CommonDescriptor(const SortDescriptor&) = delete;
+    virtual ~CommonDescriptor() = default;
+
+    // Create a descriptor for the given columns on the given table.
+    // Each vector in `column_indices` represents a chain of columns, where
+    // all but the last are Link columns (n.b.: LinkList and Backlink are not
+    // supported), and the final is any column type that can be sorted on.
+    // `column_indices` must be non-empty, and each vector within it must also
+    // be non-empty.
+    CommonDescriptor(Table const& table, std::vector<std::vector<size_t>> column_indices);
+    virtual std::unique_ptr<CommonDescriptor> clone() const;
+
+    // returns whether this descriptor is valid and can be used to sort
+    bool is_valid() const noexcept
+    {
+        return !m_columns.empty();
+    }
+
+    class Sorter;
+    virtual Sorter sorter(IntegerColumn const& row_indexes) const;
+
+    // handover support
+    std::vector<std::vector<size_t>> export_column_indices() const;
+    virtual std::vector<bool> export_order() const
+    {
+        return {};
+    }
+
+protected:
+    std::vector<std::vector<const ColumnBase*>> m_columns;
+};
+
+class SortDescriptor : public CommonDescriptor {
+public:
+    // Create a sort descriptor for the given columns on the given table.
+    // See CommonDescriptor for restrictions on `column_indices`.
+    // The sort order can be specified by using `ascending` which must either be
+    // empty or have one entry for each column index chain.
+    SortDescriptor(Table const& table, std::vector<std::vector<size_t>> column_indices,
+                   std::vector<bool> ascending = {});
+    SortDescriptor() = default;
+    ~SortDescriptor() = default;
+    std::unique_ptr<CommonDescriptor> clone() const override;
+
+    void merge_with(SortDescriptor&& other);
+
+    Sorter sorter(IntegerColumn const& row_indexes) const override;
+
+    // handover support
+    std::vector<bool> export_order() const override;
+
+private:
+    std::vector<bool> m_ascending;
+};
+
+// Distinct uses the same syntax as sort except that the order is meaningless.
+typedef CommonDescriptor DistinctDescriptor;
+
+class DescriptorOrdering {
+public:
+    DescriptorOrdering() = default;
+    DescriptorOrdering(const DescriptorOrdering&);
+    DescriptorOrdering(DescriptorOrdering&&) = default;
+    DescriptorOrdering& operator=(const DescriptorOrdering&);
+    DescriptorOrdering& operator=(DescriptorOrdering&&) = default;
+
+    void append_sort(SortDescriptor sort);
+    void append_distinct(DistinctDescriptor distinct);
+    bool descriptor_is_sort(size_t index) const;
+    bool descriptor_is_distinct(size_t index) const;
+    bool is_empty() const { return m_descriptors.empty(); }
+    size_t size() const { return m_descriptors.size(); }
+    const CommonDescriptor* operator[](size_t ndx) const;
+    bool will_apply_sort() const;
+    bool will_apply_distinct() const;
+
+    // handover support
+    using HandoverPatch = std::unique_ptr<DescriptorOrderingHandoverPatch>;
+    static void generate_patch(DescriptorOrdering const&, HandoverPatch&);
+    static DescriptorOrdering create_from_and_consume_patch(HandoverPatch&, Table const&);
+
+private:
+    std::vector<std::unique_ptr<CommonDescriptor>> m_descriptors;
+};
+
+// This class is for common functionality of ListView and LinkView which inherit from it. Currently it only
+// supports sorting and distinct.
+class RowIndexes {
+public:
+    RowIndexes(IntegerColumn::unattached_root_tag urt, realm::Allocator& alloc);
+    RowIndexes(IntegerColumn&& col);
+    RowIndexes(const RowIndexes& source, ConstSourcePayload mode);
+    RowIndexes(RowIndexes& source, MutableSourcePayload mode);
+
+    virtual ~RowIndexes()
+    {
+#ifdef REALM_COOKIE_CHECK
+        m_debug_cookie = 0x7765697633333333; // 0x77656976 = 'view'; 0x33333333 = '3333' = destructed
+#endif
+    }
+
+    // Disable copying, this is not supported.
+    RowIndexes& operator=(const RowIndexes&) = delete;
+    RowIndexes(const RowIndexes&) = delete;
+
+    // Return a column of the table that m_row_indexes are pointing at (which is the target table for LinkList and
+    // parent table for TableView)
+    virtual const ColumnBase& get_column_base(size_t index) const = 0;
+
+    virtual size_t size() const = 0;
+
+    // These two methods are overridden by TableView and LinkView.
+    virtual uint_fast64_t sync_if_needed() const = 0;
+    virtual bool is_in_sync() const
+    {
+        return true;
+    }
+
+    void check_cookie() const
+    {
+#ifdef REALM_COOKIE_CHECK
+        REALM_ASSERT_RELEASE(m_debug_cookie == cookie_expected);
+#endif
+    }
+
+    IntegerColumn m_row_indexes;
+
+protected:
+    void do_sort(const DescriptorOrdering& ordering);
+
+    static const uint64_t cookie_expected = 0x7765697677777777ull; // 0x77656976 = 'view'; 0x77777777 = '7777' = alive
+    uint64_t m_debug_cookie;
+};
+
+} // namespace realm
+
+#endif // REALM_VIEWS_HPP
diff --git a/iOS/Pods/Realm/include/execution_context_id.hpp b/iOS/Pods/Realm/include/execution_context_id.hpp
new file mode 100644 (file)
index 0000000..9c0ae62
--- /dev/null
@@ -0,0 +1,123 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 utilied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef REALM_OS_EXECUTION_CONTEXT_ID_HPP
+#define REALM_OS_EXECUTION_CONTEXT_ID_HPP
+
+#if defined(__GNUC__) && __GNUC__ < 5
+// GCC 4.9 doesn't have std::alligned_union so polyfill
+#include "util/aligned_union.hpp"
+#else
+#include <type_traits>
+#endif
+
+#include <cstring>
+#include <thread>
+
+#include <realm/util/assert.hpp>
+#include <realm/util/optional.hpp>
+
+namespace realm {
+
+// An identifier for an execution context other than a thread.
+// Different execution contexts must have different IDs.
+using AbstractExecutionContextID = uintptr_t;
+
+// Identifies an execution context in which a Realm will be used.
+// Can contain either a std::thread::id or an AbstractExecutionContextID.
+//
+// FIXME: This can eventually be:
+//        using AnyExecutionContextID = std::variant<std::thread::id, AbstractExecutionContextID>;
+class AnyExecutionContextID {
+    enum class Type {
+        Thread,
+        Abstract,
+    };
+
+public:
+
+    // Convert from the representation used by Realm::Config, where the absence of an
+    // explicit abstract execution context indicates that the current thread's identifier
+    // should be used.
+    AnyExecutionContextID(util::Optional<AbstractExecutionContextID> maybe_abstract_id)
+    {
+        if (maybe_abstract_id)
+            *this = AnyExecutionContextID(*maybe_abstract_id);
+        else
+            *this = AnyExecutionContextID(std::this_thread::get_id());
+    }
+
+    AnyExecutionContextID(std::thread::id thread_id) : AnyExecutionContextID(Type::Thread, std::move(thread_id)) { }
+    AnyExecutionContextID(AbstractExecutionContextID abstract_id) : AnyExecutionContextID(Type::Abstract, abstract_id) { }
+
+    template <typename StorageType>
+    bool contains() const
+    {
+        return TypeForStorageType<StorageType>::value == m_type;
+    }
+
+    template <typename StorageType>
+    StorageType get() const
+    {
+        REALM_ASSERT_DEBUG(contains<StorageType>());
+        return *reinterpret_cast<const StorageType*>(&m_storage);
+    }
+
+    bool operator==(const AnyExecutionContextID& other) const
+    {
+        return m_type == other.m_type && std::memcmp(&m_storage, &other.m_storage, sizeof(m_storage)) == 0;
+    }
+
+    bool operator!=(const AnyExecutionContextID& other) const
+    {
+        return !(*this == other);
+    }
+
+private:
+    template <typename T>
+    AnyExecutionContextID(Type type, T value) : m_type(type)
+    {
+        // operator== relies on being able to compare the raw bytes of m_storage,
+        // so zero everything before intializing the portion in use.
+        std::memset(&m_storage, 0, sizeof(m_storage));
+        new (&m_storage) T(std::move(value));
+    }
+
+    template <typename> struct TypeForStorageType;
+
+#if defined(__GNUC__) && __GNUC__ < 5
+    util::AlignedUnion<1, std::thread::id, AbstractExecutionContextID>::type m_storage;
+#else
+    std::aligned_union<1, std::thread::id, AbstractExecutionContextID>::type m_storage;
+#endif
+    Type m_type;
+};
+
+template <>
+struct AnyExecutionContextID::TypeForStorageType<std::thread::id> {
+    constexpr static Type value = Type::Thread;
+};
+
+template <>
+struct AnyExecutionContextID::TypeForStorageType<AbstractExecutionContextID> {
+    constexpr static Type value = Type::Abstract;
+};
+
+} // namespace realm
+
+#endif // REALM_OS_EXECUTION_CONTEXT_ID_HPP
diff --git a/iOS/Pods/Realm/include/feature_checks.hpp b/iOS/Pods/Realm/include/feature_checks.hpp
new file mode 100644 (file)
index 0000000..a3fd8a5
--- /dev/null
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_FEATURE_CHECKS_HPP
+#define REALM_OS_FEATURE_CHECKS_HPP
+
+#include <realm/version.hpp>
+
+#if REALM_ENABLE_SYNC
+
+#include <realm/sync/version.hpp>
+
+#endif // REALM_ENABLE_SYNC
+
+#endif // REALM_OS_FEATURE_CHECKS_HPP
diff --git a/iOS/Pods/Realm/include/impl/apple/external_commit_helper.hpp b/iOS/Pods/Realm/include/impl/apple/external_commit_helper.hpp
new file mode 100644 (file)
index 0000000..c87d8b2
--- /dev/null
@@ -0,0 +1,80 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 <future>
+
+namespace realm {
+class Realm;
+
+namespace _impl {
+class RealmCoordinator;
+
+class ExternalCommitHelper {
+public:
+    ExternalCommitHelper(RealmCoordinator& parent);
+    ~ExternalCommitHelper();
+
+    void notify_others();
+
+private:
+    // A RAII holder for a file descriptor which automatically closes the wrapped
+    // fd when it's deallocated
+    class FdHolder {
+    public:
+        FdHolder() = default;
+        ~FdHolder() { close(); }
+        operator int() const { return m_fd; }
+
+        FdHolder& operator=(int newFd) {
+            close();
+            m_fd = newFd;
+            return *this;
+        }
+
+    private:
+        int m_fd = -1;
+        void close();
+
+        FdHolder& operator=(FdHolder const&) = delete;
+        FdHolder(FdHolder const&) = delete;
+    };
+
+    void listen();
+
+    RealmCoordinator& m_parent;
+
+    // The listener thread
+    std::future<void> m_thread;
+
+    // Pipe which is waited on for changes and written to when there is a new
+    // commit to notify others of. When using a named pipe m_notify_fd is
+    // read-write and m_notify_fd_write is unused; when using an anonymous pipe
+    // (on tvOS) m_notify_fd is read-only and m_notify_fd_write is write-only.
+    FdHolder m_notify_fd;
+    FdHolder m_notify_fd_write;
+
+    // File descriptor for the kqueue
+    FdHolder m_kq;
+
+    // The two ends of an anonymous pipe used to notify the kqueue() thread that
+    // it should be shut down.
+    FdHolder m_shutdown_read_fd;
+    FdHolder m_shutdown_write_fd;
+};
+} // namespace _impl
+} // namespace realm
diff --git a/iOS/Pods/Realm/include/impl/apple/keychain_helper.hpp b/iOS/Pods/Realm/include/impl/apple/keychain_helper.hpp
new file mode 100644 (file)
index 0000000..8915e78
--- /dev/null
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_OS_KEYCHAIN_HELPER_HPP
+#define REALM_OS_KEYCHAIN_HELPER_HPP
+
+#include <cstdint>
+#include <stdexcept>
+#include <vector>
+
+namespace realm {
+namespace keychain {
+
+std::vector<char> metadata_realm_encryption_key(bool check_legacy_service);
+
+class KeychainAccessException : public std::runtime_error {
+public:
+    KeychainAccessException(int32_t error_code);
+};
+
+}
+}
+
+#endif // REALM_OS_KEYCHAIN_HELPER_HPP
diff --git a/iOS/Pods/Realm/include/impl/collection_change_builder.hpp b/iOS/Pods/Realm/include/impl/collection_change_builder.hpp
new file mode 100644 (file)
index 0000000..77c8391
--- /dev/null
@@ -0,0 +1,92 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_CHANGE_BUILDER_HPP
+#define REALM_COLLECTION_CHANGE_BUILDER_HPP
+
+#include "collection_notifications.hpp"
+
+#include <realm/util/optional.hpp>
+
+#include <unordered_map>
+
+namespace realm {
+namespace _impl {
+class CollectionChangeBuilder : public CollectionChangeSet {
+public:
+    CollectionChangeBuilder(CollectionChangeBuilder const&) = default;
+    CollectionChangeBuilder(CollectionChangeBuilder&&) = default;
+    CollectionChangeBuilder& operator=(CollectionChangeBuilder const&) = default;
+    CollectionChangeBuilder& operator=(CollectionChangeBuilder&&) = default;
+
+    CollectionChangeBuilder(IndexSet deletions = {},
+                            IndexSet insertions = {},
+                            IndexSet modification = {},
+                            std::vector<Move> moves = {});
+
+    // Calculate where rows need to be inserted or deleted from old_rows to turn
+    // it into new_rows, and check all matching rows for modifications
+    // If `move_candidates` is supplied they it will be used to do more accurate
+    // determination of which rows moved. This is only supported when the rows
+    // are in table order (i.e. not sorted or from a LinkList)
+    static CollectionChangeBuilder calculate(std::vector<size_t> const& old_rows,
+                                             std::vector<size_t> const& new_rows,
+                                             std::function<bool (size_t)> row_did_change,
+                                             util::Optional<IndexSet> const& move_candidates = util::none);
+
+    // generic operations {
+    CollectionChangeSet finalize() &&;
+    void merge(CollectionChangeBuilder&&);
+
+    void insert(size_t ndx, size_t count=1, bool track_moves=true);
+    void modify(size_t ndx, size_t col=-1);
+    void erase(size_t ndx);
+    void clear(size_t old_size);
+    // }
+
+    // operations only implemented for LinkList semantics {
+    void clean_up_stale_moves();
+    void move(size_t from, size_t to);
+    // }
+
+    // operations only implemented for Row semantics {
+    void move_over(size_t ndx, size_t last_ndx, bool track_moves=true);
+    // must be followed by move_over(old_ndx, ...)
+    // precondition: `new_ndx` must be a new insertion
+    void subsume(size_t old_ndx, size_t new_ndx, bool track_moves=true);
+    void swap(size_t ndx_1, size_t ndx_2, bool track_moves=true);
+
+    void parse_complete();
+    // }
+
+    void insert_column(size_t ndx);
+    void move_column(size_t from, size_t to);
+
+private:
+    std::unordered_map<size_t, size_t> m_move_mapping;
+    bool m_track_columns = true;
+
+    template<typename Func>
+    void for_each_col(Func&& f);
+
+    void verify();
+};
+} // namespace _impl
+} // namespace realm
+
+#endif // REALM_COLLECTION_CHANGE_BUILDER_HPP
diff --git a/iOS/Pods/Realm/include/impl/collection_notifier.hpp b/iOS/Pods/Realm/include/impl/collection_notifier.hpp
new file mode 100644 (file)
index 0000000..e2104e9
--- /dev/null
@@ -0,0 +1,354 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_BACKGROUND_COLLECTION_HPP
+#define REALM_BACKGROUND_COLLECTION_HPP
+
+#include "impl/collection_change_builder.hpp"
+
+#include <realm/util/assert.hpp>
+#include <realm/version_id.hpp>
+
+#include <array>
+#include <atomic>
+#include <exception>
+#include <functional>
+#include <mutex>
+#include <unordered_map>
+
+namespace realm {
+class Realm;
+class SharedGroup;
+class Table;
+
+namespace _impl {
+class RealmCoordinator;
+
+struct ListChangeInfo {
+    size_t table_ndx;
+    size_t row_ndx;
+    size_t col_ndx;
+    CollectionChangeBuilder* changes;
+};
+
+struct TransactionChangeInfo {
+    std::vector<bool> table_modifications_needed;
+    std::vector<bool> table_moves_needed;
+    std::vector<ListChangeInfo> lists;
+    std::vector<CollectionChangeBuilder> tables;
+    std::vector<std::vector<size_t>> column_indices;
+    std::vector<size_t> table_indices;
+    bool track_all;
+    bool schema_changed;
+};
+
+class DeepChangeChecker {
+public:
+    struct OutgoingLink {
+        size_t col_ndx;
+        bool is_list;
+    };
+    struct RelatedTable {
+        size_t table_ndx;
+        std::vector<OutgoingLink> links;
+    };
+
+    DeepChangeChecker(TransactionChangeInfo const& info, Table const& root_table,
+                      std::vector<RelatedTable> const& related_tables);
+
+    bool operator()(size_t row_ndx);
+
+    // Recursively add `table` and all tables it links to to `out`, along with
+    // information about the links from them
+    static void find_related_tables(std::vector<RelatedTable>& out, Table const& table);
+
+private:
+    TransactionChangeInfo const& m_info;
+    Table const& m_root_table;
+    const size_t m_root_table_ndx;
+    IndexSet const* const m_root_modifications;
+    std::vector<IndexSet> m_not_modified;
+    std::vector<RelatedTable> const& m_related_tables;
+
+    struct Path {
+        size_t table;
+        size_t row;
+        size_t col;
+        bool depth_exceeded;
+    };
+    std::array<Path, 4> m_current_path;
+
+    bool check_row(Table const& table, size_t row_ndx, size_t depth = 0);
+    bool check_outgoing_links(size_t table_ndx, Table const& table,
+                              size_t row_ndx, size_t depth = 0);
+};
+
+// A base class for a notifier that keeps a collection up to date and/or
+// generates detailed change notifications on a background thread. This manages
+// most of the lifetime-management issues related to sharing an object between
+// the worker thread and the collection on the target thread, along with the
+// thread-safe callback collection.
+class CollectionNotifier {
+public:
+    CollectionNotifier(std::shared_ptr<Realm>);
+    virtual ~CollectionNotifier();
+
+    // ------------------------------------------------------------------------
+    // Public API for the collections using this to get notifications:
+
+    // Stop receiving notifications from this background worker
+    // This must be called in the destructor of the collection
+    void unregister() noexcept;
+
+    // Add a callback to be called each time the collection changes
+    // This can only be called from the target collection's thread
+    // Returns a token which can be passed to remove_callback()
+    uint64_t add_callback(CollectionChangeCallback callback);
+    // Remove a previously added token. The token is no longer valid after
+    // calling this function and must not be used again. This function can be
+    // called from any thread.
+    void remove_callback(uint64_t token);
+
+    void suppress_next_notification(uint64_t token);
+
+    // ------------------------------------------------------------------------
+    // API for RealmCoordinator to manage running things and calling callbacks
+
+    bool is_for_realm(Realm&) const noexcept;
+    Realm* get_realm() const noexcept { return m_realm.get(); }
+
+    // Get the SharedGroup version which this collection can attach to (if it's
+    // in handover mode), or can deliver to (if it's been handed over to the BG worker alredad)
+    // precondition: RealmCoordinator::m_notifier_mutex is locked
+    VersionID version() const noexcept { return m_sg_version; }
+
+    // Release references to all core types
+    // This is called on the worker thread to ensure that non-thread-safe things
+    // can be destroyed on the correct thread, even if the last reference to the
+    // CollectionNotifier is released on a different thread
+    virtual void release_data() noexcept = 0;
+
+    // Prepare to deliver the new collection and call callbacks.
+    // Returns whether or not it has anything to deliver.
+    // precondition: RealmCoordinator::m_notifier_mutex is locked
+    bool package_for_delivery();
+
+    // Deliver the new state to the target collection using the given SharedGroup
+    // precondition: RealmCoordinator::m_notifier_mutex is unlocked
+    virtual void deliver(SharedGroup&) { }
+
+    // Pass the given error to all registered callbacks, then remove them
+    // precondition: RealmCoordinator::m_notifier_mutex is unlocked
+    void deliver_error(std::exception_ptr);
+
+    // Call each of the given callbacks with the changesets prepared by package_for_delivery()
+    // precondition: RealmCoordinator::m_notifier_mutex is unlocked
+    void before_advance();
+    void after_advance();
+
+    bool is_alive() const noexcept;
+
+    // precondition: RealmCoordinator::m_notifier_mutex is locked *or* is called on worker thread
+    bool has_run() const noexcept { return m_has_run; }
+
+    // Attach the handed-over query to `sg`. Must not be already attached to a SharedGroup.
+    // precondition: RealmCoordinator::m_notifier_mutex is locked
+    void attach_to(SharedGroup& sg);
+    // Create a new query handover object and stop using the previously attached
+    // SharedGroup
+    // precondition: RealmCoordinator::m_notifier_mutex is locked
+    void detach();
+
+    // Set `info` as the new ChangeInfo that will be populated by the next
+    // transaction advance, and register all required information in it
+    // precondition: RealmCoordinator::m_notifier_mutex is locked
+    void add_required_change_info(TransactionChangeInfo& info);
+
+    // precondition: RealmCoordinator::m_notifier_mutex is unlocked
+    virtual void run() = 0;
+
+    // precondition: RealmCoordinator::m_notifier_mutex is locked
+    void prepare_handover();
+
+    template <typename T>
+    class Handle;
+
+    bool have_callbacks() const noexcept { return m_have_callbacks; }
+protected:
+    void add_changes(CollectionChangeBuilder change);
+    void set_table(Table const& table);
+    std::unique_lock<std::mutex> lock_target();
+    SharedGroup& source_shared_group();
+
+    std::function<bool (size_t)> get_modification_checker(TransactionChangeInfo const&, Table const&);
+
+private:
+    virtual void do_attach_to(SharedGroup&) = 0;
+    virtual void do_detach_from(SharedGroup&) = 0;
+    virtual void do_prepare_handover(SharedGroup&) = 0;
+    virtual bool do_add_required_change_info(TransactionChangeInfo&) = 0;
+    virtual bool prepare_to_deliver() { return true; }
+
+    mutable std::mutex m_realm_mutex;
+    std::shared_ptr<Realm> m_realm;
+
+    VersionID m_sg_version;
+    SharedGroup* m_sg = nullptr;
+
+    bool m_has_run = false;
+    bool m_error = false;
+    std::vector<DeepChangeChecker::RelatedTable> m_related_tables;
+
+    struct Callback {
+        CollectionChangeCallback fn;
+        CollectionChangeBuilder accumulated_changes;
+        CollectionChangeSet changes_to_deliver;
+        uint64_t token;
+        bool initial_delivered;
+        bool skip_next;
+    };
+
+    // Currently registered callbacks and a mutex which must always be held
+    // while doing anything with them or m_callback_index
+    std::mutex m_callback_mutex;
+    std::vector<Callback> m_callbacks;
+
+    // Cached value for if m_callbacks is empty, needed to avoid deadlocks in
+    // run() due to lock-order inversion between m_callback_mutex and m_target_mutex
+    // It's okay if this value is stale as at worst it'll result in us doing
+    // some extra work.
+    std::atomic<bool> m_have_callbacks = {false};
+
+    // Iteration variable for looping over callbacks
+    // remove_callback() updates this when needed
+    size_t m_callback_index = -1;
+    // The number of callbacks which were present when the notifier was packaged
+    // for delivery which are still present.
+    // Updated by packaged_for_delivery and removd_callback(), and used in
+    // for_each_callback() to avoid calling callbacks registered during delivery.
+    size_t m_callback_count = -1;
+
+    uint64_t m_next_token = 0;
+
+    template<typename Fn>
+    void for_each_callback(Fn&& fn);
+
+    std::vector<Callback>::iterator find_callback(uint64_t token);
+};
+
+// A smart pointer to a CollectionNotifier that unregisters the notifier when
+// the pointer is destroyed. Movable. Copying will produce a null Handle.
+template <typename T>
+class CollectionNotifier::Handle : public std::shared_ptr<T> {
+public:
+    using std::shared_ptr<T>::shared_ptr;
+
+    Handle() = default;
+    ~Handle() { reset(); }
+
+    // Copying a Handle produces a null Handle.
+    Handle(const Handle&) : Handle() { }
+    Handle& operator=(const Handle& other)
+    {
+        if (this != &other) {
+            reset();
+        }
+        return *this;
+    }
+
+    Handle(Handle&&) = default;
+    Handle& operator=(Handle&& other)
+    {
+        reset();
+        std::shared_ptr<T>::operator=(std::move(other));
+        return *this;
+    }
+
+    Handle& operator=(std::shared_ptr<T>&& other)
+    {
+        reset();
+        std::shared_ptr<T>::operator=(std::move(other));
+        return *this;
+    }
+
+    void reset()
+    {
+        if (*this) {
+            this->get()->unregister();
+            std::shared_ptr<T>::reset();
+        }
+    }
+};
+
+// A package of CollectionNotifiers for a single Realm instance which is passed
+// around to the various places which need to actually trigger the notifications
+class NotifierPackage {
+public:
+    NotifierPackage() = default;
+    NotifierPackage(std::exception_ptr error,
+                    std::vector<std::shared_ptr<CollectionNotifier>> notifiers,
+                    RealmCoordinator* coordinator);
+
+    explicit operator bool() { return !m_notifiers.empty(); }
+
+    // Get the version which this package can deliver into, or VersionID{} if
+    // it has not yet been packaged
+    util::Optional<VersionID> version() { return m_version; }
+
+    // Package the notifiers for delivery, blocking if they aren't ready for
+    // the given version.
+    // No-op if called multiple times
+    void package_and_wait(util::Optional<VersionID::version_type> target_version);
+
+    // Send the before-change notifications
+    void before_advance();
+    // Deliver the payload associated with the contained notifiers and/or the error
+    void deliver(SharedGroup& sg);
+    // Send the after-change notifications
+    void after_advance();
+
+    void add_notifier(std::shared_ptr<CollectionNotifier> notifier);
+
+private:
+    util::Optional<VersionID> m_version;
+    std::vector<std::shared_ptr<CollectionNotifier>> m_notifiers;
+
+    RealmCoordinator* m_coordinator = nullptr;
+    std::exception_ptr m_error;
+};
+
+// Find which column of the row in the table contains the given container.
+//
+// LinkViews and Subtables know what row of their parent they're in, but not
+// what column, so we have to just check each one.
+template<typename Table, typename T, typename U>
+size_t find_container_column(Table& table, size_t row_ndx, T const& expected, int type, U (Table::*getter)(size_t, size_t))
+{
+    for (size_t i = 0, count = table.get_column_count(); i != count; ++i) {
+        if (table.get_column_type(i) == type && (table.*getter)(i, row_ndx) == expected) {
+            return i;
+        }
+    }
+    REALM_UNREACHABLE();
+}
+
+
+} // namespace _impl
+} // namespace realm
+
+#endif /* REALM_BACKGROUND_COLLECTION_HPP */
diff --git a/iOS/Pods/Realm/include/impl/external_commit_helper.hpp b/iOS/Pods/Realm/include/impl/external_commit_helper.hpp
new file mode 100644 (file)
index 0000000..8c5f3a1
--- /dev/null
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_EXTERNAL_COMMIT_HELPER_HPP
+#define REALM_EXTERNAL_COMMIT_HELPER_HPP
+
+#include <realm/util/features.h>
+
+#if (defined(REALM_HAVE_EPOLL) && REALM_HAVE_EPOLL) || REALM_ANDROID || (defined(REALM_PLATFORM_NODE) && REALM_PLATFORM_NODE && !REALM_PLATFORM_APPLE && !defined(_WIN32))
+#define REALM_USE_EPOLL 1
+#else
+#define REALM_USE_EPOLL 0
+#endif
+
+#if REALM_PLATFORM_APPLE
+#include "impl/apple/external_commit_helper.hpp"
+#elif REALM_USE_EPOLL
+#include "impl/epoll/external_commit_helper.hpp"
+#elif defined(_WIN32)
+#include "impl/windows/external_commit_helper.hpp"
+#else
+#include "impl/generic/external_commit_helper.hpp"
+#endif
+
+#endif // REALM_EXTERNAL_COMMIT_HELPER_HPP
diff --git a/iOS/Pods/Realm/include/impl/list_notifier.hpp b/iOS/Pods/Realm/include/impl/list_notifier.hpp
new file mode 100644 (file)
index 0000000..54e8bcc
--- /dev/null
@@ -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.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef REALM_LIST_NOTIFIER_HPP
+#define REALM_LIST_NOTIFIER_HPP
+
+#include "impl/collection_notifier.hpp"
+
+#include <realm/group_shared.hpp>
+
+namespace realm {
+namespace _impl {
+class ListNotifier : public CollectionNotifier {
+public:
+    ListNotifier(LinkViewRef lv, std::shared_ptr<Realm> realm);
+
+private:
+    // The linkview, in handover form if this has not been attached to the main
+    // SharedGroup yet
+    LinkViewRef m_lv;
+    std::unique_ptr<SharedGroup::Handover<LinkView>> m_lv_handover;
+
+    // The last-seen size of the LinkView so that we can report row deletions
+    // when the LinkView itself is deleted
+    size_t m_prev_size;
+
+    // The actual change, calculated in run() and delivered in prepare_handover()
+    CollectionChangeBuilder m_change;
+    TransactionChangeInfo* m_info;
+
+    void run() override;
+
+    void do_prepare_handover(SharedGroup&) override;
+
+    void do_attach_to(SharedGroup& sg) override;
+    void do_detach_from(SharedGroup& sg) override;
+
+    void release_data() noexcept override;
+    bool do_add_required_change_info(TransactionChangeInfo& info) override;
+};
+}
+}
+
+#endif // REALM_LIST_NOTIFIER_HPP
diff --git a/iOS/Pods/Realm/include/impl/notification_wrapper.hpp b/iOS/Pods/Realm/include/impl/notification_wrapper.hpp
new file mode 100644 (file)
index 0000000..f2e5a3b
--- /dev/null
@@ -0,0 +1,51 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_NOTIFICATION_WRAPPER_HPP
+#define REALM_OS_NOTIFICATION_WRAPPER_HPP
+
+#include "collection_notifications.hpp"
+
+namespace realm {
+namespace _impl {
+
+// A wrapper that stores a value and an associated notification token.
+// The point of this type is to keep the notification token alive
+// until the value can be properly processed or handled.
+template<typename T>
+struct NotificationWrapper : public T {
+    using T::T;
+
+    NotificationWrapper(T&& object)
+    : T(object)
+    { }
+
+    template <typename F>
+    void add_notification_callback(F&& callback)
+    {
+        m_token = T::add_notification_callback(std::forward<F>(callback));
+    }
+
+private:
+    NotificationToken m_token;
+};
+
+} // namespace _impl
+} // namespace realm
+
+#endif // REALM_OS_NOTIFICATION_WRAPPER_HPP
diff --git a/iOS/Pods/Realm/include/impl/object_accessor_impl.hpp b/iOS/Pods/Realm/include/impl/object_accessor_impl.hpp
new file mode 100644 (file)
index 0000000..650f46f
--- /dev/null
@@ -0,0 +1,220 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_OBJECT_ACCESSOR_IMPL_HPP
+#define REALM_OS_OBJECT_ACCESSOR_IMPL_HPP
+
+#include "object_accessor.hpp"
+
+#include <realm/util/any.hpp>
+
+namespace realm {
+using AnyDict = std::map<std::string, util::Any>;
+using AnyVector = std::vector<util::Any>;
+
+// An object accessor context which can be used to create and access objects
+// using util::Any as the type-erased value type. In addition, this serves as
+// the reference implementation of an accessor context that must be implemented
+// by each binding.
+class CppContext {
+public:
+    // This constructor is the only one used by the object accessor code, and is
+    // used when recurring into a link or array property during object creation
+    // (i.e. prop.type will always be Object or Array).
+    CppContext(CppContext& c, Property const& prop)
+    : realm(c.realm)
+    , object_schema(prop.type == PropertyType::Object ? &*realm->schema().find(prop.object_type) : c.object_schema)
+    { }
+
+    CppContext() = default;
+    CppContext(std::shared_ptr<Realm> realm, const ObjectSchema* os=nullptr)
+    : realm(std::move(realm)), object_schema(os) { }
+
+    // The use of util::Optional for the following two functions is not a hard
+    // requirement; only that it be some type which can be evaluated in a
+    // boolean context to determine if it contains a value, and if it does
+    // contain a value it must be dereferencable to obtain that value.
+
+    // Get the value for a property in an input object, or `util::none` if no
+    // value present. The property is identified both by the name of the
+    // property and its index within the ObjectScehma's persisted_properties
+    // array.
+    util::Optional<util::Any> value_for_property(util::Any& dict,
+                                                 std::string const& prop_name,
+                                                 size_t /* property_index */) const
+    {
+        auto const& v = any_cast<AnyDict&>(dict);
+        auto it = v.find(prop_name);
+        return it == v.end() ? util::none : util::make_optional(it->second);
+    }
+
+    // Get the default value for the given property in the given object schema,
+    // or `util::none` if there is none (which is distinct from the default
+    // being `null`).
+    //
+    // This implementation does not support default values; see the default
+    // value tests for an example of one which does.
+    util::Optional<util::Any>
+    default_value_for_property(ObjectSchema const&, std::string const&) const
+    {
+        return util::none;
+    }
+
+    // Invoke `fn` with each of the values from an enumerable type
+    template<typename Func>
+    void enumerate_list(util::Any& value, Func&& fn) {
+        for (auto&& v : any_cast<AnyVector&>(value))
+            fn(v);
+    }
+
+    // Determine if `value` boxes the same List as `list`
+    bool is_same_list(List const& list, util::Any const& value)
+    {
+        if (auto list2 = any_cast<List>(&value))
+            return list == *list2;
+        return false;
+    }
+
+    // Convert from core types to the boxed type
+    util::Any box(BinaryData v) const { return std::string(v); }
+    util::Any box(List v) const { return v; }
+    util::Any box(Object v) const { return v; }
+    util::Any box(Results v) const { return v; }
+    util::Any box(StringData v) const { return std::string(v); }
+    util::Any box(Timestamp v) const { return v; }
+    util::Any box(bool v) const { return v; }
+    util::Any box(double v) const { return v; }
+    util::Any box(float v) const { return v; }
+    util::Any box(int64_t v) const { return v; }
+    util::Any box(util::Optional<bool> v) const { return v; }
+    util::Any box(util::Optional<double> v) const { return v; }
+    util::Any box(util::Optional<float> v) const { return v; }
+    util::Any box(util::Optional<int64_t> v) const { return v; }
+    util::Any box(RowExpr) const;
+
+    // Any properties are only supported by the Cocoa binding to enable reading
+    // old Realm files that may have used them. Other bindings can safely not
+    // implement this.
+    util::Any box(Mixed) const { REALM_TERMINATE("not supported"); }
+
+    // Convert from the boxed type to core types. This needs to be implemented
+    // for all of the types which `box()` can take, plus `RowExpr` and optional
+    // versions of the numeric types, minus `List` and `Results`.
+    //
+    // `create` and `update` are only applicable to `unbox<RowExpr>`. If
+    // `create` is false then when given something which is not a managed Realm
+    // object `unbox()` should simply return a detached row expr, while if it's
+    // true then `unbox()` should create a new object in the context's Realm
+    // using the provided value. If `update` is true then upsert semantics
+    // should be used for this.
+    template<typename T>
+    T unbox(util::Any& v, bool /*create*/= false, bool /*update*/= false) const { return any_cast<T>(v); }
+
+    bool is_null(util::Any const& v) const noexcept { return !v.has_value(); }
+    util::Any null_value() const noexcept { return {}; }
+    util::Optional<util::Any> no_value() const noexcept { return {}; }
+
+    // KVO hooks which will be called before and after modying a property from
+    // within Object::create().
+    void will_change(Object const&, Property const&) {}
+    void did_change() {}
+
+    // Get a string representation of the given value for use in error messages.
+    std::string print(util::Any const&) const { return "not implemented"; }
+
+    // Cocoa allows supplying fewer values than there are properties when
+    // creating objects using an array of values. Other bindings should not
+    // mimick this behavior so just return false here.
+    bool allow_missing(util::Any const&) const { return false; }
+
+private:
+    std::shared_ptr<Realm> realm;
+    const ObjectSchema* object_schema = nullptr;
+
+};
+
+inline util::Any CppContext::box(RowExpr row) const
+{
+    REALM_ASSERT(object_schema);
+    return Object(realm, *object_schema, row);
+}
+
+template<>
+inline StringData CppContext::unbox(util::Any& v, bool, bool) const
+{
+    if (!v.has_value())
+        return StringData();
+    auto& value = any_cast<std::string&>(v);
+    return StringData(value.c_str(), value.size());
+}
+
+template<>
+inline BinaryData CppContext::unbox(util::Any& v, bool, bool) const
+{
+    if (!v.has_value())
+        return BinaryData();
+    auto& value = any_cast<std::string&>(v);
+    return BinaryData(value.c_str(), value.size());
+}
+
+template<>
+inline RowExpr CppContext::unbox(util::Any& v, bool create, bool update) const
+{
+    if (auto object = any_cast<Object>(&v))
+        return object->row();
+    if (auto row = any_cast<RowExpr>(&v))
+        return *row;
+    if (!create)
+        return RowExpr();
+
+    REALM_ASSERT(object_schema);
+    return Object::create(const_cast<CppContext&>(*this), realm, *object_schema, v, update).row();
+}
+
+template<>
+inline util::Optional<bool> CppContext::unbox(util::Any& v, bool, bool) const
+{
+    return v.has_value() ? util::make_optional(unbox<bool>(v)) : util::none;
+}
+
+template<>
+inline util::Optional<int64_t> CppContext::unbox(util::Any& v, bool, bool) const
+{
+    return v.has_value() ? util::make_optional(unbox<int64_t>(v)) : util::none;
+}
+
+template<>
+inline util::Optional<double> CppContext::unbox(util::Any& v, bool, bool) const
+{
+    return v.has_value() ? util::make_optional(unbox<double>(v)) : util::none;
+}
+
+template<>
+inline util::Optional<float> CppContext::unbox(util::Any& v, bool, bool) const
+{
+    return v.has_value() ? util::make_optional(unbox<float>(v)) : util::none;
+}
+
+template<>
+inline Mixed CppContext::unbox(util::Any&, bool, bool) const
+{
+    throw std::logic_error("'Any' type is unsupported");
+}
+}
+
+#endif // REALM_OS_OBJECT_ACCESSOR_IMPL_HPP
diff --git a/iOS/Pods/Realm/include/impl/object_notifier.hpp b/iOS/Pods/Realm/include/impl/object_notifier.hpp
new file mode 100644 (file)
index 0000000..8ed21d1
--- /dev/null
@@ -0,0 +1,53 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_OBJECT_NOTIFIER_HPP
+#define REALM_OS_OBJECT_NOTIFIER_HPP
+
+#include "impl/collection_notifier.hpp"
+
+#include <realm/group_shared.hpp>
+
+namespace realm {
+namespace _impl {
+class ObjectNotifier : public CollectionNotifier {
+public:
+    ObjectNotifier(Row const& row, std::shared_ptr<Realm> realm);
+
+private:
+    std::unique_ptr<Row> m_row;
+    std::unique_ptr<SharedGroup::Handover<Row>> m_handover;
+
+    // The actual change, calculated in run() and delivered in prepare_handover()
+    CollectionChangeBuilder m_change;
+    TransactionChangeInfo* m_info;
+
+    void run() override;
+
+    void do_prepare_handover(SharedGroup&) override;
+
+    void do_attach_to(SharedGroup& sg) override;
+    void do_detach_from(SharedGroup& sg) override;
+
+    void release_data() noexcept override;
+    bool do_add_required_change_info(TransactionChangeInfo& info) override;
+};
+}
+}
+
+#endif // REALM_OS_OBJECT_NOTIFIER_HPP
diff --git a/iOS/Pods/Realm/include/impl/primitive_list_notifier.hpp b/iOS/Pods/Realm/include/impl/primitive_list_notifier.hpp
new file mode 100644 (file)
index 0000000..b28e241
--- /dev/null
@@ -0,0 +1,59 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_PRIMITIVE_LIST_NOTIFIER_HPP
+#define REALM_OS_PRIMITIVE_LIST_NOTIFIER_HPP
+
+#include "impl/collection_notifier.hpp"
+
+#include <realm/group_shared.hpp>
+
+namespace realm {
+namespace _impl {
+class PrimitiveListNotifier : public CollectionNotifier {
+public:
+    PrimitiveListNotifier(TableRef lv, std::shared_ptr<Realm> realm);
+
+private:
+    // The subtable, in handover form if this has not been attached to the main
+    // SharedGroup yet
+    TableRef m_table;
+    std::unique_ptr<SharedGroup::Handover<Table>> m_table_handover;
+
+    // The last-seen size of the Table so that we can report row deletions
+    // when the Table itself is deleted
+    size_t m_prev_size;
+
+    // The actual change, calculated in run() and delivered in prepare_handover()
+    CollectionChangeBuilder m_change;
+    TransactionChangeInfo* m_info;
+
+    void run() override;
+
+    void do_prepare_handover(SharedGroup&) override;
+
+    void do_attach_to(SharedGroup& sg) override;
+    void do_detach_from(SharedGroup& sg) override;
+
+    void release_data() noexcept override;
+    bool do_add_required_change_info(TransactionChangeInfo& info) override;
+};
+}
+}
+
+#endif // REALM_OS_PRIMITIVE_LIST_NOTIFIER_HPP
diff --git a/iOS/Pods/Realm/include/impl/realm_coordinator.hpp b/iOS/Pods/Realm/include/impl/realm_coordinator.hpp
new file mode 100644 (file)
index 0000000..0d36c53
--- /dev/null
@@ -0,0 +1,210 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_COORDINATOR_HPP
+#define REALM_COORDINATOR_HPP
+
+#include "shared_realm.hpp"
+
+#include <realm/version_id.hpp>
+
+#include <condition_variable>
+#include <mutex>
+
+namespace realm {
+class Replication;
+class Schema;
+class SharedGroup;
+class StringData;
+class SyncSession;
+
+namespace _impl {
+class CollectionNotifier;
+class ExternalCommitHelper;
+class WeakRealmNotifier;
+
+// RealmCoordinator manages the weak cache of Realm instances and communication
+// between per-thread Realm instances for a given file
+class RealmCoordinator : public std::enable_shared_from_this<RealmCoordinator> {
+public:
+    // Get the coordinator for the given path, creating it if neccesary
+    static std::shared_ptr<RealmCoordinator> get_coordinator(StringData path);
+    // Get the coordinator for the given config, creating it if neccesary
+    static std::shared_ptr<RealmCoordinator> get_coordinator(const Realm::Config&);
+    // Get the coordinator for the given path, or null if there is none
+    static std::shared_ptr<RealmCoordinator> get_existing_coordinator(StringData path);
+
+    // Get a thread-local shared Realm with the given configuration
+    // If the Realm is already open on another thread, validates that the given
+    // configuration is compatible with the existing one
+    std::shared_ptr<Realm> get_realm(Realm::Config config);
+    std::shared_ptr<Realm> get_realm();
+
+    Realm::Config get_config() const { return m_config; }
+
+    uint64_t get_schema_version() const noexcept { return m_schema_version; }
+    const std::string& get_path() const noexcept { return m_config.path; }
+    const std::vector<char>& get_encryption_key() const noexcept { return m_config.encryption_key; }
+    bool is_in_memory() const noexcept { return m_config.in_memory; }
+
+    // To avoid having to re-read and validate the file's schema every time a
+    // new read transaction is begun, RealmCoordinator maintains a cache of the
+    // most recently seen file schema and the range of transaction versions
+    // which it applies to. Note that this schema may not be identical to that
+    // of any Realm instances managed by this coordinator, as individual Realms
+    // may only be using a subset of it.
+
+    // Get the latest cached schema and the transaction version which it applies
+    // to. Returns false if there is no cached schema.
+    bool get_cached_schema(Schema& schema, uint64_t& schema_version, uint64_t& transaction) const noexcept;
+
+    // Cache the state of the schema at the given transaction version
+    void cache_schema(Schema const& new_schema, uint64_t new_schema_version,
+                      uint64_t transaction_version);
+    // If there is a schema cached for transaction version `previous`, report
+    // that it is still valid at transaction version `next`
+    void advance_schema_cache(uint64_t previous, uint64_t next);
+    void clear_schema_cache_and_set_schema_version(uint64_t new_schema_version);
+
+
+    // Asynchronously call notify() on every Realm instance for this coordinator's
+    // path, including those in other processes
+    void send_commit_notifications(Realm&);
+    void wake_up_notifier_worker();
+
+    // Clear the weak Realm cache for all paths
+    // Should only be called in test code, as continuing to use the previously
+    // cached instances will have odd results
+    static void clear_cache();
+
+    // Clears all caches on existing coordinators
+    static void clear_all_caches();
+
+    // Verify that there are no Realms open for any paths
+    static void assert_no_open_realms() noexcept;
+
+    // Explicit constructor/destructor needed for the unique_ptrs to forward-declared types
+    RealmCoordinator();
+    ~RealmCoordinator();
+
+    // Called by Realm's destructor to ensure the cache is cleaned up promptly
+    // Do not call directly
+    void unregister_realm(Realm* realm);
+
+    // Called by m_notifier when there's a new commit to send notifications for
+    void on_change();
+
+    static void register_notifier(std::shared_ptr<CollectionNotifier> notifier);
+
+    // Advance the Realm to the most recent transaction version which all async
+    // work is complete for
+    void advance_to_ready(Realm& realm);
+
+    // Advance the Realm to the most recent transaction version, blocking if
+    // async notifiers are not yet ready for that version
+    // returns whether it actually changed the version
+    bool advance_to_latest(Realm& realm);
+
+    // Deliver any notifications which are ready for the Realm's version
+    void process_available_async(Realm& realm);
+
+    // Register a function which is called whenever sync makes a write to the Realm
+    void set_transaction_callback(std::function<void(VersionID, VersionID)>);
+
+    // Deliver notifications for the Realm, blocking if some aren't ready yet
+    // The calling Realm must be in a write transaction
+    void promote_to_write(Realm& realm);
+
+    // Commit a Realm's current write transaction and send notifications to all
+    // other Realm instances for that path, including in other processes
+    void commit_write(Realm& realm);
+
+    template<typename Pred>
+    std::unique_lock<std::mutex> wait_for_notifiers(Pred&& wait_predicate);
+
+private:
+    Realm::Config m_config;
+
+    mutable std::mutex m_schema_cache_mutex;
+    util::Optional<Schema> m_cached_schema;
+    uint64_t m_schema_version = -1;
+    uint64_t m_schema_transaction_version_min = 0;
+    uint64_t m_schema_transaction_version_max = 0;
+
+    std::mutex m_realm_mutex;
+    std::vector<WeakRealmNotifier> m_weak_realm_notifiers;
+
+    std::mutex m_notifier_mutex;
+    std::condition_variable m_notifier_cv;
+    std::vector<std::shared_ptr<_impl::CollectionNotifier>> m_new_notifiers;
+    std::vector<std::shared_ptr<_impl::CollectionNotifier>> m_notifiers;
+    VersionID m_notifier_skip_version = {0, 0};
+
+    // SharedGroup used for actually running async notifiers
+    // Will have a read transaction iff m_notifiers is non-empty
+    std::unique_ptr<Replication> m_notifier_history;
+    std::unique_ptr<SharedGroup> m_notifier_sg;
+
+    // SharedGroup used to advance notifiers in m_new_notifiers to the main shared
+    // group's transaction version
+    // Will have a read transaction iff m_new_notifiers is non-empty
+    std::unique_ptr<Replication> m_advancer_history;
+    std::unique_ptr<SharedGroup> m_advancer_sg;
+    std::exception_ptr m_async_error;
+
+    std::unique_ptr<_impl::ExternalCommitHelper> m_notifier;
+    std::function<void(VersionID, VersionID)> m_transaction_callback;
+
+    std::shared_ptr<SyncSession> m_sync_session;
+
+    // must be called with m_notifier_mutex locked
+    void pin_version(VersionID version);
+
+    void set_config(const Realm::Config&);
+    void create_sync_session();
+
+    void run_async_notifiers();
+    void open_helper_shared_group();
+    void advance_helper_shared_group_to_latest();
+    void clean_up_dead_notifiers();
+
+    std::vector<std::shared_ptr<_impl::CollectionNotifier>> notifiers_for_realm(Realm&);
+};
+
+
+template<typename Pred>
+std::unique_lock<std::mutex> RealmCoordinator::wait_for_notifiers(Pred&& wait_predicate)
+{
+    std::unique_lock<std::mutex> lock(m_notifier_mutex);
+    bool first = true;
+    m_notifier_cv.wait(lock, [&] {
+        if (wait_predicate())
+            return true;
+        if (first) {
+            wake_up_notifier_worker();
+            first = false;
+        }
+        return false;
+    });
+    return lock;
+}
+
+} // namespace _impl
+} // namespace realm
+
+#endif /* REALM_COORDINATOR_HPP */
diff --git a/iOS/Pods/Realm/include/impl/results_notifier.hpp b/iOS/Pods/Realm/include/impl/results_notifier.hpp
new file mode 100644 (file)
index 0000000..eee0081
--- /dev/null
@@ -0,0 +1,82 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_RESULTS_NOTIFIER_HPP
+#define REALM_RESULTS_NOTIFIER_HPP
+
+#include "collection_notifier.hpp"
+#include "results.hpp"
+
+#include <realm/group_shared.hpp>
+
+namespace realm {
+namespace _impl {
+class ResultsNotifier : public CollectionNotifier {
+public:
+    ResultsNotifier(Results& target);
+
+    void target_results_moved(Results& old_target, Results& new_target);
+
+private:
+    // Target Results to update
+    // Can only be used with lock_target() held
+    Results* m_target_results;
+
+    // The source Query, in handover form iff m_sg is null
+    std::unique_ptr<SharedGroup::Handover<Query>> m_query_handover;
+    std::unique_ptr<Query> m_query;
+
+    DescriptorOrdering::HandoverPatch m_ordering_handover;
+    DescriptorOrdering m_descriptor_ordering;
+    bool m_target_is_in_table_order;
+
+    // The TableView resulting from running the query. Will be detached unless
+    // the query was (re)run since the last time the handover object was created
+    TableView m_tv;
+    std::unique_ptr<SharedGroup::Handover<TableView>> m_tv_handover;
+    std::unique_ptr<SharedGroup::Handover<TableView>> m_tv_to_deliver;
+
+    // The table version from the last time the query was run. Used to avoid
+    // rerunning the query when there's no chance of it changing.
+    uint_fast64_t m_last_seen_version = -1;
+
+    // The rows from the previous run of the query, for calculating diffs
+    std::vector<size_t> m_previous_rows;
+
+    // The changeset calculated during run() and delivered in do_prepare_handover()
+    CollectionChangeBuilder m_changes;
+    TransactionChangeInfo* m_info = nullptr;
+
+    bool need_to_run();
+    void calculate_changes();
+    void deliver(SharedGroup&) override;
+
+    void run() override;
+    void do_prepare_handover(SharedGroup&) override;
+    bool do_add_required_change_info(TransactionChangeInfo& info) override;
+    bool prepare_to_deliver() override;
+
+    void release_data() noexcept override;
+    void do_attach_to(SharedGroup& sg) override;
+    void do_detach_from(SharedGroup& sg) override;
+};
+
+} // namespace _impl
+} // namespace realm
+
+#endif /* REALM_RESULTS_NOTIFIER_HPP */
diff --git a/iOS/Pods/Realm/include/impl/transact_log_handler.hpp b/iOS/Pods/Realm/include/impl/transact_log_handler.hpp
new file mode 100644 (file)
index 0000000..14d8233
--- /dev/null
@@ -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.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef REALM_TRANSACT_LOG_HANDLER_HPP
+#define REALM_TRANSACT_LOG_HANDLER_HPP
+
+#include <cstdint>
+#include <realm/version_id.hpp>
+
+#include <memory>
+
+namespace realm {
+class BindingContext;
+class SharedGroup;
+
+namespace _impl {
+class NotifierPackage;
+struct TransactionChangeInfo;
+
+namespace transaction {
+// Advance the read transaction version, with change notifications sent to delegate
+// Must not be called from within a write transaction.
+void advance(const std::unique_ptr<SharedGroup>& sg, BindingContext* binding_context, NotifierPackage&);
+void advance(SharedGroup& sg, BindingContext* binding_context, VersionID);
+
+// Begin a write transaction
+// If the read transaction version is not up to date, will first advance to the
+// most recent read transaction and sent notifications to delegate
+void begin(const std::unique_ptr<SharedGroup>& sg, BindingContext* binding_context, NotifierPackage&);
+void begin_without_validation(SharedGroup& sg);
+
+// Commit a write transaction
+void commit(SharedGroup& sg);
+
+// Cancel a write transaction and roll back all changes, with change notifications
+// for reverting to the old values sent to delegate
+void cancel(SharedGroup& sg, BindingContext* binding_context);
+
+// Advance the read transaction version, with change information gathered in info
+void advance(SharedGroup& sg, TransactionChangeInfo& info, VersionID version=VersionID{});
+} // namespace transaction
+} // namespace _impl
+} // namespace realm
+
+#endif /* REALM_TRANSACT_LOG_HANDLER_HPP */
diff --git a/iOS/Pods/Realm/include/impl/weak_realm_notifier.hpp b/iOS/Pods/Realm/include/impl/weak_realm_notifier.hpp
new file mode 100644 (file)
index 0000000..f0b3ad4
--- /dev/null
@@ -0,0 +1,78 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_WEAK_REALM_NOTIFIER_HPP
+#define REALM_WEAK_REALM_NOTIFIER_HPP
+
+#include "execution_context_id.hpp"
+
+#include <memory>
+#include <thread>
+
+namespace realm {
+class Realm;
+
+namespace util {
+template<typename> class EventLoopSignal;
+}
+
+namespace _impl {
+// WeakRealmNotifier stores a weak reference to a Realm instance, along with all of
+// the information about a Realm that needs to be accessed from other threads.
+// This is needed to avoid forming strong references to the Realm instances on
+// other threads, which can produce deadlocks when the last strong reference to
+// a Realm instance is released from within a function holding the cache lock.
+class WeakRealmNotifier {
+public:
+    WeakRealmNotifier(const std::shared_ptr<Realm>& realm, bool cache);
+    ~WeakRealmNotifier();
+
+    // Get a strong reference to the cached realm
+    std::shared_ptr<Realm> realm() const { return m_realm.lock(); }
+
+    // Does this WeakRealmNotifier store a Realm instance that should be used on the current thread?
+    bool is_cached_for_execution_context(const AnyExecutionContextID& execution_context) const
+    {
+        return m_cache && m_execution_context == execution_context;
+    }
+
+    // Has the Realm instance been destroyed?
+    bool expired() const { return m_realm.expired(); }
+
+    // Is this a WeakRealmNotifier for the given Realm instance?
+    bool is_for_realm(Realm* realm) const { return realm == m_realm_key; }
+
+    void notify();
+
+private:
+    std::weak_ptr<Realm> m_realm;
+    AnyExecutionContextID m_execution_context;
+    void* m_realm_key;
+    bool m_cache = false;
+
+    struct Callback {
+        const std::weak_ptr<Realm> weak_realm;
+        void operator()() const;
+    };
+    std::shared_ptr<util::EventLoopSignal<Callback>> m_signal;
+};
+
+} // namespace _impl
+} // namespace realm
+
+#endif // REALM_WEAK_REALM_NOTIFIER_HPP
diff --git a/iOS/Pods/Realm/include/index_set.hpp b/iOS/Pods/Realm/include/index_set.hpp
new file mode 100644 (file)
index 0000000..55bedcc
--- /dev/null
@@ -0,0 +1,325 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_INDEX_SET_HPP
+#define REALM_INDEX_SET_HPP
+
+#include <cstddef>
+#include <initializer_list>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace realm {
+namespace _impl {
+template<typename OuterIterator>
+class MutableChunkedRangeVectorIterator;
+
+// An iterator for ChunkedRangeVector, templated on the vector iterator/const_iterator
+template<typename OuterIterator>
+class ChunkedRangeVectorIterator {
+public:
+    using iterator_category = std::bidirectional_iterator_tag;
+    using value_type = typename std::remove_reference<decltype(*OuterIterator()->data.begin())>::type;
+    using difference_type = ptrdiff_t;
+    using pointer = const value_type*;
+    using reference = const value_type&;
+
+    ChunkedRangeVectorIterator(OuterIterator outer, OuterIterator end, value_type* inner)
+    : m_outer(outer), m_end(end), m_inner(inner) { }
+
+    reference operator*() const noexcept { return *m_inner; }
+    pointer operator->() const noexcept { return m_inner; }
+
+    template<typename Other> bool operator==(Other const& it) const noexcept;
+    template<typename Other> bool operator!=(Other const& it) const noexcept;
+
+    ChunkedRangeVectorIterator& operator++() noexcept;
+    ChunkedRangeVectorIterator operator++(int) noexcept;
+
+    ChunkedRangeVectorIterator& operator--() noexcept;
+    ChunkedRangeVectorIterator operator--(int) noexcept;
+
+    // Advance directly to the next outer block
+    void next_chunk() noexcept;
+
+    OuterIterator outer() const noexcept { return m_outer; }
+    size_t offset() const noexcept { return m_inner - &m_outer->data[0]; }
+
+private:
+    OuterIterator m_outer;
+    OuterIterator m_end;
+    value_type* m_inner;
+    friend struct ChunkedRangeVector;
+    friend class MutableChunkedRangeVectorIterator<OuterIterator>;
+};
+
+// A mutable iterator that adds some invariant-preserving mutation methods
+template<typename OuterIterator>
+class MutableChunkedRangeVectorIterator : public ChunkedRangeVectorIterator<OuterIterator> {
+public:
+    using ChunkedRangeVectorIterator<OuterIterator>::ChunkedRangeVectorIterator;
+
+    // Set this iterator to the given range and update the parent if needed
+    void set(size_t begin, size_t end);
+    // Adjust the begin and end of this iterator by the given amounts and
+    // update the parent if needed
+    void adjust(ptrdiff_t front, ptrdiff_t back);
+    // Shift this iterator by the given amount and update the parent if needed
+    void shift(ptrdiff_t distance);
+};
+
+// A vector which stores ranges in chunks with a maximum size
+struct ChunkedRangeVector {
+    struct Chunk {
+        std::vector<std::pair<size_t, size_t>> data;
+        size_t begin;
+        size_t end;
+        size_t count;
+    };
+    std::vector<Chunk> m_data;
+
+    using value_type = std::pair<size_t, size_t>;
+    using iterator = MutableChunkedRangeVectorIterator<typename decltype(m_data)::iterator>;
+    using const_iterator = ChunkedRangeVectorIterator<typename decltype(m_data)::const_iterator>;
+
+#ifdef REALM_DEBUG
+    static const size_t max_size = 4;
+#else
+    static const size_t max_size = 4096 / sizeof(std::pair<size_t, size_t>);
+#endif
+
+    iterator begin() noexcept { return empty() ? end() : iterator(m_data.begin(), m_data.end(), &m_data[0].data[0]); }
+    iterator end() noexcept { return iterator(m_data.end(), m_data.end(), nullptr); }
+    const_iterator begin() const noexcept { return cbegin(); }
+    const_iterator end() const noexcept { return cend(); }
+    const_iterator cbegin() const noexcept { return empty() ? cend() : const_iterator(m_data.cbegin(), m_data.end(), &m_data[0].data[0]); }
+    const_iterator cend() const noexcept { return const_iterator(m_data.end(), m_data.end(), nullptr); }
+
+    bool empty() const noexcept { return m_data.empty(); }
+
+    iterator insert(iterator pos, value_type value);
+    iterator erase(iterator pos) noexcept;
+    void push_back(value_type value);
+    iterator ensure_space(iterator pos);
+
+    void verify() const noexcept;
+};
+} // namespace _impl
+
+class IndexSet : private _impl::ChunkedRangeVector {
+public:
+    static const size_t npos = -1;
+
+    using ChunkedRangeVector::value_type;
+    using ChunkedRangeVector::iterator;
+    using ChunkedRangeVector::const_iterator;
+    using ChunkedRangeVector::begin;
+    using ChunkedRangeVector::end;
+    using ChunkedRangeVector::empty;
+    using ChunkedRangeVector::verify;
+
+    IndexSet() = default;
+    IndexSet(std::initializer_list<size_t>);
+
+    // Check if the index set contains the given index
+    bool contains(size_t index) const noexcept;
+
+    // Counts the number of indices in the set in the given range
+    size_t count(size_t start_index=0, size_t end_index=-1) const noexcept;
+
+    // Add an index to the set, doing nothing if it's already present
+    void add(size_t index);
+    void add(IndexSet const& is);
+
+    // Add an index which has had all of the ranges in the set before it removed
+    // Returns the unshifted index
+    size_t add_shifted(size_t index);
+    // Add indexes which have had the ranges in `shifted_by` added and the ranges
+    // in the current set removed
+    void add_shifted_by(IndexSet const& shifted_by, IndexSet const& values);
+
+    // Remove all indexes from the set and then add a single range starting from
+    // zero with the given length
+    void set(size_t len);
+
+    // Insert an index at the given position, shifting existing indexes at or
+    // after that point back by one
+    void insert_at(size_t index, size_t count=1);
+    void insert_at(IndexSet const&);
+
+    // Shift indexes at or after the given point back by one
+    void shift_for_insert_at(size_t index, size_t count=1);
+    void shift_for_insert_at(IndexSet const&);
+
+    // Delete an index at the given position, shifting indexes after that point
+    // forward by one
+    void erase_at(size_t index);
+    void erase_at(IndexSet const&);
+
+    // If the given index is in the set remove it and return npos; otherwise unshift() it
+    size_t erase_or_unshift(size_t index);
+
+    // Remove the indexes at the given index from the set, without shifting
+    void remove(size_t index, size_t count=1);
+    void remove(IndexSet const&);
+
+    // Shift an index by inserting each of the indexes in this set
+    size_t shift(size_t index) const noexcept;
+    // Shift an index by deleting each of the indexes in this set
+    size_t unshift(size_t index) const noexcept;
+
+    // Remove all indexes from the set
+    void clear() noexcept;
+
+    // An iterator over the individual indices in the set rather than the ranges
+    class IndexIterator : public std::iterator<std::forward_iterator_tag, size_t> {
+    public:
+        IndexIterator(IndexSet::const_iterator it) : m_iterator(it) { }
+        size_t operator*() const noexcept { return m_iterator->first + m_offset; }
+        bool operator==(IndexIterator const& it) const noexcept { return m_iterator == it.m_iterator; }
+        bool operator!=(IndexIterator const& it) const noexcept { return m_iterator != it.m_iterator; }
+
+        IndexIterator& operator++() noexcept
+        {
+            ++m_offset;
+            if (m_iterator->first + m_offset == m_iterator->second) {
+                ++m_iterator;
+                m_offset = 0;
+            }
+            return *this;
+        }
+
+        IndexIterator operator++(int) noexcept
+        {
+            auto value = *this;
+            ++*this;
+            return value;
+        }
+
+    private:
+        IndexSet::const_iterator m_iterator;
+        size_t m_offset = 0;
+    };
+
+    class IndexIteratableAdaptor {
+    public:
+        using value_type = size_t;
+        using iterator = IndexIterator;
+        using const_iterator = iterator;
+
+        const_iterator begin() const noexcept { return m_index_set.begin(); }
+        const_iterator end() const noexcept { return m_index_set.end(); }
+
+        IndexIteratableAdaptor(IndexSet const& is) : m_index_set(is) { }
+    private:
+        IndexSet const& m_index_set;
+    };
+
+    IndexIteratableAdaptor as_indexes() const noexcept { return *this; }
+
+private:
+    // Find the range which contains the index, or the first one after it if
+    // none do
+    iterator find(size_t index) noexcept;
+    iterator find(size_t index, iterator it) noexcept;
+    // Insert the index before the given position, combining existing ranges as
+    // applicable
+    // returns inserted position
+    iterator do_add(iterator pos, size_t index);
+    void do_erase(iterator it, size_t index);
+    iterator do_remove(iterator it, size_t index, size_t count);
+
+    void shift_until_end_by(iterator begin, ptrdiff_t shift);
+};
+
+namespace util {
+// This was added in C++14 but is missing from libstdc++ 4.9
+template<typename Iterator>
+std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it) noexcept
+{
+    return std::reverse_iterator<Iterator>(it);
+}
+} // namespace util
+
+
+namespace _impl {
+template<typename T>
+template<typename OtherIterator>
+inline bool ChunkedRangeVectorIterator<T>::operator==(OtherIterator const& it) const noexcept
+{
+    return m_outer == it.outer() && m_inner == it.operator->();
+}
+
+template<typename T>
+template<typename OtherIterator>
+inline bool ChunkedRangeVectorIterator<T>::operator!=(OtherIterator const& it) const noexcept
+{
+    return !(*this == it);
+}
+
+template<typename T>
+inline ChunkedRangeVectorIterator<T>& ChunkedRangeVectorIterator<T>::operator++() noexcept
+{
+    ++m_inner;
+    if (offset() == m_outer->data.size())
+        next_chunk();
+    return *this;
+}
+
+template<typename T>
+inline ChunkedRangeVectorIterator<T> ChunkedRangeVectorIterator<T>::operator++(int) noexcept
+{
+    auto value = *this;
+    ++*this;
+    return value;
+}
+
+template<typename T>
+inline ChunkedRangeVectorIterator<T>& ChunkedRangeVectorIterator<T>::operator--() noexcept
+{
+    if (!m_inner || m_inner == &m_outer->data.front()) {
+        --m_outer;
+        m_inner = &m_outer->data.back();
+    }
+    else {
+        --m_inner;
+    }
+    return *this;
+}
+
+template<typename T>
+inline ChunkedRangeVectorIterator<T> ChunkedRangeVectorIterator<T>::operator--(int) noexcept
+{
+    auto value = *this;
+    --*this;
+    return value;
+}
+
+template<typename T>
+inline void ChunkedRangeVectorIterator<T>::next_chunk() noexcept
+{
+    ++m_outer;
+    m_inner = m_outer != m_end ? &m_outer->data[0] : nullptr;
+}
+} // namespace _impl
+
+} // namespace realm
+
+#endif // REALM_INDEX_SET_HPP
diff --git a/iOS/Pods/Realm/include/list.hpp b/iOS/Pods/Realm/include/list.hpp
new file mode 100644 (file)
index 0000000..45d752d
--- /dev/null
@@ -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.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef REALM_OS_LIST_HPP
+#define REALM_OS_LIST_HPP
+
+#include "collection_notifications.hpp"
+#include "impl/collection_notifier.hpp"
+#include "property.hpp"
+
+#include <realm/link_view_fwd.hpp>
+#include <realm/row.hpp>
+#include <realm/table_ref.hpp>
+
+#include <functional>
+#include <memory>
+
+namespace realm {
+class ObjectSchema;
+class Query;
+class Realm;
+class Results;
+class SortDescriptor;
+template <typename T> class ThreadSafeReference;
+
+namespace _impl {
+class ListNotifier;
+}
+
+class List {
+public:
+    List() noexcept;
+    List(std::shared_ptr<Realm> r, Table& parent_table, size_t col, size_t row);
+    List(std::shared_ptr<Realm> r, LinkViewRef l) noexcept;
+    List(std::shared_ptr<Realm> r, TableRef l) noexcept;
+    ~List();
+
+    List(const List&);
+    List& operator=(const List&);
+    List(List&&);
+    List& operator=(List&&);
+
+    const std::shared_ptr<Realm>& get_realm() const { return m_realm; }
+    Query get_query() const;
+    size_t get_origin_row_index() const;
+
+    // Get the type of the values contained in this List
+    PropertyType get_type() const;
+
+    // Get the ObjectSchema of the values in this List
+    // Only valid if get_type() returns PropertyType::Object
+    ObjectSchema const& get_object_schema() const;
+
+    bool is_valid() const;
+    void verify_attached() const;
+    void verify_in_transaction() const;
+
+    size_t size() const;
+
+    void move(size_t source_ndx, size_t dest_ndx);
+    void remove(size_t list_ndx);
+    void remove_all();
+    void swap(size_t ndx1, size_t ndx2);
+    void delete_at(size_t list_ndx);
+    void delete_all();
+
+    template<typename T = RowExpr>
+    T get(size_t row_ndx) const;
+    template<typename T>
+    size_t find(T const& value) const;
+
+    // Find the index in the List of the first row matching the query
+    size_t find(Query&& query) const;
+
+    template<typename T>
+    void add(T value);
+    template<typename T>
+    void insert(size_t list_ndx, T value);
+    template<typename T>
+    void set(size_t row_ndx, T value);
+
+    Results sort(SortDescriptor order) const;
+    Results sort(std::vector<std::pair<std::string, bool>> const& keypaths) const;
+    Results filter(Query q) const;
+
+    // Return a Results representing a live view of this List.
+    Results as_results() const;
+
+    // Return a Results representing a snapshot of this List.
+    Results snapshot() const;
+
+    // Get the min/max/average/sum of the given column
+    // All but sum() returns none when there are zero matching rows
+    // sum() returns 0,
+    // Throws UnsupportedColumnTypeException for sum/average on timestamp or non-numeric column
+    // Throws OutOfBoundsIndexException for an out-of-bounds column
+    util::Optional<Mixed> max(size_t column=0);
+    util::Optional<Mixed> min(size_t column=0);
+    util::Optional<double> average(size_t column=0);
+    Mixed sum(size_t column=0);
+
+    bool operator==(List const& rgt) const noexcept;
+
+    NotificationToken add_notification_callback(CollectionChangeCallback cb) &;
+
+    template<typename Context>
+    auto get(Context&, size_t row_ndx) const;
+    template<typename T, typename Context>
+    size_t find(Context&, T&& value) const;
+
+    template<typename T, typename Context>
+    void add(Context&, T&& value, bool update=false);
+    template<typename T, typename Context>
+    void insert(Context&, size_t list_ndx, T&& value, bool update=false);
+    template<typename T, typename Context>
+    void set(Context&, size_t row_ndx, T&& value, bool update=false);
+
+    // Replace the values in this list with the values from an enumerable object
+    template<typename T, typename Context>
+    void assign(Context&, T&& value, bool update=false);
+
+    // The List object has been invalidated (due to the Realm being invalidated,
+    // or the containing object being deleted)
+    // All non-noexcept functions can throw this
+    struct InvalidatedException : public std::logic_error {
+        InvalidatedException() : std::logic_error("Access to invalidated List object") {}
+    };
+
+    // The input index parameter was out of bounds
+    struct OutOfBoundsIndexException : public std::out_of_range {
+        OutOfBoundsIndexException(size_t r, size_t c);
+        size_t requested;
+        size_t valid_count;
+    };
+
+private:
+    friend ThreadSafeReference<List>;
+
+    std::shared_ptr<Realm> m_realm;
+    mutable const ObjectSchema* m_object_schema = nullptr;
+    LinkViewRef m_link_view;
+    TableRef m_table;
+    _impl::CollectionNotifier::Handle<_impl::CollectionNotifier> m_notifier;
+
+    void verify_valid_row(size_t row_ndx, bool insertion = false) const;
+    void validate(RowExpr) const;
+
+    template<typename Fn>
+    auto dispatch(Fn&&) const;
+
+    size_t to_table_ndx(size_t row) const noexcept;
+
+    friend struct std::hash<List>;
+};
+
+template<typename Fn>
+auto List::dispatch(Fn&& fn) const
+{
+    return switch_on_type(get_type(), std::forward<Fn>(fn));
+}
+
+template<typename Context>
+auto List::get(Context& ctx, size_t row_ndx) const
+{
+    return dispatch([&](auto t) { return ctx.box(this->get<std::decay_t<decltype(*t)>>(row_ndx)); });
+}
+
+template<typename T, typename Context>
+size_t List::find(Context& ctx, T&& value) const
+{
+    return dispatch([&](auto t) { return this->find(ctx.template unbox<std::decay_t<decltype(*t)>>(value)); });
+}
+
+template<typename T, typename Context>
+void List::add(Context& ctx, T&& value, bool update)
+{
+    dispatch([&](auto t) { this->add(ctx.template unbox<std::decay_t<decltype(*t)>>(value, true, update)); });
+}
+
+template<typename T, typename Context>
+void List::insert(Context& ctx, size_t list_ndx, T&& value, bool update)
+{
+    dispatch([&](auto t) { this->insert(list_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, true, update)); });
+}
+
+template<typename T, typename Context>
+void List::set(Context& ctx, size_t row_ndx, T&& value, bool update)
+{
+    dispatch([&](auto t) { this->set(row_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, true, update)); });
+}
+
+template<typename T, typename Context>
+void List::assign(Context& ctx, T&& values, bool update)
+{
+    if (ctx.is_same_list(*this, values))
+        return;
+
+    remove_all();
+    if (ctx.is_null(values))
+        return;
+
+    ctx.enumerate_list(values, [&](auto&& element) {
+        this->add(ctx, element, update);
+    });
+}
+} // namespace realm
+
+namespace std {
+template<> struct hash<realm::List> {
+    size_t operator()(realm::List const&) const;
+};
+}
+
+#endif // REALM_OS_LIST_HPP
diff --git a/iOS/Pods/Realm/include/object.hpp b/iOS/Pods/Realm/include/object.hpp
new file mode 100644 (file)
index 0000000..bd23319
--- /dev/null
@@ -0,0 +1,143 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_OBJECT_HPP
+#define REALM_OS_OBJECT_HPP
+
+#include "impl/collection_notifier.hpp"
+
+#include <realm/row.hpp>
+
+namespace realm {
+class ObjectSchema;
+struct Property;
+using RowExpr = BasicRowExpr<Table>;
+
+namespace _impl {
+    class ObjectNotifier;
+}
+
+class Object {
+public:
+    Object();
+    Object(std::shared_ptr<Realm> r, ObjectSchema const& s, RowExpr const& o);
+    Object(std::shared_ptr<Realm> r, StringData object_type, size_t ndx);
+
+    Object(Object const&);
+    Object(Object&&);
+    Object& operator=(Object const&);
+    Object& operator=(Object&&);
+
+    ~Object();
+
+    std::shared_ptr<Realm> const& realm() const { return m_realm; }
+    ObjectSchema const& get_object_schema() const { return *m_object_schema; }
+    RowExpr row() const { return m_row; }
+
+    bool is_valid() const { return m_row.is_attached(); }
+
+    NotificationToken add_notification_callback(CollectionChangeCallback callback) &;
+
+    // The following functions require an accessor context which converts from
+    // the binding's native data types to the core data types. See CppContext
+    // for a reference implementation of such a context.
+    //
+    // The actual definitions of these tempated functions is in object_accessor.hpp
+
+    // property getter/setter
+    template<typename ValueType, typename ContextType>
+    void set_property_value(ContextType& ctx, StringData prop_name,
+                            ValueType value, bool try_update);
+
+    template<typename ValueType, typename ContextType>
+    ValueType get_property_value(ContextType& ctx, StringData prop_name);
+
+    // create an Object from a native representation
+    template<typename ValueType, typename ContextType>
+    static Object create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
+                         const ObjectSchema &object_schema, ValueType value,
+                         bool try_update = false, Row* = nullptr);
+
+    template<typename ValueType, typename ContextType>
+    static Object create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
+                         StringData object_type, ValueType value,
+                         bool try_update = false, Row* = nullptr);
+
+    template<typename ValueType, typename ContextType>
+    static Object get_for_primary_key(ContextType& ctx,
+                                      std::shared_ptr<Realm> const& realm,
+                                      const ObjectSchema &object_schema,
+                                      ValueType primary_value);
+
+    template<typename ValueType, typename ContextType>
+    static Object get_for_primary_key(ContextType& ctx,
+                                      std::shared_ptr<Realm> const& realm,
+                                      StringData object_type,
+                                      ValueType primary_value);
+
+private:
+    std::shared_ptr<Realm> m_realm;
+    const ObjectSchema *m_object_schema;
+    Row m_row;
+    _impl::CollectionNotifier::Handle<_impl::ObjectNotifier> m_notifier;
+
+
+    template<typename ValueType, typename ContextType>
+    void set_property_value_impl(ContextType& ctx, const Property &property,
+                                 ValueType value, bool try_update, bool is_default=false);
+    template<typename ValueType, typename ContextType>
+    ValueType get_property_value_impl(ContextType& ctx, const Property &property);
+
+    template<typename ValueType, typename ContextType>
+    static size_t get_for_primary_key_impl(ContextType& ctx, Table const& table,
+                                           const Property &primary_prop, ValueType primary_value);
+
+    void verify_attached() const;
+    Property const& property_for_name(StringData prop_name) const;
+};
+
+struct InvalidatedObjectException : public std::logic_error {
+    InvalidatedObjectException(const std::string& object_type);
+    const std::string object_type;
+};
+
+struct InvalidPropertyException : public std::logic_error {
+    InvalidPropertyException(const std::string& object_type, const std::string& property_name);
+    const std::string object_type;
+    const std::string property_name;
+};
+
+struct MissingPropertyValueException : public std::logic_error {
+    MissingPropertyValueException(const std::string& object_type, const std::string& property_name);
+    const std::string object_type;
+    const std::string property_name;
+};
+
+struct MissingPrimaryKeyException : public std::logic_error {
+    MissingPrimaryKeyException(const std::string& object_type);
+    const std::string object_type;
+};
+
+struct ReadOnlyPropertyException : public std::logic_error {
+    ReadOnlyPropertyException(const std::string& object_type, const std::string& property_name);
+    const std::string object_type;
+    const std::string property_name;
+};
+} // namespace realm
+
+#endif // REALM_OS_OBJECT_HPP
diff --git a/iOS/Pods/Realm/include/object_accessor.hpp b/iOS/Pods/Realm/include/object_accessor.hpp
new file mode 100644 (file)
index 0000000..fa39668
--- /dev/null
@@ -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.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef REALM_OS_OBJECT_ACCESSOR_HPP
+#define REALM_OS_OBJECT_ACCESSOR_HPP
+
+#include "object.hpp"
+
+#include "feature_checks.hpp"
+#include "list.hpp"
+#include "object_schema.hpp"
+#include "object_store.hpp"
+#include "results.hpp"
+#include "schema.hpp"
+#include "shared_realm.hpp"
+
+#include <realm/link_view.hpp>
+#include <realm/util/assert.hpp>
+#include <realm/table_view.hpp>
+
+#if REALM_ENABLE_SYNC
+#include <realm/sync/object.hpp>
+#endif // REALM_ENABLE_SYNC
+
+#include <string>
+
+namespace realm {
+template <typename ValueType, typename ContextType>
+void Object::set_property_value(ContextType& ctx, StringData prop_name, ValueType value, bool try_update)
+{
+    verify_attached();
+    m_realm->verify_in_write();
+    auto& property = property_for_name(prop_name);
+
+    // Modifying primary keys is allowed in migrations to make it possible to
+    // add a new primary key to a type (or change the property type), but it
+    // is otherwise considered the immutable identity of the row
+    if (property.is_primary && !m_realm->is_in_migration())
+        throw std::logic_error("Cannot modify primary key after creation");
+
+    set_property_value_impl(ctx, property, value, try_update);
+}
+
+template <typename ValueType, typename ContextType>
+ValueType Object::get_property_value(ContextType& ctx, StringData prop_name)
+{
+    return get_property_value_impl<ValueType>(ctx, property_for_name(prop_name));
+}
+
+template <typename ValueType, typename ContextType>
+void Object::set_property_value_impl(ContextType& ctx, const Property &property,
+                                     ValueType value, bool try_update, bool is_default)
+{
+    ctx.will_change(*this, property);
+
+    auto& table = *m_row.get_table();
+    size_t col = property.table_column;
+    size_t row = m_row.get_index();
+    if (is_nullable(property.type) && ctx.is_null(value)) {
+        if (property.type == PropertyType::Object) {
+            if (!is_default)
+                table.nullify_link(col, row);
+        }
+        else {
+            table.set_null(col, row, is_default);
+        }
+
+        ctx.did_change();
+        return;
+    }
+
+    if (is_array(property.type)) {
+        if (property.type == PropertyType::LinkingObjects)
+            throw ReadOnlyPropertyException(m_object_schema->name, property.name);
+
+        ContextType child_ctx(ctx, property);
+        List list(m_realm, *m_row.get_table(), col, m_row.get_index());
+        list.assign(child_ctx, value, try_update);
+        ctx.did_change();
+        return;
+    }
+
+    switch (property.type & ~PropertyType::Nullable) {
+        case PropertyType::Object: {
+            ContextType child_ctx(ctx, property);
+            auto link = child_ctx.template unbox<RowExpr>(value, true, try_update);
+            table.set_link(col, row, link.get_index(), is_default);
+            break;
+        }
+        case PropertyType::Bool:
+            table.set(col, row, ctx.template unbox<bool>(value), is_default);
+            break;
+        case PropertyType::Int:
+            table.set(col, row, ctx.template unbox<int64_t>(value), is_default);
+            break;
+        case PropertyType::Float:
+            table.set(col, row, ctx.template unbox<float>(value), is_default);
+            break;
+        case PropertyType::Double:
+            table.set(col, row, ctx.template unbox<double>(value), is_default);
+            break;
+        case PropertyType::String:
+            table.set(col, row, ctx.template unbox<StringData>(value), is_default);
+            break;
+        case PropertyType::Data:
+            table.set(col, row, ctx.template unbox<BinaryData>(value), is_default);
+            break;
+        case PropertyType::Any:
+            throw std::logic_error("not supported");
+        case PropertyType::Date:
+            table.set(col, row, ctx.template unbox<Timestamp>(value), is_default);
+            break;
+        default:
+            REALM_COMPILER_HINT_UNREACHABLE();
+    }
+    ctx.did_change();
+}
+
+template <typename ValueType, typename ContextType>
+ValueType Object::get_property_value_impl(ContextType& ctx, const Property &property)
+{
+    verify_attached();
+
+    size_t column = property.table_column;
+    if (is_nullable(property.type) && m_row.is_null(column))
+        return ctx.null_value();
+    if (is_array(property.type) && property.type != PropertyType::LinkingObjects)
+        return ctx.box(List(m_realm, *m_row.get_table(), column, m_row.get_index()));
+
+    switch (property.type & ~PropertyType::Flags) {
+        case PropertyType::Bool:   return ctx.box(m_row.get_bool(column));
+        case PropertyType::Int:    return ctx.box(m_row.get_int(column));
+        case PropertyType::Float:  return ctx.box(m_row.get_float(column));
+        case PropertyType::Double: return ctx.box(m_row.get_double(column));
+        case PropertyType::String: return ctx.box(m_row.get_string(column));
+        case PropertyType::Data:   return ctx.box(m_row.get_binary(column));
+        case PropertyType::Date:   return ctx.box(m_row.get_timestamp(column));
+        case PropertyType::Any:    return ctx.box(m_row.get_mixed(column));
+        case PropertyType::Object: {
+            auto linkObjectSchema = m_realm->schema().find(property.object_type);
+            TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), property.object_type);
+            return ctx.box(Object(m_realm, *linkObjectSchema, table->get(m_row.get_link(column))));
+        }
+        case PropertyType::LinkingObjects: {
+            auto target_object_schema = m_realm->schema().find(property.object_type);
+            auto link_property = target_object_schema->property_for_name(property.link_origin_property_name);
+            TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), target_object_schema->name);
+            auto tv = m_row.get_table()->get_backlink_view(m_row.get_index(), table.get(), link_property->table_column);
+            return ctx.box(Results(m_realm, std::move(tv)));
+        }
+        default: REALM_UNREACHABLE();
+    }
+}
+
+template<typename ValueType, typename ContextType>
+Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
+                      StringData object_type, ValueType value,
+                      bool try_update, Row* out_row)
+{
+    auto object_schema = realm->schema().find(object_type);
+    REALM_ASSERT(object_schema != realm->schema().end());
+    return create(ctx, realm, *object_schema, value, try_update, out_row);
+}
+
+template<typename ValueType, typename ContextType>
+Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
+                      ObjectSchema const& object_schema, ValueType value,
+                      bool try_update, Row* out_row)
+{
+    realm->verify_in_write();
+
+    // get or create our accessor
+    bool created = false;
+
+    // try to get existing row if updating
+    size_t row_index = realm::not_found;
+    TableRef table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
+
+    bool skip_primary = true;
+    if (auto primary_prop = object_schema.primary_key_property()) {
+        // search for existing object based on primary key type
+        auto primary_value = ctx.value_for_property(value, primary_prop->name,
+                                                    primary_prop - &object_schema.persisted_properties[0]);
+        if (!primary_value)
+            primary_value = ctx.default_value_for_property(object_schema, primary_prop->name);
+        if (!primary_value) {
+            if (!is_nullable(primary_prop->type))
+                throw MissingPropertyValueException(object_schema.name, primary_prop->name);
+            primary_value = ctx.null_value();
+        }
+        row_index = get_for_primary_key_impl(ctx, *table, *primary_prop, *primary_value);
+
+        if (row_index == realm::not_found) {
+            created = true;
+            if (primary_prop->type == PropertyType::Int) {
+#if REALM_ENABLE_SYNC
+                row_index = sync::create_object_with_primary_key(realm->read_group(), *table, ctx.template unbox<util::Optional<int64_t>>(*primary_value));
+#else
+                row_index = table->add_empty_row();
+                if (ctx.is_null(*primary_value))
+                    table->set_null_unique(primary_prop->table_column, row_index);
+                else
+                    table->set_unique(primary_prop->table_column, row_index, ctx.template unbox<int64_t>(*primary_value));
+#endif // REALM_ENABLE_SYNC
+            }
+            else if (primary_prop->type == PropertyType::String) {
+                auto value = ctx.template unbox<StringData>(*primary_value);
+#if REALM_ENABLE_SYNC
+                row_index = sync::create_object_with_primary_key(realm->read_group(), *table, value);
+#else
+                row_index = table->add_empty_row();
+                table->set_unique(primary_prop->table_column, row_index, value);
+#endif // REALM_ENABLE_SYNC
+            }
+            else {
+                REALM_TERMINATE("Unsupported primary key type.");
+            }
+        }
+        else if (!try_update) {
+            if (realm->is_in_migration()) {
+                // Creating objects with duplicate primary keys is allowed in migrations
+                // as long as there are no duplicates at the end, as adding an entirely
+                // new column which is the PK will inherently result in duplicates at first
+                row_index = table->add_empty_row();
+                created = true;
+                skip_primary = false;
+            }
+            else {
+                throw std::logic_error(util::format("Attempting to create an object of type '%1' with an existing primary key value '%2'.",
+                                                    object_schema.name, ctx.print(*primary_value)));
+            }
+        }
+    }
+    else {
+#if REALM_ENABLE_SYNC
+        row_index = sync::create_object(realm->read_group(), *table);
+#else
+        row_index = table->add_empty_row();
+#endif // REALM_ENABLE_SYNC
+        created = true;
+    }
+
+    // populate
+    Object object(realm, object_schema, table->get(row_index));
+    if (out_row)
+        *out_row = object.row();
+    for (size_t i = 0; i < object_schema.persisted_properties.size(); ++i) {
+        auto& prop = object_schema.persisted_properties[i];
+        if (skip_primary && prop.is_primary)
+            continue;
+
+        auto v = ctx.value_for_property(value, prop.name, i);
+        if (!created && !v)
+            continue;
+
+        bool is_default = false;
+        if (!v) {
+            v = ctx.default_value_for_property(object_schema, prop.name);
+            is_default = true;
+        }
+        if ((!v || ctx.is_null(*v)) && !is_nullable(prop.type) && !is_array(prop.type)) {
+            if (prop.is_primary || !ctx.allow_missing(value))
+                throw MissingPropertyValueException(object_schema.name, prop.name);
+        }
+        if (v)
+            object.set_property_value_impl(ctx, prop, *v, try_update, is_default);
+    }
+    return object;
+}
+
+template<typename ValueType, typename ContextType>
+Object Object::get_for_primary_key(ContextType& ctx, std::shared_ptr<Realm> const& realm,
+                      StringData object_type, ValueType primary_value)
+{
+    auto object_schema = realm->schema().find(object_type);
+    REALM_ASSERT(object_schema != realm->schema().end());
+    return get_for_primary_key(ctx, realm, *object_schema, primary_value);
+}
+
+template<typename ValueType, typename ContextType>
+Object Object::get_for_primary_key(ContextType& ctx, std::shared_ptr<Realm> const& realm,
+                                   const ObjectSchema &object_schema,
+                                   ValueType primary_value)
+{
+    auto primary_prop = object_schema.primary_key_property();
+    if (!primary_prop) {
+        throw MissingPrimaryKeyException(object_schema.name);
+    }
+
+    auto table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
+    if (!table)
+        return Object(realm, object_schema, RowExpr());
+    auto row_index = get_for_primary_key_impl(ctx, *table, *primary_prop, primary_value);
+
+    return Object(realm, object_schema, row_index == realm::not_found ? Row() : Row(table->get(row_index)));
+}
+
+template<typename ValueType, typename ContextType>
+size_t Object::get_for_primary_key_impl(ContextType& ctx, Table const& table,
+                                        const Property &primary_prop,
+                                        ValueType primary_value) {
+    bool is_null = ctx.is_null(primary_value);
+    if (is_null && !is_nullable(primary_prop.type))
+        throw std::logic_error("Invalid null value for non-nullable primary key.");
+    if (primary_prop.type == PropertyType::String) {
+        return table.find_first(primary_prop.table_column,
+                                ctx.template unbox<StringData>(primary_value));
+    }
+    if (is_nullable(primary_prop.type))
+        return table.find_first(primary_prop.table_column,
+                                ctx.template unbox<util::Optional<int64_t>>(primary_value));
+    return table.find_first(primary_prop.table_column,
+                            ctx.template unbox<int64_t>(primary_value));
+}
+
+} // namespace realm
+
+#endif // REALM_OS_OBJECT_ACCESSOR_HPP
diff --git a/iOS/Pods/Realm/include/object_schema.hpp b/iOS/Pods/Realm/include/object_schema.hpp
new file mode 100644 (file)
index 0000000..66cb118
--- /dev/null
@@ -0,0 +1,73 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_OBJECT_SCHEMA_HPP
+#define REALM_OBJECT_SCHEMA_HPP
+
+#include <realm/string_data.hpp>
+
+#include <string>
+#include <vector>
+
+namespace realm {
+class Descriptor;
+class Group;
+class Schema;
+enum class PropertyType: unsigned char;
+struct ObjectSchemaValidationException;
+struct Property;
+
+class ObjectSchema {
+public:
+    ObjectSchema();
+    ObjectSchema(std::string name, std::initializer_list<Property> persisted_properties);
+    ObjectSchema(std::string name, std::initializer_list<Property> persisted_properties,
+                 std::initializer_list<Property> computed_properties);
+    ~ObjectSchema();
+
+    // create object schema from existing table
+    // if no table is provided it is looked up in the group
+    ObjectSchema(Group const& group, StringData name, size_t index=-1);
+
+    std::string name;
+    std::vector<Property> persisted_properties;
+    std::vector<Property> computed_properties;
+    std::string primary_key;
+
+    Property *property_for_name(StringData name);
+    const Property *property_for_name(StringData name) const;
+    Property *primary_key_property() {
+        return property_for_name(primary_key);
+    }
+    const Property *primary_key_property() const {
+        return property_for_name(primary_key);
+    }
+    bool property_is_computed(Property const& property) const;
+
+    void validate(Schema const& schema, std::vector<ObjectSchemaValidationException>& exceptions) const;
+
+    friend bool operator==(ObjectSchema const& a, ObjectSchema const& b);
+
+    static PropertyType from_core_type(Descriptor const& table, size_t col);
+
+private:
+    void set_primary_key_property();
+};
+}
+
+#endif /* defined(REALM_OBJECT_SCHEMA_HPP) */
diff --git a/iOS/Pods/Realm/include/object_store.hpp b/iOS/Pods/Realm/include/object_store.hpp
new file mode 100644 (file)
index 0000000..699b2c8
--- /dev/null
@@ -0,0 +1,170 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_OBJECT_STORE_HPP
+#define REALM_OBJECT_STORE_HPP
+
+#include "property.hpp"
+
+#include <realm/table_ref.hpp>
+#include <realm/util/optional.hpp>
+
+#include <functional>
+#include <string>
+#include <vector>
+namespace realm {
+class Group;
+class Schema;
+class SchemaChange;
+class StringData;
+enum class SchemaMode : uint8_t;
+
+namespace util {
+template<typename... Args>
+std::string format(const char* fmt, Args&&... args);
+}
+
+class ObjectStore {
+public:
+    // Schema version used for uninitialized Realms
+    static constexpr uint64_t NotVersioned = std::numeric_limits<uint64_t>::max();
+
+    // Column name used for subtables which store an array
+    static constexpr const char* const ArrayColumnName = "!ARRAY_VALUE";
+
+    // get the last set schema version
+    static uint64_t get_schema_version(Group const& group);
+
+    // set the schema version without any checks
+    // and the tables for the schema version and the primary key are created if they don't exist
+    // NOTE: must be performed within a write transaction
+    static void set_schema_version(Group& group, uint64_t version);
+
+    // check if all of the changes in the list can be applied automatically, or
+    // throw if any of them require a schema version bump and migration function
+    static void verify_no_migration_required(std::vector<SchemaChange> const& changes);
+
+    // Similar to above, but returns a bool rather than throwing/not throwing
+    static bool needs_migration(std::vector<SchemaChange> const& changes);
+
+    // check if any of the schema changes in the list are forbidden in
+    // additive-only mode, and if any are throw an exception
+    // returns true if any of the changes are not no-ops
+    static bool verify_valid_additive_changes(std::vector<SchemaChange> const& changes,
+                                              bool update_indexes=false);
+
+    // check if the schema changes made by a different process made any changes
+    // which will prevent us from being able to continue (such as removing a
+    // property we were relying on)
+    static void verify_valid_external_changes(std::vector<SchemaChange> const& changes);
+
+    static void verify_compatible_for_immutable_and_readonly(std::vector<SchemaChange> const& changes);
+
+    // check if changes is empty, and throw an exception if not
+    static void verify_no_changes_required(std::vector<SchemaChange> const& changes);
+
+    // updates a Realm from old_schema to the given target schema, creating and updating tables as needed
+    // passed in target schema is updated with the correct column mapping
+    // optionally runs migration function if schema is out of date
+    // NOTE: must be performed within a write transaction
+    static void apply_schema_changes(Group& group, uint64_t schema_version,
+                                     Schema& target_schema, uint64_t target_schema_version,
+                                     SchemaMode mode, std::vector<SchemaChange> const& changes,
+                                     std::function<void()> migration_function={});
+
+    static void apply_additive_changes(Group&, std::vector<SchemaChange> const&, bool update_indexes);
+
+    // get a table for an object type
+    static realm::TableRef table_for_object_type(Group& group, StringData object_type);
+    static realm::ConstTableRef table_for_object_type(Group const& group, StringData object_type);
+
+    // get existing Schema from a group
+    static Schema schema_from_group(Group const& group);
+
+    // get the property for a existing column in the given table. return none if the column is reserved internally.
+    // NOTE: is_primary won't be set for the returned property.
+    static util::Optional<Property> property_for_column_index(ConstTableRef& table, size_t column_index);
+
+    static void set_schema_columns(Group const& group, Schema& schema);
+
+    // deletes the table for the given type
+    static void delete_data_for_object(Group& group, StringData object_type);
+
+    // indicates if this group contains any objects
+    static bool is_empty(Group const& group);
+
+    // renames the object_type's column of the old_name to the new name
+    static void rename_property(Group& group, Schema& schema, StringData object_type, StringData old_name, StringData new_name);
+
+    // get primary key property name for object type
+    static StringData get_primary_key_for_object(Group const& group, StringData object_type);
+
+    // sets primary key property for object type
+    // must be in write transaction to set
+    static void set_primary_key_for_object(Group& group, StringData object_type, StringData primary_key);
+
+    static std::string table_name_for_object_type(StringData class_name);
+    static StringData object_type_for_table_name(StringData table_name);
+
+private:
+    friend class ObjectSchema;
+};
+
+class InvalidSchemaVersionException : public std::logic_error {
+public:
+    InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version);
+    uint64_t old_version() const { return m_old_version; }
+    uint64_t new_version() const { return m_new_version; }
+private:
+    uint64_t m_old_version, m_new_version;
+};
+
+class DuplicatePrimaryKeyValueException : public std::logic_error {
+public:
+    DuplicatePrimaryKeyValueException(std::string object_type, std::string property);
+
+    std::string const& object_type() const { return m_object_type; }
+    std::string const& property() const { return m_property; }
+private:
+    std::string m_object_type;
+    std::string m_property;
+};
+
+// Schema validation exceptions
+struct ObjectSchemaValidationException : public std::logic_error {
+    ObjectSchemaValidationException(std::string message) : logic_error(std::move(message)) {}
+
+    template<typename... Args>
+    ObjectSchemaValidationException(const char* fmt, Args&&... args)
+    : std::logic_error(util::format(fmt, std::forward<Args>(args)...)) { }
+};
+
+struct SchemaValidationException : public std::logic_error {
+    SchemaValidationException(std::vector<ObjectSchemaValidationException> const& errors);
+};
+
+struct SchemaMismatchException : public std::logic_error {
+    SchemaMismatchException(std::vector<ObjectSchemaValidationException> const& errors);
+};
+
+struct InvalidSchemaChangeException : public std::logic_error {
+    InvalidSchemaChangeException(std::vector<ObjectSchemaValidationException> const& errors);
+};
+} // namespace realm
+
+#endif /* defined(REALM_OBJECT_STORE_HPP) */
diff --git a/iOS/Pods/Realm/include/property.hpp b/iOS/Pods/Realm/include/property.hpp
new file mode 100644 (file)
index 0000000..ad76b9d
--- /dev/null
@@ -0,0 +1,262 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_PROPERTY_HPP
+#define REALM_PROPERTY_HPP
+
+#include "util/tagged_bool.hpp"
+
+#include <realm/util/features.h>
+
+#include <string>
+
+namespace realm {
+namespace util {
+    template<typename> class Optional;
+}
+class StringData;
+class BinaryData;
+class Timestamp;
+class Table;
+
+template<typename> class BasicRowExpr;
+using RowExpr = BasicRowExpr<Table>;
+
+enum class PropertyType : unsigned char {
+    Int    = 0,
+    Bool   = 1,
+    String = 2,
+    Data   = 3,
+    Date   = 4,
+    Float  = 5,
+    Double = 6,
+    Object = 7, // currently must be either Array xor Nullable
+    LinkingObjects = 8, // currently must be Array and not Nullable
+
+    // deprecated and remains only for reading old files
+    Any    = 9,
+
+    // Flags which can be combined with any of the above types except as noted
+    Required  = 0,
+    Nullable  = 64,
+    Array     = 128,
+    Flags     = Nullable | Array
+};
+
+struct Property {
+    using IsPrimary = util::TaggedBool<class IsPrimaryTag>;
+    using IsIndexed = util::TaggedBool<class IsIndexedTag>;
+
+    std::string name;
+    PropertyType type = PropertyType::Int;
+    std::string object_type;
+    std::string link_origin_property_name;
+    IsPrimary is_primary = false;
+    IsIndexed is_indexed = false;
+
+    size_t table_column = -1;
+
+    Property() = default;
+
+    Property(std::string name, PropertyType type, IsPrimary primary = false, IsIndexed indexed = false);
+
+    Property(std::string name, PropertyType type, std::string object_type,
+             std::string link_origin_property_name = "");
+
+    Property(Property const&) = default;
+    Property(Property&&) = default;
+    Property& operator=(Property const&) = default;
+    Property& operator=(Property&&) = default;
+
+    bool requires_index() const { return is_primary || is_indexed; }
+
+    bool type_is_indexable() const;
+    bool type_is_nullable() const;
+
+    std::string type_string() const;
+};
+
+template<typename E>
+constexpr auto to_underlying(E e)
+{
+    return static_cast<typename std::underlying_type<E>::type>(e);
+}
+
+inline constexpr PropertyType operator&(PropertyType a, PropertyType b)
+{
+    return static_cast<PropertyType>(to_underlying(a) & to_underlying(b));
+}
+
+inline constexpr PropertyType operator|(PropertyType a, PropertyType b)
+{
+    return static_cast<PropertyType>(to_underlying(a) | to_underlying(b));
+}
+
+inline constexpr PropertyType operator^(PropertyType a, PropertyType b)
+{
+    return static_cast<PropertyType>(to_underlying(a) ^ to_underlying(b));
+}
+
+inline constexpr PropertyType operator~(PropertyType a)
+{
+    return static_cast<PropertyType>(~to_underlying(a));
+}
+
+inline constexpr bool operator==(PropertyType a, PropertyType b)
+{
+    return to_underlying(a & ~PropertyType::Flags) == to_underlying(b & ~PropertyType::Flags);
+}
+
+inline constexpr bool operator!=(PropertyType a, PropertyType b)
+{
+    return !(a == b);
+}
+
+inline PropertyType& operator&=(PropertyType & a, PropertyType b)
+{
+    a = a & b;
+    return a;
+}
+
+inline PropertyType& operator|=(PropertyType & a, PropertyType b)
+{
+    a = a | b;
+    return a;
+}
+
+inline PropertyType& operator^=(PropertyType & a, PropertyType b)
+{
+    a = a ^ b;
+    return a;
+}
+
+inline constexpr bool is_array(PropertyType a)
+{
+    return to_underlying(a & PropertyType::Array) == to_underlying(PropertyType::Array);
+}
+
+inline constexpr bool is_nullable(PropertyType a)
+{
+    return to_underlying(a & PropertyType::Nullable) == to_underlying(PropertyType::Nullable);
+}
+
+template<typename Fn>
+static auto switch_on_type(PropertyType type, Fn&& fn)
+{
+    using PT = PropertyType;
+    bool is_optional = is_nullable(type);
+    switch (type & ~PropertyType::Flags) {
+        case PT::Int:    return is_optional ? fn((util::Optional<int64_t>*)0) : fn((int64_t*)0);
+        case PT::Bool:   return is_optional ? fn((util::Optional<bool>*)0)    : fn((bool*)0);
+        case PT::Float:  return is_optional ? fn((util::Optional<float>*)0)   : fn((float*)0);
+        case PT::Double: return is_optional ? fn((util::Optional<double>*)0)  : fn((double*)0);
+        case PT::String: return fn((StringData*)0);
+        case PT::Data:   return fn((BinaryData*)0);
+        case PT::Date:   return fn((Timestamp*)0);
+        case PT::Object: return fn((RowExpr*)0);
+        default: REALM_COMPILER_HINT_UNREACHABLE();
+    }
+}
+
+static const char *string_for_property_type(PropertyType type)
+{
+    if (is_array(type)) {
+        if (type == PropertyType::LinkingObjects)
+            return "linking objects";
+        return "array";
+    }
+    switch (type & ~PropertyType::Flags) {
+        case PropertyType::String: return "string";
+        case PropertyType::Int: return "int";
+        case PropertyType::Bool: return "bool";
+        case PropertyType::Date: return "date";
+        case PropertyType::Data: return "data";
+        case PropertyType::Double: return "double";
+        case PropertyType::Float: return "float";
+        case PropertyType::Object: return "object";
+        case PropertyType::Any: return "any";
+        case PropertyType::LinkingObjects: return "linking objects";
+        default: REALM_COMPILER_HINT_UNREACHABLE();
+    }
+}
+
+inline Property::Property(std::string name, PropertyType type,
+                          IsPrimary primary, IsIndexed indexed)
+: name(std::move(name))
+, type(type)
+, is_primary(primary)
+, is_indexed(indexed)
+{
+}
+
+inline Property::Property(std::string name, PropertyType type,
+                          std::string object_type,
+                          std::string link_origin_property_name)
+: name(std::move(name))
+, type(type)
+, object_type(std::move(object_type))
+, link_origin_property_name(std::move(link_origin_property_name))
+{
+}
+
+inline bool Property::type_is_indexable() const
+{
+    return type == PropertyType::Int
+        || type == PropertyType::Bool
+        || type == PropertyType::Date
+        || type == PropertyType::String;
+}
+
+inline bool Property::type_is_nullable() const
+{
+    return !(is_array(type) && type == PropertyType::Object) && type != PropertyType::LinkingObjects;
+}
+
+inline std::string Property::type_string() const
+{
+    if (is_array(type)) {
+        if (type == PropertyType::Object)
+            return "array<" + object_type + ">";
+        if (type == PropertyType::LinkingObjects)
+            return "linking objects<" + object_type + ">";
+        return std::string("array<") + string_for_property_type(type & ~PropertyType::Flags) + ">";
+    }
+    switch (auto base_type = (type & ~PropertyType::Flags)) {
+        case PropertyType::Object:
+            return "<" + object_type + ">";
+        case PropertyType::LinkingObjects:
+            return "linking objects<" + object_type + ">";
+        default:
+            return string_for_property_type(base_type);
+    }
+}
+
+inline bool operator==(Property const& lft, Property const& rgt)
+{
+    // note: not checking table_column
+    // ordered roughly by the cost of the check
+    return to_underlying(lft.type) == to_underlying(rgt.type)
+        && lft.is_primary == rgt.is_primary
+        && lft.requires_index() == rgt.requires_index()
+        && lft.name == rgt.name
+        && lft.object_type == rgt.object_type
+        && lft.link_origin_property_name == rgt.link_origin_property_name;
+}
+} // namespace realm
+
+#endif // REALM_PROPERTY_HPP
diff --git a/iOS/Pods/Realm/include/results.hpp b/iOS/Pods/Realm/include/results.hpp
new file mode 100644 (file)
index 0000000..dff5bab
--- /dev/null
@@ -0,0 +1,306 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_RESULTS_HPP
+#define REALM_RESULTS_HPP
+
+#include "collection_notifications.hpp"
+#include "impl/collection_notifier.hpp"
+#include "property.hpp"
+
+#include <realm/table_view.hpp>
+#include <realm/util/optional.hpp>
+
+namespace realm {
+class Mixed;
+class ObjectSchema;
+
+namespace _impl {
+    class ResultsNotifier;
+}
+
+class Results {
+public:
+    // Results can be either be backed by nothing, a thin wrapper around a table,
+    // or a wrapper around a query and a sort order which creates and updates
+    // the tableview as needed
+    Results();
+    Results(std::shared_ptr<Realm> r, Table& table);
+    Results(std::shared_ptr<Realm> r, Query q, DescriptorOrdering o = {});
+    Results(std::shared_ptr<Realm> r, TableView tv, DescriptorOrdering o = {});
+    Results(std::shared_ptr<Realm> r, LinkViewRef lv, util::Optional<Query> q = {}, SortDescriptor s = {});
+    ~Results();
+
+    // Results is copyable and moveable
+    Results(Results&&);
+    Results& operator=(Results&&);
+    Results(const Results&);
+    Results& operator=(const Results&);
+
+    // Get the Realm
+    std::shared_ptr<Realm> get_realm() const { return m_realm; }
+
+    // Object schema describing the vendored object type
+    const ObjectSchema &get_object_schema() const;
+
+    // Get a query which will match the same rows as is contained in this Results
+    // Returned query will not be valid if the current mode is Empty
+    Query get_query() const;
+
+    // Get the list of sort and distinct operations applied for this Results.
+    DescriptorOrdering const& get_descriptor_ordering() const noexcept { return m_descriptor_ordering; }
+
+    // Get a tableview containing the same rows as this Results
+    TableView get_tableview();
+
+    // Get the object type which will be returned by get()
+    StringData get_object_type() const noexcept;
+
+    PropertyType get_type() const;
+
+    // Get the LinkView this Results is derived from, if any
+    LinkViewRef get_linkview() const { return m_link_view; }
+
+    // Get the size of this results
+    // Can be either O(1) or O(N) depending on the state of things
+    size_t size();
+
+    // Get the row accessor for the given index
+    // Throws OutOfBoundsIndexException if index >= size()
+    template<typename T = RowExpr>
+    T get(size_t index);
+
+    // Get the boxed row accessor for the given index
+    // Throws OutOfBoundsIndexException if index >= size()
+    template<typename Context>
+    auto get(Context&, size_t index);
+
+    // Get a row accessor for the first/last row, or none if the results are empty
+    // More efficient than calling size()+get()
+    template<typename T = RowExpr>
+    util::Optional<T> first();
+    template<typename T = RowExpr>
+    util::Optional<T> last();
+
+    // Get the index of the first row matching the query in this table
+    size_t index_of(Query&& q);
+
+    // Get the first index of the given value in this results, or not_found
+    // Throws DetachedAccessorException if row is not attached
+    // Throws IncorrectTableException if row belongs to a different table
+    template<typename T>
+    size_t index_of(T const& value);
+
+    // Delete all of the rows in this Results from the Realm
+    // size() will always be zero afterwards
+    // Throws InvalidTransactionException if not in a write transaction
+    void clear();
+
+    // Create a new Results by further filtering or sorting this Results
+    Results filter(Query&& q) const;
+    Results sort(SortDescriptor&& sort) const;
+    Results sort(std::vector<std::pair<std::string, bool>> const& keypaths) const;
+
+    // Create a new Results by removing duplicates
+    Results distinct(DistinctDescriptor&& uniqueness) const;
+    Results distinct(std::vector<std::string> const& keypaths) const;
+
+    // Return a snapshot of this Results that never updates to reflect changes in the underlying data.
+    Results snapshot() const &;
+    Results snapshot() &&;
+
+    // Get the min/max/average/sum of the given column
+    // All but sum() returns none when there are zero matching rows
+    // sum() returns 0, except for when it returns none
+    // Throws UnsupportedColumnTypeException for sum/average on timestamp or non-numeric column
+    // Throws OutOfBoundsIndexException for an out-of-bounds column
+    util::Optional<Mixed> max(size_t column=0);
+    util::Optional<Mixed> min(size_t column=0);
+    util::Optional<double> average(size_t column=0);
+    util::Optional<Mixed> sum(size_t column=0);
+
+    enum class Mode {
+        Empty, // Backed by nothing (for missing tables)
+        Table, // Backed directly by a Table
+        Query, // Backed by a query that has not yet been turned into a TableView
+        LinkView,  // Backed directly by a LinkView
+        TableView, // Backed by a TableView created from a Query
+    };
+    // Get the currrent mode of the Results
+    // Ideally this would not be public but it's needed for some KVO stuff
+    Mode get_mode() const { return m_mode; }
+
+    // Is this Results associated with a Realm that has not been invalidated?
+    bool is_valid() const;
+
+    // The Results object has been invalidated (due to the Realm being invalidated)
+    // All non-noexcept functions can throw this
+    struct InvalidatedException : public std::logic_error {
+        InvalidatedException() : std::logic_error("Access to invalidated Results objects") {}
+    };
+
+    // The input index parameter was out of bounds
+    struct OutOfBoundsIndexException : public std::out_of_range {
+        OutOfBoundsIndexException(size_t r, size_t c);
+        const size_t requested;
+        const size_t valid_count;
+    };
+
+    // The input Row object is not attached
+    struct DetatchedAccessorException : public std::logic_error {
+        DetatchedAccessorException() : std::logic_error("Atempting to access an invalid object") {}
+    };
+
+    // The input Row object belongs to a different table
+    struct IncorrectTableException : public std::logic_error {
+        IncorrectTableException(StringData e, StringData a, const std::string &error) :
+            std::logic_error(error), expected(e), actual(a) {}
+        const StringData expected;
+        const StringData actual;
+    };
+
+    // The requested aggregate operation is not supported for the column type
+    struct UnsupportedColumnTypeException : public std::logic_error {
+        size_t column_index;
+        StringData column_name;
+        PropertyType property_type;
+
+        UnsupportedColumnTypeException(size_t column, const Table* table, const char* operation);
+    };
+
+    // Create an async query from this Results
+    // The query will be run on a background thread and delivered to the callback,
+    // and then rerun after each commit (if needed) and redelivered if it changed
+    template<typename Func>
+    NotificationToken async(Func&& target);
+    NotificationToken add_notification_callback(CollectionChangeCallback cb) &;
+
+    bool wants_background_updates() const { return m_wants_background_updates; }
+
+    // Returns whether the rows are guaranteed to be in table order.
+    bool is_in_table_order() const;
+
+    // Helper type to let ResultsNotifier update the tableview without giving access
+    // to any other privates or letting anyone else do so
+    class Internal {
+        friend class _impl::ResultsNotifier;
+        static void set_table_view(Results& results, TableView&& tv);
+    };
+
+    template<typename Context> auto first(Context&);
+    template<typename Context> auto last(Context&);
+
+    template<typename Context, typename T>
+    size_t index_of(Context&, T value);
+
+    // Execute the query immediately if needed. When the relevant query is slow, size()
+    // may cost similar time compared with creating the tableview. Use this function to
+    // avoid running the query twice for size() and other accessors.
+    void evaluate_query_if_needed(bool wants_notifications = true);
+
+private:
+    enum class UpdatePolicy {
+        Auto,  // Update automatically to reflect changes in the underlying data.
+        Never, // Never update.
+    };
+
+    std::shared_ptr<Realm> m_realm;
+    mutable const ObjectSchema *m_object_schema = nullptr;
+    Query m_query;
+    TableView m_table_view;
+    LinkViewRef m_link_view;
+    TableRef m_table;
+    DescriptorOrdering m_descriptor_ordering;
+
+    _impl::CollectionNotifier::Handle<_impl::ResultsNotifier> m_notifier;
+
+    Mode m_mode = Mode::Empty;
+    UpdatePolicy m_update_policy = UpdatePolicy::Auto;
+    bool m_has_used_table_view = false;
+    bool m_wants_background_updates = true;
+
+    bool update_linkview();
+
+    void validate_read() const;
+    void validate_write() const;
+
+    void prepare_async();
+
+    template<typename T>
+    util::Optional<T> try_get(size_t);
+
+    template<typename Int, typename Float, typename Double, typename Timestamp>
+    util::Optional<Mixed> aggregate(size_t column,
+                                    const char* name,
+                                    Int agg_int, Float agg_float,
+                                    Double agg_double, Timestamp agg_timestamp);
+    void prepare_for_aggregate(size_t column, const char* name);
+
+    void set_table_view(TableView&& tv);
+
+    template<typename Fn>
+    auto dispatch(Fn&&) const;
+};
+
+template<typename Func>
+NotificationToken Results::async(Func&& target)
+{
+    return this->add_notification_callback([target = std::forward<Func>(target)](CollectionChangeSet const&, std::exception_ptr e) {
+        target(e);
+    });
+}
+
+template<typename Fn>
+auto Results::dispatch(Fn&& fn) const
+{
+    return switch_on_type(get_type(), std::forward<Fn>(fn));
+}
+
+template<typename Context>
+auto Results::get(Context& ctx, size_t row_ndx)
+{
+    return dispatch([&](auto t) { return ctx.box(this->get<std::decay_t<decltype(*t)>>(row_ndx)); });
+}
+
+template<typename Context>
+auto Results::first(Context& ctx)
+{
+    // GCC 4.9 complains about `ctx` not being defined within the lambda without this goofy capture
+    return dispatch([this, ctx = &ctx](auto t) {
+        auto value = this->first<std::decay_t<decltype(*t)>>();
+        return value ? static_cast<decltype(ctx->no_value())>(ctx->box(*value)) : ctx->no_value();
+    });
+}
+
+template<typename Context>
+auto Results::last(Context& ctx)
+{
+    return dispatch([&](auto t) {
+        auto value = this->last<std::decay_t<decltype(*t)>>();
+        return value ? static_cast<decltype(ctx.no_value())>(ctx.box(*value)) : ctx.no_value();
+    });
+}
+
+template<typename Context, typename T>
+size_t Results::index_of(Context& ctx, T value)
+{
+    return dispatch([&](auto t) { return this->index_of(ctx.template unbox<std::decay_t<decltype(*t)>>(value)); });
+}
+} // namespace realm
+
+#endif // REALM_RESULTS_HPP
diff --git a/iOS/Pods/Realm/include/schema.hpp b/iOS/Pods/Realm/include/schema.hpp
new file mode 100644 (file)
index 0000000..3feab40
--- /dev/null
@@ -0,0 +1,178 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_SCHEMA_HPP
+#define REALM_SCHEMA_HPP
+
+#include <string>
+#include <vector>
+
+#include <realm/util/features.h>
+
+namespace realm {
+class ObjectSchema;
+class SchemaChange;
+class StringData;
+struct Property;
+
+class Schema : private std::vector<ObjectSchema> {
+private:
+    using base = std::vector<ObjectSchema>;
+public:
+    Schema();
+    ~Schema();
+    // Create a schema from a vector of ObjectSchema
+    Schema(base types);
+    Schema(std::initializer_list<ObjectSchema> types);
+
+    Schema(Schema const&);
+    Schema(Schema &&);
+    Schema& operator=(Schema const&);
+    Schema& operator=(Schema&&);
+
+    // find an ObjectSchema by name
+    iterator find(StringData name);
+    const_iterator find(StringData name) const;
+
+    // find an ObjectSchema with the same name as the passed in one
+    iterator find(ObjectSchema const& object) noexcept;
+    const_iterator find(ObjectSchema const& object) const noexcept;
+
+    // Verify that this schema is internally consistent (i.e. all properties are
+    // valid, links link to types that actually exist, etc.)
+    void validate() const;
+
+    // Get the changes which must be applied to this schema to produce the passed-in schema
+    std::vector<SchemaChange> compare(Schema const&) const;
+
+    void copy_table_columns_from(Schema const&);
+
+    friend bool operator==(Schema const&, Schema const&);
+    friend bool operator!=(Schema const& a, Schema const& b) { return !(a == b); }
+
+    using base::iterator;
+    using base::const_iterator;
+    using base::begin;
+    using base::end;
+    using base::empty;
+    using base::size;
+
+private:
+    template<typename T, typename U, typename Func>
+    static void zip_matching(T&& a, U&& b, Func&& func);
+};
+
+namespace schema_change {
+struct AddTable {
+    const ObjectSchema* object;
+};
+
+struct AddInitialProperties {
+    const ObjectSchema* object;
+};
+
+struct AddProperty {
+    const ObjectSchema* object;
+    const Property* property;
+};
+
+struct RemoveProperty {
+    const ObjectSchema* object;
+    const Property* property;
+};
+
+struct ChangePropertyType {
+    const ObjectSchema* object;
+    const Property* old_property;
+    const Property* new_property;
+};
+
+struct MakePropertyNullable {
+    const ObjectSchema* object;
+    const Property* property;
+};
+
+struct MakePropertyRequired {
+    const ObjectSchema* object;
+    const Property* property;
+};
+
+struct AddIndex {
+    const ObjectSchema* object;
+    const Property* property;
+};
+
+struct RemoveIndex {
+    const ObjectSchema* object;
+    const Property* property;
+};
+
+struct ChangePrimaryKey {
+    const ObjectSchema* object;
+    const Property* property;
+};
+}
+
+#define REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(macro) \
+    macro(AddTable) \
+    macro(AddInitialProperties) \
+    macro(AddProperty) \
+    macro(RemoveProperty) \
+    macro(ChangePropertyType) \
+    macro(MakePropertyNullable) \
+    macro(MakePropertyRequired) \
+    macro(AddIndex) \
+    macro(RemoveIndex) \
+    macro(ChangePrimaryKey) \
+
+class SchemaChange {
+public:
+#define REALM_SCHEMA_CHANGE_CONSTRUCTOR(name) \
+    SchemaChange(schema_change::name value) : m_kind(Kind::name) { name = value; }
+        REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(REALM_SCHEMA_CHANGE_CONSTRUCTOR)
+#undef REALM_SCHEMA_CHANGE_CONSTRUCTOR
+
+    template<typename Visitor>
+    auto visit(Visitor&& visitor) const {
+        switch (m_kind) {
+#define REALM_SWITCH_CASE(name) case Kind::name: return visitor(name);
+        REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(REALM_SWITCH_CASE)
+#undef REALM_SWITCH_CASE
+        }
+        REALM_COMPILER_HINT_UNREACHABLE();
+    }
+
+    friend bool operator==(SchemaChange const& lft, SchemaChange const& rgt);
+private:
+    enum class Kind {
+#define REALM_SCHEMA_CHANGE_TYPE(name) name,
+        REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(REALM_SCHEMA_CHANGE_TYPE)
+#undef REALM_SCHEMA_CHANGE_TYPE
+
+    } m_kind;
+    union {
+#define REALM_DEFINE_FIELD(name) schema_change::name name;
+        REALM_FOR_EACH_SCHEMA_CHANGE_TYPE(REALM_DEFINE_FIELD)
+#undef REALM_DEFINE_FIELD
+    };
+};
+
+#undef REALM_FOR_EACH_SCHEMA_CHANGE_TYPE
+}
+
+#endif /* defined(REALM_SCHEMA_HPP) */
diff --git a/iOS/Pods/Realm/include/shared_realm.hpp b/iOS/Pods/Realm/include/shared_realm.hpp
new file mode 100644 (file)
index 0000000..67a0d59
--- /dev/null
@@ -0,0 +1,479 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 REALM_REALM_HPP
+#define REALM_REALM_HPP
+
+#include "execution_context_id.hpp"
+#include "schema.hpp"
+
+#include <realm/util/optional.hpp>
+#include <realm/binary_data.hpp>
+
+#if REALM_ENABLE_SYNC
+#include <realm/sync/client.hpp>
+#endif
+
+#include <memory>
+
+namespace realm {
+class BindingContext;
+class Group;
+class Realm;
+class Replication;
+class SharedGroup;
+class StringData;
+class Table;
+struct SyncConfig;
+class ThreadSafeReferenceBase;
+template <typename T> class ThreadSafeReference;
+struct VersionID;
+template<typename Table> class BasicRow;
+typedef BasicRow<Table> Row;
+typedef std::shared_ptr<Realm> SharedRealm;
+typedef std::weak_ptr<Realm> WeakRealm;
+
+namespace _impl {
+    class AnyHandover;
+    class CollectionNotifier;
+    class RealmCoordinator;
+    class RealmFriend;
+}
+
+// How to handle update_schema() being called on a file which has
+// already been initialized with a different schema
+enum class SchemaMode : uint8_t {
+    // If the schema version has increased, automatically apply all
+    // changes, then call the migration function.
+    //
+    // If the schema version has not changed, verify that the only
+    // changes are to add new tables and add or remove indexes, and then
+    // apply them if so. Does not call the migration function.
+    //
+    // This mode does not automatically remove tables which are not
+    // present in the schema that must be manually done in the migration
+    // function, to support sharing a Realm file between processes using
+    // different class subsets.
+    //
+    // This mode allows using schemata with different subsets of tables
+    // on different threads, but the tables which are shared must be
+    // identical.
+    Automatic,
+
+    // Open the file in immutable mode. Schema version must match the
+    // version in the file, and all tables present in the file must
+    // exactly match the specified schema, except for indexes. Tables
+    // are allowed to be missing from the file.
+    // WARNING: This is the original ReadOnly mode.
+    Immutable,
+
+    // Open the Realm in read-only mode, transactions are not allowed to
+    // be performed on the Realm instance. The schema of the existing Realm
+    // file won't be changed through this Realm instance. Extra tables and
+    // extra properties are allowed in the existing Realm schema. The
+    // difference of indexes is allowed as well. Other schema differences
+    // than those will cause an exception. This is different from Immutable
+    // mode, sync Realm can be opened with ReadOnly mode. Changes
+    // can be made to the Realm file through another writable Realm instance.
+    // Thus, notifications are also allowed in this mode.
+    // FIXME: Rename this to ReadOnly
+    // WARNING: This is not the original ReadOnly mode. The original ReadOnly
+    // has been renamed to Immutable.
+    ReadOnlyAlternative,
+
+    // If the schema version matches and the only schema changes are new
+    // tables and indexes being added or removed, apply the changes to
+    // the existing file.
+    // Otherwise delete the file and recreate it from scratch.
+    // The migration function is not used.
+    //
+    // This mode allows using schemata with different subsets of tables
+    // on different threads, but the tables which are shared must be
+    // identical.
+    ResetFile,
+
+    // The only changes allowed are to add new tables, add columns to
+    // existing tables, and to add or remove indexes from existing
+    // columns. Extra tables not present in the schema are ignored.
+    // Indexes are only added to or removed from existing columns if the
+    // schema version is greater than the existing one (and unlike other
+    // modes, the schema version is allowed to be less than the existing
+    // one).
+    // The migration function is not used.
+    //
+    // This mode allows updating the schema with additive changes even
+    // if the Realm is already open on another thread.
+    Additive,
+
+    // Verify that the schema version has increased, call the migraiton
+    // function, and then verify that the schema now matches.
+    // The migration function is mandatory for this mode.
+    //
+    // This mode requires that all threads and processes which open a
+    // file use identical schemata.
+    Manual
+};
+
+class Realm : public std::enable_shared_from_this<Realm> {
+public:
+    // A callback function to be called during a migration for Automatic and
+    // Manual schema modes. It is passed a SharedRealm at the version before
+    // the migration, the SharedRealm in the migration, and a mutable reference
+    // to the realm's Schema. Updating the schema with changes made within the
+    // migration function is only required if you wish to use the ObjectStore
+    // functions which take a Schema from within the migration function.
+    using MigrationFunction = std::function<void (SharedRealm old_realm, SharedRealm realm, Schema&)>;
+
+    // A callback function to be called the first time when a schema is created.
+    // It is passed a SharedRealm which is in a write transaction with the schema
+    // initialized. So it is possible to create some initial objects inside the callback
+    // with the given SharedRealm. Those changes will be committed together with the
+    // schema creation in a single transaction.
+    using DataInitializationFunction = std::function<void (SharedRealm realm)>;
+
+    // A callback function called when opening a SharedRealm when no cached
+    // version of this Realm exists. It is passed the total bytes allocated for
+    // the file (file size) and the total bytes used by data in the file.
+    // Return `true` to indicate that an attempt to compact the file should be made
+    // if it is possible to do so.
+    // Won't compact the file if another process is accessing it.
+    //
+    // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
+    // because it's not crash safe! It may corrupt your database if something fails
+    using ShouldCompactOnLaunchFunction = std::function<bool (uint64_t total_bytes, uint64_t used_bytes)>;
+
+    struct Config {
+        // Path and binary data are mutually exclusive
+        std::string path;
+        BinaryData realm_data;
+        // User-supplied encryption key. Must be either empty or 64 bytes.
+        std::vector<char> encryption_key;
+
+        bool in_memory = false;
+        SchemaMode schema_mode = SchemaMode::Automatic;
+
+        // Optional schema for the file.
+        // If the schema and schema version are supplied, update_schema() is
+        // called with the supplied schema, version and migration function when
+        // the Realm is actually opened and not just retrieved from the cache
+        util::Optional<Schema> schema;
+        uint64_t schema_version = -1;
+        MigrationFunction migration_function;
+
+        DataInitializationFunction initialization_function;
+
+        // A callback function called when opening a SharedRealm when no cached
+        // version of this Realm exists. It is passed the total bytes allocated for
+        // the file (file size) and the total bytes used by data in the file.
+        // Return `true` to indicate that an attempt to compact the file should be made
+        // if it is possible to do so.
+        // Won't compact the file if another process is accessing it.
+        //
+        // WARNING / FIXME: compact() should NOT be exposed publicly on Windows
+        // because it's not crash safe! It may corrupt your database if something fails
+        ShouldCompactOnLaunchFunction should_compact_on_launch_function;
+
+        // WARNING: The original read_only() has been renamed to immutable().
+        bool immutable() const { return schema_mode == SchemaMode::Immutable; }
+        // FIXME: Rename this to read_only().
+        bool read_only_alternative() const { return schema_mode == SchemaMode::ReadOnlyAlternative; }
+
+        // The following are intended for internal/testing purposes and
+        // should not be publicly exposed in binding APIs
+
+        // If false, always return a new Realm instance, and don't return
+        // that Realm instance for other requests for a cached Realm. Useful
+        // for dynamic Realms and for tests that need multiple instances on
+        // one thread
+        bool cache = true;
+        // Throw an exception rather than automatically upgrading the file
+        // format. Used by the browser to warn the user that it'll modify
+        // the file.
+        bool disable_format_upgrade = false;
+        // Disable the background worker thread for producing change
+        // notifications. Useful for tests for those notifications so that
+        // everything can be done deterministically on one thread, and
+        // speeds up tests that don't need notifications.
+        bool automatic_change_notifications = true;
+
+        // The identifier of the abstract execution context in which this Realm will be used.
+        // If unset, the current thread's identifier will be used to identify the execution context.
+        util::Optional<AbstractExecutionContextID> execution_context;
+
+        /// A data structure storing data used to configure the Realm for sync support.
+        std::shared_ptr<SyncConfig> sync_config;
+
+        // FIXME: Realm Java manages sync at the Java level, so it needs to create Realms using the sync history
+        //        format.
+        bool force_sync_history = false;
+    };
+
+    // Get a cached Realm or create a new one if no cached copies exists
+    // Caching is done by path - mismatches for in_memory, schema mode or
+    // encryption key will raise an exception.
+    static SharedRealm get_shared_realm(Config config);
+
+    // Updates a Realm to a given schema, using the Realm's pre-set schema mode.
+    void update_schema(Schema schema, uint64_t version=0,
+                       MigrationFunction migration_function=nullptr,
+                       DataInitializationFunction initialization_function=nullptr,
+                       bool in_transaction=false);
+
+    // Set the schema used for this Realm, but do not update the file's schema
+    // if it is not compatible (and instead throw an error).
+    // Cannot be called multiple times on a single Realm instance or an instance
+    // which has already had update_schema() called on it.
+    void set_schema_subset(Schema schema);
+
+    // Read the schema version from the file specified by the given config, or
+    // ObjectStore::NotVersioned if it does not exist
+    static uint64_t get_schema_version(Config const& config);
+
+    Config const& config() const { return m_config; }
+    Schema const& schema() const { return m_schema; }
+    uint64_t schema_version() const { return m_schema_version; }
+
+    void begin_transaction();
+    void commit_transaction();
+    void cancel_transaction();
+    bool is_in_transaction() const noexcept;
+    bool is_in_read_transaction() const { return !!m_group; }
+
+    bool is_in_migration() const noexcept { return m_in_migration; }
+
+    bool refresh();
+    void set_auto_refresh(bool auto_refresh) { m_auto_refresh = auto_refresh; }
+    bool auto_refresh() const { return m_auto_refresh; }
+    void notify();
+
+    void invalidate();
+
+    // 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();
+    void write_copy(StringData path, BinaryData encryption_key);
+    OwnedBinaryData write_copy();
+
+    void verify_thread() const;
+    void verify_in_write() const;
+    void verify_open() const;
+
+    bool can_deliver_notifications() const noexcept;
+
+    // Close this Realm and remove it from the cache. Continuing to use a
+    // Realm after closing it will throw ClosedRealmException
+    void close();
+    bool is_closed() const { return !m_read_only_group && !m_shared_group; }
+
+    // returns the file format version upgraded from if an upgrade took place
+    util::Optional<int> file_format_upgraded_from_version() const;
+
+    Realm(const Realm&) = delete;
+    Realm& operator=(const Realm&) = delete;
+    Realm(Realm&&) = delete;
+    Realm& operator=(Realm&&) = delete;
+    ~Realm();
+
+    // Construct a thread safe reference, pinning the version in the process.
+    template <typename T>
+    ThreadSafeReference<T> obtain_thread_safe_reference(T const& value);
+
+    // Advances the read transaction to the latest version, resolving the thread safe reference and unpinning the
+    // version in the process.
+    template <typename T>
+    T resolve_thread_safe_reference(ThreadSafeReference<T> reference);
+
+    static SharedRealm make_shared_realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator = nullptr) {
+        struct make_shared_enabler : public Realm {
+            make_shared_enabler(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator)
+            : Realm(std::move(config), std::move(coordinator)) { }
+        };
+        return std::make_shared<make_shared_enabler>(std::move(config), std::move(coordinator));
+    }
+
+    // Expose some internal functionality to other parts of the ObjectStore
+    // without making it public to everyone
+    class Internal {
+        friend class _impl::CollectionNotifier;
+        friend class _impl::RealmCoordinator;
+        friend class ThreadSafeReferenceBase;
+        friend class GlobalNotifier;
+        friend class TestHelper;
+
+        // ResultsNotifier and ListNotifier need access to the SharedGroup
+        // to be able to call the handover functions, which are not very wrappable
+        static const std::unique_ptr<SharedGroup>& get_shared_group(Realm& realm) { return realm.m_shared_group; }
+
+        // CollectionNotifier needs to be able to access the owning
+        // coordinator to wake up the worker thread when a callback is
+        // added, and coordinators need to be able to get themselves from a Realm
+        static _impl::RealmCoordinator& get_coordinator(Realm& realm) { return *realm.m_coordinator; }
+
+        static void begin_read(Realm&, VersionID);
+    };
+
+    static void open_with_config(const Config& config,
+                                 std::unique_ptr<Replication>& history,
+                                 std::unique_ptr<SharedGroup>& shared_group,
+                                 std::unique_ptr<Group>& read_only_group,
+                                 Realm* realm);
+
+private:
+    // `enable_shared_from_this` is unsafe with public constructors; use `make_shared_realm` instead
+    Realm(Config config, std::shared_ptr<_impl::RealmCoordinator> coordinator);
+
+    Config m_config;
+    AnyExecutionContextID m_execution_context;
+    bool m_auto_refresh = true;
+
+    std::unique_ptr<Replication> m_history;
+    std::unique_ptr<SharedGroup> m_shared_group;
+    std::unique_ptr<Group> m_read_only_group;
+
+    Group *m_group = nullptr;
+
+    uint64_t m_schema_version;
+    Schema m_schema;
+    util::Optional<Schema> m_new_schema;
+    uint64_t m_schema_transaction_version = -1;
+
+    // FIXME: this should be a Dynamic schema mode instead, but only once
+    // that's actually fully working
+    bool m_dynamic_schema = true;
+
+    std::shared_ptr<_impl::RealmCoordinator> m_coordinator;
+
+    // File format versions populated when a file format upgrade takes place during realm opening
+    int upgrade_initial_version = 0, upgrade_final_version = 0;
+
+    // True while sending the notifications caused by advancing the read
+    // transaction version, to avoid recursive notifications where possible
+    bool m_is_sending_notifications = false;
+
+    // True while we're performing a schema migration via this Realm instance
+    // to allow for different behavior (such as allowing modifications to
+    // primary key values)
+    bool m_in_migration = false;
+
+    void begin_read(VersionID);
+
+    void set_schema(Schema const& reference, Schema schema);
+    bool reset_file(Schema& schema, std::vector<SchemaChange>& changes_required);
+    bool schema_change_needs_write_transaction(Schema& schema, std::vector<SchemaChange>& changes, uint64_t version);
+    Schema get_full_schema();
+
+    // Ensure that m_schema and m_schema_version match that of the current
+    // version of the file
+    void read_schema_from_group_if_needed();
+
+    void add_schema_change_handler();
+    void cache_new_schema();
+    void notify_schema_changed();
+
+public:
+    std::unique_ptr<BindingContext> m_binding_context;
+
+    // FIXME private
+    Group& read_group();
+
+    Replication *history() { return m_history.get(); }
+
+    friend class _impl::RealmFriend;
+};
+
+class RealmFileException : public std::runtime_error {
+public:
+    enum class Kind {
+        /** Thrown for any I/O related exception scenarios when a realm is opened. */
+        AccessError,
+        /** Thrown if the history type of the on-disk Realm is unexpected or incompatible. */
+        BadHistoryError,
+        /** Thrown if the user does not have permission to open or create
+         the specified file in the specified access mode when the realm is opened. */
+        PermissionDenied,
+        /** Thrown if create_Always was specified and the file did already exist when the realm is opened. */
+        Exists,
+        /** Thrown if no_create was specified and the file was not found when the realm is opened. */
+        NotFound,
+        /** Thrown if the database file is currently open in another
+         process which cannot share with the current process due to an
+         architecture mismatch. */
+        IncompatibleLockFile,
+        /** Thrown if the file needs to be upgraded to a new format, but upgrades have been explicitly disabled. */
+        FormatUpgradeRequired,
+        /** Thrown if the local copy of a synced Realm file was created using an incompatible version of Realm.
+         The specified path is where the local file was moved for recovery. */
+        IncompatibleSyncedRealm,
+    };
+    RealmFileException(Kind kind, std::string path, std::string message, std::string underlying)
+    : std::runtime_error(std::move(message)), m_kind(kind), m_path(std::move(path)), m_underlying(std::move(underlying)) {}
+    Kind kind() const { return m_kind; }
+    const std::string& path() const { return m_path; }
+    const std::string& underlying() const { return m_underlying; }
+
+private:
+    Kind m_kind;
+    std::string m_path;
+    std::string m_underlying;
+};
+
+class MismatchedConfigException : public std::logic_error {
+public:
+    MismatchedConfigException(StringData message, StringData path);
+};
+
+class MismatchedRealmException : public std::logic_error {
+public:
+    MismatchedRealmException(StringData message);
+};
+
+class InvalidTransactionException : public std::logic_error {
+public:
+    InvalidTransactionException(std::string message) : std::logic_error(message) {}
+};
+
+class IncorrectThreadException : public std::logic_error {
+public:
+    IncorrectThreadException() : std::logic_error("Realm accessed from incorrect thread.") {}
+};
+
+class ClosedRealmException : public std::logic_error {
+public:
+    ClosedRealmException() : std::logic_error("Cannot access realm that has been closed.") {}
+};
+
+class UninitializedRealmException : public std::runtime_error {
+public:
+    UninitializedRealmException(std::string message) : std::runtime_error(message) {}
+};
+
+class InvalidEncryptionKeyException : public std::logic_error {
+public:
+    InvalidEncryptionKeyException() : std::logic_error("Encryption key must be 64 bytes.") {}
+};
+
+// FIXME Those are exposed for Java async queries, mainly because of handover related methods.
+class _impl::RealmFriend {
+public:
+    static SharedGroup& get_shared_group(Realm& realm);
+    static Group& read_group_to(Realm& realm, VersionID version);
+};
+
+} // namespace realm
+
+#endif /* defined(REALM_REALM_HPP) */
diff --git a/iOS/Pods/Realm/include/sync/impl/apple/network_reachability_observer.hpp b/iOS/Pods/Realm/include/sync/impl/apple/network_reachability_observer.hpp
new file mode 100644 (file)
index 0000000..f782563
--- /dev/null
@@ -0,0 +1,69 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_OS_NETWORK_REACHABILITY_OBSERVER_HPP
+#define REALM_OS_NETWORK_REACHABILITY_OBSERVER_HPP
+
+#include <functional>
+#include <string>
+
+#include <realm/util/cf_ptr.hpp>
+#include <realm/util/optional.hpp>
+
+#include "sync/impl/network_reachability.hpp"
+
+#if NETWORK_REACHABILITY_AVAILABLE
+
+#include "sync/impl/apple/system_configuration.hpp"
+
+namespace realm {
+namespace _impl {
+
+enum NetworkReachabilityStatus {
+    NotReachable,
+    ReachableViaWiFi,
+    ReachableViaWWAN
+};
+
+class NetworkReachabilityObserver {
+public:
+    NetworkReachabilityObserver(util::Optional<std::string> hostname,
+                                std::function<void (const NetworkReachabilityStatus)> handler);
+
+    ~NetworkReachabilityObserver();
+
+    NetworkReachabilityStatus reachability_status() const;
+
+    bool start_observing();
+    void stop_observing();
+
+private:
+    void reachability_changed();
+
+    util::CFPtr<SCNetworkReachabilityRef> m_reachability_ref;
+    NetworkReachabilityStatus m_previous_status;
+    dispatch_queue_t m_callback_queue;
+    std::function<void (const NetworkReachabilityStatus)> m_change_handler;
+};
+
+} // namespace _impl
+} // namespace realm
+
+#endif // NETWORK_REACHABILITY_AVAILABLE
+
+#endif // REALM_OS_NETWORK_REACHABILITY_OBSERVER_HPP
diff --git a/iOS/Pods/Realm/include/sync/impl/apple/system_configuration.hpp b/iOS/Pods/Realm/include/sync/impl/apple/system_configuration.hpp
new file mode 100644 (file)
index 0000000..fa3feb5
--- /dev/null
@@ -0,0 +1,72 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_SYSTEM_CONFIGURATION_HPP
+#define REALM_OS_SYSTEM_CONFIGURATION_HPP
+
+#include "sync/impl/network_reachability.hpp"
+
+#if NETWORK_REACHABILITY_AVAILABLE
+
+#include <SystemConfiguration/SystemConfiguration.h>
+
+namespace realm {
+namespace _impl {
+
+class SystemConfiguration {
+public:
+    static SystemConfiguration& shared();
+
+    SCNetworkReachabilityRef network_reachability_create_with_name(CFAllocatorRef allocator,
+                                                                   const char *hostname);
+    SCNetworkReachabilityRef network_reachability_create_with_address(CFAllocatorRef allocator,
+                                                                      const sockaddr *address);
+    bool network_reachability_set_dispatch_queue(SCNetworkReachabilityRef target,
+                                                 dispatch_queue_t queue);
+    bool network_reachability_set_callback(SCNetworkReachabilityRef target,
+                                           SCNetworkReachabilityCallBack callback,
+                                           SCNetworkReachabilityContext *context);
+    bool network_reachability_get_flags(SCNetworkReachabilityRef target,
+                                        SCNetworkReachabilityFlags *flags);
+
+private:
+    using create_with_name_t = SCNetworkReachabilityRef(*)(CFAllocatorRef, const char*);
+    using create_with_address_t = SCNetworkReachabilityRef(*)(CFAllocatorRef, const sockaddr*);
+    using set_dispatch_queue_t = bool(*)(SCNetworkReachabilityRef, dispatch_queue_t);
+    using set_callback_t = bool(*)(SCNetworkReachabilityRef,
+                                   SCNetworkReachabilityCallBack,
+                                   SCNetworkReachabilityContext*);
+    using get_flags_t = bool(*)(SCNetworkReachabilityRef, SCNetworkReachabilityFlags*);
+
+    SystemConfiguration();
+
+    void* m_framework_handle;
+
+    create_with_name_t m_create_with_name = nullptr;
+    create_with_address_t m_create_with_address = nullptr;
+    set_dispatch_queue_t m_set_dispatch_queue = nullptr;
+    set_callback_t m_set_callback = nullptr;
+    get_flags_t m_get_flags = nullptr;
+};
+
+} // namespace _impl
+} // namespace realm
+
+#endif // NETWORK_REACHABILITY_AVAILABLE
+
+#endif // REALM_OS_SYSTEM_CONFIGURATION_HPP
diff --git a/iOS/Pods/Realm/include/sync/impl/network_reachability.hpp b/iOS/Pods/Realm/include/sync/impl/network_reachability.hpp
new file mode 100644 (file)
index 0000000..8dbf558
--- /dev/null
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_NETWORK_REACHABILITY_HPP
+#define REALM_OS_NETWORK_REACHABILITY_HPP
+
+#include <realm/util/features.h>
+
+#if REALM_PLATFORM_APPLE
+#define NETWORK_REACHABILITY_AVAILABLE !REALM_WATCHOS
+#else
+#define NETWORK_REACHABILITY_AVAILABLE 0
+#endif
+
+#endif // REALM_OS_NETWORK_REACHABILITY_HPP
diff --git a/iOS/Pods/Realm/include/sync/impl/sync_client.hpp b/iOS/Pods/Realm/include/sync/impl/sync_client.hpp
new file mode 100644 (file)
index 0000000..2c8b450
--- /dev/null
@@ -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.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef REALM_OS_SYNC_CLIENT_HPP
+#define REALM_OS_SYNC_CLIENT_HPP
+
+#include "binding_callback_thread_observer.hpp"
+
+#include <realm/sync/client.hpp>
+#include <realm/util/scope_exit.hpp>
+
+#include <thread>
+
+#include "sync/sync_manager.hpp"
+#include "sync/impl/network_reachability.hpp"
+
+#if NETWORK_REACHABILITY_AVAILABLE
+#include "sync/impl/apple/network_reachability_observer.hpp"
+#endif
+
+namespace realm {
+namespace _impl {
+
+using ReconnectMode = sync::Client::ReconnectMode;
+
+struct SyncClient {
+    SyncClient(std::unique_ptr<util::Logger> logger, ReconnectMode reconnect_mode, bool multiplex_sessions)
+        : m_client(make_client(*logger, reconnect_mode, multiplex_sessions)) // Throws
+        , m_logger(std::move(logger))
+        , m_thread([this] {
+            if (g_binding_callback_thread_observer) {
+                g_binding_callback_thread_observer->did_create_thread();
+                auto will_destroy_thread = util::make_scope_exit([&]() noexcept {
+                    g_binding_callback_thread_observer->will_destroy_thread();
+                });
+                try {
+                    m_client.run(); // Throws
+                }
+                catch (std::exception const& e) {
+                    g_binding_callback_thread_observer->handle_error(e);
+                }
+            }
+            else {
+                m_client.run(); // Throws
+            }
+        }) // Throws
+#if NETWORK_REACHABILITY_AVAILABLE
+    , m_reachability_observer(none, [=](const NetworkReachabilityStatus status) {
+        if (status != NotReachable)
+            SyncManager::shared().reconnect();
+    })
+    {
+        if (!m_reachability_observer.start_observing())
+            m_logger->error("Failed to set up network reachability observer");
+    }
+#else
+    {
+    }
+#endif
+
+    void cancel_reconnect_delay() {
+        m_client.cancel_reconnect_delay();
+    }
+
+    void stop()
+    {
+        m_client.stop();
+        if (m_thread.joinable())
+            m_thread.join();
+    }
+
+    std::unique_ptr<sync::Session> make_session(std::string path, sync::Session::Config config)
+    {
+        return std::make_unique<sync::Session>(m_client, std::move(path), std::move(config));
+    }
+
+
+    ~SyncClient()
+    {
+        stop();
+    }
+
+private:
+    static sync::Client make_client(util::Logger& logger, ReconnectMode reconnect_mode, bool multiplex_sessions)
+    {
+        sync::Client::Config config;
+        config.logger = &logger;
+        config.reconnect_mode = std::move(reconnect_mode);
+        config.one_connection_per_session = !multiplex_sessions;
+        return sync::Client(std::move(config)); // Throws
+    }
+
+    sync::Client m_client;
+    const std::unique_ptr<util::Logger> m_logger;
+    std::thread m_thread;
+#if NETWORK_REACHABILITY_AVAILABLE
+    NetworkReachabilityObserver m_reachability_observer;
+#endif
+};
+
+}
+}
+
+#endif // REALM_OS_SYNC_CLIENT_HPP
diff --git a/iOS/Pods/Realm/include/sync/impl/sync_file.hpp b/iOS/Pods/Realm/include/sync/impl/sync_file.hpp
new file mode 100644 (file)
index 0000000..8321911
--- /dev/null
@@ -0,0 +1,129 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_OS_SYNC_FILE_HPP
+#define REALM_OS_SYNC_FILE_HPP
+
+#include <string>
+
+#include "sync/sync_user.hpp"
+
+#include <realm/util/optional.hpp>
+
+namespace realm {
+
+namespace util {
+
+enum class FilePathType {
+    File, Directory
+};
+
+// FIXME: Does it make sense to use realm::StringData arguments for these functions instead of std::string?
+
+/// Given a string, turn it into a percent-encoded string.
+std::string make_percent_encoded_string(const std::string& raw_string);
+
+/// Given a percent-encoded string, turn it into the original (non-encoded) string.
+std::string make_raw_string(const std::string& percent_encoded_string);
+
+/// Given a file path and a path component, return a new path created by appending the component to the path.
+std::string file_path_by_appending_component(const std::string& path,
+                                             const std::string& component,
+                                             FilePathType path_type=FilePathType::File);
+
+/// Given a file path and an extension, append the extension to the path.
+std::string file_path_by_appending_extension(const std::string& path, const std::string& extension);
+
+/// Create a timestamped `mktemp`-compatible template string using the current local time.
+std::string create_timestamped_template(const std::string& prefix, int wildcard_count=8);
+
+/// Reserve a unique file name based on a base directory path and a `mktemp`-compatible template string.
+/// Returns the path of the file.
+std::string reserve_unique_file_name(const std::string& path, const std::string& template_string);
+
+} // util
+
+class SyncFileManager {
+public:
+    SyncFileManager(std::string base_path) : m_base_path(std::move(base_path)) { }
+
+    /// Return the user directory for a given user, creating it if it does not already exist.
+    std::string user_directory(const std::string& local_identity,
+                               util::Optional<SyncUserIdentifier> user_info=none) const;
+
+    /// Remove the user directory for a given user.
+    void remove_user_directory(const std::string& local_identity) const;       // throws
+
+    /// Rename a user directory. Returns true if a directory at `old_name` existed
+    /// and was successfully renamed to `new_name`. Returns false if no directory
+    /// exists at `old_name`.
+    bool try_rename_user_directory(const std::string& old_name, const std::string& new_name) const;
+
+    /// Return the path for a given Realm, creating the user directory if it does not already exist.
+    std::string path(const std::string&, const std::string&,
+                     util::Optional<SyncUserIdentifier> user_info=none) const;
+
+    /// Remove the Realm at a given path for a given user. Returns `true` if the remove operation fully succeeds.
+    bool remove_realm(const std::string& local_identity, const std::string& raw_realm_path) const;
+
+    /// Remove the Realm whose primary Realm file is located at `absolute_path`. Returns `true` if the remove
+    /// operation fully succeeds.
+    bool remove_realm(const std::string& absolute_path) const;
+
+    /// Copy the Realm file at the location `old_path` to the location of `new_path`.
+    bool copy_realm_file(const std::string& old_path, const std::string& new_path) const;
+
+    /// Return the path for the metadata Realm files.
+    std::string metadata_path() const;
+
+    /// Remove the metadata Realm.
+    bool remove_metadata_realm() const;
+
+    const std::string& base_path() const
+    {
+        return m_base_path;
+    }
+
+    std::string recovery_directory_path() const
+    {
+        return get_special_directory(c_recovery_directory);
+    }
+
+private:
+    std::string m_base_path;
+
+    static constexpr const char c_sync_directory[] = "realm-object-server";
+    static constexpr const char c_utility_directory[] = "io.realm.object-server-utility";
+    static constexpr const char c_recovery_directory[] = "io.realm.object-server-recovered-realms";
+    static constexpr const char c_metadata_directory[] = "metadata";
+    static constexpr const char c_metadata_realm[] = "sync_metadata.realm";
+    static constexpr const char c_user_info_file[] = "__user_info";
+
+    std::string get_special_directory(std::string directory_name) const;
+
+    std::string get_utility_directory() const
+    {
+        return get_special_directory(c_utility_directory);
+    }
+
+    std::string get_base_sync_directory() const;
+};
+
+} // realm
+
+#endif // REALM_OS_SYNC_FILE_HPP
diff --git a/iOS/Pods/Realm/include/sync/impl/sync_metadata.hpp b/iOS/Pods/Realm/include/sync/impl/sync_metadata.hpp
new file mode 100644 (file)
index 0000000..22d7c0c
--- /dev/null
@@ -0,0 +1,229 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_OS_SYNC_METADATA_HPP
+#define REALM_OS_SYNC_METADATA_HPP
+
+#include <string>
+
+#include <realm/row.hpp>
+#include <realm/table.hpp>
+#include <realm/util/optional.hpp>
+
+#include "results.hpp"
+#include "shared_realm.hpp"
+
+namespace realm {
+template<typename T> class BasicRowExpr;
+using RowExpr = BasicRowExpr<Table>;
+class SyncMetadataManager;
+
+// A facade for a metadata Realm object representing a sync user.
+class SyncUserMetadata {
+public:
+    struct Schema {
+        // The ROS identity of the user. This, plus the auth server URL, uniquely identifies a user.
+        size_t idx_identity;
+        // A locally issued UUID for the user. This is used to generate the on-disk user directory.
+        size_t idx_local_uuid;
+        // Whether or not this user has been marked for removal.
+        size_t idx_marked_for_removal;
+        // The cached refresh token for this user.
+        size_t idx_user_token;
+        // The URL of the authentication server this user resides upon.
+        size_t idx_auth_server_url;
+        // Whether or not the auth server reported that this user is marked as an administrator.
+        size_t idx_user_is_admin;
+    };
+
+    // Cannot be set after creation.
+    std::string identity() const;
+
+    // Cannot be set after creation.
+    std::string local_uuid() const;
+
+    util::Optional<std::string> user_token() const;
+    void set_user_token(util::Optional<std::string>);
+
+    // Cannot be set after creation.
+    std::string auth_server_url() const;
+
+    bool is_admin() const;
+    void set_is_admin(bool);
+
+    // Mark the user as "ready for removal". Since Realm files cannot be safely deleted after being opened, the actual
+    // deletion of a user must be deferred until the next time the host application is launched.
+    void mark_for_removal();
+
+    void remove();
+
+    bool is_valid() const
+    {
+        return !m_invalid;
+    }
+
+    // INTERNAL USE ONLY
+    SyncUserMetadata(Schema schema, SharedRealm realm, RowExpr row);
+private:
+    bool m_invalid = false;
+    SharedRealm m_realm;
+    Schema m_schema;
+    Row m_row;
+};
+
+// A facade for a metadata Realm object representing a pending action to be carried out upon a specific file(s).
+class SyncFileActionMetadata {
+public:
+    struct Schema {
+        // The original path on disk of the file (generally, the main file for an on-disk Realm).
+        size_t idx_original_name;
+        // A new path on disk for a file to be written to. Context-dependent.
+        size_t idx_new_name;
+        // An enum describing the action to take.
+        size_t idx_action;
+        // The full remote URL of the Realm on the ROS.
+        size_t idx_url;
+        // The local UUID of the user to whom the file action applies (despite the internal column name).
+        size_t idx_user_identity;
+    };
+
+    enum class Action {
+        // The Realm files at the given directory will be deleted.
+        DeleteRealm,
+        // The Realm file will be copied to a 'recovery' directory, and the original Realm files will be deleted.
+        BackUpThenDeleteRealm
+    };
+
+    // The absolute path to the Realm file in question.
+    std::string original_name() const;
+
+    // The meaning of this parameter depends on the `Action` specified.
+    // For `BackUpThenDeleteRealm`, it is the absolute path where the backup copy
+    // of the Realm file found at `original_name()` will be placed.
+    // For all other `Action`s, it is ignored.
+    util::Optional<std::string> new_name() const;
+
+    // Get the local UUID of the user associated with this file action metadata.
+    std::string user_local_uuid() const;
+
+    Action action() const;
+    std::string url() const;
+    void remove();
+
+    // INTERNAL USE ONLY
+    SyncFileActionMetadata(Schema schema, SharedRealm realm, RowExpr row);
+private:
+    SharedRealm m_realm;
+    Schema m_schema;
+    Row m_row;
+};
+
+class SyncClientMetadata {
+public:
+    struct Schema {
+        // A UUID that identifies this client.
+        size_t idx_uuid;
+    };
+};
+
+template<class T>
+class SyncMetadataResults {
+public:
+    size_t size() const
+    {
+        return m_results.size();
+    }
+
+    T get(size_t idx) const
+    {
+        RowExpr row = m_results.get(idx);
+        return T(m_schema, m_realm, row);
+    }
+
+    SyncMetadataResults(Results results, SharedRealm realm, typename T::Schema schema)
+    : m_schema(std::move(schema))
+    , m_realm(std::move(realm))
+    , m_results(std::move(results))
+    { }
+private:
+    typename T::Schema m_schema;
+    SharedRealm m_realm;
+    // FIXME: remove 'mutable' once `realm::Results` is properly annotated for const
+    mutable Results m_results;
+};
+using SyncUserMetadataResults = SyncMetadataResults<SyncUserMetadata>;
+using SyncFileActionMetadataResults = SyncMetadataResults<SyncFileActionMetadata>;
+
+// A facade for the application's metadata Realm.
+class SyncMetadataManager {
+friend class SyncUserMetadata;
+friend class SyncFileActionMetadata;
+public:
+    // Return a Results object containing all users not marked for removal.
+    SyncUserMetadataResults all_unmarked_users() const;
+
+    // Return a Results object containing all users marked for removal. It is the binding's responsibility to call
+    // `remove()` on each user to actually remove it from the database. (This is so that already-open Realm files can be
+    // safely cleaned up the next time the host is launched.)
+    SyncUserMetadataResults all_users_marked_for_removal() const;
+
+    // Return a Results object containing all pending actions.
+    SyncFileActionMetadataResults all_pending_actions() const;
+
+    // Delete an existing metadata action given the original name of the Realm it involves.
+    // Returns true iff there was an existing metadata action and it was deleted.
+    bool delete_metadata_action(const std::string&) const;
+
+    // Retrieve or create user metadata.
+    // Note: if `make_is_absent` is true and the user has been marked for deletion, it will be unmarked.
+    util::Optional<SyncUserMetadata> get_or_make_user_metadata(const std::string& identity, const std::string& url,
+                                                               bool make_if_absent=true) const;
+
+    // Retrieve file action metadata.
+    util::Optional<SyncFileActionMetadata> get_file_action_metadata(const std::string& path) const;
+
+    // Create file action metadata.
+    SyncFileActionMetadata make_file_action_metadata(const std::string& original_name,
+                                                     const std::string& url,
+                                                     const std::string& local_uuid,
+                                                     SyncFileActionMetadata::Action action,
+                                                     util::Optional<std::string> new_name=none) const;
+
+    // Get the unique identifier of this client, generating one if it does not already exist.
+    std::string client_uuid() const;
+
+    /// Construct the metadata manager.
+    ///
+    /// If the platform supports it, setting `should_encrypt` to `true` and not specifying an encryption key will make
+    /// the object store handle generating and persisting an encryption key for the metadata database. Otherwise, an
+    /// exception will be thrown.
+    SyncMetadataManager(std::string path,
+                        bool should_encrypt,
+                        util::Optional<std::vector<char>> encryption_key=none);
+
+private:
+    SyncUserMetadataResults get_users(bool marked) const;
+    Realm::Config m_metadata_config;
+    SyncUserMetadata::Schema m_user_schema;
+    SyncFileActionMetadata::Schema m_file_action_schema;
+    SyncClientMetadata::Schema m_client_schema;
+};
+
+}
+
+#endif // REALM_OS_SYNC_METADATA_HPP
diff --git a/iOS/Pods/Realm/include/sync/partial_sync.hpp b/iOS/Pods/Realm/include/sync/partial_sync.hpp
new file mode 100644 (file)
index 0000000..8186993
--- /dev/null
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_PARTIAL_SYNC_HPP
+#define REALM_OS_PARTIAL_SYNC_HPP
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace realm {
+
+class Realm;
+class Results;
+
+namespace partial_sync {
+
+void register_query(std::shared_ptr<Realm>, const std::string &object_class,
+                    const std::string &query,
+                    std::function<void (Results, std::exception_ptr)>);
+
+} // namespace partial_sync
+} // namespace realm
+
+#endif // REALM_OS_PARTIAL_SYNC_HPP
diff --git a/iOS/Pods/Realm/include/sync/sync_config.hpp b/iOS/Pods/Realm/include/sync/sync_config.hpp
new file mode 100644 (file)
index 0000000..6fb4774
--- /dev/null
@@ -0,0 +1,150 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_OS_SYNC_CONFIG_HPP
+#define REALM_OS_SYNC_CONFIG_HPP
+
+#include "sync_user.hpp"
+#include "sync_manager.hpp"
+
+#include <realm/util/assert.hpp>
+#include <realm/sync/client.hpp>
+#include <realm/sync/protocol.hpp>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <unordered_map>
+
+#include <realm/sync/history.hpp>
+
+namespace realm {
+
+class SyncUser;
+class SyncSession;
+
+using ChangesetTransformer = sync::ClientHistory::ChangesetCooker;
+
+enum class SyncSessionStopPolicy;
+
+struct SyncConfig;
+using SyncBindSessionHandler = void(const std::string&,          // path on disk of the Realm file.
+                                    const SyncConfig&,           // the sync configuration object.
+                                    std::shared_ptr<SyncSession> // the session which should be bound.
+                                    );
+
+struct SyncError;
+using SyncSessionErrorHandler = void(std::shared_ptr<SyncSession>, SyncError);
+
+struct SyncError {
+    using ProtocolError = realm::sync::ProtocolError;
+
+    std::error_code error_code;
+    std::string message;
+    bool is_fatal;
+    std::unordered_map<std::string, std::string> user_info;
+    /// The sync server may send down an error that the client does not recognize,
+    /// whether because of a version mismatch or an oversight. It is still valuable
+    /// to expose these errors so that users can do something about them.
+    bool is_unrecognized_by_client = false;
+
+    SyncError(std::error_code error_code, std::string message, bool is_fatal)
+        : error_code(std::move(error_code))
+        , message(std::move(message))
+        , is_fatal(is_fatal)
+    {
+    }
+
+    static constexpr const char c_original_file_path_key[] = "ORIGINAL_FILE_PATH";
+    static constexpr const char c_recovery_file_path_key[] = "RECOVERY_FILE_PATH";
+
+    /// The error is a client error, which applies to the client and all its sessions.
+    bool is_client_error() const
+    {
+        return error_code.category() == realm::sync::client_error_category();
+    }
+
+    /// The error is a protocol error, which may either be connection-level or session-level.
+    bool is_connection_level_protocol_error() const
+    {
+        if (error_code.category() != realm::sync::protocol_error_category()) {
+            return false;
+        }
+        return !realm::sync::is_session_level_error(static_cast<ProtocolError>(error_code.value()));
+    }
+
+    /// The error is a connection-level protocol error.
+    bool is_session_level_protocol_error() const
+    {
+        if (error_code.category() != realm::sync::protocol_error_category()) {
+            return false;
+        }
+        return realm::sync::is_session_level_error(static_cast<ProtocolError>(error_code.value()));
+    }
+
+    /// The error indicates a client reset situation.
+    bool is_client_reset_requested() const
+    {
+        if (error_code.category() != realm::sync::protocol_error_category()) {
+            return false;
+        }
+        // Documented here: https://realm.io/docs/realm-object-server/#client-recovery-from-a-backup
+        return (error_code == ProtocolError::bad_server_file_ident
+                || error_code == ProtocolError::bad_client_file_ident
+                || error_code == ProtocolError::bad_server_version
+                || error_code == ProtocolError::diverging_histories);
+    }
+};
+
+struct SyncConfig {
+    std::shared_ptr<SyncUser> user;
+    // The URL of the Realm, or of the reference Realm if partial sync is being used.
+    // The URL that will be used when connecting to the object server is that returned by `realm_url()`,
+    // and will differ from `reference_realm_url` if partial sync is being used.
+    // Set this field, but read from `realm_url()`.
+    std::string reference_realm_url;
+    SyncSessionStopPolicy stop_policy = SyncSessionStopPolicy::AfterChangesUploaded;
+    std::function<SyncBindSessionHandler> bind_session_handler = nullptr;
+    std::function<SyncSessionErrorHandler> error_handler = nullptr;
+    std::shared_ptr<ChangesetTransformer> transformer = nullptr;
+    util::Optional<std::array<char, 64>> realm_encryption_key = none;
+    bool client_validate_ssl = true;
+    util::Optional<std::string> ssl_trust_certificate_path = none;
+    std::function<sync::Session::SSLVerifyCallback> ssl_verify_callback = nullptr;
+    bool is_partial = false;
+    util::Optional<std::string> custom_partial_sync_identifier = none;
+
+    bool validate_sync_history = true;
+
+    // The URL that will be used when connecting to the object server.
+    // This will differ from `reference_realm_url` when partial sync is being used.
+    std::string realm_url() const;
+
+    SyncConfig(std::shared_ptr<SyncUser> user, std::string reference_realm_url)
+    : user(std::move(user))
+    , reference_realm_url(std::move(reference_realm_url))
+    {
+        if (this->reference_realm_url.find("/__partial/") != npos)
+            throw std::invalid_argument("A Realm URL may not contain the reserved string \"/__partial/\".");
+    }
+};
+
+} // namespace realm
+
+#endif // REALM_OS_SYNC_CONFIG_HPP
diff --git a/iOS/Pods/Realm/include/sync/sync_manager.hpp b/iOS/Pods/Realm/include/sync/sync_manager.hpp
new file mode 100644 (file)
index 0000000..2fbd470
--- /dev/null
@@ -0,0 +1,211 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_OS_SYNC_MANAGER_HPP
+#define REALM_OS_SYNC_MANAGER_HPP
+
+#include "shared_realm.hpp"
+
+#include "sync_user.hpp"
+
+#include <realm/sync/client.hpp>
+#include <realm/util/logger.hpp>
+#include <realm/util/optional.hpp>
+
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+namespace realm {
+
+struct SyncConfig;
+class SyncSession;
+class SyncUser;
+class SyncFileManager;
+class SyncMetadataManager;
+class SyncFileActionMetadata;
+
+namespace _impl {
+struct SyncClient;
+}
+
+enum class SyncSessionStopPolicy {
+    Immediately,                    // Immediately stop the session as soon as all Realms/Sessions go out of scope.
+    LiveIndefinitely,               // Never stop the session.
+    AfterChangesUploaded,           // Once all Realms/Sessions go out of scope, wait for uploads to complete and stop.
+};
+
+class SyncLoggerFactory {
+public:
+    virtual std::unique_ptr<util::Logger> make_logger(util::Logger::Level) = 0;
+};
+
+class SyncManager {
+friend class SyncSession;
+public:
+    enum class MetadataMode {
+        NoEncryption,                   // Enable metadata, but disable encryption.
+        Encryption,                     // Enable metadata, and use encryption (automatic if possible).
+        NoMetadata,                     // Disable metadata.
+    };
+
+    static SyncManager& shared();
+
+    // Configure the metadata and file management subsystems. This MUST be called upon startup.
+    void configure_file_system(const std::string& base_file_path,
+                               MetadataMode metadata_mode=MetadataMode::Encryption,
+                               util::Optional<std::vector<char>> custom_encryption_key=none,
+                               bool reset_metadata_on_error=false);
+
+    // Immediately run file actions for a single Realm at a given original path.
+    // Returns whether or not a file action was successfully executed for the specified Realm.
+    // Preconditions: all references to the Realm at the given path must have already been invalidated.
+    // The metadata and file management subsystems must also have already been configured.
+    bool immediately_run_file_actions(const std::string& original_name);
+
+    // Use a single connection for all sync sessions for each host/port rather
+    // than one per session.
+    // This must be called before any sync sessions are created, cannot be
+    // disabled afterwards, and currently is incompatible with using a load
+    // balancer or automatic failover.
+    void enable_session_multiplexing();
+
+    void set_log_level(util::Logger::Level) noexcept;
+    void set_logger_factory(SyncLoggerFactory&) noexcept;
+
+    /// Control whether the sync client attempts to reconnect immediately. Only set this to `true` for testing purposes.
+    void set_client_should_reconnect_immediately(bool reconnect_immediately);
+    bool client_should_reconnect_immediately() const noexcept;
+
+    /// Ask all valid sync sessions to perform whatever tasks might be necessary to
+    /// re-establish connectivity with the Realm Object Server. It is presumed that
+    /// the caller knows that network connectivity has been restored.
+    ///
+    /// Refer to `SyncSession::handle_reconnect()` to see what sort of work is done
+    /// on a per-session basis.
+    void reconnect();
+
+    util::Logger::Level log_level() const noexcept;
+
+    std::shared_ptr<SyncSession> get_session(const std::string& path, const SyncConfig& config);
+    std::shared_ptr<SyncSession> get_existing_session(const std::string& path) const;
+    std::shared_ptr<SyncSession> get_existing_active_session(const std::string& path) const;
+
+    // If the metadata manager is configured, perform an update. Returns `true` iff the code was run.
+    bool perform_metadata_update(std::function<void(const SyncMetadataManager&)> update_function) const;
+
+    // Get a sync user for a given identity, or create one if none exists yet, and set its token.
+    // If a logged-out user exists, it will marked as logged back in.
+    std::shared_ptr<SyncUser> get_user(const SyncUserIdentifier& identifier, std::string refresh_token);
+
+    // Get or create an admin token user based on the given identity.
+    // Please note: a future version will remove this method and deprecate the
+    // use of identities for admin users completely.
+    // Warning: it is an error to create or get an admin token user with a given identity and
+    // specifying a URL, and later get that same user by specifying only the identity and no
+    // URL, or vice versa.
+    std::shared_ptr<SyncUser> get_admin_token_user_from_identity(const std::string& identity,
+                                                                 util::Optional<std::string> server_url,
+                                                                 const std::string& token);
+
+    // Get or create an admin token user for the given URL.
+    // If the user already exists, the token value will be ignored.
+    // If an old identity is provided and a directory for the user already exists, the directory
+    // will be renamed.
+    std::shared_ptr<SyncUser> get_admin_token_user(const std::string& server_url,
+                                                   const std::string& token,
+                                                   util::Optional<std::string> old_identity=none);
+
+    // Get an existing user for a given identifier, if one exists and is logged in.
+    std::shared_ptr<SyncUser> get_existing_logged_in_user(const SyncUserIdentifier&) const;
+
+    // Get all the users that are logged in and not errored out.
+    std::vector<std::shared_ptr<SyncUser>> all_logged_in_users() const;
+    // Gets the currently logged in user. If there are more than 1 users logged in, an exception is thrown.
+    std::shared_ptr<SyncUser> get_current_user() const;
+
+    // Get the default path for a Realm for the given user and absolute unresolved URL.
+    std::string path_for_realm(const SyncUser& user, const std::string& raw_realm_url) const;
+
+    // Get the path of the recovery directory for backed-up or recovered Realms.
+    std::string recovery_directory_path() const;
+
+    // Get the unique identifier of this client.
+    std::string client_uuid() const;
+
+    // Reset the singleton state for testing purposes. DO NOT CALL OUTSIDE OF TESTING CODE.
+    // Precondition: any synced Realms or `SyncSession`s must be closed or rendered inactive prior to
+    // calling this method.
+    void reset_for_testing();
+
+private:
+    using ReconnectMode = sync::Client::ReconnectMode;
+    
+    static constexpr const char c_admin_identity[] = "__auth";
+
+    // Stop tracking the session for the given path if it is inactive.
+    // No-op if the session is either still active or in the active sessions list
+    // due to someone holding a strong reference to it.
+    void unregister_session(const std::string& path);
+
+    SyncManager() = default;
+    SyncManager(const SyncManager&) = delete;
+    SyncManager& operator=(const SyncManager&) = delete;
+
+    _impl::SyncClient& get_sync_client() const;
+    std::unique_ptr<_impl::SyncClient> create_sync_client() const;
+
+    std::shared_ptr<SyncSession> get_existing_session_locked(const std::string& path) const;
+
+    mutable std::mutex m_mutex;
+
+    // FIXME: Should probably be util::Logger::Level::error
+    util::Logger::Level m_log_level = util::Logger::Level::info;
+    SyncLoggerFactory* m_logger_factory = nullptr;
+    ReconnectMode m_client_reconnect_mode = ReconnectMode::normal;
+
+    bool run_file_action(const SyncFileActionMetadata&);
+
+    // Protects m_users
+    mutable std::mutex m_user_mutex;
+
+    // A map of user ID/auth server URL pairs to (shared pointers to) SyncUser objects.
+    std::unordered_map<SyncUserIdentifier, std::shared_ptr<SyncUser>> m_users;
+    // A map of local identifiers to admin token users.
+    std::unordered_map<std::string, std::shared_ptr<SyncUser>> m_admin_token_users;
+
+    mutable std::unique_ptr<_impl::SyncClient> m_sync_client;
+    bool m_multiplex_sessions = false;
+
+    // Protects m_file_manager and m_metadata_manager
+    mutable std::mutex m_file_system_mutex;
+    std::unique_ptr<SyncFileManager> m_file_manager;
+    std::unique_ptr<SyncMetadataManager> m_metadata_manager;
+
+    // Protects m_sessions
+    mutable std::mutex m_session_mutex;
+
+    // Map of sessions by path name.
+    // Sessions remove themselves from this map by calling `unregister_session` once they're
+    // inactive and have performed any necessary cleanup work.
+    std::unordered_map<std::string, std::shared_ptr<SyncSession>> m_sessions;
+};
+
+} // namespace realm
+
+#endif // REALM_OS_SYNC_MANAGER_HPP
diff --git a/iOS/Pods/Realm/include/sync/sync_permission.hpp b/iOS/Pods/Realm/include/sync/sync_permission.hpp
new file mode 100644 (file)
index 0000000..393c342
--- /dev/null
@@ -0,0 +1,179 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_SYNC_PERMISSION_HPP
+#define REALM_OS_SYNC_PERMISSION_HPP
+
+#include "results.hpp"
+#include "shared_realm.hpp"
+
+namespace realm {
+
+class Permissions;
+class SyncUser;
+class Object;
+
+namespace util {
+    class Any;
+}
+
+// A permission encapsulates a single access level.
+// Each level includes all the capabilities of the level
+// above it (for example, 'write' implies 'read').
+enum class AccessLevel {
+    None,
+    Read,
+    Write,
+    Admin,
+};
+
+// Permission object used to represent a user permission.
+// Permission objects can be passed into or returned by various permissions
+// APIs. They are immutable objects.
+struct Permission {
+    // The path of the Realm to which this permission pertains.
+    std::string path;
+
+    AccessLevel access;
+
+    // Return the string description of an `AccessLevel`.
+    static std::string description_for_access_level(AccessLevel level);
+
+    // Return whether two paths are equivalent: either because they are exactly
+    // equal, or because user ID subtitution of one tilde-delimited path results
+    // in a path identical to the other path.
+    // Warning: this method does NOT strip or add leading or trailing slashes or whitespace.
+    // For example: "/~/foo" is equivalent to "/~/foo"; "/1/foo" is equivalent to "/1/foo".
+    // "/~/foo" is equivalent to "/1/foo" for a user ID of 1.
+    static bool paths_are_equivalent(std::string path_1, std::string path_2,
+                                     const std::string& user_id_1, const std::string& user_id_2);
+
+    // Condition is a userId or a KeyValue pair
+    // Other conditions may be supported in the future
+    struct Condition {
+        enum class Type {
+            // The permission is applied to a single user based on their user ID
+            UserId,
+            // The permission is based on any user that meets a criterion specified by key/value.
+            KeyValue,
+        };
+        Type type;
+
+        // FIXME: turn this back into a union type
+        std::string user_id;
+        std::pair<std::string, std::string> key_value;
+
+        Condition() {}
+
+        Condition(std::string id)
+        : type(Type::UserId)
+        , user_id(std::move(id))
+        { }
+
+        Condition(std::string key, std::string value)
+        : type(Type::KeyValue)
+        , key_value(std::make_pair(std::move(key), std::move(value)))
+        { }
+    };
+    Condition condition;
+
+    Timestamp updated_at;
+
+    /// Create a Permission value from an `Object`.
+    Permission(Object&);
+
+    /// Create a Permission value from raw values.
+    Permission(std::string path, AccessLevel, Condition, Timestamp updated_at=Timestamp());
+};
+
+struct PermissionOffer {
+    std::string path;
+    AccessLevel access;
+    Timestamp expiration;
+};
+
+class Permissions {
+public:
+    // Consumers of these APIs need to pass in a method which creates a Config with the proper
+    // SyncConfig and associated callbacks, as well as the path and other parameters.
+    using ConfigMaker = std::function<Realm::Config(std::shared_ptr<SyncUser>, std::string url)>;
+
+    // Callback used to asynchronously vend permissions results.
+    using PermissionResultsCallback = std::function<void(Results, std::exception_ptr)>;
+
+    // Callback used to asynchronously vend permission offer or response URL.
+    using PermissionOfferCallback = std::function<void(util::Optional<std::string>, std::exception_ptr)>;
+
+    // Asynchronously retrieve a `Results` containing the permissions for the provided user.
+    static void get_permissions(std::shared_ptr<SyncUser>, PermissionResultsCallback, const ConfigMaker&);
+
+    // Callback used to monitor success or errors when changing permissions
+    // or accepting a permission offer.
+    // `exception_ptr` is null_ptr on success
+    using PermissionChangeCallback = std::function<void(std::exception_ptr)>;
+
+    // Set a permission as the provided user.
+    static void set_permission(std::shared_ptr<SyncUser>, Permission, PermissionChangeCallback, const ConfigMaker&);
+
+    // Delete a permission as the provided user.
+    static void delete_permission(std::shared_ptr<SyncUser>, Permission, PermissionChangeCallback, const ConfigMaker&);
+
+    // Create a permission offer. The callback will be passed the token, if successful.
+    static void make_offer(std::shared_ptr<SyncUser>, PermissionOffer, PermissionOfferCallback, const ConfigMaker&);
+
+    // Accept a permission offer based on the token value within the offer.
+    static void accept_offer(std::shared_ptr<SyncUser>, const std::string&, PermissionOfferCallback, const ConfigMaker&);
+
+    using AsyncOperationHandler = std::function<void(Object*, std::exception_ptr)>;
+
+private:
+    static SharedRealm management_realm(std::shared_ptr<SyncUser>, const ConfigMaker&);
+    static SharedRealm permission_realm(std::shared_ptr<SyncUser>, const ConfigMaker&);
+
+    /**
+     Perform an asynchronous operation that involves writing an object to the
+     user's management Realm, and then waiting for the operation to succeed or
+     fail.
+
+     The object in question must have at least `id`, `createdAt`, and `updatedAt`,
+     properties to be set as part of the request, and it must report its success
+     or failure by setting its `statusCode` and `statusMessage` properties.
+
+     The callback is invoked upon success or failure, and will be called with
+     exactly one of its two arguments not set to null. The object can be used to
+     extract additional data to be returned to the caller.
+     */
+    static void perform_async_operation(const std::string& object_type,
+                                        std::shared_ptr<SyncUser>,
+                                        AsyncOperationHandler,
+                                        std::map<std::string, util::Any>,
+                                        const ConfigMaker&);
+};
+
+struct PermissionActionException : std::runtime_error {
+    long long code;
+
+    PermissionActionException(std::string message, long long code)
+    : std::runtime_error(std::move(message))
+    , code(code)
+    { }
+};
+
+}
+
+#endif /* REALM_OS_SYNC_PERMISSION_HPP */
diff --git a/iOS/Pods/Realm/include/sync/sync_session.hpp b/iOS/Pods/Realm/include/sync/sync_session.hpp
new file mode 100644 (file)
index 0000000..2c2237f
--- /dev/null
@@ -0,0 +1,352 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_OS_SYNC_SESSION_HPP
+#define REALM_OS_SYNC_SESSION_HPP
+
+#include "feature_checks.hpp"
+#include "sync/sync_config.hpp"
+
+#include <realm/util/optional.hpp>
+#include <realm/version_id.hpp>
+
+#include <mutex>
+#include <unordered_map>
+
+namespace realm {
+
+class SyncManager;
+class SyncUser;
+
+namespace _impl {
+class RealmCoordinator;
+struct SyncClient;
+
+namespace sync_session_states {
+struct WaitingForAccessToken;
+struct Active;
+struct Dying;
+struct Inactive;
+}
+}
+
+namespace sync {
+class Session;
+}
+
+using SyncSessionTransactCallback = void(VersionID old_version, VersionID new_version);
+using SyncProgressNotifierCallback = void(uint64_t transferred_bytes, uint64_t transferrable_bytes);
+
+class SyncSession : public std::enable_shared_from_this<SyncSession> {
+public:
+    enum class PublicState {
+        WaitingForAccessToken,
+        Active,
+        Dying,
+        Inactive,
+
+        // FIXME: This state no longer exists. This should be removed.
+        Error,
+    };
+    PublicState state() const;
+
+    // FIXME: The error state no longer exists. This should be removed.
+    bool is_in_error_state() const { return false; }
+
+    // The on-disk path of the Realm file backing the Realm this `SyncSession` represents.
+    std::string const& path() const { return m_realm_path; }
+
+    // Register a callback that will be called when all pending uploads have completed.
+    // The callback is run asynchronously, and upon whatever thread the underlying sync client
+    // chooses to run it on. The method returns immediately with true if the callback was
+    // successfully registered, false otherwise. If the method returns false the callback will
+    // never be run.
+    // This method will return true if the completion handler was registered, either immediately
+    // or placed in a queue. If it returns true the completion handler will always be called
+    // at least once, except in the case where a logged-out session is never logged back in.
+    bool wait_for_upload_completion(std::function<void(std::error_code)> callback);
+
+    // Register a callback that will be called when all pending downloads have been completed.
+    // Works the same way as `wait_for_upload_completion()`.
+    bool wait_for_download_completion(std::function<void(std::error_code)> callback);
+
+    enum class NotifierType {
+        upload, download
+    };
+    // Register a notifier that updates the app regarding progress.
+    //
+    // If `m_current_progress` is populated when this method is called, the notifier
+    // will be called synchronously, to provide the caller with an initial assessment
+    // of the state of synchronization. Otherwise, the progress notifier will be
+    // registered, and only called once sync has begun providing progress data.
+    //
+    // If `is_streaming` is true, then the notifier will be called forever, and will
+    // always contain the most up-to-date number of downloadable or uploadable bytes.
+    // Otherwise, the number of downloaded or uploaded bytes will always be reported
+    // relative to the number of downloadable or uploadable bytes at the point in time
+    // when the notifier was registered.
+    //
+    // An integer representing a token is returned. This token can be used to manually
+    // unregister the notifier. If the integer is 0, the notifier was not registered.
+    //
+    // Note that bindings should dispatch the callback onto a separate thread or queue
+    // in order to avoid blocking the sync client.
+    uint64_t register_progress_notifier(std::function<SyncProgressNotifierCallback>, NotifierType, bool is_streaming);
+
+    // Unregister a previously registered notifier. If the token is invalid,
+    // this method does nothing.
+    void unregister_progress_notifier(uint64_t);
+
+    // If possible, take the session and do anything necessary to make it `Active`.
+    // Specifically:
+    // If the sync session is currently `Dying`, ask it to stay alive instead.
+    // If the sync session is currently `WaitingForAccessToken`, cancel any deferred close.
+    // If the sync session is currently `Inactive`, recreate it.
+    // Otherwise, a no-op.
+    void revive_if_needed();
+
+    // Perform any actions needed in response to regaining network connectivity.
+    // Specifically:
+    // If the sync session is currently `WaitingForAccessToken`, make the binding ask the auth server for a token.
+    // Otherwise, a no-op.
+    void handle_reconnect();
+
+    // Give the `SyncSession` a new, valid token, and ask it to refresh the underlying session.
+    // If the session can't accept a new token, this method does nothing.
+    // Note that, if this is the first time the session will be given a token, `server_url` must
+    // be set.
+    void refresh_access_token(std::string access_token, util::Optional<std::string> server_url);
+
+    // FIXME: we need an API to allow the binding to tell sync that the access token fetch failed
+    // or was cancelled, and cannot be retried.
+
+    // Set the multiplex identifier used for this session. Sessions with different identifiers are
+    // never multiplexed into a single connection, even if they are connecting to the same host.
+    // The value of the token is otherwise treated as an opaque token.
+    //
+    // Has no effect if session multiplexing is not enabled (see SyncManager::enable_session_multiplexing())
+    // or if called after the Sync session is created. In particular, changing the multiplex identity will
+    // not make the session reconnect.
+    void set_multiplex_identifier(std::string multiplex_identity);
+
+    // Inform the sync session that it should close.
+    void close();
+
+    // Inform the sync session that it should log out.
+    void log_out();
+
+    // Override the address and port of the server that this `SyncSession` is connected to. If the
+    // session is already connected, it will disconnect and then reconnect to the specified address.
+    // If it's not already connected, future connection attempts will be to the specified address.
+    //
+    // NOTE: This is intended for use only in very specific circumstances. Please check with the
+    // object store team before using it.
+    void override_server(std::string address, int port);
+
+    // An object representing the user who owns the Realm this `SyncSession` represents.
+    std::shared_ptr<SyncUser> user() const
+    {
+        return m_config.user;
+    }
+
+    // A copy of the configuration object describing the Realm this `SyncSession` represents.
+    const SyncConfig& config() const
+    {
+        return m_config;
+    }
+
+    // If the `SyncSession` has been configured, the full remote URL of the Realm
+    // this `SyncSession` represents.
+    util::Optional<std::string> full_realm_url() const
+    {
+        return m_server_url;
+    }
+
+    // Create an external reference to this session. The sync session attempts to remain active
+    // as long as an external reference to the session exists.
+    std::shared_ptr<SyncSession> external_reference();
+
+    // Return an existing external reference to this session, if one exists. Otherwise, returns `nullptr`.
+    std::shared_ptr<SyncSession> existing_external_reference();
+
+    // Expose some internal functionality to other parts of the ObjectStore
+    // without making it public to everyone
+    class Internal {
+        friend class _impl::RealmCoordinator;
+
+        static void set_sync_transact_callback(SyncSession& session,
+                                               std::function<SyncSessionTransactCallback> callback)
+        {
+            session.set_sync_transact_callback(std::move(callback));
+        }
+
+        static void nonsync_transact_notify(SyncSession& session, VersionID::version_type version)
+        {
+            session.nonsync_transact_notify(version);
+        }
+    };
+
+    // Expose some internal functionality to testing code.
+    struct OnlyForTesting {
+        static void handle_error(SyncSession& session, SyncError error)
+        {
+            session.handle_error(std::move(error));
+        }
+
+        static void handle_progress_update(SyncSession& session, uint64_t downloaded, uint64_t downloadable,
+                                           uint64_t uploaded, uint64_t uploadable, bool is_fresh=true) {
+            session.handle_progress_update(downloaded, downloadable, uploaded, uploadable, is_fresh);
+        }
+
+        static bool has_stale_progress(SyncSession& session)
+        {
+            return session.m_current_progress != none && !session.m_latest_progress_data_is_fresh;
+        }
+
+        static bool has_fresh_progress(SyncSession& session)
+        {
+            return session.m_latest_progress_data_is_fresh;
+        }
+    };
+
+private:
+    using std::enable_shared_from_this<SyncSession>::shared_from_this;
+
+    struct State;
+    friend struct _impl::sync_session_states::WaitingForAccessToken;
+    friend struct _impl::sync_session_states::Active;
+    friend struct _impl::sync_session_states::Dying;
+    friend struct _impl::sync_session_states::Inactive;
+
+    friend class realm::SyncManager;
+    // Called by SyncManager {
+    static std::shared_ptr<SyncSession> create(_impl::SyncClient& client, std::string realm_path, SyncConfig config)
+    {
+        struct MakeSharedEnabler : public SyncSession {
+            MakeSharedEnabler(_impl::SyncClient& client, std::string realm_path, SyncConfig config)
+            : SyncSession(client, std::move(realm_path), std::move(config))
+            {}
+        };
+        return std::make_shared<MakeSharedEnabler>(client, std::move(realm_path), std::move(config));
+    }
+    // }
+
+    SyncSession(_impl::SyncClient&, std::string realm_path, SyncConfig);
+
+    void handle_error(SyncError);
+    void cancel_pending_waits();
+    enum class ShouldBackup { yes, no };
+    void update_error_and_mark_file_for_deletion(SyncError&, ShouldBackup);
+    static std::string get_recovery_file_path();
+    void handle_progress_update(uint64_t, uint64_t, uint64_t, uint64_t, bool);
+
+    void set_sync_transact_callback(std::function<SyncSessionTransactCallback>);
+    void nonsync_transact_notify(VersionID::version_type);
+
+    void advance_state(std::unique_lock<std::mutex>& lock, const State&);
+
+    void create_sync_session();
+    void unregister(std::unique_lock<std::mutex>& lock);
+    void did_drop_external_reference();
+
+    std::function<SyncSessionTransactCallback> m_sync_transact_callback;
+
+    // How many bytes are uploadable or downloadable.
+    struct Progress {
+        uint64_t uploadable;
+        uint64_t downloadable;
+        uint64_t uploaded;
+        uint64_t downloaded;
+    };
+
+    // A PODS encapsulating some information for progress notifier callbacks a binding
+    // can register upon this session.
+    struct NotifierPackage {
+        std::function<SyncProgressNotifierCallback> notifier;
+        bool is_streaming;
+        NotifierType direction;
+        util::Optional<uint64_t> captured_transferrable;
+
+        void update(const Progress&, bool);
+        std::function<void()> create_invocation(const Progress&, bool&) const;
+    };
+
+    // A counter used as a token to identify progress notifier callbacks registered on this session.
+    uint64_t m_progress_notifier_token = 1;
+    bool m_latest_progress_data_is_fresh;
+
+    // Will be `none` until we've received the initial notification from sync.  Note that this
+    // happens only once ever during the lifetime of a given `SyncSession`, since these values are
+    // expected to semi-monotonically increase, and a lower-bounds estimate is still useful in the
+    // event more up-to-date information isn't yet available.  FIXME: If we support transparent
+    // client reset in the future, we might need to reset the progress state variables if the Realm
+    // is rolled back.
+    util::Optional<Progress> m_current_progress;
+
+    std::unordered_map<uint64_t, NotifierPackage> m_notifiers;
+
+    mutable std::mutex m_state_mutex;
+    mutable std::mutex m_progress_notifier_mutex;
+
+    const State* m_state = nullptr;
+    size_t m_death_count = 0;
+
+    SyncConfig m_config;
+
+    std::string m_realm_path;
+    _impl::SyncClient& m_client;
+
+    // For storing wait-for-completion requests if the session isn't yet ready to handle them.
+    struct CompletionWaitPackage {
+        void(sync::Session::*waiter)(std::function<void(std::error_code)>);
+        std::function<void(std::error_code)> callback;
+    };
+    std::vector<CompletionWaitPackage> m_completion_wait_packages;
+
+    struct ServerOverride {
+        std::string address;
+        int port;
+    };
+    util::Optional<ServerOverride> m_server_override;
+
+    // The underlying `Session` object that is owned and managed by this `SyncSession`.
+    // The session is first created when the `SyncSession` is moved out of its initial `inactive` state.
+    // The session might be destroyed if the `SyncSession` becomes inactive again (for example, if the
+    // user owning the session logs out). It might be created anew if the session is revived (if a
+    // logged-out user logs back in, the object store sync code will revive their sessions).
+    std::unique_ptr<sync::Session> m_session;
+
+    // Whether or not the session object in `m_session` has been `bind()`ed before.
+    // This determines how the `SyncSession` behaves when refreshing tokens.
+    bool m_session_has_been_bound;
+
+    util::Optional<int_fast64_t> m_deferred_commit_notification;
+    bool m_deferred_close = false;
+
+    // The fully-resolved URL of this Realm, including the server and the path.
+    util::Optional<std::string> m_server_url;
+
+    std::string m_multiplex_identity;
+
+    class ExternalReference;
+    std::weak_ptr<ExternalReference> m_external_reference;
+};
+
+}
+
+#endif // REALM_OS_SYNC_SESSION_HPP
diff --git a/iOS/Pods/Realm/include/sync/sync_user.hpp b/iOS/Pods/Realm/include/sync/sync_user.hpp
new file mode 100644 (file)
index 0000000..faec7cf
--- /dev/null
@@ -0,0 +1,198 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_OS_SYNC_USER_HPP
+#define REALM_OS_SYNC_USER_HPP
+
+#include <string>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+#include <mutex>
+
+#include "util/atomic_shared_ptr.hpp"
+
+#include <realm/util/optional.hpp>
+
+namespace realm {
+
+class SyncSession;
+
+// A superclass that bindings can inherit from in order to store information
+// upon a `SyncUser` object.
+class SyncUserContext {
+public:
+    virtual ~SyncUserContext() = default;
+};
+
+using SyncUserContextFactory = std::function<std::shared_ptr<SyncUserContext>()>;
+
+// A struct that uniquely identifies a user. Consists of ROS identity and auth server URL.
+struct SyncUserIdentifier {
+    std::string user_id;
+    std::string auth_server_url;
+
+    bool operator==(const SyncUserIdentifier& other) const
+    {
+        return user_id == other.user_id && auth_server_url == other.auth_server_url;
+    }
+};
+
+// A `SyncUser` represents a single user account. Each user manages the sessions that
+// are associated with it.
+class SyncUser {
+friend class SyncSession;
+public:
+    enum class TokenType {
+        Normal,
+        Admin,
+    };
+
+    enum class State {
+        LoggedOut,
+        Active,
+        Error,
+    };
+
+    // Don't use this directly; use the `SyncManager` APIs. Public for use with `make_shared`.
+    SyncUser(std::string refresh_token,
+             std::string identity,
+             util::Optional<std::string> server_url,
+             util::Optional<std::string> local_identity=none,
+             TokenType token_type=TokenType::Normal);
+
+    // Return a list of all sessions belonging to this user.
+    std::vector<std::shared_ptr<SyncSession>> all_sessions();
+
+    // Return a session for a given on disk path.
+    // In most cases, bindings shouldn't expose this to consumers, since the on-disk
+    // path for a synced Realm is an opaque implementation detail. This API is retained
+    // for testing purposes, and for bindings for consumers that are servers or tools.
+    std::shared_ptr<SyncSession> session_for_on_disk_path(const std::string& path);
+
+    // Update the user's refresh token. If the user is logged out, it will log itself back in.
+    // Note that this is called by the SyncManager, and should not be directly called.
+    void update_refresh_token(std::string token);
+
+    // Log the user out and mark it as such. This will also close its associated Sessions.
+    void log_out();
+
+    // Whether the user has administrator privileges.
+    bool is_admin() const noexcept
+    {
+        return m_token_type == TokenType::Admin || m_is_admin;
+    }
+
+    TokenType token_type() const noexcept
+    {
+        return m_token_type;
+    }
+
+    // Specify whether the user has administrator privileges.
+    // Note that this is an internal flag meant for bindings to communicate information
+    // originating from the server. It is *NOT* possible to unilaterally change a user's
+    // administrator status from the client through this or any other API.
+    void set_is_admin(bool);
+
+    std::string identity() const noexcept
+    {
+        return m_identity;
+    }
+
+    const std::string& server_url() const noexcept
+    {
+        return m_server_url;
+    }
+
+    const std::string& local_identity() const noexcept
+    {
+        return m_local_identity;
+    }
+
+    std::string refresh_token() const;
+    State state() const;
+
+    std::shared_ptr<SyncUserContext> binding_context() const
+    {
+        return m_binding_context.load();
+    }
+
+    // Register a session to this user.
+    // A registered session will be bound at the earliest opportunity: either
+    // immediately, or upon the user becoming Active.
+    // Note that this is called by the SyncManager, and should not be directly called.
+    void register_session(std::shared_ptr<SyncSession>);
+
+    // Optionally set a context factory. If so, must be set before any sessions are created.
+    static void set_binding_context_factory(SyncUserContextFactory factory);
+
+    // Internal APIs. Do not call.
+    void register_management_session(const std::string&);
+    void register_permission_session(const std::string&);
+
+private:
+    static SyncUserContextFactory s_binding_context_factory;
+    static std::mutex s_binding_context_factory_mutex;
+
+    State m_state;
+
+    util::AtomicSharedPtr<SyncUserContext> m_binding_context;
+
+    // A locally assigned UUID intended to provide a level of indirection for various features.
+    std::string m_local_identity;
+
+    std::weak_ptr<SyncSession> m_management_session;
+    std::weak_ptr<SyncSession> m_permission_session;
+
+    // The auth server URL associated with this user. Set upon creation. The empty string for
+    // auth token users.
+    std::string m_server_url;
+
+    // Mark the user as invalid, since a fatal user-related error was encountered.
+    void invalidate();
+
+    mutable std::mutex m_mutex;
+
+    // The token type of the user.
+    // FIXME: remove this flag once bindings take responsible for admin token users
+    TokenType m_token_type;
+
+    bool m_is_admin;
+
+    // The user's refresh token.
+    std::string m_refresh_token;
+    // Set by the server. The unique ID of the user account on the Realm Object Server.
+    std::string m_identity;
+
+    // Sessions are owned by the SyncManager, but the user keeps a map of weak references
+    // to them.
+    std::unordered_map<std::string, std::weak_ptr<SyncSession>> m_sessions;
+
+    // Waiting sessions are those that should be asked to connect once this user is logged in.
+    std::unordered_map<std::string, std::weak_ptr<SyncSession>> m_waiting_sessions;
+};
+
+}
+
+namespace std {
+template<> struct hash<realm::SyncUserIdentifier> {
+    size_t operator()(realm::SyncUserIdentifier const&) const;
+};
+}
+
+#endif // REALM_OS_SYNC_USER_HPP
diff --git a/iOS/Pods/Realm/include/thread_safe_reference.hpp b/iOS/Pods/Realm/include/thread_safe_reference.hpp
new file mode 100644 (file)
index 0000000..ca3cc19
--- /dev/null
@@ -0,0 +1,113 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_THREAD_SAFE_REFERENCE_HPP
+#define REALM_THREAD_SAFE_REFERENCE_HPP
+
+
+#include <realm/group_shared.hpp>
+
+namespace realm {
+class LinkView;
+class List;
+class Object;
+class Query;
+class Realm;
+class Results;
+class TableView;
+template<typename T> class BasicRow;
+typedef BasicRow<Table> Row;
+
+// Opaque type representing an object for handover
+class ThreadSafeReferenceBase {
+public:
+    ThreadSafeReferenceBase(const ThreadSafeReferenceBase&) = delete;
+    ThreadSafeReferenceBase& operator=(const ThreadSafeReferenceBase&) = delete;
+    ThreadSafeReferenceBase(ThreadSafeReferenceBase&&) = default;
+    ThreadSafeReferenceBase& operator=(ThreadSafeReferenceBase&&) = default;
+    ThreadSafeReferenceBase();
+    virtual ~ThreadSafeReferenceBase();
+
+    bool is_invalidated() const { return m_source_realm == nullptr; };
+
+protected:
+    // Precondition: The associated Realm is for the current thread and is not in a write transaction;.
+    ThreadSafeReferenceBase(std::shared_ptr<Realm> source_realm);
+
+    SharedGroup& get_source_shared_group() const;
+
+    template <typename V, typename T>
+    V invalidate_after_import(Realm& destination_realm, T construct_with_shared_group);
+
+private:
+    friend Realm;
+
+    VersionID m_version_id;
+    std::shared_ptr<Realm> m_source_realm; // Strong reference keeps alive so version stays pinned! Don't touch!!
+
+    bool has_same_config(Realm& realm) const;
+    void invalidate();
+};
+
+template <typename T>
+class ThreadSafeReference;
+
+template<>
+class ThreadSafeReference<List>: public ThreadSafeReferenceBase {
+    friend class Realm;
+
+    std::unique_ptr<SharedGroup::Handover<LinkView>> m_link_view;
+    std::unique_ptr<SharedGroup::Handover<Table>> m_table;
+
+    // Precondition: The associated Realm is for the current thread and is not in a write transaction;.
+    ThreadSafeReference(List const& value);
+
+    // Precondition: Realm and handover are on same version.
+    List import_into_realm(std::shared_ptr<Realm> realm) &&;
+};
+
+template<>
+class ThreadSafeReference<Object>: public ThreadSafeReferenceBase {
+    friend class Realm;
+
+    std::unique_ptr<SharedGroup::Handover<Row>> m_row;
+    std::string m_object_schema_name;
+
+    // Precondition: The associated Realm is for the current thread and is not in a write transaction;.
+    ThreadSafeReference(Object const& value);
+
+    // Precondition: Realm and handover are on same version.
+    Object import_into_realm(std::shared_ptr<Realm> realm) &&;
+};
+
+template<>
+class ThreadSafeReference<Results>: public ThreadSafeReferenceBase {
+    friend class Realm;
+
+    std::unique_ptr<SharedGroup::Handover<Query>> m_query;
+    DescriptorOrdering::HandoverPatch m_ordering_patch;
+
+    // Precondition: The associated Realm is for the current thread and is not in a write transaction;.
+    ThreadSafeReference(Results const& value);
+
+    // Precondition: Realm and handover are on same version.
+    Results import_into_realm(std::shared_ptr<Realm> realm) &&;
+};
+}
+
+#endif /* REALM_THREAD_SAFE_REFERENCE_HPP */
diff --git a/iOS/Pods/Realm/include/util/aligned_union.hpp b/iOS/Pods/Realm/include/util/aligned_union.hpp
new file mode 100644 (file)
index 0000000..b75c649
--- /dev/null
@@ -0,0 +1,65 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 utilied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef REALM_OS_ALIGNED_UNION_HPP
+#define REALM_OS_ALIGNED_UNION_HPP
+
+#include <cstddef>
+#include <initializer_list>
+#include <type_traits>
+
+namespace realm {
+
+// Provide our own implementation of max as GCC 4.9's is not marked as constexpr.
+namespace _impl {
+
+template <typename T>
+static constexpr const T& constexpr_max(const T& a, const T& b)
+{
+    return a > b ? a : b;
+}
+
+template <typename T>
+static constexpr const T& constexpr_max(const T* begin, const T *end)
+{
+    return begin + 1 == end ? *begin : constexpr_max(*begin, constexpr_max(begin + 1, end));
+}
+
+template <typename T>
+static constexpr const T& constexpr_max(std::initializer_list<T> list)
+{
+    return constexpr_max(list.begin(), list.end());
+}
+
+} // namespace _impl
+
+namespace util {
+
+// Provide our own implementation of `std::aligned_union` as it is missing from GCC 4.9.
+template <size_t Len, typename... Types>
+struct AlignedUnion
+{
+    static constexpr size_t alignment_value = _impl::constexpr_max({alignof(Types)...});
+    static constexpr size_t storage_size = _impl::constexpr_max({Len, sizeof(Types)...});
+    using type = typename std::aligned_storage<storage_size, alignment_value>::type;
+};
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_OS_ALIGNED_UNION_HPP
diff --git a/iOS/Pods/Realm/include/util/apple/event_loop_signal.hpp b/iOS/Pods/Realm/include/util/apple/event_loop_signal.hpp
new file mode 100644 (file)
index 0000000..37a3d36
--- /dev/null
@@ -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 <atomic>
+#include <CoreFoundation/CFRunLoop.h>
+
+namespace realm {
+namespace util {
+template<typename Callback>
+class EventLoopSignal {
+public:
+    EventLoopSignal(Callback&& callback)
+    {
+        struct RefCountedRunloopCallback {
+            Callback callback;
+            std::atomic<size_t> ref_count;
+        };
+
+        CFRunLoopSourceContext ctx{};
+        ctx.info = new RefCountedRunloopCallback{std::move(callback), {0}};
+        ctx.perform = [](void* info) {
+            static_cast<RefCountedRunloopCallback*>(info)->callback();
+        };
+        ctx.retain = [](const void* info) {
+            static_cast<RefCountedRunloopCallback*>(const_cast<void*>(info))->ref_count.fetch_add(1, std::memory_order_relaxed);
+            return info;
+        };
+        ctx.release = [](const void* info) {
+            auto ptr = static_cast<RefCountedRunloopCallback*>(const_cast<void*>(info));
+            if (ptr->ref_count.fetch_add(-1, std::memory_order_acq_rel) == 1) {
+                delete ptr;
+            }
+        };
+
+        m_runloop = CFRunLoopGetCurrent();
+        CFRetain(m_runloop);
+        m_signal = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &ctx);
+        CFRunLoopAddSource(m_runloop, m_signal, kCFRunLoopDefaultMode);
+    }
+
+    ~EventLoopSignal()
+    {
+        CFRunLoopSourceInvalidate(m_signal);
+        CFRelease(m_signal);
+        CFRelease(m_runloop);
+    }
+
+    EventLoopSignal(EventLoopSignal&&) = delete;
+    EventLoopSignal& operator=(EventLoopSignal&&) = delete;
+    EventLoopSignal(EventLoopSignal const&) = delete;
+    EventLoopSignal& operator=(EventLoopSignal const&) = delete;
+
+    void notify()
+    {
+        CFRunLoopSourceSignal(m_signal);
+        // Signalling the source makes it run the next time the runloop gets
+        // to it, but doesn't make the runloop start if it's currently idle
+        // waiting for events
+        CFRunLoopWakeUp(m_runloop);
+    }
+
+private:
+    CFRunLoopRef m_runloop;
+    CFRunLoopSourceRef m_signal;
+};
+} // namespace util
+} // namespace realm
diff --git a/iOS/Pods/Realm/include/util/atomic_shared_ptr.hpp b/iOS/Pods/Realm/include/util/atomic_shared_ptr.hpp
new file mode 100644 (file)
index 0000000..b4eb52f
--- /dev/null
@@ -0,0 +1,148 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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_ATOMIC_SHARED_PTR_HPP
+#define REALM_ATOMIC_SHARED_PTR_HPP
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+
+namespace realm {
+namespace _impl {
+
+// Check if std::atomic_load has an overload taking a std::shared_ptr, and set
+// HasAtomicPtrOps to either true_type or false_type
+
+template<typename... Ts> struct make_void { typedef void type; };
+template<typename... Ts> using void_t = typename make_void<Ts...>::type;
+
+template<typename, typename = void_t<>>
+struct HasAtomicPtrOps : std::false_type { };
+
+template<class T>
+struct HasAtomicPtrOps<T, void_t<decltype(std::atomic_load(std::declval<T*>()))>> : std::true_type { };
+} // namespace _impl
+
+namespace util {
+// A wrapper for std::shared_ptr that enables sharing a shared_ptr instance
+// (and not just a thing *pointed to* by a shared_ptr) between threads. Is
+// lock-free iff the underlying shared_ptr implementation supports atomic
+// operations. Currently the only implemented operation other than copy/move
+// construction/assignment is exchange().
+template<typename T, bool = _impl::HasAtomicPtrOps<std::shared_ptr<T>>::value>
+class AtomicSharedPtr;
+
+template<typename T>
+class AtomicSharedPtr<T, true> {
+public:
+    AtomicSharedPtr() = default;
+    AtomicSharedPtr(std::shared_ptr<T> ptr) : m_ptr(std::move(ptr)) { }
+
+    AtomicSharedPtr(AtomicSharedPtr const& ptr) : m_ptr(std::atomic_load(&ptr.m_ptr)) { }
+    AtomicSharedPtr(AtomicSharedPtr&& ptr) : m_ptr(std::atomic_exchange(&ptr.m_ptr, {})) { }
+
+    AtomicSharedPtr& operator=(AtomicSharedPtr const& ptr)
+    {
+        if (&ptr != this) {
+            std::atomic_store(&m_ptr, std::atomic_load(&ptr.m_ptr));
+        }
+        return *this;
+    }
+
+    AtomicSharedPtr& operator=(AtomicSharedPtr&& ptr)
+    {
+        std::atomic_store(&m_ptr, std::atomic_exchange(&ptr.m_ptr, {}));
+        return *this;
+    }
+
+    std::shared_ptr<T> exchange(std::shared_ptr<T> ptr)
+    {
+        return std::atomic_exchange(&m_ptr, std::move(ptr));
+    }
+
+    std::shared_ptr<T> load() const noexcept
+    {
+        return std::atomic_load(&m_ptr);
+    }
+
+private:
+    std::shared_ptr<T> m_ptr = nullptr;
+};
+
+template<typename T>
+class AtomicSharedPtr<T, false> {
+public:
+    AtomicSharedPtr() = default;
+    AtomicSharedPtr(std::shared_ptr<T> ptr) : m_ptr(std::move(ptr)) { }
+
+    AtomicSharedPtr(AtomicSharedPtr const& ptr)
+    {
+        std::lock_guard<std::mutex> lock(ptr.m_mutex);
+        m_ptr = ptr.m_ptr;
+    }
+    AtomicSharedPtr(AtomicSharedPtr&& ptr)
+    {
+        std::lock_guard<std::mutex> lock(ptr.m_mutex);
+        m_ptr = std::move(ptr.m_ptr);
+    }
+
+    AtomicSharedPtr& operator=(AtomicSharedPtr const& ptr)
+    {
+        if (&ptr != this) {
+            // std::lock() ensures that these are locked in a consistent order
+            // to avoid deadlock
+            std::lock(m_mutex, ptr.m_mutex);
+            m_ptr = ptr.m_ptr;
+            m_mutex.unlock();
+            ptr.m_mutex.unlock();
+        }
+        return *this;
+    }
+
+    AtomicSharedPtr& operator=(AtomicSharedPtr&& ptr)
+    {
+        std::lock(m_mutex, ptr.m_mutex);
+        m_ptr = std::move(ptr.m_ptr);
+        m_mutex.unlock();
+        ptr.m_mutex.unlock();
+        return *this;
+    }
+
+    std::shared_ptr<T> exchange(std::shared_ptr<T> ptr)
+    {
+        std::lock_guard<std::mutex> lock(m_mutex);
+        m_ptr.swap(ptr);
+        return ptr;
+    }
+
+    std::shared_ptr<T> load() const noexcept
+    {
+        std::lock_guard<std::mutex> lock(m_mutex);
+        return m_ptr;
+    }
+
+private:
+    mutable std::mutex m_mutex;
+    std::shared_ptr<T> m_ptr = nullptr;
+};
+
+}
+}
+
+#endif // REALM_ATOMIC_SHARED_PTR_HPP
diff --git a/iOS/Pods/Realm/include/util/event_loop_signal.hpp b/iOS/Pods/Realm/include/util/event_loop_signal.hpp
new file mode 100644 (file)
index 0000000..a4f9665
--- /dev/null
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 utilied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#ifndef REALM_EVENT_LOOP_SIGNAL_HPP
+#define REALM_EVENT_LOOP_SIGNAL_HPP
+
+#include <realm/util/features.h>
+
+#if (defined(REALM_HAVE_UV) && REALM_HAVE_UV && !REALM_PLATFORM_APPLE) || (defined(REALM_PLATFORM_NODE) && REALM_PLATFORM_NODE)
+#define REALM_USE_UV 1
+#else
+#define REALM_USE_UV 0
+#endif
+
+#if !defined(REALM_USE_CF) && REALM_PLATFORM_APPLE
+#define REALM_USE_CF 1
+#elif !defined(REALM_USE_ALOOPER) && REALM_ANDROID
+#define REALM_USE_ALOOPER 1
+#endif
+
+#if REALM_USE_UV
+#include "util/uv/event_loop_signal.hpp"
+#elif REALM_USE_CF
+#include "util/apple/event_loop_signal.hpp"
+#elif REALM_USE_ALOOPER
+#include "util/android/event_loop_signal.hpp"
+#else
+#include "util/generic/event_loop_signal.hpp"
+#endif
+
+#endif // REALM_EVENT_LOOP_SIGNAL_HPP
diff --git a/iOS/Pods/Realm/include/util/tagged_bool.hpp b/iOS/Pods/Realm/include/util/tagged_bool.hpp
new file mode 100644 (file)
index 0000000..31513a0
--- /dev/null
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_UTIL_TAGGED_BOOL_HPP
+#define REALM_OS_UTIL_TAGGED_BOOL_HPP
+
+#include <type_traits>
+
+namespace realm {
+namespace util {
+// A type factory which defines a type which is implicitly convertable to and
+// from `bool`, but not to other TaggedBool types
+//
+// Usage:
+// using IsIndexed = util::TaggedBool<class IsIndexedTag>;
+// using IsPrimary = util::TaggedBool<class IsPrimaryTag>;
+// void foo(IsIndexed is_indexed, IsPrimary is_primary);
+//
+// foo(IsIndexed{true}, IsPrimary{false}); // compiles
+// foo(IsPrimary{true}, IsIndexed{false}); // doesn't compile
+template <typename Tag>
+struct TaggedBool {
+    // Allow explicit construction from anything convertible to bool
+    constexpr explicit TaggedBool(bool v) : m_value(v) { }
+
+    // Allow implicit construction from *just* bool and not things convertible
+    // to bool (such as other types of tagged bools)
+    template <typename Bool, typename = typename std::enable_if<std::is_same<Bool, bool>::value>::type>
+    constexpr TaggedBool(Bool v) : m_value(v) {}
+
+    constexpr TaggedBool(TaggedBool const& v) : m_value(v.m_value) {}
+
+    constexpr operator bool() const { return m_value; }
+    constexpr TaggedBool operator!() const { return TaggedBool{!m_value}; }
+
+    friend constexpr bool operator==(TaggedBool l, TaggedBool r) { return l.m_value == r.m_value; }
+    friend constexpr bool operator!=(TaggedBool l, TaggedBool r) { return l.m_value != r.m_value; }
+
+private:
+    bool m_value;
+};
+
+}
+}
+#endif // REALM_OS_UTIL_TAGGED_BOOL_HPP
diff --git a/iOS/Pods/Realm/include/util/time.hpp b/iOS/Pods/Realm/include/util/time.hpp
new file mode 100644 (file)
index 0000000..c0fceb8
--- /dev/null
@@ -0,0 +1,72 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_UTIL_TIME_HPP
+#define REALM_OS_UTIL_TIME_HPP
+
+#include <cstring>
+#include <ctime>
+#include <string>
+#include <system_error>
+
+namespace realm {
+namespace util {
+
+// Like std::localtime, but safe with reentrancy and multiple threads.
+inline std::tm localtime(std::time_t time)
+{
+    std::tm calendar_time;
+#ifdef _WIN32
+    // note that VC++'s localtime_s has a different signature from what C++11 specifies
+    auto error = localtime_s(&calendar_time, &time);
+    if (error)
+        throw std::system_error(error, std::system_category());
+#else
+    auto result = localtime_r(&time, &calendar_time);
+    if (!result)
+        throw std::system_error(errno, std::system_category());
+#endif
+
+    return calendar_time;
+}
+
+// Like std::put_time, but compatible with GCC 4.9.
+inline std::string put_time(std::time_t time, const char *format)
+{
+    std::tm calendar_time = localtime(time);
+    size_t estimated_length = std::strlen(format) + 1;
+
+    size_t formatted_length;
+    std::string buffer;
+
+    // Loop until the buffer is large enough to hold the string generated by `strftime`, growing the
+    // buffer by 8 characters whenever it is too small to hold the resulting string.
+    do {
+        buffer.resize(estimated_length);
+        formatted_length = strftime(&buffer[0], buffer.size(), format, &calendar_time);
+        estimated_length += 8;
+    } while (formatted_length == 0);
+
+    buffer.resize(formatted_length);
+    return buffer;
+}
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_OS_UTIL_TIME_HPP
diff --git a/iOS/Pods/Realm/include/util/uuid.hpp b/iOS/Pods/Realm/include/util/uuid.hpp
new file mode 100644 (file)
index 0000000..8f37e52
--- /dev/null
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 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_OS_UTIL_UUID_HPP
+#define REALM_OS_UTIL_UUID_HPP
+
+#include <string>
+
+namespace realm {
+namespace util {
+
+// Generate a random UUID and return its formatted string representation.
+std::string uuid_string();
+
+} // namespace util
+} // namespace realm
+
+#endif // REALM_OS_UTIL_UUID_HPP
diff --git a/iOS/Pods/RealmSwift/LICENSE b/iOS/Pods/RealmSwift/LICENSE
new file mode 100644 (file)
index 0000000..57a0e0b
--- /dev/null
@@ -0,0 +1,248 @@
+TABLE OF CONTENTS
+
+1. Apache License version 2.0
+2. Realm Components
+3. Export Compliance
+
+1. -------------------------------------------------------------------------------
+
+                                 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.
+
+2. -------------------------------------------------------------------------------
+
+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 Platform Extensions component
+
+  Realm Platform Extensions License
+
+  Copyright (c) 2011-2017 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.
+
+3. -------------------------------------------------------------------------------
+
+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/iOS/Pods/RealmSwift/README.md b/iOS/Pods/RealmSwift/README.md
new file mode 100644 (file)
index 0000000..ae255b2
--- /dev/null
@@ -0,0 +1,74 @@
+![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 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**](https://realm.io/realm-news-subscribe) 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 8.x.
+* If cloning from git, submodules are required: `git submodule update --init --recursive`.
+* 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) 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/iOS/Pods/RealmSwift/RealmSwift/Aliases.swift b/iOS/Pods/RealmSwift/RealmSwift/Aliases.swift
new file mode 100644 (file)
index 0000000..3437e5c
--- /dev/null
@@ -0,0 +1,62 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+
+// These types don't change when wrapping in Swift
+// so we just typealias them to remove the 'RLM' prefix
+
+// MARK: Aliases
+
+/**
+ `PropertyType` is an enum describing all property types supported in Realm models.
+
+ For more information, see [Realm Models](https://realm.io/docs/swift/latest/#models).
+
+ ### Primitive types
+
+ * `Int`
+ * `Bool`
+ * `Float`
+ * `Double`
+
+ ### Object types
+
+ * `String`
+ * `Data`
+ * `Date`
+
+ ### Relationships: Array (in Swift, `List`) and `Object` types
+
+ * `Object`
+ * `Array`
+*/
+public typealias PropertyType = RLMPropertyType
+
+/**
+ An opaque token which is returned from methods which subscribe to changes to a Realm.
+
+ - see: `Realm.observe(_:)`
+ */
+public typealias NotificationToken = RLMNotificationToken
+
+extension NotificationToken {
+    @available(*, unavailable, renamed: "invalidate()")
+    @nonobjc public func stop() { fatalError() }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/Error.swift b/iOS/Pods/RealmSwift/RealmSwift/Error.swift
new file mode 100644 (file)
index 0000000..134db05
--- /dev/null
@@ -0,0 +1,128 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Realm
+
+extension Realm {
+    /**
+     Struct that describes the error codes within the Realm error domain.
+     The values can be used to catch a variety of _recoverable_ errors, especially those
+     happening when initializing a Realm instance.
+
+     ```swift
+     let realm: Realm?
+     do {
+         realm = try Realm()
+     } catch Realm.Error.incompatibleLockFile {
+         print("Realm Browser app may be attached to Realm on device?")
+     }
+     ```
+    */
+    public struct Error {
+        public typealias Code = RLMError.Code
+
+        /// Error thrown by Realm if no other specific error is returned when a realm is opened.
+        public static let fail: Code = .fail
+
+        /// Error thrown by Realm for any I/O related exception scenarios when a realm is opened.
+        public static let fileAccess: Code = .fileAccess
+
+        /// Error thrown by Realm if the user does not have permission to open or create
+        /// the specified file in the specified access mode when the realm is opened.
+        public static let filePermissionDenied: Code = .filePermissionDenied
+
+        /// Error thrown by Realm if the file already exists when a copy should be written.
+        public static let fileExists: Code = .fileExists
+
+        /// Error thrown by Realm if no file was found when a realm was opened as
+        /// read-only or if the directory part of the specified path was not found
+        /// when a copy should be written.
+        public static let fileNotFound: Code = .fileNotFound
+
+        /// Error thrown by Realm if the database file is currently open in another process which
+        /// cannot share with the current process due to an architecture mismatch.
+        public static let incompatibleLockFile: Code = .incompatibleLockFile
+
+        /// Error thrown by Realm if a file format upgrade is required to open the file,
+        /// but upgrades were explicitly disabled.
+        public static let fileFormatUpgradeRequired: Code = .fileFormatUpgradeRequired
+
+        /// Error thrown by Realm if there is insufficient available address space.
+        public static let addressSpaceExhausted: Code = .addressSpaceExhausted
+
+        /// Error thrown by Realm if there is a schema version mismatch, so that a migration is required.
+        public static let schemaMismatch: Code = .schemaMismatch
+
+        /// Error thrown by Realm when attempting to open an incompatible synchronized Realm file.
+        ///
+        /// This error occurs when the Realm file was created with an older version of Realm and an automatic
+        /// migration to the current version is not possible. When such an error occurs, the original file is moved
+        /// to a backup location, and future attempts to open the synchronized Realm will result in a new file being
+        /// created. If you wish to migrate any data from the backup Realm, you can open it using the provided
+        /// Realm configuration.
+        public static let incompatibleSyncedFile: Code = .incompatibleSyncedFile
+
+        /// :nodoc:
+        public var code: Code {
+            return (_nsError as! RLMError).code
+        }
+
+        /// :nodoc:
+        public var _nsError: NSError
+
+        /// :nodoc:
+        public init(_nsError error: NSError) {
+            _nsError = error
+        }
+
+        /// Realm configuration that can be used to open the backup copy of a Realm file
+        ///
+        //// Only applicable to `incompatibleSyncedFile`. Will be `nil` for all other errors.
+        public var backupConfiguration: Realm.Configuration? {
+            let configuration = userInfo[RLMBackupRealmConfigurationErrorKey] as! RLMRealmConfiguration?
+            return configuration.map(Realm.Configuration.fromRLMRealmConfiguration)
+        }
+    }
+}
+
+/// :nodoc:
+// Provide bridging from errors with domain RLMErrorDomain to Error.
+extension Realm.Error: _BridgedStoredNSError {
+    /// :nodoc:
+    public static var _nsErrorDomain = RLMErrorDomain
+}
+
+// MARK: Equatable
+
+extension Realm.Error: Equatable {}
+
+/// Returns a Boolean indicating whether the errors are identical.
+public func == (lhs: Error, rhs: Error) -> Bool {
+    return lhs._code == rhs._code
+        && lhs._domain == rhs._domain
+}
+
+// MARK: Pattern Matching
+
+/**
+ Pattern matching matching for `Realm.Error`, so that the instances can be used with Swift's
+ `do { ... } catch { ... }` syntax.
+*/
+public func ~= (lhs: Realm.Error, rhs: Error) -> Bool {
+    return lhs == rhs
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/LinkingObjects.swift b/iOS/Pods/RealmSwift/RealmSwift/LinkingObjects.swift
new file mode 100644 (file)
index 0000000..cc0f82b
--- /dev/null
@@ -0,0 +1,440 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+
+/// :nodoc:
+/// Internal class. Do not use directly. Used for reflection and initialization
+public class LinkingObjectsBase: NSObject, NSFastEnumeration {
+    internal let objectClassName: String
+    internal let propertyName: String
+
+    fileprivate var cachedRLMResults: RLMResults<AnyObject>?
+    @objc fileprivate var object: RLMWeakObjectHandle?
+    @objc fileprivate var property: RLMProperty?
+
+    internal var rlmResults: RLMResults<AnyObject> {
+        if cachedRLMResults == nil {
+            if let object = self.object, let property = self.property {
+                cachedRLMResults = RLMDynamicGet(object.object, property)! as? RLMResults
+                self.object = nil
+                self.property = nil
+            } else {
+                cachedRLMResults = RLMResults.emptyDetached()
+            }
+        }
+        return cachedRLMResults!
+    }
+
+    init(fromClassName objectClassName: String, property propertyName: String) {
+        self.objectClassName = objectClassName
+        self.propertyName = propertyName
+    }
+
+    // MARK: Fast Enumeration
+    public func countByEnumerating(with state: UnsafeMutablePointer<NSFastEnumerationState>,
+                                   objects buffer: AutoreleasingUnsafeMutablePointer<AnyObject?>,
+                                   count len: Int) -> Int {
+        return Int(rlmResults.countByEnumerating(with: state,
+                                                 objects: buffer,
+                                                 count: UInt(len)))
+    }
+}
+
+/**
+ `LinkingObjects` is an auto-updating container type. It represents zero or more objects that are linked to its owning
+ model object through a property relationship.
+
+ `LinkingObjects` can be queried with the same predicates as `List<Element>` and `Results<Element>`.
+
+ `LinkingObjects` always reflects 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` enumeration, which will always
+ enumerate over the linking objects that were present when the enumeration is begun, even if some of them are deleted or
+ modified to no longer link to the target object during the enumeration.
+
+ `LinkingObjects` can only be used as a property on `Object` models. Properties of this type must be declared as `let`
+ and cannot be `dynamic`.
+ */
+public final class LinkingObjects<Element: Object>: LinkingObjectsBase {
+    /// The type of the objects represented by the linking objects.
+    public typealias ElementType = Element
+
+    // MARK: Properties
+
+    /// The Realm which manages the linking objects, or `nil` if the linking objects are unmanaged.
+    public var realm: Realm? { return rlmResults.isAttached ? Realm(rlmResults.realm) : nil }
+
+    /// Indicates if the linking objects are no longer valid.
+    ///
+    /// The linking objects become invalid if `invalidate()` is called on the containing `realm` instance.
+    ///
+    /// An invalidated linking objects can be accessed, but will always be empty.
+    public var isInvalidated: Bool { return rlmResults.isInvalidated }
+
+    /// The number of linking objects.
+    public var count: Int { return Int(rlmResults.count) }
+
+    // MARK: Initializers
+
+    /**
+     Creates an instance of a `LinkingObjects`. This initializer should only be called when declaring a property on a
+     Realm model.
+
+     - parameter type:         The type of the object owning the property the linking objects should refer to.
+     - parameter propertyName: The property name of the property the linking objects should refer to.
+     */
+    public init(fromType type: Element.Type, property propertyName: String) {
+        let className = (Element.self as Object.Type).className()
+        super.init(fromClassName: className, property: propertyName)
+    }
+
+    /// A human-readable description of the objects represented by the linking objects.
+    public override var description: String {
+        return RLMDescriptionWithMaxDepth("LinkingObjects", rlmResults, RLMDescriptionMaxDepth)
+    }
+
+    // MARK: Index Retrieval
+
+    /**
+     Returns the index of an object in the linking objects, or `nil` if the object is not present.
+
+     - parameter object: The object whose index is being queried.
+     */
+    public func index(of object: Element) -> Int? {
+        return notFoundToNil(index: rlmResults.index(of: object.unsafeCastToRLMObject()))
+    }
+
+    /**
+     Returns the index of the first object matching the given predicate, or `nil` if no objects match.
+
+     - parameter predicate: The predicate with which to filter the objects.
+     */
+    public func index(matching predicate: NSPredicate) -> Int? {
+        return notFoundToNil(index: rlmResults.indexOfObject(with: predicate))
+    }
+
+    /**
+     Returns the index of the first object matching the given predicate, or `nil` if no objects match.
+
+     - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
+     */
+    public func index(matching predicateFormat: String, _ args: Any...) -> Int? {
+        return notFoundToNil(index: rlmResults.indexOfObject(with: NSPredicate(format: predicateFormat,
+                                                                               argumentArray: unwrapOptionals(in: args))))
+    }
+
+    // MARK: Object Retrieval
+
+    /**
+     Returns the object at the given `index`.
+
+     - parameter index: The index.
+     */
+    public subscript(index: Int) -> Element {
+        get {
+            throwForNegativeIndex(index)
+            return unsafeBitCast(rlmResults[UInt(index)], to: Element.self)
+        }
+    }
+
+    /// Returns the first object in the linking objects, or `nil` if the linking objects are empty.
+    public var first: Element? { return unsafeBitCast(rlmResults.firstObject(), to: Optional<Element>.self) }
+
+    /// Returns the last object in the linking objects, or `nil` if the linking objects are empty.
+    public var last: Element? { return unsafeBitCast(rlmResults.lastObject(), to: Optional<Element>.self) }
+
+    // MARK: KVC
+
+    /**
+     Returns an `Array` containing the results of invoking `valueForKey(_:)` with `key` on each of the linking objects.
+
+     - parameter key: The name of the property whose values are desired.
+     */
+    public override func value(forKey key: String) -> Any? {
+        return value(forKeyPath: key)
+    }
+
+    /**
+     Returns an `Array` containing the results of invoking `valueForKeyPath(_:)` with `keyPath` on each of the linking
+     objects.
+
+     - parameter keyPath: The key path to the property whose values are desired.
+     */
+    public override func value(forKeyPath keyPath: String) -> Any? {
+        return rlmResults.value(forKeyPath: keyPath)
+    }
+
+    /**
+     Invokes `setValue(_:forKey:)` on each of the linking objects using the specified `value` and `key`.
+
+     - warning: This method may only be called during a write transaction.
+
+     - parameter value: The value to set the property to.
+     - parameter key:   The name of the property whose value should be set on each object.
+     */
+    public override func setValue(_ value: Any?, forKey key: String) {
+        return rlmResults.setValue(value, forKeyPath: key)
+    }
+
+    // MARK: Filtering
+
+    /**
+     Returns a `Results` containing all objects matching the given predicate in the linking objects.
+
+     - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
+     */
+    public func filter(_ predicateFormat: String, _ args: Any...) -> Results<Element> {
+        return Results(rlmResults.objects(with: NSPredicate(format: predicateFormat,
+                                                            argumentArray: unwrapOptionals(in: args))))
+    }
+
+    /**
+     Returns a `Results` containing all objects matching the given predicate in the linking objects.
+
+     - parameter predicate: The predicate with which to filter the objects.
+     */
+    public func filter(_ predicate: NSPredicate) -> Results<Element> {
+        return Results(rlmResults.objects(with: predicate))
+    }
+
+    // MARK: Sorting
+
+    /**
+     Returns a `Results` containing all the linking objects, but sorted.
+
+     Objects are sorted based on the values of the given key path. For example, to sort a collection of `Student`s from
+     youngest to oldest based on their `age` property, you might call
+     `students.sorted(byKeyPath: "age", ascending: true)`.
+
+     - warning: Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
+                floating point, integer, and string types.
+
+     - parameter keyPath:  The key path to sort by.
+     - parameter ascending: The direction to sort in.
+     */
+    public func sorted(byKeyPath keyPath: String, ascending: Bool = true) -> Results<Element> {
+        return sorted(by: [SortDescriptor(keyPath: keyPath, ascending: ascending)])
+    }
+
+    /**
+     Returns a `Results` containing all the linking objects, but sorted.
+
+     - warning: Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
+                floating point, integer, and string types.
+
+     - see: `sorted(byKeyPath:ascending:)`
+
+     - parameter sortDescriptors: A sequence of `SortDescriptor`s to sort by.
+     */
+    public func sorted<S: Sequence>(by sortDescriptors: S) -> Results<Element>
+        where S.Iterator.Element == SortDescriptor {
+            return Results(rlmResults.sortedResults(using: sortDescriptors.map { $0.rlmSortDescriptorValue }))
+    }
+
+    // MARK: Aggregate Operations
+
+    /**
+     Returns the minimum (lowest) value of the given property among all the linking objects, or `nil` if the linking
+     objects are empty.
+
+     - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
+
+     - parameter property: The name of a property whose minimum value is desired.
+     */
+    public func min<T: MinMaxType>(ofProperty property: String) -> T? {
+        return rlmResults.min(ofProperty: property).map(dynamicBridgeCast)
+    }
+
+    /**
+     Returns the maximum (highest) value of the given property among all the linking objects, or `nil` if the linking
+     objects are empty.
+
+     - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
+
+     - parameter property: The name of a property whose minimum value is desired.
+     */
+    public func max<T: MinMaxType>(ofProperty property: String) -> T? {
+        return rlmResults.max(ofProperty: property).map(dynamicBridgeCast)
+    }
+
+    /**
+     Returns the sum of the values of a given property over all the linking objects.
+
+     - warning: Only a property whose type conforms to the `AddableType` protocol can be specified.
+
+     - parameter property: The name of a property whose values should be summed.
+     */
+    public func sum<T: AddableType>(ofProperty property: String) -> T {
+        return dynamicBridgeCast(fromObjectiveC: rlmResults.sum(ofProperty: property))
+    }
+
+    /**
+     Returns the average value of a given property over all the linking objects, or `nil` if the linking objects are
+     empty.
+
+     - warning: Only the name of a property whose type conforms to the `AddableType` protocol can be specified.
+
+     - parameter property: The name of a property whose average value should be calculated.
+     */
+    public func average<T: AddableType>(ofProperty property: String) -> T? {
+        return rlmResults.average(ofProperty: property).map(dynamicBridgeCast)
+    }
+
+    // MARK: Notifications
+
+    /**
+     Registers a block to be called each time the collection changes.
+
+     The block will be asynchronously called with the initial results, 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 that is passed to the block reports, in the form of indices within the collection, which of
+     the objects were added, removed, or modified during each write transaction. See the `RealmCollectionChange`
+     documentation for more information on the change information supplied and an example of how to use it to update a
+     `UITableView`.
+
+     At the time when the block is called, the collection 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 `realm.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.
+
+     ```swift
+     let results = realm.objects(Dog.self)
+     print("dogs.count: \(dogs?.count)") // => 0
+     let token = dogs.observe { changes in
+         switch changes {
+         case .initial(let dogs):
+             // Will print "dogs.count: 1"
+             print("dogs.count: \(dogs.count)")
+             break
+         case .update:
+             // Will not be hit in this example
+             break
+         case .error:
+             break
+         }
+     }
+     try! realm.write {
+         let dog = Dog()
+         dog.name = "Rex"
+         person.dogs.append(dog)
+     }
+     // end of run loop execution context
+     ```
+
+     You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving
+     updates, call `invalidate()` on the token.
+
+     - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only.
+
+     - parameter block: The block to be called whenever a change occurs.
+     - returns: A token which must be held for as long as you want updates to be delivered.
+     */
+    public func observe(_ block: @escaping (RealmCollectionChange<LinkingObjects>) -> Void) -> NotificationToken {
+        return rlmResults.addNotificationBlock { _, change, error in
+            block(RealmCollectionChange.fromObjc(value: self, change: change, error: error))
+        }
+    }
+}
+
+extension LinkingObjects: RealmCollection {
+    // MARK: Sequence Support
+
+    /// Returns an iterator that yields successive elements in the linking objects.
+    public func makeIterator() -> RLMIterator<Element> {
+        return RLMIterator(collection: rlmResults)
+    }
+
+    // MARK: Collection Support
+
+    /// The position of the first element in a non-empty collection.
+    /// Identical to endIndex in an empty collection.
+    public var startIndex: Int { return 0 }
+
+    /// The collection's "past the end" position.
+    /// endIndex is not a valid argument to subscript, and is always reachable from startIndex by
+    /// zero or more applications of successor().
+    public var endIndex: Int { return count }
+
+    public func index(after: Int) -> Int {
+      return after + 1
+    }
+
+    public func index(before: Int) -> Int {
+      return before - 1
+    }
+
+    /// :nodoc:
+    public func _observe(_ block: @escaping (RealmCollectionChange<AnyRealmCollection<Element>>) -> Void) ->
+        NotificationToken {
+            let anyCollection = AnyRealmCollection(self)
+            return rlmResults.addNotificationBlock { _, change, error in
+                block(RealmCollectionChange.fromObjc(value: anyCollection, change: change, error: error))
+            }
+    }
+}
+
+// MARK: AssistedObjectiveCBridgeable
+
+extension LinkingObjects: AssistedObjectiveCBridgeable {
+    internal static func bridging(from objectiveCValue: Any, with metadata: Any?) -> LinkingObjects {
+        guard let metadata = metadata as? LinkingObjectsBridgingMetadata else { preconditionFailure() }
+
+        let swiftValue = LinkingObjects(fromType: Element.self, property: metadata.propertyName)
+        switch (objectiveCValue, metadata) {
+        case (let object as RLMObjectBase, .uncached(let property)):
+            swiftValue.object = RLMWeakObjectHandle(object: object)
+            swiftValue.property = property
+        case (let results as RLMResults<AnyObject>, .cached):
+            swiftValue.cachedRLMResults = results
+        default:
+            preconditionFailure()
+        }
+        return swiftValue
+    }
+
+    internal var bridged: (objectiveCValue: Any, metadata: Any?) {
+        if let results = cachedRLMResults {
+            return (objectiveCValue: results,
+                    metadata: LinkingObjectsBridgingMetadata.cached(propertyName: propertyName))
+        } else {
+            return (objectiveCValue: (object!.copy() as! RLMWeakObjectHandle).object,
+                    metadata: LinkingObjectsBridgingMetadata.uncached(property: property!))
+        }
+    }
+}
+
+internal enum LinkingObjectsBridgingMetadata {
+    case uncached(property: RLMProperty)
+    case cached(propertyName: String)
+
+    fileprivate var propertyName: String {
+        switch self {
+        case .uncached(let property):   return property.name
+        case .cached(let propertyName): return propertyName
+        }
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/List.swift b/iOS/Pods/RealmSwift/RealmSwift/List.swift
new file mode 100644 (file)
index 0000000..61f522a
--- /dev/null
@@ -0,0 +1,704 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+import Realm.Private
+
+/// :nodoc:
+/// Internal class. Do not use directly.
+public class ListBase: RLMListBase {
+    // Printable requires a description property defined in Swift (and not obj-c),
+    // and it has to be defined as override, which can't be done in a
+    // generic class.
+    /// Returns a human-readable description of the objects contained in the List.
+    @objc public override var description: String {
+        return descriptionWithMaxDepth(RLMDescriptionMaxDepth)
+    }
+
+    @objc private func descriptionWithMaxDepth(_ depth: UInt) -> String {
+        return RLMDescriptionWithMaxDepth("List", _rlmArray, depth)
+    }
+
+    /// Returns the number of objects in this List.
+    public var count: Int { return Int(_rlmArray.count) }
+}
+
+/**
+ `List` is the container type in Realm used to define to-many relationships.
+
+ Like Swift's `Array`, `List` is a generic type that is parameterized on the type of `Object` it stores.
+
+ Unlike Swift's native collections, `List`s are reference types, and are only immutable if the Realm that manages them
+ is opened as read-only.
+
+ Lists can be filtered and sorted with the same predicates as `Results<Element>`.
+
+ Properties of `List` type defined on `Object` subclasses must be declared as `let` and cannot be `dynamic`.
+ */
+public final class List<Element: RealmCollectionValue>: ListBase {
+
+    // MARK: Properties
+
+    /// The Realm which manages the list, or `nil` if the list is unmanaged.
+    public var realm: Realm? {
+        return _rlmArray.realm.map { Realm($0) }
+    }
+
+    /// Indicates if the list can no longer be accessed.
+    public var isInvalidated: Bool { return _rlmArray.isInvalidated }
+
+    // MARK: Initializers
+
+    /// Creates a `List` that holds Realm model objects of type `Element`.
+    public override init() {
+        super.init(array: Element._rlmArray())
+    }
+
+    internal init(rlmArray: RLMArray<AnyObject>) {
+        super.init(array: rlmArray)
+    }
+
+    // MARK: Index Retrieval
+
+    /**
+     Returns the index of an object in the list, or `nil` if the object is not present.
+
+     - parameter object: An object to find.
+     */
+    public func index(of object: Element) -> Int? {
+        return notFoundToNil(index: _rlmArray.index(of: dynamicBridgeCast(fromSwift: object) as AnyObject))
+    }
+
+    /**
+     Returns the index of the first object in the list matching the predicate, or `nil` if no objects match.
+
+     - parameter predicate: The predicate with which to filter the objects.
+    */
+    public func index(matching predicate: NSPredicate) -> Int? {
+        return notFoundToNil(index: _rlmArray.indexOfObject(with: predicate))
+    }
+
+    /**
+     Returns the index of the first object in the list matching the predicate, or `nil` if no objects match.
+
+     - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
+    */
+    public func index(matching predicateFormat: String, _ args: Any...) -> Int? {
+        return index(matching: NSPredicate(format: predicateFormat, argumentArray: unwrapOptionals(in: args)))
+    }
+
+    // MARK: Object Retrieval
+
+    /**
+     Returns the object at the given index (get), or replaces the object at the given index (set).
+
+     - warning: You can only set an object during a write transaction.
+
+     - parameter index: The index of the object to retrieve or replace.
+     */
+    public subscript(position: Int) -> Element {
+        get {
+            throwForNegativeIndex(position)
+            return dynamicBridgeCast(fromObjectiveC: _rlmArray.object(at: UInt(position)))
+        }
+        set {
+            throwForNegativeIndex(position)
+            _rlmArray.replaceObject(at: UInt(position), with: dynamicBridgeCast(fromSwift: newValue) as AnyObject)
+        }
+    }
+
+    /// Returns the first object in the list, or `nil` if the list is empty.
+    public var first: Element? { return _rlmArray.firstObject().map(dynamicBridgeCast) }
+
+    /// Returns the last object in the list, or `nil` if the list is empty.
+    public var last: Element? { return _rlmArray.lastObject().map(dynamicBridgeCast) }
+
+    // MARK: KVC
+
+    /**
+     Returns an `Array` containing the results of invoking `valueForKey(_:)` using `key` on each of the collection's
+     objects.
+     */
+    @nonobjc public func value(forKey key: String) -> [AnyObject] {
+        return _rlmArray.value(forKeyPath: key)! as! [AnyObject]
+    }
+
+    /**
+     Returns an `Array` containing the results of invoking `valueForKeyPath(_:)` using `keyPath` on each of the
+     collection's objects.
+
+     - parameter keyPath: The key path to the property whose values are desired.
+     */
+    @nonobjc public func value(forKeyPath keyPath: String) -> [AnyObject] {
+        return _rlmArray.value(forKeyPath: keyPath) as! [AnyObject]
+    }
+
+    /**
+     Invokes `setValue(_:forKey:)` on each of the collection's objects using the specified `value` and `key`.
+
+     - warning: This method can only be called during a write transaction.
+
+     - parameter value: The object value.
+     - parameter key:   The name of the property whose value should be set on each object.
+    */
+    public override func setValue(_ value: Any?, forKey key: String) {
+        return _rlmArray.setValue(value, forKeyPath: key)
+    }
+
+    // MARK: Filtering
+
+    /**
+     Returns a `Results` containing all objects matching the given predicate in the list.
+
+     - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
+    */
+    public func filter(_ predicateFormat: String, _ args: Any...) -> Results<Element> {
+        return Results<Element>(_rlmArray.objects(with: NSPredicate(format: predicateFormat,
+                                                              argumentArray: unwrapOptionals(in: args))))
+    }
+
+    /**
+     Returns a `Results` containing all objects matching the given predicate in the list.
+
+     - parameter predicate: The predicate with which to filter the objects.
+     */
+    public func filter(_ predicate: NSPredicate) -> Results<Element> {
+        return Results<Element>(_rlmArray.objects(with: predicate))
+    }
+
+    // MARK: Sorting
+
+    /**
+     Returns a `Results` containing the objects in the list, but sorted.
+
+     Objects are sorted based on the values of the given key path. For example, to sort a list of `Student`s from
+     youngest to oldest based on their `age` property, you might call
+     `students.sorted(byKeyPath: "age", ascending: true)`.
+
+     - warning: Lists may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
+                floating point, integer, and string types.
+
+     - parameter keyPath:  The key path to sort by.
+     - parameter ascending: The direction to sort in.
+     */
+    public func sorted(byKeyPath keyPath: String, ascending: Bool = true) -> Results<Element> {
+        return sorted(by: [SortDescriptor(keyPath: keyPath, ascending: ascending)])
+    }
+
+    /**
+     Returns a `Results` containing the objects in the list, but sorted.
+
+     - warning: Lists may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
+                floating point, integer, and string types.
+
+     - see: `sorted(byKeyPath:ascending:)`
+    */
+    public func sorted<S: Sequence>(by sortDescriptors: S) -> Results<Element>
+        where S.Iterator.Element == SortDescriptor {
+            return Results<Element>(_rlmArray.sortedResults(using: sortDescriptors.map { $0.rlmSortDescriptorValue }))
+    }
+
+    // MARK: Aggregate Operations
+
+    /**
+     Returns the minimum (lowest) value of the given property among all the objects in the list, or `nil` if the list is
+     empty.
+
+     - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
+
+     - parameter property: The name of a property whose minimum value is desired.
+     */
+    public func min<T: MinMaxType>(ofProperty property: String) -> T? {
+        return _rlmArray.min(ofProperty: property).map(dynamicBridgeCast)
+    }
+
+    /**
+     Returns the maximum (highest) value of the given property among all the objects in the list, or `nil` if the list
+     is empty.
+
+     - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
+
+     - parameter property: The name of a property whose maximum value is desired.
+     */
+    public func max<T: MinMaxType>(ofProperty property: String) -> T? {
+        return _rlmArray.max(ofProperty: property).map(dynamicBridgeCast)
+    }
+
+    /**
+     Returns the sum of the values of a given property over all the objects in the list.
+
+     - warning: Only a property whose type conforms to the `AddableType` protocol can be specified.
+
+     - parameter property: The name of a property whose values should be summed.
+     */
+    public func sum<T: AddableType>(ofProperty property: String) -> T {
+        return dynamicBridgeCast(fromObjectiveC: _rlmArray.sum(ofProperty: property))
+    }
+
+    /**
+     Returns the average value of a given property over all the objects in the list, or `nil` if the list is empty.
+
+     - warning: Only a property whose type conforms to the `AddableType` protocol can be specified.
+
+     - parameter property: The name of a property whose average value should be calculated.
+     */
+    public func average(ofProperty property: String) -> Double? {
+        return _rlmArray.average(ofProperty: property).map(dynamicBridgeCast)
+    }
+
+    // MARK: Mutation
+
+    /**
+     Appends the given object to the end of the list.
+
+     If the object is managed by a different Realm than the receiver, a copy is made and added to the Realm managing
+     the receiver.
+
+     - warning: This method may only be called during a write transaction.
+
+     - parameter object: An object.
+     */
+    public func append(_ object: Element) {
+        _rlmArray.add(dynamicBridgeCast(fromSwift: object) as AnyObject)
+    }
+
+    /**
+     Appends the objects in the given sequence to the end of the list.
+
+     - warning: This method may only be called during a write transaction.
+    */
+    public func append<S: Sequence>(objectsIn objects: S) where S.Iterator.Element == Element {
+        for obj in objects {
+            _rlmArray.add(dynamicBridgeCast(fromSwift: obj) as AnyObject)
+        }
+    }
+
+    /**
+     Inserts an object at the given index.
+
+     - warning: This method may only be called during a write transaction.
+
+     - warning: This method will throw an exception if called with an invalid index.
+
+     - parameter object: An object.
+     - parameter index:  The index at which to insert the object.
+     */
+    public func insert(_ object: Element, at index: Int) {
+        throwForNegativeIndex(index)
+        _rlmArray.insert(dynamicBridgeCast(fromSwift: object) as AnyObject, at: UInt(index))
+    }
+
+    /**
+     Removes an object at the given index. The object is not removed from the Realm that manages it.
+
+     - warning: This method may only be called during a write transaction.
+
+     - warning: This method will throw an exception if called with an invalid index.
+
+     - parameter index: The index at which to remove the object.
+     */
+    public func remove(at index: Int) {
+        throwForNegativeIndex(index)
+        _rlmArray.removeObject(at: UInt(index))
+    }
+
+    /**
+     Removes all objects from the list. The objects are not removed from the Realm that manages them.
+
+     - warning: This method may only be called during a write transaction.
+     */
+    public func removeAll() {
+        _rlmArray.removeAllObjects()
+    }
+
+    /**
+     Replaces an object at the given index with a new object.
+
+     - warning: This method may only be called during a write transaction.
+
+     - warning: This method will throw an exception if called with an invalid index.
+
+     - parameter index:  The index of the object to be replaced.
+     - parameter object: An object.
+     */
+    public func replace(index: Int, object: Element) {
+        throwForNegativeIndex(index)
+        _rlmArray.replaceObject(at: UInt(index), with: dynamicBridgeCast(fromSwift: object) as AnyObject)
+    }
+
+    /**
+     Moves the object at the given source index to the given destination index.
+
+     - warning: This method may only be called during a write transaction.
+
+     - warning: This method will throw an exception if called with invalid indices.
+
+     - parameter from:  The index of the object to be moved.
+     - parameter to:    index to which the object at `from` should be moved.
+     */
+    public func move(from: Int, to: Int) {
+        throwForNegativeIndex(from)
+        throwForNegativeIndex(to)
+        _rlmArray.moveObject(at: UInt(from), to: UInt(to))
+    }
+
+    /**
+     Exchanges the objects in the list at given indices.
+
+     - warning: This method may only be called during a write transaction.
+
+     - warning: This method will throw an exception if called with invalid indices.
+
+     - parameter index1: The index of the object which should replace the object at index `index2`.
+     - parameter index2: The index of the object which should replace the object at index `index1`.
+     */
+    public func swapAt(_ index1: Int, _ index2: Int) {
+        throwForNegativeIndex(index1, parameterName: "index1")
+        throwForNegativeIndex(index2, parameterName: "index2")
+        _rlmArray.exchangeObject(at: UInt(index1), withObjectAt: UInt(index2))
+    }
+
+    // MARK: Notifications
+
+    /**
+     Registers a block to be called each time the collection changes.
+
+     The block will be asynchronously called with the initial results, 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 that is passed to the block reports, in the form of indices within the collection, which of
+     the objects were added, removed, or modified during each write transaction. See the `RealmCollectionChange`
+     documentation for more information on the change information supplied and an example of how to use it to update a
+     `UITableView`.
+
+     At the time when the block is called, the collection 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 `realm.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.
+
+     ```swift
+     let results = realm.objects(Dog.self)
+     print("dogs.count: \(dogs?.count)") // => 0
+     let token = dogs.observe { changes in
+         switch changes {
+         case .initial(let dogs):
+             // Will print "dogs.count: 1"
+             print("dogs.count: \(dogs.count)")
+             break
+         case .update:
+             // Will not be hit in this example
+             break
+         case .error:
+             break
+         }
+     }
+     try! realm.write {
+         let dog = Dog()
+         dog.name = "Rex"
+         person.dogs.append(dog)
+     }
+     // end of run loop execution context
+     ```
+
+     You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving
+     updates, call `invalidate()` on the token.
+
+     - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only.
+
+     - parameter block: The block to be called whenever a change occurs.
+     - returns: A token which must be held for as long as you want updates to be delivered.
+     */
+    public func observe(_ block: @escaping (RealmCollectionChange<List>) -> Void) -> NotificationToken {
+        return _rlmArray.addNotificationBlock { _, change, error in
+            block(RealmCollectionChange.fromObjc(value: self, change: change, error: error))
+        }
+    }
+}
+
+extension List where Element: MinMaxType {
+    /**
+     Returns the minimum (lowest) value in the list, or `nil` if the list is empty.
+     */
+    public func min() -> Element? {
+        return _rlmArray.min(ofProperty: "self").map(dynamicBridgeCast)
+    }
+
+    /**
+     Returns the maximum (highest) value in the list, or `nil` if the list is empty.
+     */
+    public func max() -> Element? {
+        return _rlmArray.max(ofProperty: "self").map(dynamicBridgeCast)
+    }
+}
+
+extension List where Element: AddableType {
+    /**
+     Returns the sum of the values in the list.
+     */
+    public func sum() -> Element {
+        return sum(ofProperty: "self")
+    }
+
+    /**
+     Returns the average of the values in the list, or `nil` if the list is empty.
+     */
+    public func average() -> Double? {
+        return average(ofProperty: "self")
+    }
+}
+
+extension List: RealmCollection {
+    /// The type of the objects stored within the list.
+    public typealias ElementType = Element
+
+    // MARK: Sequence Support
+
+    /// Returns a `RLMIterator` that yields successive elements in the `List`.
+    public func makeIterator() -> RLMIterator<Element> {
+        return RLMIterator(collection: _rlmArray)
+    }
+
+    /**
+     Replace the given `subRange` of elements with `newElements`.
+
+     - parameter subrange:    The range of elements to be replaced.
+     - parameter newElements: The new elements to be inserted into the List.
+     */
+    public func replaceSubrange<C: Collection>(_ subrange: Range<Int>, with newElements: C)
+        where C.Iterator.Element == Element {
+            for _ in subrange.lowerBound..<subrange.upperBound {
+                remove(at: subrange.lowerBound)
+            }
+            for x in newElements.reversed() {
+                insert(x, at: subrange.lowerBound)
+            }
+    }
+
+    // This should be inferred, but Xcode 8.1 is unable to
+    /// :nodoc:
+    public typealias Indices = DefaultRandomAccessIndices<List>
+
+    /// The position of the first element in a non-empty collection.
+    /// Identical to endIndex in an empty collection.
+    public var startIndex: Int { return 0 }
+
+    /// The collection's "past the end" position.
+    /// endIndex is not a valid argument to subscript, and is always reachable from startIndex by
+    /// zero or more applications of successor().
+    public var endIndex: Int { return count }
+
+    public func index(after i: Int) -> Int { return i + 1 }
+    public func index(before i: Int) -> Int { return i - 1 }
+
+    /// :nodoc:
+    public func _observe(_ block: @escaping (RealmCollectionChange<AnyRealmCollection<Element>>) -> Void) -> NotificationToken {
+        let anyCollection = AnyRealmCollection(self)
+        return _rlmArray.addNotificationBlock { _, change, error in
+            block(RealmCollectionChange.fromObjc(value: anyCollection, change: change, error: error))
+        }
+    }
+}
+
+#if swift(>=4.0)
+// MARK: - MutableCollection conformance, range replaceable collection emulation
+extension List: MutableCollection {
+    public typealias SubSequence = RandomAccessSlice<List>
+
+    /**
+     Returns the objects at the given range (get), or replaces the objects at the
+     given range with new objects (set).
+
+     - warning: Objects may only be set during a write transaction.
+
+     - parameter index: The index of the object to retrieve or replace.
+     */
+    public subscript(bounds: Range<Int>) -> SubSequence {
+        get {
+            return SubSequence(base: self, bounds: bounds)
+        }
+        set {
+            replaceSubrange(bounds.lowerBound..<bounds.upperBound, with: newValue)
+        }
+    }
+
+    /**
+     Removes the specified number of objects from the beginning of the list. The
+     objects are not removed from the Realm that manages them.
+
+     - warning: This method may only be called during a write transaction.
+     */
+    public func removeFirst(_ number: Int = 1) {
+        let count = Int(_rlmArray.count)
+        guard number <= count else {
+            throwRealmException("It is not possible to remove more objects (\(number)) from a list"
+                + " than it already contains (\(count)).")
+            return
+        }
+        for _ in 0..<number {
+            _rlmArray.removeObject(at: 0)
+        }
+    }
+
+    /**
+     Removes the specified number of objects from the end of the list. The objects
+     are not removed from the Realm that manages them.
+
+     - warning: This method may only be called during a write transaction.
+     */
+    public func removeLast(_ number: Int = 1) {
+        let count = Int(_rlmArray.count)
+        guard number <= count else {
+            throwRealmException("It is not possible to remove more objects (\(number)) from a list"
+                + " than it already contains (\(count)).")
+            return
+        }
+        for _ in 0..<number {
+            _rlmArray.removeLastObject()
+        }
+    }
+
+    /**
+     Inserts the items in the given collection into the list at the given position.
+
+     - warning: This method may only be called during a write transaction.
+     */
+    public func insert<C: Collection>(contentsOf newElements: C, at i: Int) where C.Iterator.Element == Element {
+        var currentIndex = i
+        for item in newElements {
+            insert(item, at: currentIndex)
+            currentIndex += 1
+        }
+    }
+
+    /**
+     Removes objects from the list at the given range.
+
+     - warning: This method may only be called during a write transaction.
+     */
+    public func removeSubrange(_ bounds: Range<Int>) {
+        removeSubrange(bounds.lowerBound..<bounds.upperBound)
+    }
+
+    /// :nodoc:
+    public func removeSubrange(_ bounds: ClosedRange<Int>) {
+        removeSubrange(bounds.lowerBound...bounds.upperBound)
+    }
+
+    //// :nodoc:
+    public func removeSubrange(_ bounds: CountableRange<Int>) {
+        for _ in bounds {
+            remove(at: bounds.lowerBound)
+        }
+    }
+
+    /// :nodoc:
+    public func removeSubrange(_ bounds: CountableClosedRange<Int>) {
+        for _ in bounds {
+            remove(at: bounds.lowerBound)
+        }
+    }
+
+    /// :nodoc:
+    public func removeSubrange(_ bounds: DefaultRandomAccessIndices<List>) {
+        removeSubrange(bounds.startIndex..<bounds.endIndex)
+    }
+
+    /// :nodoc:
+    public func replaceSubrange<C: Collection>(_ subrange: ClosedRange<Int>, with newElements: C)
+        where C.Iterator.Element == Element {
+            removeSubrange(subrange)
+            insert(contentsOf: newElements, at: subrange.lowerBound)
+    }
+
+    /// :nodoc:
+    public func replaceSubrange<C: Collection>(_ subrange: CountableRange<Int>, with newElements: C)
+        where C.Iterator.Element == Element {
+            removeSubrange(subrange)
+            insert(contentsOf: newElements, at: subrange.lowerBound)
+    }
+
+    /// :nodoc:
+    public func replaceSubrange<C: Collection>(_ subrange: CountableClosedRange<Int>, with newElements: C)
+        where C.Iterator.Element == Element {
+            removeSubrange(subrange)
+            insert(contentsOf: newElements, at: subrange.lowerBound)
+    }
+
+    /// :nodoc:
+    public func replaceSubrange<C: Collection>(_ subrange: DefaultRandomAccessIndices<List>, with newElements: C)
+        where C.Iterator.Element == Element {
+            removeSubrange(subrange)
+            insert(contentsOf: newElements, at: subrange.startIndex)
+    }
+}
+#else
+// MARK: - RangeReplaceableCollection support
+extension List: RangeReplaceableCollection {
+    /**
+     Removes the last object in the list. The object is not removed from the Realm that manages it.
+
+     - warning: This method may only be called during a write transaction.
+     */
+    public func removeLast() {
+        guard _rlmArray.count > 0 else {
+            throwRealmException("It is not possible to remove an object from an empty list.")
+            return
+        }
+        _rlmArray.removeLastObject()
+    }
+
+#if swift(>=3.1)
+    // These should not be necessary, but Swift 3.1's compiler fails to infer the `SubSequence`,
+    // and the standard library neglects to provide the default implementation of `subscript`
+    /// :nodoc:
+    public typealias SubSequence = RangeReplaceableRandomAccessSlice<List>
+
+    /// :nodoc:
+    public subscript(slice: Range<Int>) -> SubSequence {
+        return SubSequence(base: self, bounds: slice)
+    }
+#endif
+}
+#endif
+
+// MARK: - AssistedObjectiveCBridgeable
+
+extension List: AssistedObjectiveCBridgeable {
+    internal static func bridging(from objectiveCValue: Any, with metadata: Any?) -> List {
+        guard let objectiveCValue = objectiveCValue as? RLMArray<AnyObject> else { preconditionFailure() }
+        return List(rlmArray: objectiveCValue)
+    }
+
+    internal var bridged: (objectiveCValue: Any, metadata: Any?) {
+        return (objectiveCValue: _rlmArray, metadata: nil)
+    }
+}
+// MARK: - Unavailable
+
+extension List {
+    @available(*, unavailable, renamed: "remove(at:)")
+    public func remove(objectAtIndex: Int) { fatalError() }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/Migration.swift b/iOS/Pods/RealmSwift/RealmSwift/Migration.swift
new file mode 100644 (file)
index 0000000..66c4714
--- /dev/null
@@ -0,0 +1,197 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+import Realm.Private
+
+/**
+ The type of a migration block used to migrate a Realm.
+
+ - parameter migration:  A `Migration` object used to perform the migration. The migration object allows you to
+                         enumerate and alter any existing objects which require migration.
+
+ - parameter oldSchemaVersion: The schema version of the Realm being migrated.
+ */
+public typealias MigrationBlock = (_ migration: Migration, _ oldSchemaVersion: UInt64) -> Void
+
+/// An object class used during migrations.
+public typealias MigrationObject = DynamicObject
+
+/**
+ A block type which provides both the old and new versions of an object in the Realm. Object
+ properties can only be accessed using subscripting.
+
+ - parameter oldObject: The object from the original Realm (read-only).
+ - parameter newObject: The object from the migrated Realm (read-write).
+ */
+public typealias MigrationObjectEnumerateBlock = (_ oldObject: MigrationObject?, _ newObject: MigrationObject?) -> Void
+
+/**
+ Returns the schema version for a Realm at a given local URL.
+
+ - parameter fileURL:       Local URL to a Realm file.
+ - parameter encryptionKey: 64-byte key used to encrypt the file, or `nil` if it is unencrypted.
+
+ - throws: An `NSError` that describes the problem.
+ */
+public func schemaVersionAtURL(_ fileURL: URL, encryptionKey: Data? = nil) throws -> UInt64 {
+    var error: NSError?
+    let version = RLMRealm.__schemaVersion(at: fileURL, encryptionKey: encryptionKey, error: &error)
+    guard version != RLMNotVersioned else {
+        throw error!
+    }
+    return version
+}
+
+extension Realm {
+    /**
+     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.
+
+     - parameter configuration: The Realm configuration used to open and migrate the Realm.
+     */
+    public static func performMigration(for configuration: Realm.Configuration = Realm.Configuration.defaultConfiguration) throws {
+        try RLMRealm.performMigration(for: configuration.rlmConfiguration)
+    }
+}
+
+/**
+ `Migration` instances encapsulate information intended to facilitate a schema migration.
+
+ A `Migration` instance is passed into a user-defined `MigrationBlock` 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.
+ */
+public struct Migration {
+
+    // MARK: Properties
+
+    /// The old schema, describing the Realm before applying a migration.
+    public var oldSchema: Schema { return Schema(rlmMigration.oldSchema) }
+
+    /// The new schema, describing the Realm after applying a migration.
+    public var newSchema: Schema { return Schema(rlmMigration.newSchema) }
+
+    internal var rlmMigration: RLMMigration
+
+    // MARK: Altering Objects During a Migration
+
+    /**
+     Enumerates all the objects of a given type in this Realm, providing both the old and new versions of each object.
+     Properties on an object can be accessed using subscripting.
+
+     - parameter objectClassName: The name of the `Object` class to enumerate.
+     - parameter block:           The block providing both the old and new versions of an object in this Realm.
+     */
+    public func enumerateObjects(ofType typeName: String, _ block: MigrationObjectEnumerateBlock) {
+        rlmMigration.enumerateObjects(typeName) { oldObject, newObject in
+            block(unsafeBitCast(oldObject, to: MigrationObject.self),
+                  unsafeBitCast(newObject, to: MigrationObject.self))
+        }
+    }
+
+    /**
+     Creates and returns an `Object` 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 `Array` as the `value` argument, all properties must be present, valid and in the same order as
+     the properties defined in the model.
+
+     - parameter className: The name of the `Object` class to create.
+     - parameter value:     The value used to populate the created object.
+
+     - returns: The newly created object.
+     */
+    @discardableResult
+    public func create(_ typeName: String, value: Any = [:]) -> MigrationObject {
+        return unsafeBitCast(rlmMigration.createObject(typeName, withValue: value), to: MigrationObject.self)
+    }
+
+    /**
+     Deletes an object from a Realm during a migration.
+
+     It is permitted to call this method from within the block passed to `enumerate(_:block:)`.
+
+     - parameter object: An object to be deleted from the Realm being migrated.
+     */
+    public func delete(_ object: MigrationObject) {
+        rlmMigration.delete(object.unsafeCastToRLMObject())
+    }
+
+    /**
+     Deletes the data for the class with the given name.
+
+     All objects of the given class will be deleted. If the `Object` subclass no longer exists in your program, any
+     remaining metadata for the class will be removed from the Realm file.
+
+     - parameter objectClassName: The name of the `Object` class to delete.
+
+     - returns: A Boolean value indicating whether there was any data to delete.
+     */
+    @discardableResult
+    public func deleteData(forType typeName: String) -> Bool {
+        return rlmMigration.deleteData(forClassName: typeName)
+    }
+
+    /**
+     Renames a property of the given class from `oldName` to `newName`.
+
+     - parameter className:  The name of the class whose property should be renamed. This class must be present
+                             in both the old and new Realm schemas.
+     - parameter 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.
+     - parameter 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.
+     */
+    public func renameProperty(onType typeName: String, from oldName: String, to newName: String) {
+        rlmMigration.renameProperty(forClass: typeName, oldName: oldName, newName: newName)
+    }
+
+    internal init(_ rlmMigration: RLMMigration) {
+        self.rlmMigration = rlmMigration
+    }
+}
+
+
+// MARK: Private Helpers
+
+internal func accessorMigrationBlock(_ migrationBlock: @escaping MigrationBlock) -> RLMMigrationBlock {
+    return { migration, oldVersion in
+        // set all accessor classes to MigrationObject
+        for objectSchema in migration.oldSchema.objectSchema {
+            objectSchema.accessorClass = MigrationObject.self
+            // isSwiftClass is always `false` for object schema generated
+            // from the table, but we need to pretend it's from a swift class
+            // (even if it isn't) for the accessors to be initialized correctly.
+            objectSchema.isSwiftClass = true
+        }
+        for objectSchema in migration.newSchema.objectSchema {
+            objectSchema.accessorClass = MigrationObject.self
+        }
+
+        // run migration
+        migrationBlock(Migration(migration), oldVersion)
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/Object.swift b/iOS/Pods/RealmSwift/RealmSwift/Object.swift
new file mode 100644 (file)
index 0000000..76b0d98
--- /dev/null
@@ -0,0 +1,558 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+import Realm.Private
+
+/**
+ `Object` is a class used to define Realm model objects.
+
+ In Realm you define your model classes by subclassing `Object` and adding properties to be managed.
+ You then instantiate and use your custom subclasses instead of using the `Object` class directly.
+
+ ```swift
+ class Dog: Object {
+     @objc dynamic var name: String = ""
+     @objc dynamic var adopted: Bool = false
+     let siblings = List<Dog>()
+ }
+ ```
+
+ ### Supported property types
+
+ - `String`, `NSString`
+ - `Int`
+ - `Int8`, `Int16`, `Int32`, `Int64`
+ - `Float`
+ - `Double`
+ - `Bool`
+ - `Date`, `NSDate`
+ - `Data`, `NSData`
+ - `RealmOptional<Value>` for optional numeric properties
+ - `Object` subclasses, to model many-to-one relationships
+ - `List<Element>`, to model many-to-many relationships
+
+ `String`, `NSString`, `Date`, `NSDate`, `Data`, `NSData` and `Object` subclass properties can be declared as optional.
+ `Int`, `Int8`, `Int16`, `Int32`, `Int64`, `Float`, `Double`, `Bool`, and `List` properties cannot. To store an optional
+ number, use `RealmOptional<Int>`, `RealmOptional<Float>`, `RealmOptional<Double>`, or `RealmOptional<Bool>` instead,
+ which wraps an optional numeric value.
+
+ All property types except for `List` and `RealmOptional` *must* be declared as `@objc dynamic var`. `List` and
+ `RealmOptional` properties must be declared as non-dynamic `let` properties. Swift `lazy` properties are not allowed.
+
+ Note that none of the restrictions listed above apply to properties that are configured to be ignored by Realm.
+
+ ### Querying
+
+ You can retrieve all objects of a given type from a Realm by calling the `objects(_:)` instance method.
+
+ ### Relationships
+
+ See our [Cocoa guide](http://realm.io/docs/cocoa) for more details.
+ */
+@objc(RealmSwiftObject)
+open class Object: RLMObjectBase, ThreadConfined, RealmCollectionValue {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    public static func _rlmArray() -> RLMArray<AnyObject> {
+        return RLMArray(objectClassName: className())
+    }
+
+    // MARK: Initializers
+
+    /**
+     Creates an unmanaged instance of a Realm object.
+
+     Call `add(_:)` on a `Realm` instance to add an unmanaged object into that Realm.
+
+     - see: `Realm().add(_:)`
+     */
+    public override required init() {
+        super.init()
+    }
+
+    /**
+     Creates an unmanaged instance of a Realm 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.
+
+     Call `add(_:)` on a `Realm` instance to add an unmanaged object into that Realm.
+
+     - parameter value:  The value used to populate the object.
+     */
+    public init(value: Any) {
+        super.init(value: value, schema: .partialPrivateShared())
+    }
+
+
+    // MARK: Properties
+
+    /// The Realm which manages the object, or `nil` if the object is unmanaged.
+    public var realm: Realm? {
+        if let rlmReam = RLMObjectBaseRealm(self) {
+            return Realm(rlmReam)
+        }
+        return nil
+    }
+
+    /// The object schema which lists the managed properties for the object.
+    public var objectSchema: ObjectSchema {
+        return ObjectSchema(RLMObjectBaseObjectSchema(self)!)
+    }
+
+    /// 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.
+    public override final var isInvalidated: Bool { return super.isInvalidated }
+
+    /// A human-readable description of the object.
+    open override var description: String { return super.description }
+
+    /**
+     WARNING: This is an internal helper method not intended for public use.
+     It is not considered part of the public API.
+     :nodoc:
+     */
+    public override final class func objectUtilClass(_ isSwift: Bool) -> AnyClass {
+        return ObjectUtil.self
+    }
+
+
+    // MARK: Object Customization
+
+    /**
+     Override this method to specify the name of a property to be used as the primary key.
+
+     Only properties of types `String` and `Int` 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.
+
+     - returns: The name of the property designated as the primary key, or `nil` if the model has no primary key.
+     */
+    @objc open class func primaryKey() -> String? { return nil }
+
+    /**
+     Override this method to specify the names of properties to ignore. These properties will not be managed by
+     the Realm that manages the object.
+
+     - returns: An array of property names to ignore.
+     */
+    @objc open class func ignoredProperties() -> [String] { return [] }
+
+    /**
+     Returns an array of property names for properties which should be indexed.
+
+     Only string, integer, boolean, `Date`, and `NSDate` properties are supported.
+
+     - returns: An array of property names.
+     */
+    @objc open class func indexedProperties() -> [String] { return [] }
+
+    // MARK: Key-Value Coding & Subscripting
+
+    /// Returns or sets the value of the property with the given name.
+    @objc open subscript(key: String) -> Any? {
+        get {
+            if realm == nil {
+                return value(forKey: key)
+            }
+            return RLMDynamicGetByName(self, key, true)
+        }
+        set(value) {
+            if realm == nil {
+                setValue(value, forKey: key)
+            } else {
+                RLMDynamicValidatedSet(self, key, value)
+            }
+        }
+    }
+
+    // MARK: Notifications
+
+    /**
+     Registers a block to be called each time the object changes.
+
+     The block will be asynchronously called after each write transaction which
+     deletes the object or modifies any of the managed properties of the object,
+     including self-assignments that set a property to its existing value.
+
+     For write transactions performed on different threads or in different
+     processes, the block will be called when the managing Realm is
+     (auto)refreshed to a version including the changes, while for local write
+     transactions it will be called at some point in the future after the write
+     transaction is committed.
+
+     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.
+
+     Unlike with `List` and `Results`, there is no "initial" callback made after
+     you add a new notification block.
+
+     Only objects which are managed by a Realm can be observed in this way. You
+     must retain the returned token for as long as you want updates to be sent
+     to the block. To stop receiving updates, call `invalidate()` on the token.
+
+     It is safe to capture a strong reference to the observed object within the
+     callback block. There is no retain cycle due to that the callback is
+     retained by the returned token and not by the object itself.
+
+     - warning: This method cannot be called during a write transaction, or when
+                the containing Realm is read-only.
+
+     - parameter block: The block to call with information about changes to the object.
+     - returns: A token which must be held for as long as you want updates to be delivered.
+     */
+    public func observe(_ block: @escaping (ObjectChange) -> Void) -> NotificationToken {
+        return RLMObjectAddNotificationBlock(self, { names, oldValues, newValues, error in
+            if let error = error {
+                block(.error(error as NSError))
+                return
+            }
+            guard let names = names, let newValues = newValues else {
+                block(.deleted)
+                return
+            }
+
+            block(.change((0..<newValues.count).map { i in
+                PropertyChange(name: names[i], oldValue: oldValues?[i], newValue: newValues[i])
+            }))
+        })
+    }
+
+    // MARK: Dynamic list
+
+    /**
+     Returns a list of `DynamicObject`s for a given property name.
+
+     - 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 instance variables or cast the values returned from key-value coding.
+
+     - parameter propertyName: The name of the property.
+
+     - returns: A list of `DynamicObject`s.
+
+     :nodoc:
+     */
+    public func dynamicList(_ propertyName: String) -> List<DynamicObject> {
+        return noWarnUnsafeBitCast(RLMDynamicGetByName(self, propertyName, true) as! RLMListBase,
+                                   to: List<DynamicObject>.self)
+    }
+
+    // MARK: Comparison
+    /**
+     Returns whether two Realm objects are the same.
+
+     Objects are considered the same if and only if they are both managed by the same
+     Realm and point to the same underlying object in the database.
+     
+     - note: Equality comparison is implemented by `isEqual(_:)`. If the object type
+             is defined with a primary key, `isEqual(_:)` behaves identically to this
+             method. If the object type is not defined with a primary key,
+             `isEqual(_:)` uses the `NSObject` behavior of comparing object identity.
+             This method can be used to compare two objects for database equality
+             whether or not their object type defines a primary key.
+
+     - parameter object: The object to compare the receiver to.
+     */
+    public func isSameObject(as object: Object?) -> Bool {
+        return RLMObjectBaseAreEqual(self, object)
+    }
+
+    // MARK: Private functions
+
+    // FIXME: None of these functions should be exposed in the public interface.
+
+    /**
+    WARNING: This is an internal initializer not intended for public use.
+    :nodoc:
+    */
+    public override required init(realm: RLMRealm, schema: RLMObjectSchema) {
+        super.init(realm: realm, schema: schema)
+    }
+
+    /**
+    WARNING: This is an internal initializer not intended for public use.
+    :nodoc:
+    */
+    public override required init(value: Any, schema: RLMSchema) {
+        super.init(value: value, schema: schema)
+    }
+}
+
+/**
+ Information about a specific property which changed in an `Object` change notification.
+ */
+public struct PropertyChange {
+    /**
+     The name of the property which changed.
+    */
+    public let name: String
+
+    /**
+     Value of the property before the change occurred. This is not supplied if
+     the change happened on the same thread as the notification and for `List`
+     properties.
+
+     For object properties this will give the object which was previously
+     linked to, but that object will have its new values and not the values it
+     had before the changes. This means that `previousValue` may be a deleted
+     object, and you will need to check `isInvalidated` before accessing any
+     of its properties.
+    */
+    public let oldValue: Any?
+
+    /**
+     The value of the property after the change occurred. This is not supplied
+     for `List` properties and will always be nil.
+    */
+    public let newValue: Any?
+}
+
+/**
+ Information about the changes made to an object which is passed to `Object`'s
+ notification blocks.
+ */
+public enum ObjectChange {
+    /**
+     If an error occurs, notification blocks are called one time with a `.error`
+     result and an `NSError` containing details about the error. Currently the
+     only errors which can occur are when opening the Realm on a background
+     worker thread to calculate the change set. The callback will never be
+     called again after `.error` is delivered.
+     */
+    case error(_: NSError)
+    /**
+     One or more of the properties of the object have been changed.
+     */
+    case change(_: [PropertyChange])
+    /// The object has been deleted from the Realm.
+    case deleted
+}
+
+/// Object interface which allows untyped getters and setters for Objects.
+/// :nodoc:
+public final class DynamicObject: Object {
+    public override subscript(key: String) -> Any? {
+        get {
+            let value = RLMDynamicGetByName(self, key, false)
+            if let array = value as? RLMArray<AnyObject> {
+                return List<DynamicObject>(rlmArray: array)
+            }
+            return value
+        }
+        set(value) {
+            RLMDynamicValidatedSet(self, key, value)
+        }
+    }
+
+    /// :nodoc:
+    public override func value(forUndefinedKey key: String) -> Any? {
+        return self[key]
+    }
+
+    /// :nodoc:
+    public override func setValue(_ value: Any?, forUndefinedKey key: String) {
+        self[key] = value
+    }
+
+    /// :nodoc:
+    public override class func shouldIncludeInDefaultSchema() -> Bool {
+        return false
+    }
+}
+
+/// :nodoc:
+/// Internal class. Do not use directly.
+@objc(RealmSwiftObjectUtil)
+public class ObjectUtil: NSObject {
+    @objc private class func swiftVersion() -> NSString {
+        return swiftLanguageVersion as NSString
+    }
+
+    @objc private class func ignoredPropertiesForClass(_ type: AnyClass) -> NSArray? {
+        if let type = type as? Object.Type {
+            return type.ignoredProperties() as NSArray?
+        }
+        return nil
+    }
+
+    @objc private class func indexedPropertiesForClass(_ type: AnyClass) -> NSArray? {
+        if let type = type as? Object.Type {
+            return type.indexedProperties() as NSArray?
+        }
+        return nil
+    }
+
+    @objc private class func linkingObjectsPropertiesForClass(_ type: AnyClass) -> NSDictionary? {
+        // Not used for Swift. getLinkingObjectsProperties(_:) is used instead.
+        return nil
+    }
+
+    // If the property is a storage property for a lazy Swift property, return
+    // the base property name (e.g. `foo.storage` becomes `foo`). Otherwise, nil.
+    private static func baseName(forLazySwiftProperty name: String) -> String? {
+        // 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.
+        if let storageRange = name.range(of: ".storage", options: [.anchored, .backwards]) {
+            #if swift(>=4.0)
+                return String(name[..<storageRange.lowerBound])
+            #else
+                return name.substring(to: storageRange.lowerBound)
+            #endif
+        }
+        return nil
+    }
+
+    // Reflect an object, returning only children representing managed Realm properties.
+    private static func getNonIgnoredMirrorChildren(for object: Any) -> [Mirror.Child] {
+        let ignoredPropNames: Set<String>
+        if let realmObject = object as? Object {
+            ignoredPropNames = Set(type(of: realmObject).ignoredProperties())
+        } else {
+            ignoredPropNames = Set()
+        }
+        // No HKT in Swift, unfortunately
+        return Mirror(reflecting: object).children.filter { (prop: Mirror.Child) -> Bool in
+            guard let label = prop.label else {
+                return false
+            }
+            if ignoredPropNames.contains(label) {
+                // Ignored property.
+                return false
+            }
+            if let lazyBaseName = baseName(forLazySwiftProperty: label) {
+                if ignoredPropNames.contains(lazyBaseName) {
+                    // Ignored lazy property.
+                    return false
+                }
+                // Managed lazy property; not currently supported.
+                // FIXME: revisit this once Swift gets property behaviors/property macros.
+                throwRealmException("Lazy managed property '\(lazyBaseName)' is not allowed on a Realm Swift object"
+                    + " class. Either add the property to the ignored properties list or make it non-lazy.")
+            }
+            return true
+        }
+    }
+
+    // Build optional property metadata for a given property.
+    // swiftlint:disable:next cyclomatic_complexity
+    private static func getOptionalPropertyMetadata(for child: Mirror.Child, at index: Int) -> RLMSwiftPropertyMetadata? {
+        guard let name = child.label else {
+            return nil
+        }
+        let mirror = Mirror(reflecting: child.value)
+        let type = mirror.subjectType
+        let code: PropertyType
+        if type is Optional<String>.Type || type is Optional<NSString>.Type {
+            code = .string
+        } else if type is Optional<Date>.Type {
+            code = .date
+        } else if type is Optional<Data>.Type {
+            code = .data
+        } else if type is Optional<Object>.Type {
+            code = .object
+        } else if type is RealmOptional<Int>.Type ||
+            type is RealmOptional<Int8>.Type ||
+            type is RealmOptional<Int16>.Type ||
+            type is RealmOptional<Int32>.Type ||
+            type is RealmOptional<Int64>.Type {
+            code = .int
+        } else if type is RealmOptional<Float>.Type {
+            code = .float
+        } else if type is RealmOptional<Double>.Type {
+            code = .double
+        } else if type is RealmOptional<Bool>.Type {
+            code = .bool
+        } else if child.value is RLMOptionalBase {
+            throwRealmException("'\(type)' is not a valid RealmOptional type.")
+            code = .int // ignored
+        } else if mirror.displayStyle == .optional || type is ExpressibleByNilLiteral.Type {
+            return RLMSwiftPropertyMetadata(forNilLiteralOptionalProperty: name)
+        } else {
+            return nil
+        }
+        return RLMSwiftPropertyMetadata(forOptionalProperty: name, type: code)
+    }
+
+    @objc private class func getSwiftProperties(_ object: Any) -> [RLMSwiftPropertyMetadata] {
+        return getNonIgnoredMirrorChildren(for: object).enumerated().flatMap { idx, prop in
+            if let value = prop.value as? LinkingObjectsBase {
+                return RLMSwiftPropertyMetadata(forLinkingObjectsProperty: prop.label!,
+                                                className: value.objectClassName,
+                                                linkedPropertyName: value.propertyName)
+            } else if prop.value is RLMListBase {
+                return RLMSwiftPropertyMetadata(forListProperty: prop.label!)
+            } else if let optional = getOptionalPropertyMetadata(for: prop, at: idx) {
+                return optional
+            } else {
+                return RLMSwiftPropertyMetadata(forOtherProperty: prop.label!)
+            }
+        }
+    }
+
+    @objc private class func requiredPropertiesForClass(_: Any) -> [String] {
+        return []
+    }
+}
+
+// MARK: AssistedObjectiveCBridgeable
+
+// FIXME: Remove when `as! Self` can be written
+private func forceCastToInferred<T, V>(_ x: T) -> V {
+    return x as! V
+}
+
+extension Object: AssistedObjectiveCBridgeable {
+    static func bridging(from objectiveCValue: Any, with metadata: Any?) -> Self {
+        return forceCastToInferred(objectiveCValue)
+    }
+
+    var bridged: (objectiveCValue: Any, metadata: Any?) {
+        return (objectiveCValue: unsafeCastToRLMObject(), metadata: nil)
+    }
+}
+
+// MARK: - Migration assistance
+
+extension Object {
+    /// :nodoc:
+    @available(*, unavailable, renamed: "observe()")
+    public func addNotificationBlock(_ block: @escaping (ObjectChange) -> Void) -> NotificationToken {
+        fatalError()
+    }
+
+#if os(OSX)
+#else
+    /// :nodoc:
+    @available(*, unavailable, renamed: "isSameObject(as:)") public func isEqual(to object: Any?) -> Bool {
+        fatalError()
+    }
+#endif
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/ObjectSchema.swift b/iOS/Pods/RealmSwift/RealmSwift/ObjectSchema.swift
new file mode 100644 (file)
index 0000000..ce5b3b8
--- /dev/null
@@ -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 Foundation
+import Realm
+
+/**
+ This class represents Realm model object schemas.
+
+ When using Realm, `ObjectSchema` instances allow performing migrations and introspecting the database's schema.
+
+ Object schemas map to tables in the core database.
+ */
+public struct ObjectSchema: CustomStringConvertible {
+
+    // MARK: Properties
+
+    internal let rlmObjectSchema: RLMObjectSchema
+
+    /**
+     An array of `Property` instances representing the managed properties of a class described by the schema.
+
+     - see: `Property`
+     */
+    public var properties: [Property] {
+        return rlmObjectSchema.properties.map { Property($0) }
+    }
+
+    /// The name of the class the schema describes.
+    public var className: String { return rlmObjectSchema.className }
+
+    /// The property which serves as the primary key for the class the schema describes, if any.
+    public var primaryKeyProperty: Property? {
+        if let rlmProperty = rlmObjectSchema.primaryKeyProperty {
+            return Property(rlmProperty)
+        }
+        return nil
+    }
+
+    /// A human-readable description of the properties contained in the object schema.
+    public var description: String { return rlmObjectSchema.description }
+
+    // MARK: Initializers
+
+    internal init(_ rlmObjectSchema: RLMObjectSchema) {
+        self.rlmObjectSchema = rlmObjectSchema
+    }
+
+    // MARK: Property Retrieval
+
+    /// Returns the property with the given name, if it exists.
+    public subscript(propertyName: String) -> Property? {
+        if let rlmProperty = rlmObjectSchema[propertyName] {
+            return Property(rlmProperty)
+        }
+        return nil
+    }
+}
+
+// MARK: Equatable
+
+extension ObjectSchema: Equatable {
+    /// Returns whether the two object schemas are equal.
+    public static func == (lhs: ObjectSchema, rhs: ObjectSchema) -> Bool {
+        return lhs.rlmObjectSchema.isEqual(to: rhs.rlmObjectSchema)
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/ObjectiveCSupport.swift b/iOS/Pods/RealmSwift/RealmSwift/ObjectiveCSupport.swift
new file mode 100644 (file)
index 0000000..46a1dc3
--- /dev/null
@@ -0,0 +1,166 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Realm
+
+/**
+ `ObjectiveCSupport` is a class providing methods for Swift/Objective-C interoperability.
+
+ With `ObjectiveCSupport` you can either retrieve the internal ObjC representations of the Realm objects,
+ or wrap ObjC Realm objects with their Swift equivalents.
+
+ Use this to provide public APIs that support both platforms.
+
+ :nodoc:
+ **/
+public final class ObjectiveCSupport {
+
+    /// Convert a `Results` to a `RLMResults`.
+    public static func convert<T>(object: Results<T>) -> RLMResults<AnyObject> {
+        return object.rlmResults
+    }
+
+    /// Convert a `RLMResults` to a `Results`.
+    public static func convert(object: RLMResults<AnyObject>) -> Results<Object> {
+        return Results(object)
+    }
+
+    /// Convert a `List` to a `RLMArray`.
+    public static func convert<T>(object: List<T>) -> RLMArray<AnyObject> {
+        return object._rlmArray
+    }
+
+    /// Convert a `RLMArray` to a `List`.
+    public static func convert(object: RLMArray<AnyObject>) -> List<Object> {
+        return List(rlmArray: object)
+    }
+
+    /// Convert a `LinkingObjects` to a `RLMResults`.
+    public static func convert<T>(object: LinkingObjects<T>) -> RLMResults<AnyObject> {
+        return object.rlmResults
+    }
+
+    /// Convert a `RLMLinkingObjects` to a `Results`.
+    public static func convert(object: RLMLinkingObjects<RLMObject>) -> Results<Object> {
+        return Results(object)
+    }
+
+    /// Convert a `Realm` to a `RLMRealm`.
+    public static func convert(object: Realm) -> RLMRealm {
+        return object.rlmRealm
+    }
+
+    /// Convert a `RLMRealm` to a `Realm`.
+    public static func convert(object: RLMRealm) -> Realm {
+        return Realm(object)
+    }
+
+    /// Convert a `Migration` to a `RLMMigration`.
+    public static func convert(object: Migration) -> RLMMigration {
+        return object.rlmMigration
+    }
+
+    /// Convert a `RLMMigration` to a `Migration`.
+    public static func convert(object: RLMMigration) -> Migration {
+        return Migration(object)
+    }
+
+    /// Convert a `ObjectSchema` to a `RLMObjectSchema`.
+    public static func convert(object: ObjectSchema) -> RLMObjectSchema {
+        return object.rlmObjectSchema
+    }
+
+    /// Convert a `RLMObjectSchema` to a `ObjectSchema`.
+    public static func convert(object: RLMObjectSchema) -> ObjectSchema {
+        return ObjectSchema(object)
+    }
+
+    /// Convert a `Property` to a `RLMProperty`.
+    public static func convert(object: Property) -> RLMProperty {
+        return object.rlmProperty
+    }
+
+    /// Convert a `RLMProperty` to a `Property`.
+    public static func convert(object: RLMProperty) -> Property {
+        return Property(object)
+    }
+
+    /// Convert a `Realm.Configuration` to a `RLMRealmConfiguration`.
+    public static func convert(object: Realm.Configuration) -> RLMRealmConfiguration {
+        return object.rlmConfiguration
+    }
+
+    /// Convert a `RLMRealmConfiguration` to a `Realm.Configuration`.
+    public static func convert(object: RLMRealmConfiguration) -> Realm.Configuration {
+        return .fromRLMRealmConfiguration(object)
+    }
+
+    /// Convert a `Schema` to a `RLMSchema`.
+    public static func convert(object: Schema) -> RLMSchema {
+        return object.rlmSchema
+    }
+
+    /// Convert a `RLMSchema` to a `Schema`.
+    public static func convert(object: RLMSchema) -> Schema {
+        return Schema(object)
+    }
+
+    /// Convert a `SortDescriptor` to a `RLMSortDescriptor`.
+    public static func convert(object: SortDescriptor) -> RLMSortDescriptor {
+        return object.rlmSortDescriptorValue
+    }
+
+    /// Convert a `RLMSortDescriptor` to a `SortDescriptor`.
+    public static func convert(object: RLMSortDescriptor) -> SortDescriptor {
+        return SortDescriptor(keyPath: object.keyPath, ascending: object.ascending)
+    }
+
+    /// Convert a `SyncCredentials` to a `RLMSyncCredentials`.
+    public static func convert(object: SyncCredentials) -> RLMSyncCredentials {
+        return RLMSyncCredentials(object)
+    }
+
+    /// Convert a `RLMSyncCredentials` to a `SyncCredentials`.
+    public static func convert(object: RLMSyncCredentials) -> SyncCredentials {
+        return SyncCredentials(object)
+    }
+
+    /// Convert a `RLMShouldCompactOnLaunchBlock` to a Realm Swift compact block.
+    public static func convert(object: @escaping RLMShouldCompactOnLaunchBlock) -> (Int, Int) -> Bool {
+        return { totalBytes, usedBytes in
+            return object(UInt(totalBytes), UInt(usedBytes))
+        }
+    }
+
+    /// Convert a Realm Swift compact block to a `RLMShouldCompactOnLaunchBlock`.
+    public static func convert(object: @escaping (Int, Int) -> Bool) -> RLMShouldCompactOnLaunchBlock {
+        return { totalBytes, usedBytes in
+            return object(Int(totalBytes), Int(usedBytes))
+        }
+    }
+
+    /// Convert a `SyncConfiguration` to a `RLMSyncConfiguration`.
+    public static func convert(object: SyncConfiguration) -> RLMSyncConfiguration {
+        return object.asConfig()
+    }
+
+    /// Convert a `RLMSyncConfiguration` to a `SyncConfiguration`.
+    public static func convert(object: RLMSyncConfiguration) -> SyncConfiguration {
+        return SyncConfiguration(config: object)
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/Optional.swift b/iOS/Pods/RealmSwift/RealmSwift/Optional.swift
new file mode 100644 (file)
index 0000000..85676eb
--- /dev/null
@@ -0,0 +1,66 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Realm
+
+/// A protocol describing types that can parameterize a `RealmOptional`.
+public protocol RealmOptionalType {
+}
+
+public extension RealmOptionalType {
+    /// :nodoc:
+    public static func className() -> String {
+        return ""
+    }
+}
+extension Int: RealmOptionalType {}
+extension Int8: RealmOptionalType {}
+extension Int16: RealmOptionalType {}
+extension Int32: RealmOptionalType {}
+extension Int64: RealmOptionalType {}
+extension Float: RealmOptionalType {}
+extension Double: RealmOptionalType {}
+extension Bool: RealmOptionalType {}
+
+/**
+ A `RealmOptional` instance represents an optional value for types that can't be
+ directly declared as `@objc` in Swift, such as `Int`, `Float`, `Double`, and `Bool`.
+
+ To change the underlying value stored by a `RealmOptional` instance, mutate the instance's `value` property.
+ */
+public final class RealmOptional<Value: RealmOptionalType>: RLMOptionalBase {
+    /// The value the optional represents.
+    public var value: Value? {
+        get {
+            return underlyingValue.map(dynamicBridgeCast)
+        }
+        set {
+            underlyingValue = newValue.map(dynamicBridgeCast)
+        }
+    }
+
+    /**
+     Creates a `RealmOptional` instance encapsulating the given default value.
+
+     - parameter value: The value to store in the optional, or `nil` if not specified.
+     */
+    public init(_ value: Value? = nil) {
+        super.init()
+        self.value = value
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/Property.swift b/iOS/Pods/RealmSwift/RealmSwift/Property.swift
new file mode 100644 (file)
index 0000000..f3393fa
--- /dev/null
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+
+/**
+ `Property` 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 in the Realm.
+
+ When using Realm, property instances allow performing migrations and introspecting the database's schema.
+
+ Property instances map to columns in the core database.
+ */
+public struct Property: CustomStringConvertible {
+    // MARK: Properties
+
+    internal let rlmProperty: RLMProperty
+
+    /// The name of the property.
+    public var name: String { return rlmProperty.name }
+
+    /// The type of the property.
+    public var type: PropertyType { return rlmProperty.type }
+
+    /// Indicates whether this property is an array of the property type.
+    public var isArray: Bool { return rlmProperty.array }
+
+    /// Indicates whether this property is indexed.
+    public var isIndexed: Bool { return rlmProperty.indexed }
+
+    /// Indicates whether this property is optional. (Note that certain numeric types must be wrapped in a
+    /// `RealmOptional` instance in order to be declared as optional.)
+    public var isOptional: Bool { return rlmProperty.optional }
+
+    /// For `Object` and `List` properties, the name of the class of object stored in the property.
+    public var objectClassName: String? { return rlmProperty.objectClassName }
+
+    /// A human-readable description of the property object.
+    public var description: String { return rlmProperty.description }
+
+    // MARK: Initializers
+
+    internal init(_ rlmProperty: RLMProperty) {
+        self.rlmProperty = rlmProperty
+    }
+}
+
+// MARK: Equatable
+
+extension Property: Equatable {
+    /// Returns whether the two properties are equal.
+    public static func == (lhs: Property, rhs: Property) -> Bool {
+        return lhs.rlmProperty.isEqual(to: rhs.rlmProperty)
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/Realm.swift b/iOS/Pods/RealmSwift/RealmSwift/Realm.swift
new file mode 100644 (file)
index 0000000..def1432
--- /dev/null
@@ -0,0 +1,713 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+import Realm.Private
+
+/**
+ A `Realm` instance (also referred to as "a Realm") represents a Realm database.
+
+ Realms can either be stored on disk (see `init(path:)`) or in memory (see `Configuration`).
+
+ `Realm` instances are cached internally, and constructing equivalent `Realm` objects (for example, by using the same
+ path or identifier) produces limited overhead.
+
+ If you specifically want to ensure a `Realm` 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: `Realm` instances are not thread safe and cannot be shared across threads or dispatch queues. You must
+ construct a new instance for each thread in which a Realm will be accessed. For dispatch queues, this means
+ that you must construct a new instance in each block which is dispatched, as a queue is not guaranteed to
+ run all of its blocks on the same thread.
+ */
+public final class Realm {
+
+    // MARK: Properties
+
+    /// The `Schema` used by the Realm.
+    public var schema: Schema { return Schema(rlmRealm.schema) }
+
+    /// The `Configuration` value that was used to create the `Realm` instance.
+    public var configuration: Configuration { return Configuration.fromRLMRealmConfiguration(rlmRealm.configuration) }
+
+    /// Indicates if the Realm contains any objects.
+    public var isEmpty: Bool { return rlmRealm.isEmpty }
+
+    // MARK: Initializers
+
+    /**
+     Obtains an instance of the default Realm.
+
+     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 `Configuration`, which can be changed by setting the
+     `Realm.Configuration.defaultConfiguration` property to a new value.
+
+     - throws: An `NSError` if the Realm could not be initialized.
+     */
+    public convenience init() throws {
+        let rlmRealm = try RLMRealm(configuration: RLMRealmConfiguration.default())
+        self.init(rlmRealm)
+    }
+
+    /**
+     Obtains a `Realm` instance with the given configuration.
+
+     - parameter configuration: A configuration value to use when creating the Realm.
+
+     - throws: An `NSError` if the Realm could not be initialized.
+     */
+    public convenience init(configuration: Configuration) throws {
+        let rlmRealm = try RLMRealm(configuration: configuration.rlmConfiguration)
+        self.init(rlmRealm)
+    }
+
+    /**
+     Obtains a `Realm` instance persisted at a specified file URL.
+
+     - parameter fileURL: The local URL of the file the Realm should be saved at.
+
+     - throws: An `NSError` if the Realm could not be initialized.
+     */
+    public convenience init(fileURL: URL) throws {
+        var configuration = Configuration.defaultConfiguration
+        configuration.fileURL = fileURL
+        try self.init(configuration: configuration)
+    }
+
+    // MARK: Async
+
+    /**
+     Asynchronously open a Realm and deliver it to a block on the given queue.
+
+     Opening a Realm asynchronously will perform all work needed to get the Realm to
+     a usable state (such as running potentially time-consuming migrations) on a
+     background thread before dispatching to the given queue. In addition,
+     synchronized Realms wait for all remote content available at the time the
+     operation began to be downloaded and available locally.
+
+     - parameter configuration: A configuration object to use when opening the Realm.
+     - parameter callbackQueue: The dispatch queue on which the callback should be run.
+     - parameter callback:      A callback block. If the Realm was successfully opened, an
+                                it will be passed in as an argument.
+                                Otherwise, a `Swift.Error` describing what went wrong will be
+                                passed to the block instead.
+
+     - note: The returned Realm is confined to the thread on which it was created.
+             Because GCD does not guarantee that queues will always use the same
+             thread, accessing the returned Realm outside the callback block (even if
+             accessed from `callbackQueue`) is unsafe.
+     */
+    public static func asyncOpen(configuration: Realm.Configuration = .defaultConfiguration,
+                                 callbackQueue: DispatchQueue = .main,
+                                 callback: @escaping (Realm?, Swift.Error?) -> Void) {
+        RLMRealm.asyncOpen(with: configuration.rlmConfiguration, callbackQueue: callbackQueue) { rlmRealm, error in
+            callback(rlmRealm.flatMap(Realm.init), error)
+        }
+    }
+
+    // MARK: Transactions
+
+    /**
+     Performs actions contained within the given block inside a write transaction.
+
+     If the block throws an error, the transaction will be canceled and any
+     changes made before the error will be rolled back.
+
+     Only one write transaction can be open at a time for each Realm file. 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 `write` from `Realm` instances for the same Realm file in other
+     threads or other processes will block until the current write transaction
+     completes or is cancelled.
+
+     Before beginning the write transaction, `write` updates the `Realm`
+     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.
+
+     - parameter block: The block containing actions to perform.
+
+     - throws: An `NSError` if the transaction could not be completed successfully.
+               If `block` throws, the function throws the propagated `ErrorType` instead.
+     */
+    public func write(_ block: (() throws -> Void)) throws {
+        beginWrite()
+        do {
+            try block()
+        } catch let error {
+            if isInWriteTransaction { cancelWrite() }
+            throw error
+        }
+        if isInWriteTransaction { try commitWrite() }
+    }
+
+    /**
+     Begins a write transaction on the Realm.
+
+     Only one write transaction can be open at a time for each Realm file. 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 `beginWrite` from `Realm` instances for the same Realm file in
+     other threads or other processes will block until the current write
+     transaction completes or is cancelled.
+
+     Before beginning the write transaction, `beginWrite` updates the `Realm`
+     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.
+     */
+    public func beginWrite() {
+        rlmRealm.beginWriteTransaction()
+    }
+
+    /**
+     Commits all write operations in the current write transaction, and ends
+     the transaction.
+
+     After saving the changes and completing the write transaction, all
+     notification blocks registered on this specific `Realm` instance are called
+     synchronously. Notification blocks for `Realm` instances on other threads
+     and blocks registered for any Realm collection (including those on the
+     current thread) are scheduled to be called synchronously.
+
+     You can skip notifiying specific notification blocks about the changes made
+     in this write transaction by passing in their associated notification
+     tokens. This is primarily useful when the write transaction is saving
+     changes already made in the UI and you do not want to have the notification
+     block attempt to re-apply the same changes.
+
+     The tokens passed to this function must be for notifications for this Realm
+     which were added on the same thread as the write transaction is being
+     performed on. Notifications for different threads cannot be skipped using
+     this method.
+
+     - warning: This method may only be called during a write transaction.
+
+     - throws: An `NSError` if the transaction could not be written due to
+               running out of disk space or other i/o errors.
+     */
+    public func commitWrite(withoutNotifying tokens: [NotificationToken] = []) throws {
+        try rlmRealm.commitWriteTransactionWithoutNotifying(tokens)
+    }
+
+    /**
+     Reverts all writes made in 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 `Object`s which were added to the Realm will be
+     invalidated rather than becoming unmanaged.
+
+     Given the following code:
+
+     ```swift
+     let oldObject = objects(ObjectType).first!
+     let newObject = ObjectType()
+
+     realm.beginWrite()
+     realm.add(newObject)
+     realm.delete(oldObject)
+     realm.cancelWrite()
+     ```
+
+     Both `oldObject` and `newObject` will return `true` for `isInvalidated`,
+     but re-running the query which provided `oldObject` will once again return
+     the valid object.
+
+     KVO observers on any objects which were modified during the transaction
+     will be notified about the change back to their initial values, but no
+     other notifcations are produced by a cancelled write transaction.
+
+     - warning: This method may only be called during a write transaction.
+     */
+    public func cancelWrite() {
+        rlmRealm.cancelWriteTransaction()
+    }
+
+    /**
+     Indicates whether the Realm is currently 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.
+     */
+    public var isInWriteTransaction: Bool {
+        return rlmRealm.inWriteTransaction
+    }
+
+    // MARK: Adding and Creating objects
+
+    /**
+     Adds or updates an existing object into the Realm.
+
+     Only pass `true` to `update` if the object has a primary key. If no object exists in the Realm with the same
+     primary key value, the object is inserted. Otherwise, the existing object is updated with any changed values.
+
+     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 error will be
+     thrown. Instead, use one of the `create` functions 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 `false`).
+
+     - parameter object: The object to be added to this Realm.
+     - parameter update: If `true`, the Realm will try to find an existing copy of the object (with the same primary
+                         key), and update it. Otherwise, the object will be added.
+     */
+    public func add(_ object: Object, update: Bool = false) {
+        if update && object.objectSchema.primaryKeyProperty == nil {
+            throwRealmException("'\(object.objectSchema.className)' does not have a primary key and can not be updated")
+        }
+        RLMAddObjectToRealm(object, rlmRealm, update)
+    }
+
+    /**
+     Adds or updates all the objects in a collection into the Realm.
+
+     - see: `add(_:update:)`
+
+     - warning: This method may only be called during a write transaction.
+
+     - parameter objects: A sequence which contains objects to be added to the Realm.
+     - parameter update: If `true`, objects that are already in the Realm will be updated instead of added anew.
+     */
+    public func add<S: Sequence>(_ objects: S, update: Bool = false) where S.Iterator.Element: Object {
+        for obj in objects {
+            add(obj, update: update)
+        }
+    }
+
+    /**
+     Creates or updates a Realm object with a given value, adding it to the Realm and returning it.
+
+     You may only pass `true` to `update` if the object has a primary key. If no object exists
+     in the Realm with the same primary key value, the object is inserted. Otherwise, the
+     existing object is updated with any changed values.
+
+     The `value` argument can be a Realm object, 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. Do not pass in a `LinkingObjects` instance, either
+     by itself or as a member of a collection.
+
+     If the object is being created, all required properties that were not defined with default
+     values must be given initial values through the `value` argument. Otherwise, an Objective-C
+     exception will be thrown.
+
+     If the object is being updated, all properties defined in its schema will be set by copying
+     from `value` using key-value coding. If the `value` argument does not respond to `value(forKey:)`
+     for a given property name (or getter name, if defined), that value will remain untouched.
+     Nullable properties on the object can be set to nil by using `NSNull` as the updated value,
+     or (if you are passing in an instance of an `Object` subclass) setting the corresponding
+     property on `value` to nil.
+
+     If the `value` argument is an array, all properties must be present, valid and in the same
+     order as the properties defined in the model.
+
+     - warning: This method may only be called during a write transaction.
+
+     - parameter type:   The type of the object to create.
+     - parameter value:  The value used to populate the object.
+     - parameter update: If `true`, the Realm will try to find an existing copy of the object (with the same primary
+                         key), and update it. Otherwise, the object will be added.
+
+     - returns: The newly created object.
+     */
+    @discardableResult
+    public func create<T: Object>(_ type: T.Type, value: Any = [:], update: Bool = false) -> T {
+        let typeName = (type as Object.Type).className()
+        if update && schema[typeName]?.primaryKeyProperty == nil {
+            throwRealmException("'\(typeName)' does not have a primary key and can not be updated")
+        }
+        return unsafeDowncast(RLMCreateObjectInRealmWithValue(rlmRealm, typeName, value, update), to: T.self)
+    }
+
+    /**
+     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 the typed method `create(_:value:update:)`.
+
+     Creates or updates an object with the given class name and adds it to the `Realm`, populating
+     the object with the given value.
+
+     You may only pass `true` to `update` if the object has a primary key. If no object exists
+     in the Realm with the same primary key value, the object is inserted. Otherwise, the
+     existing object is updated with any changed values.
+
+     The `value` argument can be a Realm object, 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. Do not pass in a `LinkingObjects` instance, either
+     by itself or as a member of a collection.
+
+     If the object is being created, all required properties that were not defined with default
+     values must be given initial values through the `value` argument. Otherwise, an Objective-C
+     exception will be thrown.
+
+     If the object is being updated, all properties defined in its schema will be set by copying
+     from `value` using key-value coding. If the `value` argument does not respond to `value(forKey:)`
+     for a given property name (or getter name, if defined), that value will remain untouched.
+     Nullable properties on the object can be set to nil by using `NSNull` as the updated value.
+
+     If the `value` argument is an array, all properties must be present, valid and in the same
+     order as the properties defined in the model.
+
+     - warning: This method can only be called during a write transaction.
+
+     - parameter className:  The class name of the object to create.
+     - parameter value:      The value used to populate the object.
+     - parameter update:     If true will try to update existing objects with the same primary key.
+
+     - returns: The created object.
+
+     :nodoc:
+     */
+    @discardableResult
+    public func dynamicCreate(_ typeName: String, value: Any = [:], update: Bool = false) -> DynamicObject {
+        if update && schema[typeName]?.primaryKeyProperty == nil {
+            throwRealmException("'\(typeName)' does not have a primary key and can not be updated")
+        }
+        return noWarnUnsafeBitCast(RLMCreateObjectInRealmWithValue(rlmRealm, typeName, value, update),
+                                   to: DynamicObject.self)
+    }
+
+    // MARK: Deleting objects
+
+    /**
+     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.
+
+     - parameter object: The object to be deleted.
+     */
+    public func delete(_ object: Object) {
+        RLMDeleteObjectFromRealm(object, rlmRealm)
+    }
+
+    /**
+     Deletes zero or more objects from the Realm.
+
+     Do not pass in a slice to a `Results` or any other auto-updating Realm collection
+     type (for example, the type returned by the Swift `suffix(_:)` standard library
+     method). Instead, make a copy of the objects to delete using `Array()`, and pass
+     that instead. Directly passing in a view into an auto-updating collection may
+     result in 'index out of bounds' exceptions being thrown.
+
+     - warning: This method may only be called during a write transaction.
+
+     - parameter objects:   The objects to be deleted. This can be a `List<Object>`,
+                            `Results<Object>`, or any other Swift `Sequence` whose
+                            elements are `Object`s (subject to the caveats above).
+     */
+    public func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: Object {
+        for obj in objects {
+            delete(obj)
+        }
+    }
+
+    /**
+     Deletes zero or more objects from the Realm.
+
+     - warning: This method may only be called during a write transaction.
+
+     - parameter objects: A list of objects to delete.
+
+     :nodoc:
+     */
+    public func delete<Element: Object>(_ objects: List<Element>) {
+        rlmRealm.deleteObjects(objects._rlmArray)
+    }
+
+    /**
+     Deletes zero or more objects from the Realm.
+
+     - warning: This method may only be called during a write transaction.
+
+     - parameter objects: A `Results` containing the objects to be deleted.
+
+     :nodoc:
+     */
+    public func delete<Element: Object>(_ objects: Results<Element>) {
+        rlmRealm.deleteObjects(objects.rlmResults)
+    }
+
+    /**
+     Deletes all objects from the Realm.
+
+     - warning: This method may only be called during a write transaction.
+     */
+    public func deleteAll() {
+        RLMDeleteAllObjectsFromRealm(rlmRealm)
+    }
+
+    // MARK: Object Retrieval
+
+    /**
+     Returns all objects of the given type stored in the Realm.
+
+     - parameter type: The type of the objects to be returned.
+
+     - returns: A `Results` containing the objects.
+     */
+    public func objects<Element: Object>(_ type: Element.Type) -> Results<Element> {
+        return Results(RLMGetObjects(rlmRealm, type.className(), nil))
+    }
+
+    /**
+     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 the typed method `objects(type:)`.
+
+     Returns all objects for a given class name in the Realm.
+
+     - parameter typeName: The class name of the objects to be returned.
+     - returns: All objects for the given class name as dynamic objects
+
+     :nodoc:
+     */
+    public func dynamicObjects(_ typeName: String) -> Results<DynamicObject> {
+        return Results<DynamicObject>(RLMGetObjects(rlmRealm, typeName, nil))
+    }
+
+    /**
+     Retrieves the single instance of a given object type with the given primary key from the Realm.
+
+     This method requires that `primaryKey()` be overridden on the given object class.
+
+     - see: `Object.primaryKey()`
+
+     - parameter type: The type of the object to be returned.
+     - parameter key:  The primary key of the desired object.
+
+     - returns: An object of type `type`, or `nil` if no instance with the given primary key exists.
+     */
+    public func object<Element: Object, KeyType>(ofType type: Element.Type, forPrimaryKey key: KeyType) -> Element? {
+        return unsafeBitCast(RLMGetObject(rlmRealm, (type as Object.Type).className(),
+                                          dynamicBridgeCast(fromSwift: key)) as! RLMObjectBase?,
+                             to: Optional<Element>.self)
+    }
+
+    /**
+     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 the typed method `objectForPrimaryKey(_:key:)`.
+
+     Get a dynamic object with the given class name and primary key.
+
+     Returns `nil` if no object exists with the given class name and primary key.
+
+     This method requires that `primaryKey()` be overridden on the given subclass.
+
+     - see: Object.primaryKey()
+
+     - warning: This method is useful only in specialized circumstances.
+
+     - parameter className:  The class name of the object to be returned.
+     - parameter key:        The primary key of the desired object.
+
+     - returns: An object of type `DynamicObject` or `nil` if an object with the given primary key does not exist.
+
+     :nodoc:
+     */
+    public func dynamicObject(ofType typeName: String, forPrimaryKey key: Any) -> DynamicObject? {
+        return unsafeBitCast(RLMGetObject(rlmRealm, typeName, key) as! RLMObjectBase?, to: Optional<DynamicObject>.self)
+    }
+
+    // MARK: Notifications
+
+    /**
+     Adds a notification handler for changes made to this Realm, and returns a notification token.
+
+     Notification handlers are called after each write transaction is committed, independent of the thread or process.
+
+     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.
+
+     Notifications can't be delivered as long as the run loop is blocked by other activity. When notifications can't be
+     delivered instantly, multiple notifications may be coalesced.
+
+     You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving
+     updates, call `invalidate()` on the token.
+
+     - parameter block: A block which is called to process Realm notifications. It receives the following parameters:
+                        `notification`: the incoming notification; `realm`: the Realm for which the notification
+                        occurred.
+
+     - returns: A token which must be held for as long as you wish to continue receiving change notifications.
+     */
+    public func observe(_ block: @escaping NotificationBlock) -> NotificationToken {
+        return rlmRealm.addNotificationBlock { rlmNotification, _ in
+            switch rlmNotification {
+            case RLMNotification.DidChange:
+                block(.didChange, self)
+            case RLMNotification.RefreshRequired:
+                block(.refreshRequired, self)
+            default:
+                fatalError("Unhandled notification type: \(rlmNotification)")
+            }
+        }
+    }
+
+    // MARK: Autorefresh and Refresh
+
+    /**
+     Set this property to `true` to automatically update this Realm when changes happen in other threads.
+
+     If set to `true` (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 `false`, 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 `true`.
+
+     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 `true` the next time the Realm is created. This is normally irrelevant as it
+     means that there is nothing to refresh (as managed `Object`s, `List`s, and `Results` have strong references to the
+     `Realm` that manages them), but it means that setting `autorefresh = false` in
+     `application(_:didFinishLaunchingWithOptions:)` and only later storing Realm objects will not work.
+
+     Defaults to `true`.
+     */
+    public var autorefresh: Bool {
+        get {
+            return rlmRealm.autorefresh
+        }
+        set {
+            rlmRealm.autorefresh = newValue
+        }
+    }
+
+    /**
+     Updates the Realm and outstanding objects managed by the Realm to point to the most recent data.
+
+     - returns: Whether there were any updates for the Realm. Note that `true` may be returned even if no data actually
+     changed.
+     */
+    @discardableResult
+    public func refresh() -> Bool {
+        return rlmRealm.refresh()
+    }
+
+    // MARK: Invalidation
+
+    /**
+     Invalidates all `Object`s, `Results`, `LinkingObjects`, and `List`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 `Object`, `Results` and `List` instances obtained from this `Realm` instance on the current thread are
+     invalidated. `Object`s and `Array`s cannot be used. `Results` 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.
+     */
+    public func invalidate() {
+        rlmRealm.invalidate()
+    }
+
+    // MARK: Writing a Copy
+
+    /**
+     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.
+
+     - parameter fileURL:       Local URL to save the Realm to.
+     - parameter encryptionKey: Optional 64-byte encryption key to encrypt the new file with.
+
+     - throws: An `NSError` if the copy could not be written.
+     */
+    public func writeCopy(toFile fileURL: URL, encryptionKey: Data? = nil) throws {
+        try rlmRealm.writeCopy(to: fileURL, encryptionKey: encryptionKey)
+    }
+
+    // MARK: Internal
+
+    internal var rlmRealm: RLMRealm
+
+    internal init(_ rlmRealm: RLMRealm) {
+        self.rlmRealm = rlmRealm
+    }
+}
+
+// MARK: Equatable
+
+extension Realm: Equatable {
+    /// Returns whether two `Realm` instances are equal.
+    public static func == (lhs: Realm, rhs: Realm) -> Bool {
+        return lhs.rlmRealm == rhs.rlmRealm
+    }
+}
+
+// MARK: Notifications
+
+extension Realm {
+    /// A notification indicating that changes were made to a Realm.
+    public enum Notification: String {
+        /**
+         This notification is posted when the data in a Realm has changed.
+
+         `didChange` is posted after a Realm has been refreshed to reflect a write transaction, This can happen when an
+         autorefresh occurs, `refresh()` is called, after an implicit refresh from `write(_:)`/`beginWrite()`, or after
+         a local write transaction is committed.
+         */
+        case didChange = "RLMRealmDidChangeNotification"
+
+        /**
+         This notification is posted when a write transaction has been committed to a Realm on a different thread for
+         the same file.
+
+         It is not posted if `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
+         `refresh()` after doing some work. Refreshing the Realm is optional, but not refreshing the Realm may lead to
+         large Realm files. This is because an extra copy of the data must be kept for the stale Realm.
+         */
+        case refreshRequired = "RLMRealmRefreshRequiredNotification"
+    }
+}
+
+/// The type of a block to run for notification purposes when the data in a Realm is modified.
+public typealias NotificationBlock = (_ notification: Realm.Notification, _ realm: Realm) -> Void
diff --git a/iOS/Pods/RealmSwift/RealmSwift/RealmCollection.swift b/iOS/Pods/RealmSwift/RealmSwift/RealmCollection.swift
new file mode 100644 (file)
index 0000000..7465c9c
--- /dev/null
@@ -0,0 +1,1123 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+
+/**
+ An iterator for a `RealmCollection` instance.
+ */
+public struct RLMIterator<Element: RealmCollectionValue>: IteratorProtocol {
+    private var generatorBase: NSFastEnumerationIterator
+
+    init(collection: RLMCollection) {
+        generatorBase = NSFastEnumerationIterator(collection)
+    }
+
+    /// Advance to the next element and return it, or `nil` if no next element exists.
+    public mutating func next() -> Element? {
+        let next = generatorBase.next()
+        if let next = next as? Object? {
+            if next == nil {
+                return nil as Element?
+            }
+            return unsafeBitCast(next, to: Optional<Element>.self)
+        }
+        return next as! Element?
+    }
+}
+
+/**
+ A `RealmCollectionChange` value encapsulates information about changes to collections
+ that are reported by Realm notifications.
+
+ 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.
+
+ The arrays of indices in the `.update` case follow `UITableView`'s batching
+ conventions, and can be passed as-is to a table view's batch update functions after being converted to index paths.
+ For example, for a simple one-section table view, you can do the following:
+
+ ```swift
+ self.notificationToken = results.observe { changes in
+     switch changes {
+     case .initial:
+         // Results are now populated and can be accessed without blocking the UI
+         self.tableView.reloadData()
+         break
+     case .update(_, let deletions, let insertions, let modifications):
+         // Query results have changed, so apply them to the TableView
+         self.tableView.beginUpdates()
+         self.tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) },
+            with: .automatic)
+         self.tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) },
+            with: .automatic)
+         self.tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) },
+            with: .automatic)
+         self.tableView.endUpdates()
+         break
+     case .error(let err):
+         // An error occurred while opening the Realm file on the background worker thread
+         fatalError("\(err)")
+         break
+     }
+ }
+ ```
+ */
+public enum RealmCollectionChange<CollectionType> {
+    /**
+     `.initial` indicates that the initial run of the query has completed (if
+     applicable), and the collection can now be used without performing any
+     blocking work.
+     */
+    case initial(CollectionType)
+
+    /**
+     `.update` indicates that a write transaction has been committed which
+     either changed which objects are in the collection, and/or modified one
+     or more of the objects in the collection.
+
+     All three of the change arrays are always sorted in ascending order.
+
+     - parameter deletions:     The indices in the previous version of the collection which were removed from this one.
+     - parameter insertions:    The indices in the new collection which were added in this version.
+     - parameter modifications: The indices of the objects in the new collection which were modified in this version.
+     */
+    case update(CollectionType, deletions: [Int], insertions: [Int], modifications: [Int])
+
+    /**
+     If an error occurs, notification blocks are called one time with a `.error`
+     result and an `NSError` containing details about the error. This can only
+     currently happen if opening the Realm on a background thread to calcuate
+     the change set fails. The callback will never be called again after it is
+     invoked with a .error value.
+     */
+    case error(Error)
+
+    static func fromObjc(value: CollectionType, change: RLMCollectionChange?, error: Error?) -> RealmCollectionChange {
+        if let error = error {
+            return .error(error)
+        }
+        if let change = change {
+            return .update(value,
+                deletions: forceCast(change.deletions, to: [Int].self),
+                insertions: forceCast(change.insertions, to: [Int].self),
+                modifications: forceCast(change.modifications, to: [Int].self))
+        }
+        return .initial(value)
+    }
+}
+
+private func forceCast<A, U>(_ from: A, to type: U.Type) -> U {
+    return from as! U
+}
+
+/// A type which can be stored in a Realm List or Results
+public protocol RealmCollectionValue {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    static func _rlmArray() -> RLMArray<AnyObject>
+}
+
+extension RealmCollectionValue {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    public static func _rlmArray() -> RLMArray<AnyObject> {
+        return RLMArray(objectType: .int, optional: false)
+    }
+}
+
+extension Optional: RealmCollectionValue {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    public static func _rlmArray() -> RLMArray<AnyObject> {
+        switch Wrapped.self {
+        case is Int.Type, is Int8.Type, is Int16.Type, is Int32.Type, is Int64.Type:
+            return RLMArray(objectType: .int, optional: true)
+        case is Bool.Type:   return RLMArray(objectType: .bool, optional: true)
+        case is Float.Type:  return RLMArray(objectType: .float, optional: true)
+        case is Double.Type: return RLMArray(objectType: .double, optional: true)
+        case is String.Type: return RLMArray(objectType: .string, optional: true)
+        case is Data.Type:   return RLMArray(objectType: .data, optional: true)
+        case is Date.Type:   return RLMArray(objectType: .date, optional: true)
+        default: fatalError("Unsupported type for List: \(Wrapped.self)?")
+        }
+    }
+}
+
+extension Int: RealmCollectionValue {}
+extension Int8: RealmCollectionValue {}
+extension Int16: RealmCollectionValue {}
+extension Int32: RealmCollectionValue {}
+extension Int64: RealmCollectionValue {}
+extension Float: RealmCollectionValue {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    public static func _rlmArray() -> RLMArray<AnyObject> {
+        return RLMArray(objectType: .float, optional: false)
+    }
+}
+extension Double: RealmCollectionValue {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    public static func _rlmArray() -> RLMArray<AnyObject> {
+        return RLMArray(objectType: .double, optional: false)
+    }
+}
+extension Bool: RealmCollectionValue {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    public static func _rlmArray() -> RLMArray<AnyObject> {
+        return RLMArray(objectType: .bool, optional: false)
+    }
+}
+
+extension String: RealmCollectionValue {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    public static func _rlmArray() -> RLMArray<AnyObject> {
+        return RLMArray(objectType: .string, optional: false)
+    }
+}
+extension Date: RealmCollectionValue {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    public static func _rlmArray() -> RLMArray<AnyObject> {
+        return RLMArray(objectType: .date, optional: false)
+    }
+}
+extension Data: RealmCollectionValue {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    public static func _rlmArray() -> RLMArray<AnyObject> {
+        return RLMArray(objectType: .data, optional: false)
+    }
+}
+
+#if swift(>=3.2)
+// FIXME: When we drop support for Swift 3.1, change ElementType to Element
+// throughout the project (this is a non-breaking change). We use ElementType
+// only because of limitations in Swift 3.1's compiler.
+/// :nodoc:
+public protocol RealmCollectionBase: RandomAccessCollection, LazyCollectionProtocol, CustomStringConvertible, ThreadConfined where Element: RealmCollectionValue {
+    typealias ElementType = Element
+}
+#else
+/// :nodoc:
+public protocol RealmCollectionBase: RandomAccessCollection, LazyCollectionProtocol, CustomStringConvertible, ThreadConfined {
+    /// The type of the objects contained in the collection.
+    associatedtype ElementType: RealmCollectionValue
+}
+#endif
+
+/**
+ A homogenous collection of `Object`s which can be retrieved, filtered, sorted, and operated upon.
+*/
+public protocol RealmCollection: RealmCollectionBase {
+    // Must also conform to `AssistedObjectiveCBridgeable`
+
+    // MARK: Properties
+
+    /// The Realm which manages the collection, or `nil` for unmanaged collections.
+    var realm: Realm? { get }
+
+    /**
+     Indicates if the collection can no longer be accessed.
+
+     The collection can no longer be accessed if `invalidate()` is called on the `Realm` that manages the collection.
+     */
+    var isInvalidated: Bool { get }
+
+    /// The number of objects in the collection.
+    var count: Int { get }
+
+    /// A human-readable description of the objects contained in the collection.
+    var description: String { get }
+
+
+    // MARK: Index Retrieval
+
+    /**
+     Returns the index of an object in the collection, or `nil` if the object is not present.
+
+     - parameter object: An object.
+     */
+    func index(of object: ElementType) -> Int?
+
+    /**
+     Returns the index of the first object matching the predicate, or `nil` if no objects match.
+
+     - parameter predicate: The predicate to use to filter the objects.
+     */
+    func index(matching predicate: NSPredicate) -> Int?
+
+    /**
+     Returns the index of the first object matching the predicate, or `nil` if no objects match.
+
+     - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
+     */
+    func index(matching predicateFormat: String, _ args: Any...) -> Int?
+
+
+    // MARK: Filtering
+
+    /**
+     Returns a `Results` containing all objects matching the given predicate in the collection.
+
+     - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
+     */
+    func filter(_ predicateFormat: String, _ args: Any...) -> Results<ElementType>
+
+    /**
+     Returns a `Results` containing all objects matching the given predicate in the collection.
+
+     - parameter predicate: The predicate to use to filter the objects.
+     */
+    func filter(_ predicate: NSPredicate) -> Results<ElementType>
+
+
+    // MARK: Sorting
+
+    /**
+     Returns a `Results` containing the objects in the collection, but sorted.
+
+     Objects are sorted based on the values of the given key path. For example, to sort a collection of `Student`s from
+     youngest to oldest based on their `age` property, you might call
+     `students.sorted(byKeyPath: "age", ascending: true)`.
+
+     - warning: Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
+                floating point, integer, and string types.
+
+     - parameter keyPath:   The key path to sort by.
+     - parameter ascending: The direction to sort in.
+     */
+    func sorted(byKeyPath keyPath: String, ascending: Bool) -> Results<ElementType>
+
+    /**
+     Returns a `Results` containing the objects in the collection, but sorted.
+
+     - warning: Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
+                floating point, integer, and string types.
+
+     - see: `sorted(byKeyPath:ascending:)`
+
+     - parameter sortDescriptors: A sequence of `SortDescriptor`s to sort by.
+     */
+    func sorted<S: Sequence>(by sortDescriptors: S) -> Results<ElementType> where S.Iterator.Element == SortDescriptor
+
+    // MARK: Aggregate Operations
+
+    /**
+     Returns the minimum (lowest) value of the given property among all the objects in the collection, or `nil` if the
+     collection is empty.
+
+     - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
+
+     - parameter property: The name of a property whose minimum value is desired.
+     */
+    func min<T: MinMaxType>(ofProperty property: String) -> T?
+
+    /**
+     Returns the maximum (highest) value of the given property among all the objects in the collection, or `nil` if the
+     collection is empty.
+
+     - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
+
+     - parameter property: The name of a property whose minimum value is desired.
+     */
+    func max<T: MinMaxType>(ofProperty property: String) -> T?
+
+    /**
+    Returns the sum of the given property for objects in the collection, or `nil` if the collection is empty.
+
+    - warning: Only names of properties of a type conforming to the `AddableType` protocol can be used.
+
+    - parameter property: The name of a property conforming to `AddableType` to calculate sum on.
+    */
+    func sum<T: AddableType>(ofProperty property: String) -> T
+
+    /**
+     Returns the average value of a given property over all the objects in the collection, or `nil` if
+     the collection is empty.
+
+     - warning: Only a property whose type conforms to the `AddableType` protocol can be specified.
+
+     - parameter property: The name of a property whose values should be summed.
+     */
+    func average(ofProperty property: String) -> Double?
+
+
+    // MARK: Key-Value Coding
+
+    /**
+     Returns an `Array` containing the results of invoking `valueForKey(_:)` with `key` on each of the collection's
+     objects.
+
+     - parameter key: The name of the property whose values are desired.
+     */
+    func value(forKey key: String) -> Any?
+
+    /**
+     Returns an `Array` containing the results of invoking `valueForKeyPath(_:)` with `keyPath` on each of the
+     collection's objects.
+
+     - parameter keyPath: The key path to the property whose values are desired.
+     */
+    func value(forKeyPath keyPath: String) -> Any?
+
+    /**
+     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.
+
+     - parameter value: The object value.
+     - parameter key:   The name of the property whose value should be set on each object.
+     */
+    func setValue(_ value: Any?, forKey key: String)
+
+    // MARK: Notifications
+
+    /**
+     Registers a block to be called each time the collection changes.
+
+     The block will be asynchronously called with the initial results, 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 that is passed to the block reports, in the form of indices within the collection, which of
+     the objects were added, removed, or modified during each write transaction. See the `RealmCollectionChange`
+     documentation for more information on the change information supplied and an example of how to use it to update a
+     `UITableView`.
+
+     At the time when the block is called, the collection 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 `realm.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.
+
+     ```swift
+     let results = realm.objects(Dog.self)
+     print("dogs.count: \(dogs?.count)") // => 0
+     let token = dogs.observe { changes in
+     switch changes {
+         case .initial(let dogs):
+             // Will print "dogs.count: 1"
+             print("dogs.count: \(dogs.count)")
+             break
+         case .update:
+             // Will not be hit in this example
+             break
+         case .error:
+             break
+         }
+     }
+     try! realm.write {
+         let dog = Dog()
+         dog.name = "Rex"
+         person.dogs.append(dog)
+     }
+     // end of run loop execution context
+     ```
+
+     You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving
+     updates, call `invalidate()` on the token.
+
+     - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only.
+
+     - parameter block: The block to be called whenever a change occurs.
+     - returns: A token which must be held for as long as you want updates to be delivered.
+     */
+    func observe(_ block: @escaping (RealmCollectionChange<Self>) -> Void) -> NotificationToken
+
+    /// :nodoc:
+    func _observe(_ block: @escaping (RealmCollectionChange<AnyRealmCollection<ElementType>>) -> Void) -> NotificationToken
+}
+
+/// :nodoc:
+public protocol OptionalProtocol {
+    associatedtype Wrapped
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    func _rlmInferWrappedType() -> Wrapped
+}
+
+extension Optional: OptionalProtocol {
+    /// :nodoc:
+    // swiftlint:disable:next identifier_name
+    public func _rlmInferWrappedType() -> Wrapped { return self! }
+}
+
+
+// FIXME: See the declaration of RealmCollectionBase for why this `#if` is required.
+#if swift(>=3.2)
+public extension RealmCollection where Element: MinMaxType {
+    /**
+     Returns the minimum (lowest) value of the collection, or `nil` if the collection is empty.
+     */
+    public func min() -> Element? {
+        return min(ofProperty: "self")
+    }
+    /**
+     Returns the maximum (highest) value of the collection, or `nil` if the collection is empty.
+     */
+    public func max() -> Element? {
+        return max(ofProperty: "self")
+    }
+}
+
+public extension RealmCollection where Element: OptionalProtocol, Element.Wrapped: MinMaxType {
+    /**
+     Returns the minimum (lowest) value of the collection, or `nil` if the collection is empty.
+     */
+    public func min() -> Element.Wrapped? {
+        return min(ofProperty: "self")
+    }
+    /**
+     Returns the maximum (highest) value of the collection, or `nil` if the collection is empty.
+     */
+    public func max() -> Element.Wrapped? {
+        return max(ofProperty: "self")
+    }
+}
+
+public extension RealmCollection where Element: AddableType {
+    /**
+     Returns the sum of the values in the collection, or `nil` if the collection is empty.
+     */
+    public func sum() -> Element {
+        return sum(ofProperty: "self")
+    }
+    /**
+     Returns the average of all of the values in the collection.
+     */
+    public func average() -> Double? {
+        return average(ofProperty: "self")
+    }
+}
+
+public extension RealmCollection where Element: OptionalProtocol, Element.Wrapped: AddableType {
+    /**
+     Returns the sum of the values in the collection, or `nil` if the collection is empty.
+     */
+    public func sum() -> Element.Wrapped {
+        return sum(ofProperty: "self")
+    }
+    /**
+     Returns the average of all of the values in the collection.
+     */
+    public func average() -> Double? {
+        return average(ofProperty: "self")
+    }
+}
+
+public extension RealmCollection where Element: Comparable {
+    /**
+     Returns a `Results` containing the objects in the collection, but sorted.
+
+     Objects are sorted based on their values. For example, to sort a collection of `Date`s from
+     neweset to oldest based, you might call `dates.sorted(ascending: true)`.
+
+     - parameter ascending: The direction to sort in.
+     */
+    public func sorted(ascending: Bool = true) -> Results<Element> {
+        return sorted(byKeyPath: "self", ascending: ascending)
+    }
+}
+
+public extension RealmCollection where Element: OptionalProtocol, Element.Wrapped: Comparable {
+    /**
+     Returns a `Results` containing the objects in the collection, but sorted.
+
+     Objects are sorted based on their values. For example, to sort a collection of `Date`s from
+     neweset to oldest based, you might call `dates.sorted(ascending: true)`.
+
+     - parameter ascending: The direction to sort in.
+     */
+    public func sorted(ascending: Bool = true) -> Results<Element> {
+        return sorted(byKeyPath: "self", ascending: ascending)
+    }
+}
+#else
+public extension RealmCollection where ElementType: MinMaxType {
+    /**
+     Returns the minimum (lowest) value of the collection, or `nil` if the collection is empty.
+     */
+    public func min() -> ElementType? {
+        return min(ofProperty: "self")
+    }
+    /**
+     Returns the maximum (highest) value of the collection, or `nil` if the collection is empty.
+     */
+    public func max() -> ElementType? {
+        return max(ofProperty: "self")
+    }
+}
+
+public extension RealmCollection where ElementType: OptionalProtocol, ElementType.Wrapped: MinMaxType {
+    /**
+     Returns the minimum (lowest) value of the collection, or `nil` if the collection is empty.
+     */
+    public func min() -> ElementType.Wrapped? {
+        return min(ofProperty: "self")
+    }
+    /**
+     Returns the maximum (highest) value of the collection, or `nil` if the collection is empty.
+     */
+    public func max() -> ElementType.Wrapped? {
+        return max(ofProperty: "self")
+    }
+}
+
+public extension RealmCollection where ElementType: AddableType {
+    /**
+     Returns the sum of the values in the collection, or `nil` if the collection is empty.
+     */
+    public func sum() -> ElementType {
+        return sum(ofProperty: "self")
+    }
+    /**
+     Returns the average of all of the values in the collection.
+     */
+    public func average() -> Double? {
+        return average(ofProperty: "self")
+    }
+}
+
+public extension RealmCollection where ElementType: OptionalProtocol, ElementType.Wrapped: AddableType {
+    /**
+     Returns the sum of the values in the collection, or `nil` if the collection is empty.
+     */
+    public func sum() -> ElementType.Wrapped {
+        return sum(ofProperty: "self")
+    }
+    /**
+     Returns the average of all of the values in the collection.
+     */
+    public func average() -> Double? {
+        return average(ofProperty: "self")
+    }
+}
+
+public extension RealmCollection where ElementType: Comparable {
+    /**
+     Returns a `Results` containing the objects in the collection, but sorted.
+
+     Objects are sorted based on their values. For example, to sort a collection of `Date`s from
+     neweset to oldest based, you might call `dates.sorted(ascending: true)`.
+
+     - parameter ascending: The direction to sort in.
+     */
+    public func sorted(ascending: Bool = true) -> Results<ElementType> {
+        return sorted(byKeyPath: "self", ascending: ascending)
+    }
+}
+
+public extension RealmCollection where ElementType: OptionalProtocol, ElementType.Wrapped: Comparable {
+    /**
+     Returns a `Results` containing the objects in the collection, but sorted.
+
+     Objects are sorted based on their values. For example, to sort a collection of `Date`s from
+     neweset to oldest based, you might call `dates.sorted(ascending: true)`.
+
+     - parameter ascending: The direction to sort in.
+     */
+    public func sorted(ascending: Bool = true) -> Results<ElementType> {
+        return sorted(byKeyPath: "self", ascending: ascending)
+    }
+}
+#endif
+
+private class _AnyRealmCollectionBase<T: RealmCollectionValue>: AssistedObjectiveCBridgeable {
+    typealias Wrapper = AnyRealmCollection<Element>
+    typealias Element = T
+    var realm: Realm? { fatalError() }
+    var isInvalidated: Bool { fatalError() }
+    var count: Int { fatalError() }
+    var description: String { fatalError() }
+    func index(of object: Element) -> Int? { fatalError() }
+    func index(matching predicate: NSPredicate) -> Int? { fatalError() }
+    func index(matching predicateFormat: String, _ args: Any...) -> Int? { fatalError() }
+    func filter(_ predicateFormat: String, _ args: Any...) -> Results<Element> { fatalError() }
+    func filter(_ predicate: NSPredicate) -> Results<Element> { fatalError() }
+    func sorted(byKeyPath keyPath: String, ascending: Bool) -> Results<Element> { fatalError() }
+    func sorted<S: Sequence>(by sortDescriptors: S) -> Results<Element> where S.Iterator.Element == SortDescriptor {
+        fatalError()
+    }
+    func min<T: MinMaxType>(ofProperty property: String) -> T? { fatalError() }
+    func max<T: MinMaxType>(ofProperty property: String) -> T? { fatalError() }
+    func sum<T: AddableType>(ofProperty property: String) -> T { fatalError() }
+    func average(ofProperty property: String) -> Double? { fatalError() }
+    subscript(position: Int) -> Element { fatalError() }
+    func makeIterator() -> RLMIterator<T> { fatalError() }
+    var startIndex: Int { fatalError() }
+    var endIndex: Int { fatalError() }
+    func value(forKey key: String) -> Any? { fatalError() }
+    func value(forKeyPath keyPath: String) -> Any? { fatalError() }
+    func setValue(_ value: Any?, forKey key: String) { fatalError() }
+    func _observe(_ block: @escaping (RealmCollectionChange<Wrapper>) -> Void)
+        -> NotificationToken { fatalError() }
+    class func bridging(from objectiveCValue: Any, with metadata: Any?) -> Self { fatalError() }
+    var bridged: (objectiveCValue: Any, metadata: Any?) { fatalError() }
+}
+
+private final class _AnyRealmCollection<C: RealmCollection>: _AnyRealmCollectionBase<C.ElementType> {
+    let base: C
+    init(base: C) {
+        self.base = base
+    }
+
+    // MARK: Properties
+
+    override var realm: Realm? { return base.realm }
+    override var isInvalidated: Bool { return base.isInvalidated }
+    override var count: Int { return base.count }
+    override var description: String { return base.description }
+
+
+    // MARK: Index Retrieval
+
+    override func index(of object: C.ElementType) -> Int? { return base.index(of: object) }
+
+    override func index(matching predicate: NSPredicate) -> Int? { return base.index(matching: predicate) }
+
+    override func index(matching predicateFormat: String, _ args: Any...) -> Int? {
+        return base.index(matching: NSPredicate(format: predicateFormat, argumentArray: unwrapOptionals(in: args)))
+    }
+
+    // MARK: Filtering
+
+    override func filter(_ predicateFormat: String, _ args: Any...) -> Results<C.ElementType> {
+        return base.filter(NSPredicate(format: predicateFormat, argumentArray: unwrapOptionals(in: args)))
+    }
+
+    override func filter(_ predicate: NSPredicate) -> Results<C.ElementType> { return base.filter(predicate) }
+
+    // MARK: Sorting
+
+    override func sorted(byKeyPath keyPath: String, ascending: Bool) -> Results<C.ElementType> {
+        return base.sorted(byKeyPath: keyPath, ascending: ascending)
+    }
+
+    override func sorted<S: Sequence>
+        (by sortDescriptors: S) -> Results<C.ElementType> where S.Iterator.Element == SortDescriptor {
+        return base.sorted(by: sortDescriptors)
+    }
+
+
+    // MARK: Aggregate Operations
+
+    override func min<T: MinMaxType>(ofProperty property: String) -> T? {
+        return base.min(ofProperty: property)
+    }
+
+    override func max<T: MinMaxType>(ofProperty property: String) -> T? {
+        return base.max(ofProperty: property)
+    }
+
+    override func sum<T: AddableType>(ofProperty property: String) -> T {
+        return base.sum(ofProperty: property)
+    }
+
+    override func average(ofProperty property: String) -> Double? {
+        return base.average(ofProperty: property)
+    }
+
+
+    // MARK: Sequence Support
+
+    override subscript(position: Int) -> C.ElementType {
+        #if swift(>=3.2)
+            return base[position as! C.Index]
+        #else
+            return base[position as! C.Index] as! C.ElementType
+        #endif
+    }
+
+    override func makeIterator() -> RLMIterator<Element> {
+        // FIXME: it should be possible to avoid this force-casting
+        return base.makeIterator() as! RLMIterator<Element>
+    }
+
+
+    // MARK: Collection Support
+
+    override var startIndex: Int {
+        // FIXME: it should be possible to avoid this force-casting
+        return base.startIndex as! Int
+    }
+
+    override var endIndex: Int {
+        // FIXME: it should be possible to avoid this force-casting
+        return base.endIndex as! Int
+    }
+
+
+    // MARK: Key-Value Coding
+
+    override func value(forKey key: String) -> Any? { return base.value(forKey: key) }
+
+    override func value(forKeyPath keyPath: String) -> Any? { return base.value(forKeyPath: keyPath) }
+
+    override func setValue(_ value: Any?, forKey key: String) { base.setValue(value, forKey: key) }
+
+    // MARK: Notifications
+
+    /// :nodoc:
+    override func _observe(_ block: @escaping (RealmCollectionChange<Wrapper>) -> Void)
+        -> NotificationToken { return base._observe(block) }
+
+    // MARK: AssistedObjectiveCBridgeable
+
+    override class func bridging(from objectiveCValue: Any, with metadata: Any?) -> _AnyRealmCollection {
+        return _AnyRealmCollection(
+            base: (C.self as! AssistedObjectiveCBridgeable.Type).bridging(from: objectiveCValue, with: metadata) as! C)
+    }
+
+    override var bridged: (objectiveCValue: Any, metadata: Any?) {
+        return (base as! AssistedObjectiveCBridgeable).bridged
+    }
+}
+
+/**
+ A type-erased `RealmCollection`.
+
+ Instances of `RealmCollection` forward operations to an opaque underlying collection having the same `Element` type.
+ */
+public final class AnyRealmCollection<Element: RealmCollectionValue>: RealmCollection {
+
+    /// The type of the objects contained within the collection.
+    public typealias ElementType = Element
+
+    public func index(after i: Int) -> Int { return i + 1 }
+    public func index(before i: Int) -> Int { return i - 1 }
+
+    /// The type of the objects contained in the collection.
+    fileprivate let base: _AnyRealmCollectionBase<Element>
+
+    fileprivate init(base: _AnyRealmCollectionBase<Element>) {
+        self.base = base
+    }
+
+    /// Creates an `AnyRealmCollection` wrapping `base`.
+    public init<C: RealmCollection>(_ base: C) where C.ElementType == Element {
+        self.base = _AnyRealmCollection(base: base)
+    }
+
+    // MARK: Properties
+
+    /// The Realm which manages the collection, or `nil` if the collection is unmanaged.
+    public var realm: Realm? { return base.realm }
+
+    /**
+     Indicates if the collection can no longer be accessed.
+
+     The collection can no longer be accessed if `invalidate()` is called on the containing `realm`.
+     */
+    public var isInvalidated: Bool { return base.isInvalidated }
+
+    /// The number of objects in the collection.
+    public var count: Int { return base.count }
+
+    /// A human-readable description of the objects contained in the collection.
+    public var description: String { return base.description }
+
+
+    // MARK: Index Retrieval
+
+    /**
+     Returns the index of the given object, or `nil` if the object is not in the collection.
+
+     - parameter object: An object.
+     */
+    public func index(of object: Element) -> Int? { return base.index(of: object) }
+
+    /**
+     Returns the index of the first object matching the given predicate, or `nil` if no objects match.
+
+     - parameter predicate: The predicate with which to filter the objects.
+     */
+    public func index(matching predicate: NSPredicate) -> Int? { return base.index(matching: predicate) }
+
+    /**
+     Returns the index of the first object matching the given predicate, or `nil` if no objects match.
+
+     - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
+     */
+    public func index(matching predicateFormat: String, _ args: Any...) -> Int? {
+        return base.index(matching: NSPredicate(format: predicateFormat, argumentArray: unwrapOptionals(in: args)))
+    }
+
+    // MARK: Filtering
+
+    /**
+     Returns a `Results` containing all objects matching the given predicate in the collection.
+
+     - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
+     */
+    public func filter(_ predicateFormat: String, _ args: Any...) -> Results<Element> {
+        return base.filter(NSPredicate(format: predicateFormat, argumentArray: unwrapOptionals(in: args)))
+    }
+
+    /**
+     Returns a `Results` containing all objects matching the given predicate in the collection.
+
+     - parameter predicate: The predicate with which to filter the objects.
+
+     - returns: A `Results` containing objects that match the given predicate.
+     */
+    public func filter(_ predicate: NSPredicate) -> Results<Element> { return base.filter(predicate) }
+
+
+    // MARK: Sorting
+
+    /**
+     Returns a `Results` containing the objects in the collection, but sorted.
+
+     Objects are sorted based on the values of the given key path. For example, to sort a collection of `Student`s from
+     youngest to oldest based on their `age` property, you might call
+     `students.sorted(byKeyPath: "age", ascending: true)`.
+
+     - warning:  Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
+                 floating point, integer, and string types.
+
+     - parameter keyPath:  The key path to sort by.
+     - parameter ascending: The direction to sort in.
+     */
+    public func sorted(byKeyPath keyPath: String, ascending: Bool) -> Results<Element> {
+        return base.sorted(byKeyPath: keyPath, ascending: ascending)
+    }
+
+    /**
+     Returns a `Results` containing the objects in the collection, but sorted.
+
+     - warning:  Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
+                 floating point, integer, and string types.
+
+     - see: `sorted(byKeyPath:ascending:)`
+
+     - parameter sortDescriptors: A sequence of `SortDescriptor`s to sort by.
+     */
+    public func sorted<S: Sequence>(by sortDescriptors: S) -> Results<Element>
+        where S.Iterator.Element == SortDescriptor {
+        return base.sorted(by: sortDescriptors)
+    }
+
+
+    // MARK: Aggregate Operations
+
+    /**
+     Returns the minimum (lowest) value of the given property among all the objects in the collection, or `nil` if the
+     collection is empty.
+
+     - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
+
+     - parameter property: The name of a property whose minimum value is desired.
+     */
+    public func min<T: MinMaxType>(ofProperty property: String) -> T? {
+        return base.min(ofProperty: property)
+    }
+
+    /**
+     Returns the maximum (highest) value of the given property among all the objects in the collection, or `nil` if the
+     collection is empty.
+
+     - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
+
+     - parameter property: The name of a property whose minimum value is desired.
+     */
+    public func max<T: MinMaxType>(ofProperty property: String) -> T? {
+        return base.max(ofProperty: property)
+    }
+
+    /**
+     Returns the sum of the values of a given property over all the objects in the collection.
+
+     - warning: Only a property whose type conforms to the `AddableType` protocol can be specified.
+
+     - parameter property: The name of a property whose values should be summed.
+     */
+    public func sum<T: AddableType>(ofProperty property: String) -> T { return base.sum(ofProperty: property) }
+
+    /**
+     Returns the average value of a given property over all the objects in the collection, or `nil` if the collection is
+     empty.
+
+     - warning: Only the name of a property whose type conforms to the `AddableType` protocol can be specified.
+
+     - parameter property: The name of a property whose average value should be calculated.
+     */
+    public func average(ofProperty property: String) -> Double? { return base.average(ofProperty: property) }
+
+
+    // MARK: Sequence Support
+
+    /**
+     Returns the object at the given `index`.
+
+     - parameter index: The index.
+     */
+    public subscript(position: Int) -> Element { return base[position] }
+
+    /// Returns a `RLMIterator` that yields successive elements in the collection.
+    public func makeIterator() -> RLMIterator<Element> { return base.makeIterator() }
+
+
+    // MARK: Collection Support
+
+    /// The position of the first element in a non-empty collection.
+    /// Identical to endIndex in an empty collection.
+    public var startIndex: Int { return base.startIndex }
+
+    /// The collection's "past the end" position.
+    /// endIndex is not a valid argument to subscript, and is always reachable from startIndex by
+    /// zero or more applications of successor().
+    public var endIndex: Int { return base.endIndex }
+
+
+    // MARK: Key-Value Coding
+
+    /**
+     Returns an `Array` containing the results of invoking `valueForKey(_:)` with `key` on each of the collection's
+     objects.
+
+     - parameter key: The name of the property whose values are desired.
+     */
+    public func value(forKey key: String) -> Any? { return base.value(forKey: key) }
+
+    /**
+     Returns an `Array` containing the results of invoking `valueForKeyPath(_:)` with `keyPath` on each of the
+     collection's objects.
+
+     - parameter keyPath: The key path to the property whose values are desired.
+     */
+    public func value(forKeyPath keyPath: String) -> Any? { return base.value(forKeyPath: keyPath) }
+
+    /**
+     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.
+
+     - parameter value: The value to set the property to.
+     - parameter key:   The name of the property whose value should be set on each object.
+     */
+    public func setValue(_ value: Any?, forKey key: String) { base.setValue(value, forKey: key) }
+
+    // MARK: Notifications
+
+    /**
+     Registers a block to be called each time the collection changes.
+
+     The block will be asynchronously called with the initial results, 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 that is passed to the block reports, in the form of indices within the collection, which of
+     the objects were added, removed, or modified during each write transaction. See the `RealmCollectionChange`
+     documentation for more information on the change information supplied and an example of how to use it to update a
+     `UITableView`.
+
+     At the time when the block is called, the collection 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 `realm.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.
+
+     ```swift
+     let results = realm.objects(Dog.self)
+     print("dogs.count: \(dogs?.count)") // => 0
+     let token = dogs.observe { changes in
+         switch changes {
+         case .initial(let dogs):
+             // Will print "dogs.count: 1"
+             print("dogs.count: \(dogs.count)")
+             break
+         case .update:
+             // Will not be hit in this example
+             break
+         case .error:
+             break
+         }
+     }
+     try! realm.write {
+         let dog = Dog()
+         dog.name = "Rex"
+         person.dogs.append(dog)
+     }
+     // end of run loop execution context
+     ```
+
+     You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving
+     updates, call `invalidate()` on the token.
+
+     - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only.
+
+     - parameter block: The block to be called whenever a change occurs.
+     - returns: A token which must be held for as long as you want updates to be delivered.
+     */
+    public func observe(_ block: @escaping (RealmCollectionChange<AnyRealmCollection>) -> Void)
+        -> NotificationToken { return base._observe(block) }
+
+    /// :nodoc:
+    public func _observe(_ block: @escaping (RealmCollectionChange<AnyRealmCollection>) -> Void)
+        -> NotificationToken { return base._observe(block) }
+}
+
+// MARK: AssistedObjectiveCBridgeable
+
+private struct AnyRealmCollectionBridgingMetadata<T: RealmCollectionValue> {
+    var baseMetadata: Any?
+    var baseType: _AnyRealmCollectionBase<T>.Type
+}
+
+extension AnyRealmCollection: AssistedObjectiveCBridgeable {
+    static func bridging(from objectiveCValue: Any, with metadata: Any?) -> AnyRealmCollection {
+        guard let metadata = metadata as? AnyRealmCollectionBridgingMetadata<Element> else { preconditionFailure() }
+        return AnyRealmCollection(base: metadata.baseType.bridging(from: objectiveCValue, with: metadata.baseMetadata))
+    }
+
+    var bridged: (objectiveCValue: Any, metadata: Any?) {
+        return (
+            objectiveCValue: base.bridged.objectiveCValue,
+            metadata: AnyRealmCollectionBridgingMetadata(baseMetadata: base.bridged.metadata, baseType: type(of: base))
+        )
+    }
+}
+
+// MARK: Unavailable
+
+extension RealmCollection {
+    @available(*, unavailable, renamed: "sorted(byKeyPath:ascending:)")
+    func sorted(byProperty property: String, ascending: Bool) -> Results<ElementType> { fatalError() }
+
+    @available(*, unavailable, renamed: "observe(_:)")
+    public func addNotificationBlock(_ block: @escaping (RealmCollectionChange<Self>) -> Void) -> NotificationToken {
+        fatalError()
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/RealmConfiguration.swift b/iOS/Pods/RealmSwift/RealmSwift/RealmConfiguration.swift
new file mode 100644 (file)
index 0000000..aae1201
--- /dev/null
@@ -0,0 +1,271 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+import Realm.Private
+
+extension Realm {
+    /**
+     A `Configuration` instance describes the different options used to create an instance of a Realm.
+
+     `Configuration` instances are just plain Swift structs. Unlike `Realm`s and `Object`s, they can be freely shared
+     between threads as long as you do not mutate them.
+
+     Creating configuration values 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 value for each distinct configuration
+     rather than creating a new value each time you open a Realm.
+     */
+    public struct Configuration {
+
+        // MARK: Default Configuration
+
+        /**
+         The default `Configuration` used to create Realms when no configuration is explicitly specified (i.e.
+         `Realm()`)
+         */
+        public static var defaultConfiguration: Configuration {
+            get {
+                return fromRLMRealmConfiguration(RLMRealmConfiguration.default())
+            }
+            set {
+                RLMRealmConfiguration.setDefault(newValue.rlmConfiguration)
+            }
+        }
+
+        // MARK: Initialization
+
+        /**
+         Creates a `Configuration` which can be used to create new `Realm` instances.
+
+         - note: The `fileURL`, `inMemoryIdentifier`, and `syncConfiguration` parameters are mutually exclusive. Only
+                 set one of them, or none if you wish to use the default file URL.
+
+         - parameter fileURL:            The local URL to the Realm file.
+         - parameter inMemoryIdentifier: A string used to identify a particular in-memory Realm.
+         - parameter syncConfiguration:  For Realms intended to sync with the Realm Object Server, a sync configuration.
+         - parameter encryptionKey:      An optional 64-byte key to use to encrypt the data.
+         - parameter readOnly:           Whether the Realm is read-only (must be true for read-only files).
+         - parameter schemaVersion:      The current schema version.
+         - parameter migrationBlock:     The block which migrates the Realm to the current version.
+         - parameter deleteRealmIfMigrationNeeded: If `true`, recreate the Realm file with the provided
+                                                   schema if a migration is required.
+         - parameter shouldCompactOnLaunch: A block called when opening a Realm for the first time during the
+                                            life of a process to determine if it should be compacted before being
+                                            returned to the user. It is passed the total file size (data + free space)
+                                            and the total bytes used by data in the file.
+
+                                            Return `true ` to indicate that an attempt to compact the file should be made.
+                                            The compaction will be skipped if another process is accessing it.
+         - parameter objectTypes:        The subset of `Object` subclasses persisted in the Realm.
+        */
+        public init(fileURL: URL? = URL(fileURLWithPath: RLMRealmPathForFile("default.realm"), isDirectory: false),
+                    inMemoryIdentifier: String? = nil,
+                    syncConfiguration: SyncConfiguration? = nil,
+                    encryptionKey: Data? = nil,
+                    readOnly: Bool = false,
+                    schemaVersion: UInt64 = 0,
+                    migrationBlock: MigrationBlock? = nil,
+                    deleteRealmIfMigrationNeeded: Bool = false,
+                    shouldCompactOnLaunch: ((Int, Int) -> Bool)? = nil,
+                    objectTypes: [Object.Type]? = nil) {
+                self.fileURL = fileURL
+                if let inMemoryIdentifier = inMemoryIdentifier {
+                    self.inMemoryIdentifier = inMemoryIdentifier
+                }
+                if let syncConfiguration = syncConfiguration {
+                    self.syncConfiguration = syncConfiguration
+                }
+                self.encryptionKey = encryptionKey
+                self.readOnly = readOnly
+                self.schemaVersion = schemaVersion
+                self.migrationBlock = migrationBlock
+                self.deleteRealmIfMigrationNeeded = deleteRealmIfMigrationNeeded
+                self.shouldCompactOnLaunch = shouldCompactOnLaunch
+                self.objectTypes = objectTypes
+        }
+
+        // MARK: Configuration Properties
+
+        /**
+         A configuration value used to configure a Realm for synchronization with the Realm Object Server. Mutually
+         exclusive with `inMemoryIdentifier` and `fileURL`.
+         */
+        public var syncConfiguration: SyncConfiguration? {
+            set {
+                _path = nil
+                _inMemoryIdentifier = nil
+                _syncConfiguration = newValue
+            }
+            get {
+                return _syncConfiguration
+            }
+        }
+
+        private var _syncConfiguration: SyncConfiguration?
+
+        /// The local URL of the Realm file. Mutually exclusive with `inMemoryIdentifier` and `syncConfiguration`.
+        public var fileURL: URL? {
+            set {
+                _inMemoryIdentifier = nil
+                _syncConfiguration = nil
+                _path = newValue?.path
+            }
+            get {
+                return _path.map { URL(fileURLWithPath: $0) }
+            }
+        }
+
+        private var _path: String?
+
+        /// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL` and
+        /// `syncConfiguration`.
+        public var inMemoryIdentifier: String? {
+            set {
+                _path = nil
+                _syncConfiguration = nil
+                _inMemoryIdentifier = newValue
+            }
+            get {
+                return _inMemoryIdentifier
+            }
+        }
+
+        private var _inMemoryIdentifier: String?
+
+        /// A 64-byte key to use to encrypt the data, or `nil` if encryption is not enabled.
+        public var encryptionKey: Data?
+
+        /**
+         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.
+         */
+        public var readOnly: Bool = false
+
+        /// The current schema version.
+        public var schemaVersion: UInt64 = 0
+
+        /// The block which migrates the Realm to the current version.
+        public var migrationBlock: 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 `true` deletes the file if a migration would otherwise be required
+         or executed.
+
+         - note: Setting this property to `true` doesn't disable file format migrations.
+         */
+        public var deleteRealmIfMigrationNeeded: Bool = false
+
+        /**
+         A block called when opening a Realm for the first time during the
+         life of a process to determine if it should be compacted before being
+         returned to the user. It is passed the total file size (data + free space)
+         and the total bytes used by data in the file.
+
+         Return `true ` to indicate that an attempt to compact the file should be made.
+         The compaction will be skipped if another process is accessing it.
+         */
+        public var shouldCompactOnLaunch: ((Int, Int) -> Bool)?
+
+        /// The classes managed by the Realm.
+        public var objectTypes: [Object.Type]? {
+            set {
+                self.customSchema = newValue.map { RLMSchema(objectClasses: $0) }
+            }
+            get {
+                return self.customSchema.map { $0.objectSchema.map { $0.objectClass as! Object.Type } }
+            }
+        }
+
+        /// A custom schema to use for the Realm.
+        private var customSchema: RLMSchema?
+
+        /// If `true`, disables automatic format upgrades when accessing the Realm.
+        internal var disableFormatUpgrade: Bool = false
+
+        // MARK: Private Methods
+
+        internal var rlmConfiguration: RLMRealmConfiguration {
+            let configuration = RLMRealmConfiguration()
+            if let fileURL = fileURL {
+                configuration.fileURL = fileURL
+            } else if let inMemoryIdentifier = inMemoryIdentifier {
+                configuration.inMemoryIdentifier = inMemoryIdentifier
+            } else if let syncConfiguration = syncConfiguration {
+                configuration.syncConfiguration = syncConfiguration.asConfig()
+            } else {
+                fatalError("A Realm Configuration must specify a path or an in-memory identifier.")
+            }
+            configuration.encryptionKey = self.encryptionKey
+            configuration.readOnly = self.readOnly
+            configuration.schemaVersion = self.schemaVersion
+            configuration.migrationBlock = self.migrationBlock.map { accessorMigrationBlock($0) }
+            configuration.deleteRealmIfMigrationNeeded = self.deleteRealmIfMigrationNeeded
+            if let shouldCompactOnLaunch = self.shouldCompactOnLaunch {
+                configuration.shouldCompactOnLaunch = ObjectiveCSupport.convert(object: shouldCompactOnLaunch)
+            } else {
+                configuration.shouldCompactOnLaunch = nil
+            }
+            configuration.setCustomSchemaWithoutCopying(self.customSchema)
+            configuration.disableFormatUpgrade = self.disableFormatUpgrade
+            return configuration
+        }
+
+        internal static func fromRLMRealmConfiguration(_ rlmConfiguration: RLMRealmConfiguration) -> Configuration {
+            var configuration = Configuration()
+            configuration._path = rlmConfiguration.fileURL?.path
+            configuration._inMemoryIdentifier = rlmConfiguration.inMemoryIdentifier
+            if let objcSyncConfig = rlmConfiguration.syncConfiguration {
+                configuration._syncConfiguration = SyncConfiguration(config: objcSyncConfig)
+            } else {
+                configuration._syncConfiguration = nil
+            }
+            configuration.encryptionKey = rlmConfiguration.encryptionKey
+            configuration.readOnly = rlmConfiguration.readOnly
+            configuration.schemaVersion = rlmConfiguration.schemaVersion
+            configuration.migrationBlock = rlmConfiguration.migrationBlock.map { rlmMigration in
+                return { migration, schemaVersion in
+                    rlmMigration(migration.rlmMigration, schemaVersion)
+                }
+            }
+            configuration.deleteRealmIfMigrationNeeded = rlmConfiguration.deleteRealmIfMigrationNeeded
+            configuration.shouldCompactOnLaunch = rlmConfiguration.shouldCompactOnLaunch.map(ObjectiveCSupport.convert)
+            configuration.customSchema = rlmConfiguration.customSchema
+            configuration.disableFormatUpgrade = rlmConfiguration.disableFormatUpgrade
+            return configuration
+        }
+    }
+}
+
+// MARK: CustomStringConvertible
+
+extension Realm.Configuration: CustomStringConvertible {
+    /// A human-readable description of the configuration value.
+    public var description: String {
+        return gsub(pattern: "\\ARLMRealmConfiguration",
+                    template: "Realm.Configuration",
+                    string: rlmConfiguration.description) ?? ""
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/Results.swift b/iOS/Pods/RealmSwift/RealmSwift/Results.swift
new file mode 100644 (file)
index 0000000..068c779
--- /dev/null
@@ -0,0 +1,420 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+
+// MARK: MinMaxType
+
+/**
+ Types of properties which can be used with the minimum and maximum value APIs.
+
+ - see: `min(ofProperty:)`, `max(ofProperty:)`
+ */
+public protocol MinMaxType {}
+extension NSNumber: MinMaxType {}
+extension Double: MinMaxType {}
+extension Float: MinMaxType {}
+extension Int: MinMaxType {}
+extension Int8: MinMaxType {}
+extension Int16: MinMaxType {}
+extension Int32: MinMaxType {}
+extension Int64: MinMaxType {}
+extension Date: MinMaxType {}
+extension NSDate: MinMaxType {}
+
+// MARK: AddableType
+
+/**
+ Types of properties which can be used with the sum and average value APIs.
+
+ - see: `sum(ofProperty:)`, `average(ofProperty:)`
+ */
+public protocol AddableType {
+    /// :nodoc:
+    init()
+}
+extension NSNumber: AddableType {}
+extension Double: AddableType {}
+extension Float: AddableType {}
+extension Int: AddableType {}
+extension Int8: AddableType {}
+extension Int16: AddableType {}
+extension Int32: AddableType {}
+extension Int64: AddableType {}
+
+/**
+ `Results` is an auto-updating container type in Realm returned from object queries.
+
+ `Results` can be queried with the same predicates as `List<Element>`, and you can
+ chain queries to further filter query results.
+
+ `Results` 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` 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.
+
+ `Results` 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 `Results` to sort and filter your data does not perform any
+ unnecessary 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.
+
+ Results instances cannot be directly instantiated.
+ */
+public final class Results<Element: RealmCollectionValue>: NSObject, NSFastEnumeration {
+
+    internal let rlmResults: RLMResults<AnyObject>
+
+    /// A human-readable description of the objects represented by the results.
+    public override var description: String {
+        return RLMDescriptionWithMaxDepth("Results", rlmResults, RLMDescriptionMaxDepth)
+    }
+
+    // MARK: Fast Enumeration
+
+    /// :nodoc:
+    public func countByEnumerating(with state: UnsafeMutablePointer<NSFastEnumerationState>,
+                                   objects buffer: AutoreleasingUnsafeMutablePointer<AnyObject?>,
+                                   count len: Int) -> Int {
+        return Int(rlmResults.countByEnumerating(with: state, objects: buffer, count: UInt(len)))
+    }
+
+    /// The type of the objects described by the results.
+    public typealias ElementType = Element
+
+    // MARK: Properties
+
+    /// The Realm which manages this results. Note that this property will never return `nil`.
+    public var realm: Realm? { return Realm(rlmResults.realm) }
+
+    /**
+     Indicates if the results are no longer valid.
+
+     The results becomes invalid if `invalidate()` is called on the containing `realm`. An invalidated results can be
+     accessed, but will always be empty.
+     */
+    public var isInvalidated: Bool { return rlmResults.isInvalidated }
+
+    /// The number of objects in the results.
+    public var count: Int { return Int(rlmResults.count) }
+
+    // MARK: Initializers
+
+    internal init(_ rlmResults: RLMResults<AnyObject>) {
+        self.rlmResults = rlmResults
+    }
+
+    // MARK: Index Retrieval
+
+    /**
+     Returns the index of the given object in the results, or `nil` if the object is not present.
+     */
+    public func index(of object: Element) -> Int? {
+        return notFoundToNil(index: rlmResults.index(of: object as AnyObject))
+    }
+
+    /**
+     Returns the index of the first object matching the predicate, or `nil` if no objects match.
+
+     - parameter predicate: The predicate with which to filter the objects.
+     */
+    public func index(matching predicate: NSPredicate) -> Int? {
+        return notFoundToNil(index: rlmResults.indexOfObject(with: predicate))
+    }
+
+    /**
+     Returns the index of the first object matching the predicate, or `nil` if no objects match.
+
+     - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
+     */
+    public func index(matching predicateFormat: String, _ args: Any...) -> Int? {
+        return notFoundToNil(index: rlmResults.indexOfObject(with: NSPredicate(format: predicateFormat,
+                                                                               argumentArray: unwrapOptionals(in: args))))
+    }
+
+    // MARK: Object Retrieval
+
+    /**
+     Returns the object at the given `index`.
+
+     - parameter index: The index.
+     */
+    public subscript(position: Int) -> Element {
+        throwForNegativeIndex(position)
+        return dynamicBridgeCast(fromObjectiveC: rlmResults.object(at: UInt(position)))
+    }
+
+    /// Returns the first object in the results, or `nil` if the results are empty.
+    public var first: Element? { return rlmResults.firstObject().map(dynamicBridgeCast) }
+
+    /// Returns the last object in the results, or `nil` if the results are empty.
+    public var last: Element? { return rlmResults.lastObject().map(dynamicBridgeCast) }
+
+    // MARK: KVC
+
+    /**
+     Returns an `Array` containing the results of invoking `valueForKey(_:)` with `key` on each of the results.
+
+     - parameter key: The name of the property whose values are desired.
+     */
+    public override func value(forKey key: String) -> Any? {
+        return value(forKeyPath: key)
+    }
+
+    /**
+     Returns an `Array` containing the results of invoking `valueForKeyPath(_:)` with `keyPath` on each of the results.
+
+     - parameter keyPath: The key path to the property whose values are desired.
+     */
+    public override func value(forKeyPath keyPath: String) -> Any? {
+        return rlmResults.value(forKeyPath: keyPath)
+    }
+
+    /**
+     Invokes `setValue(_:forKey:)` on each of the objects represented by the results using the specified `value` and
+     `key`.
+
+     - warning: This method may only be called during a write transaction.
+
+     - parameter value: The object value.
+     - parameter key:   The name of the property whose value should be set on each object.
+     */
+    public override func setValue(_ value: Any?, forKey key: String) {
+        return rlmResults.setValue(value, forKeyPath: key)
+    }
+
+    // MARK: Filtering
+
+    /**
+     Returns a `Results` containing all objects matching the given predicate in the collection.
+
+     - parameter predicateFormat: A predicate format string, optionally followed by a variable number of arguments.
+     */
+    public func filter(_ predicateFormat: String, _ args: Any...) -> Results<Element> {
+        return Results<Element>(rlmResults.objects(with: NSPredicate(format: predicateFormat,
+                                                                     argumentArray: unwrapOptionals(in: args))))
+    }
+
+    /**
+     Returns a `Results` containing all objects matching the given predicate in the collection.
+
+     - parameter predicate: The predicate with which to filter the objects.
+     */
+    public func filter(_ predicate: NSPredicate) -> Results<Element> {
+        return Results<Element>(rlmResults.objects(with: predicate))
+    }
+
+    // MARK: Sorting
+
+    /**
+     Returns a `Results` containing the objects represented by the results, but sorted.
+
+     Objects are sorted based on the values of the given key path. For example, to sort a collection of `Student`s from
+     youngest to oldest based on their `age` property, you might call
+     `students.sorted(byKeyPath: "age", ascending: true)`.
+
+     - warning: Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
+                floating point, integer, and string types.
+
+     - parameter keyPath:   The key path to sort by.
+     - parameter ascending: The direction to sort in.
+     */
+    public func sorted(byKeyPath keyPath: String, ascending: Bool = true) -> Results<Element> {
+        return sorted(by: [SortDescriptor(keyPath: keyPath, ascending: ascending)])
+    }
+
+    /**
+     Returns a `Results` containing the objects represented by the results, but sorted.
+
+     - warning: Collections may only be sorted by properties of boolean, `Date`, `NSDate`, single and double-precision
+                floating point, integer, and string types.
+
+     - see: `sorted(byKeyPath:ascending:)`
+
+     - parameter sortDescriptors: A sequence of `SortDescriptor`s to sort by.
+     */
+    public func sorted<S: Sequence>(by sortDescriptors: S) -> Results<Element>
+        where S.Iterator.Element == SortDescriptor {
+            return Results<Element>(rlmResults.sortedResults(using: sortDescriptors.map { $0.rlmSortDescriptorValue }))
+    }
+
+    /**
+     Returns a `Results` containing distinct objects based on the specified key paths
+     
+     - parameter keyPaths:  The key paths used produce distinct results
+     */
+    public func distinct<S: Sequence>(by keyPaths: S) -> Results<Element>
+        where S.Iterator.Element == String {
+            return Results<Element>(rlmResults.distinctResults(usingKeyPaths: Array(keyPaths)))
+    }
+
+    // MARK: Aggregate Operations
+
+    /**
+     Returns the minimum (lowest) value of the given property among all the results, or `nil` if the results are empty.
+
+     - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
+
+     - parameter property: The name of a property whose minimum value is desired.
+     */
+    public func min<T: MinMaxType>(ofProperty property: String) -> T? {
+        return rlmResults.min(ofProperty: property).map(dynamicBridgeCast)
+    }
+
+    /**
+     Returns the maximum (highest) value of the given property among all the results, or `nil` if the results are empty.
+
+     - warning: Only a property whose type conforms to the `MinMaxType` protocol can be specified.
+
+     - parameter property: The name of a property whose minimum value is desired.
+     */
+    public func max<T: MinMaxType>(ofProperty property: String) -> T? {
+        return rlmResults.max(ofProperty: property).map(dynamicBridgeCast)
+    }
+
+    /**
+     Returns the sum of the values of a given property over all the results.
+
+     - warning: Only a property whose type conforms to the `AddableType` protocol can be specified.
+
+     - parameter property: The name of a property whose values should be summed.
+     */
+    public func sum<T: AddableType>(ofProperty property: String) -> T {
+        return dynamicBridgeCast(fromObjectiveC: rlmResults.sum(ofProperty: property))
+    }
+
+    /**
+     Returns the average value of a given property over all the results, or `nil` if the results are empty.
+
+     - warning: Only the name of a property whose type conforms to the `AddableType` protocol can be specified.
+
+     - parameter property: The name of a property whose average value should be calculated.
+     */
+    public func average<T: AddableType>(ofProperty property: String) -> T? {
+        return rlmResults.average(ofProperty: property).map(dynamicBridgeCast)
+    }
+
+    // MARK: Notifications
+
+    /**
+     Registers a block to be called each time the collection changes.
+
+     The block will be asynchronously called with the initial results, 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 that is passed to the block reports, in the form of indices within the collection, which of
+     the objects were added, removed, or modified during each write transaction. See the `RealmCollectionChange`
+     documentation for more information on the change information supplied and an example of how to use it to update a
+     `UITableView`.
+
+     At the time when the block is called, the collection 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 `realm.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.
+
+     ```swift
+     let results = realm.objects(Dog.self)
+     print("dogs.count: \(dogs?.count)") // => 0
+     let token = dogs.observe { changes in
+         switch changes {
+         case .initial(let dogs):
+             // Will print "dogs.count: 1"
+             print("dogs.count: \(dogs.count)")
+             break
+         case .update:
+             // Will not be hit in this example
+             break
+         case .error:
+             break
+         }
+     }
+     try! realm.write {
+         let dog = Dog()
+         dog.name = "Rex"
+         person.dogs.append(dog)
+     }
+     // end of run loop execution context
+     ```
+
+     You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving
+     updates, call `invalidate()` on the token.
+
+     - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only.
+
+     - parameter block: The block to be called whenever a change occurs.
+     - returns: A token which must be held for as long as you want updates to be delivered.
+     */
+    public func observe(_ block: @escaping (RealmCollectionChange<Results>) -> Void) -> NotificationToken {
+        return rlmResults.addNotificationBlock { _, change, error in
+            block(RealmCollectionChange.fromObjc(value: self, change: change, error: error))
+        }
+    }
+}
+
+extension Results: RealmCollection {
+    // MARK: Sequence Support
+
+    /// Returns a `RLMIterator` that yields successive elements in the results.
+    public func makeIterator() -> RLMIterator<Element> {
+        return RLMIterator(collection: rlmResults)
+    }
+
+    // MARK: Collection Support
+
+    /// The position of the first element in a non-empty collection.
+    /// Identical to endIndex in an empty collection.
+    public var startIndex: Int { return 0 }
+
+    /// The collection's "past the end" position.
+    /// endIndex is not a valid argument to subscript, and is always reachable from startIndex by
+    /// zero or more applications of successor().
+    public var endIndex: Int { return count }
+
+    public func index(after i: Int) -> Int { return i + 1 }
+    public func index(before i: Int) -> Int { return i - 1 }
+
+    /// :nodoc:
+    public func _observe(_ block: @escaping (RealmCollectionChange<AnyRealmCollection<Element>>) -> Void) ->
+        NotificationToken {
+        let anyCollection = AnyRealmCollection(self)
+        return rlmResults.addNotificationBlock { _, change, error in
+            block(RealmCollectionChange.fromObjc(value: anyCollection, change: change, error: error))
+        }
+    }
+}
+
+// MARK: AssistedObjectiveCBridgeable
+
+extension Results: AssistedObjectiveCBridgeable {
+    static func bridging(from objectiveCValue: Any, with metadata: Any?) -> Results {
+        return Results(objectiveCValue as! RLMResults)
+    }
+
+    var bridged: (objectiveCValue: Any, metadata: Any?) {
+        return (objectiveCValue: rlmResults, metadata: nil)
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/Schema.swift b/iOS/Pods/RealmSwift/RealmSwift/Schema.swift
new file mode 100644 (file)
index 0000000..81180a0
--- /dev/null
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+
+/**
+ `Schema` instances represent collections of model object schemas managed by a Realm.
+
+ When using Realm, `Schema` instances allow performing migrations and introspecting the database's schema.
+
+ Schemas map to collections of tables in the core database.
+ */
+public struct Schema: CustomStringConvertible {
+
+    // MARK: Properties
+
+    internal let rlmSchema: RLMSchema
+
+    /**
+     An array of `ObjectSchema`s for all object types in the Realm.
+
+     This property is intended to be used during migrations for dynamic introspection.
+     */
+    public var objectSchema: [ObjectSchema] {
+        return rlmSchema.objectSchema.map(ObjectSchema.init)
+    }
+
+    /// A human-readable description of the object schemas contained within.
+    public var description: String { return rlmSchema.description }
+
+    // MARK: Initializers
+
+    internal init(_ rlmSchema: RLMSchema) {
+        self.rlmSchema = rlmSchema
+    }
+
+    // MARK: ObjectSchema Retrieval
+
+    /// Looks up and returns an `ObjectSchema` for the given class name in the Realm, if it exists.
+    public subscript(className: String) -> ObjectSchema? {
+        if let rlmObjectSchema = rlmSchema.schema(forClassName: className) {
+            return ObjectSchema(rlmObjectSchema)
+        }
+        return nil
+    }
+}
+
+// MARK: Equatable
+
+extension Schema: Equatable {
+    /// Returns whether the two schemas are equal.
+    public static func == (lhs: Schema, rhs: Schema) -> Bool {
+        return lhs.rlmSchema.isEqual(to: rhs.rlmSchema)
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/SortDescriptor.swift b/iOS/Pods/RealmSwift/RealmSwift/SortDescriptor.swift
new file mode 100644 (file)
index 0000000..562044d
--- /dev/null
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+
+/**
+ A `SortDescriptor` stores a key path and a sort order for use with `sorted(sortDescriptors:)`. It is similar to
+ `NSSortDescriptor`, but supports only the subset of functionality which can be efficiently run by Realm's query engine.
+ */
+public struct SortDescriptor {
+
+    // MARK: Properties
+
+    /// The key path which the sort descriptor orders results by.
+    public let keyPath: String
+
+    /// Whether this descriptor sorts in ascending or descending order.
+    public let ascending: Bool
+
+    /// Converts the receiver to an `RLMSortDescriptor`.
+    internal var rlmSortDescriptorValue: RLMSortDescriptor {
+        return RLMSortDescriptor(keyPath: keyPath, ascending: ascending)
+    }
+
+    // MARK: Initializers
+
+    /**
+     Creates a sort descriptor with the given key path and sort order values.
+
+     - parameter keyPath:   The key path which the sort descriptor orders results by.
+     - parameter ascending: Whether the descriptor sorts in ascending or descending order.
+     */
+    public init(keyPath: String, ascending: Bool = true) {
+        self.keyPath = keyPath
+        self.ascending = ascending
+    }
+
+    // MARK: Functions
+
+    /// Returns a copy of the sort descriptor with the sort order reversed.
+    public func reversed() -> SortDescriptor {
+        return SortDescriptor(keyPath: keyPath, ascending: !ascending)
+    }
+}
+
+// MARK: CustomStringConvertible
+
+extension SortDescriptor: CustomStringConvertible {
+    /// A human-readable description of the sort descriptor.
+    public var description: String {
+        let direction = ascending ? "ascending" : "descending"
+        return "SortDescriptor(keyPath: \(keyPath), direction: \(direction))"
+    }
+}
+
+// MARK: Equatable
+
+extension SortDescriptor: Equatable {
+    /// Returns whether the two sort descriptors are equal.
+    public static func == (lhs: SortDescriptor, rhs: SortDescriptor) -> Bool {
+        return lhs.keyPath == rhs.keyPath &&
+            lhs.ascending == lhs.ascending
+    }
+}
+
+// MARK: StringLiteralConvertible
+
+extension SortDescriptor: ExpressibleByStringLiteral {
+
+    public typealias UnicodeScalarLiteralType = StringLiteralType
+    public typealias ExtendedGraphemeClusterLiteralType = StringLiteralType
+
+    /**
+     Creates a `SortDescriptor` out of a Unicode scalar literal.
+
+     - parameter unicodeScalarLiteral: Property name literal.
+    */
+    public init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
+        self.init(keyPath: value)
+    }
+
+    /**
+     Creates a `SortDescriptor` out of a character literal.
+
+     - parameter extendedGraphemeClusterLiteral: Property name literal.
+     */
+    public init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
+        self.init(keyPath: value)
+    }
+
+    /**
+     Creates a `SortDescriptor` out of a string literal.
+
+     - parameter stringLiteral: Property name literal.
+     */
+    public init(stringLiteral value: StringLiteralType) {
+        self.init(keyPath: value)
+    }
+}
+
+// MARK: Unavailable
+
+extension SortDescriptor {
+    @available(*, unavailable, renamed: "init(keyPath:ascending:)")
+    public init(property: String, ascending: Bool = true) { fatalError() }
+
+    @available(*, unavailable, renamed: "keyPath")
+    public var property: String { fatalError() }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/SwiftVersion.swift b/iOS/Pods/RealmSwift/RealmSwift/SwiftVersion.swift
new file mode 100644 (file)
index 0000000..3e5c519
--- /dev/null
@@ -0,0 +1 @@
+let swiftLanguageVersion = "4.0.3"
diff --git a/iOS/Pods/RealmSwift/RealmSwift/Sync.swift b/iOS/Pods/RealmSwift/RealmSwift/Sync.swift
new file mode 100644 (file)
index 0000000..22461e7
--- /dev/null
@@ -0,0 +1,668 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Realm
+import Foundation
+
+/**
+ An object representing a Realm Object Server user.
+
+ - see: `RLMSyncUser`
+ */
+public typealias SyncUser = RLMSyncUser
+
+/**
+ An immutable data object representing information retrieved from the Realm Object
+ Server about a particular user.
+
+ - see: `RLMSyncUserInfo`
+ */
+public typealias SyncUserInfo = RLMSyncUserInfo
+
+/**
+ An immutable data object representing an account belonging to a particular user.
+
+ - see: `SyncUserInfo`, `RLMSyncUserAccountInfo`
+ */
+public typealias SyncUserAccountInfo = RLMSyncUserAccountInfo
+
+/**
+ A singleton which configures and manages the Realm Object Server synchronization-related
+ functionality.
+
+ - see: `RLMSyncManager`
+ */
+public typealias SyncManager = RLMSyncManager
+
+extension SyncManager {
+    /// The sole instance of the singleton.
+    public static var shared: SyncManager {
+        return __shared()
+    }
+}
+
+/**
+ A session object which represents communication between the client and server for a specific
+ Realm.
+
+ - see: `RLMSyncSession`
+ */
+public typealias SyncSession = RLMSyncSession
+
+/**
+ A closure type for a closure which can be set on the `SyncManager` to allow errors to be reported
+ to the application.
+
+ - see: `RLMSyncErrorReportingBlock`
+ */
+public typealias ErrorReportingBlock = RLMSyncErrorReportingBlock
+
+/**
+ A closure type for a closure which is used by certain APIs to asynchronously return a `SyncUser`
+ object to the application.
+
+ - see: `RLMUserCompletionBlock`
+ */
+public typealias UserCompletionBlock = RLMUserCompletionBlock
+
+/**
+ An error associated with the SDK's synchronization functionality. All errors reported by
+ an error handler registered on the `SyncManager` are of this type.
+
+ - see: `RLMSyncError`
+ */
+public typealias SyncError = RLMSyncError
+
+extension SyncError {
+    /**
+     An opaque token allowing the user to take action after certain types of
+     errors have been reported.
+
+     - see: `RLMSyncErrorActionToken`
+     */
+    public typealias ActionToken = RLMSyncErrorActionToken
+
+    /**
+     Given a client reset error, extract and return the recovery file path
+     and the action token.
+
+     The action token can be passed into `SyncSession.immediatelyHandleError(_:)`
+     to immediately delete the local copy of the Realm which experienced the
+     client reset error. The local copy of the Realm must be deleted before
+     your application attempts to open the Realm again.
+
+     The recovery file path is the path to which the current copy of the Realm
+     on disk will be saved once the client reset occurs.
+
+     - warning: Do not call `SyncSession.immediatelyHandleError(_:)` until you are
+                sure that all references to the Realm and managed objects belonging
+                to the Realm have been nil'ed out, and that all autorelease pools
+                containing these references have been drained.
+
+     - see: `SyncError.ActionToken`, `SyncSession.immediatelyHandleError(_:)`
+     */
+    public func clientResetInfo() -> (String, SyncError.ActionToken)? {
+        if code == SyncError.clientResetError,
+            let recoveryPath = userInfo[kRLMSyncPathOfRealmBackupCopyKey] as? String,
+            let token = _nsError.__rlmSync_errorActionToken() {
+            return (recoveryPath, token)
+        }
+        return nil
+    }
+
+    /**
+     Given a permission denied error, extract and return the action token.
+
+     This action token can be passed into `SyncSession.immediatelyHandleError(_:)`
+     to immediately delete the local copy of the Realm which experienced the
+     permission denied error. The local copy of the Realm must be deleted before
+     your application attempts to open the Realm again.
+
+     - warning: Do not call `SyncSession.immediatelyHandleError(_:)` until you are
+                sure that all references to the Realm and managed objects belonging
+                to the Realm have been nil'ed out, and that all autorelease pools
+                containing these references have been drained.
+
+     - see: `SyncError.ActionToken`, `SyncSession.immediatelyHandleError(_:)`
+     */
+    public func deleteRealmUserInfo() -> SyncError.ActionToken? {
+        return _nsError.__rlmSync_errorActionToken()
+    }
+}
+
+/**
+ An error associated with network requests made to the authentication server. This type of error
+ may be returned in the callback block to `SyncUser.logIn()` upon certain types of failed login
+ attempts (for example, if the request is malformed or if the server is experiencing an issue).
+
+ - see: `RLMSyncAuthError`
+ */
+public typealias SyncAuthError = RLMSyncAuthError
+
+/**
+ An error associated with retrieving or modifying user permissions to access a synchronized Realm.
+
+ - see: `RLMSyncPermissionError`
+ */
+public typealias SyncPermissionError = RLMSyncPermissionError
+
+/**
+ An enum which can be used to specify the level of logging.
+
+ - see: `RLMSyncLogLevel`
+ */
+public typealias SyncLogLevel = RLMSyncLogLevel
+
+/**
+ A data type whose values represent different authentication providers that can be used with
+ the Realm Object Server.
+
+ - see: `RLMIdentityProvider`
+ */
+public typealias Provider = RLMIdentityProvider
+
+/**
+ A `SyncConfiguration` represents configuration parameters for Realms intended to sync with
+ a Realm Object Server.
+ */
+public struct SyncConfiguration {
+    /// The `SyncUser` who owns the Realm that this configuration should open.
+    public let user: SyncUser
+
+    /**
+     The URL of the Realm on the Realm Object Server that this configuration should open.
+
+     - warning: The URL must be absolute (e.g. `realms://example.com/~/foo`), and cannot end with
+                `.realm`, `.realm.lock` or `.realm.management`.
+     */
+    public let realmURL: URL
+
+    /**
+     A policy that determines what should happen when all references to Realms opened by this
+     configuration go out of scope.
+     */
+    internal let stopPolicy: RLMSyncStopPolicy
+
+    /**
+     Whether the SSL certificate of the Realm Object Server should be validated.
+     */
+    public let enableSSLValidation: Bool
+
+    /**
+     Whether this Realm should be opened in 'partial synchronization' mode.
+     Partial synchronization mode means that no objects are synchronized from the remote Realm
+     except those matching queries that the user explicitly specifies.
+
+     -warning: Partial synchronization is a tech preview. Its APIs are subject to change.
+     */
+    public let isPartial: Bool
+
+    internal init(config: RLMSyncConfiguration) {
+        self.user = config.user
+        self.realmURL = config.realmURL
+        self.stopPolicy = config.stopPolicy
+        self.enableSSLValidation = config.enableSSLValidation
+        self.isPartial = config.isPartial
+    }
+
+    func asConfig() -> RLMSyncConfiguration {
+        let config = RLMSyncConfiguration(user: user, realmURL: realmURL)
+        config.stopPolicy = stopPolicy
+        config.enableSSLValidation = enableSSLValidation
+        config.isPartial = isPartial
+        return config
+    }
+
+    /**
+     Initialize a sync configuration with a user and a Realm URL.
+
+     Additional settings can be optionally specified. Descriptions of these
+     settings follow.
+
+     `enableSSLValidation` is true by default. It can be disabled for debugging
+     purposes.
+
+     - warning: The URL must be absolute (e.g. `realms://example.com/~/foo`), and cannot end with
+                `.realm`, `.realm.lock` or `.realm.management`.
+
+     - warning: NEVER disable SSL validation for a system running in production.
+     */
+    public init(user: SyncUser, realmURL: URL, enableSSLValidation: Bool = true, isPartial: Bool = false) {
+        self.user = user
+        self.realmURL = realmURL
+        self.stopPolicy = .afterChangesUploaded
+        self.enableSSLValidation = enableSSLValidation
+        self.isPartial = isPartial
+    }
+}
+
+/// A `SyncCredentials` represents data that uniquely identifies a Realm Object Server user.
+public struct SyncCredentials {
+    public typealias Token = String
+
+    internal var token: Token
+    internal var provider: Provider
+    internal var userInfo: [String: Any]
+
+    /**
+     Initialize new credentials using a custom token, authentication provider, and user information
+     dictionary. In most cases, the convenience initializers should be used instead.
+     */
+    public init(customToken token: Token, provider: Provider, userInfo: [String: Any] = [:]) {
+        self.token = token
+        self.provider = provider
+        self.userInfo = userInfo
+    }
+
+    internal init(_ credentials: RLMSyncCredentials) {
+        self.token = credentials.token
+        self.provider = credentials.provider
+        self.userInfo = credentials.userInfo
+    }
+
+    /// Initialize new credentials using a Facebook account token.
+    public static func facebook(token: Token) -> SyncCredentials {
+        return SyncCredentials(RLMSyncCredentials(facebookToken: token))
+    }
+
+    /// Initialize new credentials using a Google account token.
+    public static func google(token: Token) -> SyncCredentials {
+        return SyncCredentials(RLMSyncCredentials(googleToken: token))
+    }
+
+    /// Initialize new credentials using a CloudKit account token.
+    public static func cloudKit(token: Token) -> SyncCredentials {
+        return SyncCredentials(RLMSyncCredentials(cloudKitToken: token))
+    }
+
+    /// Initialize new credentials using a Realm Object Server username and password.
+    public static func usernamePassword(username: String,
+                                        password: String,
+                                        register: Bool = false) -> SyncCredentials {
+        return SyncCredentials(RLMSyncCredentials(username: username, password: password, register: register))
+    }
+
+    /// Initialize new credentials using a Realm Object Server access token.
+    public static func accessToken(_ accessToken: String, identity: String) -> SyncCredentials {
+        return SyncCredentials(RLMSyncCredentials(accessToken: accessToken, identity: identity))
+    }
+
+    /// Initialize new credentials using a JSON Web Token.
+    public static func jwt(_ token: Token) -> SyncCredentials {
+        return SyncCredentials(RLMSyncCredentials(jwt: token))
+    }
+
+    /// Initialize new credentials using a nickname.
+    public static func nickname(_ nickname: String, isAdmin: Bool = false) -> SyncCredentials {
+        return SyncCredentials(RLMSyncCredentials(nickname: nickname, isAdmin: isAdmin))
+    }
+
+    /// Initialize new credentials anonymously
+    public static func anonymous() -> SyncCredentials {
+        return SyncCredentials(RLMSyncCredentials.anonymous())
+    }
+}
+
+extension RLMSyncCredentials {
+    internal convenience init(_ credentials: SyncCredentials) {
+        self.init(customToken: credentials.token, provider: credentials.provider, userInfo: credentials.userInfo)
+    }
+}
+
+extension SyncUser {
+    /**
+     Log in a user and asynchronously retrieve a user object.
+
+     If the log in completes successfully, the completion block will be called, and a
+     `SyncUser` representing the logged-in user will be passed to it. This user object
+     can be used to open `Realm`s and retrieve `SyncSession`s. Otherwise, the
+     completion block will be called with an error.
+
+     - parameter credentials: A `SyncCredentials` object representing the user to log in.
+     - parameter authServerURL: The URL of the authentication server (e.g. "http://realm.example.org:9080").
+     - parameter timeout: How long the network client should wait, in seconds, before timing out.
+     - parameter callbackQueue: The dispatch queue upon which the callback should run. Defaults to the main queue.
+     - parameter completion: A callback block to be invoked once the log in completes.
+     */
+    public static func logIn(with credentials: SyncCredentials,
+                             server authServerURL: URL,
+                             timeout: TimeInterval = 30,
+                             callbackQueue queue: DispatchQueue = DispatchQueue.main,
+                             onCompletion completion: @escaping UserCompletionBlock) {
+        return SyncUser.__logIn(with: RLMSyncCredentials(credentials),
+                                authServerURL: authServerURL,
+                                timeout: timeout,
+                                callbackQueue: queue,
+                                onCompletion: completion)
+    }
+
+    /// A dictionary of all valid, logged-in user identities corresponding to their `SyncUser` objects.
+    public static var all: [String: SyncUser] {
+        return __allUsers()
+    }
+
+    /**
+     The logged-in user. `nil` if none exists. Only use this property if your application expects
+     no more than one logged-in user at any given time.
+
+     - warning: Throws an Objective-C exception if more than one logged-in user exists.
+     */
+    public static var current: SyncUser? {
+        return __current()
+    }
+
+    /**
+     An optional error handler which can be set to notify the host application when
+     the user encounters an error.
+     
+     - note: Check for `.invalidAccessToken` to see if the user has been remotely logged
+             out because its refresh token expired, or because the third party authentication
+             service providing the user's identity has logged the user out.
+
+     - warning: Regardless of whether an error handler is defined, certain user errors
+                will automatically cause the user to enter the logged out state.
+     */
+    @nonobjc public var errorHandler: ((SyncUser, SyncAuthError) -> Void)? {
+        get {
+            return __errorHandler
+        }
+        set {
+            if let newValue = newValue {
+                __errorHandler = { (user, error) in
+                    newValue(user, error as! SyncAuthError)
+                }
+            } else {
+                __errorHandler = nil
+            }
+        }
+    }
+
+    /**
+     Retrieve permissions for this user. Permissions describe which synchronized
+     Realms this user has access to and what they are allowed to do with them.
+
+     Permissions are retrieved asynchronously and returned via the callback. The
+     callback is run on the same thread that the method is invoked upon.
+
+     - warning: This method must be invoked on a thread with an active run loop.
+
+     - warning: Do not pass the `Results` returned by the callback between threads.
+
+     - parameter callback: A callback providing either a `Results` containing the
+                           permissions, or an error describing what went wrong.
+     */
+    public func retrievePermissions(callback: @escaping (SyncPermissionResults?, SyncPermissionError?) -> Void) {
+        self.__retrievePermissions { (results, error) in
+            guard let results = results else {
+                callback(nil, error as! SyncPermissionError?)
+                return
+            }
+            let upcasted: RLMResults<SyncPermission> = results
+            callback(Results(upcasted as! RLMResults<AnyObject>), nil)
+        }
+    }
+
+    /**
+     Create a permission offer for a Realm.
+
+     A permission offer is used to grant access to a Realm this user manages to another
+     user. Creating a permission offer produces a string token which can be passed to the
+     recepient in any suitable way (for example, via e-mail).
+
+     The operation will take place asynchronously. The token can be accepted by the recepient
+     using the `SyncUser.acceptOffer(forToken:, callback:)` method.
+
+     - parameter url: The URL of the Realm for which the permission offer should pertain. This
+                      may be the URL of any Realm which this user is allowed to manage. If the URL
+                      has a `~` wildcard it will be replaced with this user's user identity.
+     - parameter accessLevel: What access level to grant to whoever accepts the token.
+     - parameter expiration: Optionally, a date which indicates when the offer expires. If the
+                             recepient attempts to accept the offer after the date it will be rejected.
+                             If nil, the offer will never expire.
+     - parameter callback: A callback indicating whether the operation succeeded or failed. If it
+                           succeeded the token will be passed in as a string.
+     */
+    public func createOfferForRealm(at url: URL,
+                                    accessLevel: SyncAccessLevel,
+                                    expiration: Date? = nil,
+                                    callback: @escaping (String?, SyncPermissionError?) -> Void) {
+        self.__createOfferForRealm(at: url, accessLevel: accessLevel, expiration: expiration) { (token, error) in
+            guard let token = token else {
+                callback(nil, error as! SyncPermissionError?)
+                return
+            }
+            callback(token, nil)
+        }
+    }
+}
+
+/**
+ A value which represents a permission granted to a user to interact
+ with a Realm. These values are passed into APIs on `SyncUser`, and
+ returned from `SyncPermissionResults`.
+
+ - see: `RLMSyncPermission`
+ */
+public typealias SyncPermission = RLMSyncPermission
+
+/**
+ An enumeration describing possible access levels.
+
+ - see: `RLMSyncAccessLevel`
+ */
+public typealias SyncAccessLevel = RLMSyncAccessLevel
+
+public extension SyncSession {
+    /**
+     The transfer direction (upload or download) tracked by a given progress notification block.
+
+     Progress notification blocks can be registered on sessions if your app wishes to be informed
+     how many bytes have been uploaded or downloaded, for example to show progress indicator UIs.
+     */
+    public enum ProgressDirection {
+        /// For monitoring upload progress.
+        case upload
+        /// For monitoring download progress.
+        case download
+    }
+
+    /**
+     The desired behavior of a progress notification block.
+
+     Progress notification blocks can be registered on sessions if your app wishes to be informed
+     how many bytes have been uploaded or downloaded, for example to show progress indicator UIs.
+     */
+    public enum ProgressMode {
+        /**
+         The block will be called forever, or until it is unregistered by calling
+         `ProgressNotificationToken.invalidate()`.
+
+         Notifications will always report the latest number of transferred bytes, and the
+         most up-to-date number of total transferrable bytes.
+         */
+        case reportIndefinitely
+        /**
+         The block will, upon registration, store the total number of bytes
+         to be transferred. When invoked, it will always report the most up-to-date number
+         of transferrable bytes out of that original number of transferrable bytes.
+
+         When the number of transferred bytes reaches or exceeds the
+         number of transferrable bytes, the block will be unregistered.
+         */
+        case forCurrentlyOutstandingWork
+    }
+
+    /**
+     A token corresponding to a progress notification block.
+
+     Call `invalidate()` on the token to stop notifications. If the notification block has already
+     been automatically stopped, calling `invalidate()` does nothing. `invalidate()` should be called
+     before the token is destroyed.
+     */
+    public typealias ProgressNotificationToken = RLMProgressNotificationToken
+
+    /**
+     A struct encapsulating progress information, as well as useful helper methods.
+     */
+    public struct Progress {
+        /// The number of bytes that have been transferred.
+        public let transferredBytes: Int
+
+        /**
+         The total number of transferrable bytes (bytes that have been transferred,
+         plus bytes pending transfer).
+
+         If the notification block is tracking downloads, this number represents the size of the
+         changesets generated by all other clients using the Realm.
+         If the notification block is tracking uploads, this number represents the size of the
+         changesets representing the local changes on this client.
+         */
+        public let transferrableBytes: Int
+
+        /// The fraction of bytes transferred out of all transferrable bytes. If this value is 1,
+        /// no bytes are waiting to be transferred (either all bytes have already been transferred,
+        /// or there are no bytes to be transferred in the first place).
+        public var fractionTransferred: Double {
+            if transferrableBytes == 0 {
+                return 1
+            }
+            let percentage = Double(transferredBytes) / Double(transferrableBytes)
+            return percentage > 1 ? 1 : percentage
+        }
+
+        /// Whether all pending bytes have already been transferred.
+        public var isTransferComplete: Bool {
+            return transferredBytes >= transferrableBytes
+        }
+
+        fileprivate init(transferred: UInt, transferrable: UInt) {
+            transferredBytes = Int(transferred)
+            transferrableBytes = Int(transferrable)
+        }
+    }
+
+    /**
+     Register a progress notification block.
+
+     If the session has already received progress information from the
+     synchronization subsystem, the block will be called immediately. Otherwise, it
+     will be called as soon as progress information becomes available.
+
+     Multiple blocks can be registered with the same session at once. Each block
+     will be invoked on a side queue devoted to progress notifications.
+
+     The token returned by this method must be retained as long as progress
+     notifications are desired, and the `invalidate()` method should be called on it
+     when notifications are no longer needed and before the token is destroyed.
+
+     If no token is returned, the notification block will never be called again.
+     There are a number of reasons this might be true. If the session has previously
+     experienced a fatal error it will not accept progress notification blocks. If
+     the block was configured in the `forCurrentlyOutstandingWork` mode but there
+     is no additional progress to report (for example, the number of transferrable bytes
+     and transferred bytes are equal), the block will not be called again.
+
+     - parameter direction: The transfer direction (upload or download) to track in this progress notification block.
+     - parameter mode:      The desired behavior of this progress notification block.
+     - parameter block:     The block to invoke when notifications are available.
+
+     - returns: A token which must be held for as long as you want notifications to be delivered.
+
+     - see: `ProgressDirection`, `Progress`, `ProgressNotificationToken`
+     */
+    public func addProgressNotification(for direction: ProgressDirection,
+                                        mode: ProgressMode,
+                                        block: @escaping (Progress) -> Void) -> ProgressNotificationToken? {
+        return __addProgressNotification(for: (direction == .upload ? .upload : .download),
+                                         mode: (mode == .reportIndefinitely
+                                            ? .reportIndefinitely
+                                            : .forCurrentlyOutstandingWork)) { transferred, transferrable in
+                                                block(Progress(transferred: transferred, transferrable: transferrable))
+        }
+    }
+}
+
+extension Realm {
+    /**
+     If the Realm is a partially synchronized Realm, fetch and synchronize the objects
+     of a given object type that match the given query (in string format).
+
+     The results will be returned asynchronously in the callback.
+     Use `Results.observe(_:)` to be notified to changes to the set of synchronized objects.
+
+     -warning: Partial synchronization is a tech preview. Its APIs are subject to change.
+     */
+    public func subscribe<T: Object>(to objects: T.Type, where: String,
+                                     completion: @escaping (Results<T>?, Swift.Error?) -> Void) {
+        rlmRealm.subscribe(toObjects: objects, where: `where`) { (results, error) in
+            completion(results.map { Results<T>($0) }, error)
+        }
+    }
+}
+
+// MARK: - Permissions and permission results
+
+extension SyncPermission: RealmCollectionValue { }
+
+/**
+ A `Results` collection containing sync permission results.
+ */
+public typealias SyncPermissionResults = Results<SyncPermission>
+
+/**
+ A property upon which a `SyncPermissionResults` can be sorted or queried.
+ The raw value string can be used to construct predicates and queries
+ manually.
+
+ - warning: If building `NSPredicate`s using format strings including these
+            raw values, use `%K` instead of `%@` as the substitution
+            parameter.
+
+ - see: `RLMSyncPermissionSortProperty`
+ */
+public typealias SyncPermissionSortProperty = RLMSyncPermissionSortProperty
+
+extension SortDescriptor {
+    /**
+     Construct a sort descriptor using a `SyncPermissionSortProperty`.
+     */
+    public init(sortProperty: SyncPermissionSortProperty, ascending: Bool = true) {
+        self.init(keyPath: sortProperty.rawValue, ascending: ascending)
+    }
+}
+
+#if swift(>=3.1)
+extension Results where Element == SyncPermission {
+    /**
+     Return a `Results<SyncPermissionValue>` containing the objects represented
+     by the results, but sorted on the specified property.
+
+     - see: `sorted(byKeyPath:, ascending:)`
+     */
+    public func sorted(bySortProperty sortProperty: SyncPermissionSortProperty,
+                       ascending: Bool = true) -> Results<Element> {
+        return sorted(by: [SortDescriptor(sortProperty: sortProperty, ascending: ascending)])
+    }
+}
+#endif
+
+// MARK: - Migration assistance
+
+/// :nodoc:
+@available(*, unavailable, renamed: "SyncPermission")
+public final class SyncPermissionValue { }
diff --git a/iOS/Pods/RealmSwift/RealmSwift/ThreadSafeReference.swift b/iOS/Pods/RealmSwift/RealmSwift/ThreadSafeReference.swift
new file mode 100644 (file)
index 0000000..37379fd
--- /dev/null
@@ -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 Realm
+
+/**
+ Objects of types which conform to `ThreadConfined` can be managed by a Realm, which will make
+ them bound to a thread-specific `Realm` instance. Managed objects must be explicitly exported
+ and imported to be passed between threads.
+
+ Managed instances of objects conforming to this protocol can be converted to a thread-safe
+ reference for transport between threads by passing to the `ThreadSafeReference(to:)` constructor.
+
+ Note that only types defined by Realm can meaningfully conform to this protocol, and defining new
+ classes which attempt to conform to it will not make them work with `ThreadSafeReference`.
+ */
+public protocol ThreadConfined {
+    // Must also conform to `AssistedObjectiveCBridgeable`
+
+    /**
+     The Realm which manages the object, or `nil` if the object is unmanaged.
+
+     Unmanaged objects are not confined to a thread and cannot be passed to methods expecting a
+     `ThreadConfined` object.
+     */
+    var realm: Realm? { get }
+
+    /// Indicates if the object can no longer be accessed because it is now invalid.
+    var isInvalidated: Bool { get }
+}
+
+/**
+ An object intended to be passed between threads containing a thread-safe reference to its
+ thread-confined object.
+
+ To resolve a thread-safe reference on a target Realm on a different thread, pass to
+ `Realm.resolve(_:)`.
+
+ - warning: A `ThreadSafeReference` object must be resolved at most once.
+            Failing to resolve a `ThreadSafeReference` will result in the source version of the
+            Realm being pinned until the reference is deallocated.
+
+ - note: Prefer short-lived `ThreadSafeReference`s as the data for the version of the source Realm
+         will be retained until all references have been resolved or deallocated.
+
+ - see: `ThreadConfined`
+ - see: `Realm.resolve(_:)`
+ */
+public class ThreadSafeReference<Confined: ThreadConfined> {
+    private let swiftMetadata: Any?
+
+    /**
+     Indicates if the reference can no longer be resolved because an attempt to resolve it has
+     already occurred. References can only be resolved once.
+     */
+    public var isInvalidated: Bool { return objectiveCReference.isInvalidated }
+
+    private let objectiveCReference: RLMThreadSafeReference<RLMThreadConfined>
+
+    /**
+     Create a thread-safe reference to the thread-confined object.
+
+     - parameter threadConfined: The thread-confined object to create a thread-safe reference to.
+
+     - note: You may continue to use and access the thread-confined object after passing it to this
+             constructor.
+     */
+    public init(to threadConfined: Confined) {
+        let bridged = (threadConfined as! AssistedObjectiveCBridgeable).bridged
+        swiftMetadata = bridged.metadata
+        objectiveCReference = RLMThreadSafeReference(threadConfined: bridged.objectiveCValue as! RLMThreadConfined)
+    }
+
+    internal func resolve(in realm: Realm) -> Confined? {
+        guard let objectiveCValue = realm.rlmRealm.__resolve(objectiveCReference) else { return nil }
+        return ((Confined.self as! AssistedObjectiveCBridgeable.Type).bridging(from: objectiveCValue, with: swiftMetadata) as! Confined)
+    }
+}
+
+extension Realm {
+    /**
+     Returns the same object as the one referenced when the `ThreadSafeReference` was first
+     created, but resolved for the current Realm for this thread. Returns `nil` if this object was
+     deleted after the reference was created.
+
+     - parameter reference: The thread-safe reference to the thread-confined object to resolve in
+                            this Realm.
+
+     - warning: A `ThreadSafeReference` object must be resolved at most once.
+                Failing to resolve a `ThreadSafeReference` will result in the source version of the
+                Realm being pinned until the reference is deallocated.
+                An exception will be thrown if a reference is resolved more than once.
+
+     - warning: Cannot call within a write transaction.
+
+     - note: Will refresh this Realm if the source Realm was at a later version than this one.
+
+     - see: `ThreadSafeReference(to:)`
+     */
+    public func resolve<Confined>(_ reference: ThreadSafeReference<Confined>) -> Confined? {
+        return reference.resolve(in: self)
+    }
+}
diff --git a/iOS/Pods/RealmSwift/RealmSwift/Util.swift b/iOS/Pods/RealmSwift/RealmSwift/Util.swift
new file mode 100644 (file)
index 0000000..bfe9f7f
--- /dev/null
@@ -0,0 +1,200 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// 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 Foundation
+import Realm
+
+#if BUILDING_REALM_SWIFT_TESTS
+import RealmSwift
+#endif
+
+// MARK: Internal Helpers
+
+// Swift 3.1 provides fixits for some of our uses of unsafeBitCast
+// to use unsafeDowncast instead, but the bitcast is required.
+internal func noWarnUnsafeBitCast<T, U>(_ x: T, to type: U.Type) -> U {
+    return unsafeBitCast(x, to: type)
+}
+
+/// Given a list of `Any`-typed varargs, unwrap any optionals and
+/// replace them with the underlying value or NSNull.
+internal func unwrapOptionals(in varargs: [Any]) -> [Any] {
+    return varargs.map { arg in
+#if swift(>=3.1)
+        if let someArg = arg as Any? {
+            return someArg
+        }
+        return NSNull()
+#else
+        if let optionalArg = arg as? RealmAnyOptionalUnboxingWorkaround {
+            return optionalArg.rlm_unwrappedValue()
+        } else {
+            return arg
+        }
+#endif
+    }
+}
+
+// FIXME: Kill this when we drop Xcode 8.0 support.
+#if swift(>=3.1)
+#else
+private protocol RealmAnyOptionalUnboxingWorkaround {
+    func rlm_unwrappedValue() -> Any
+}
+
+extension Optional: RealmAnyOptionalUnboxingWorkaround {
+    func rlm_unwrappedValue() -> Any {
+        switch self {
+        case .none: return NSNull()
+        case let .some(underlying): return underlying
+        }
+    }
+}
+#endif
+
+internal func notFoundToNil(index: UInt) -> Int? {
+    if index == UInt(NSNotFound) {
+        return nil
+    }
+    return Int(index)
+}
+
+internal func throwRealmException(_ message: String, userInfo: [AnyHashable: Any]? = nil) {
+    NSException(name: NSExceptionName(rawValue: RLMExceptionName), reason: message, userInfo: userInfo).raise()
+}
+
+internal func throwForNegativeIndex(_ int: Int, parameterName: String = "index") {
+    if int < 0 {
+        throwRealmException("Cannot pass a negative value for '\(parameterName)'.")
+    }
+}
+
+internal func gsub(pattern: String, template: String, string: String, error: NSErrorPointer = nil) -> String? {
+    let regex = try? NSRegularExpression(pattern: pattern, options: [])
+    return regex?.stringByReplacingMatches(in: string, options: [],
+                                           range: NSRange(location: 0, length: string.utf16.count),
+                                           withTemplate: template)
+}
+
+internal func cast<U, V>(_ value: U, to: V.Type) -> V {
+    if let v = value as? V {
+        return v
+    }
+    return unsafeBitCast(value, to: to)
+}
+
+extension Object {
+    // Must *only* be used to call Realm Objective-C APIs that are exposed on `RLMObject`
+    // but actually operate on `RLMObjectBase`. Do not expose cast value to user.
+    internal func unsafeCastToRLMObject() -> RLMObject {
+        return unsafeBitCast(self, to: RLMObject.self)
+    }
+}
+
+// MARK: CustomObjectiveCBridgeable
+
+internal func dynamicBridgeCast<T>(fromObjectiveC x: Any) -> T {
+    if T.self == DynamicObject.self {
+        return unsafeBitCast(x as AnyObject, to: T.self)
+    } else if let BridgeableType = T.self as? CustomObjectiveCBridgeable.Type {
+        return BridgeableType.bridging(objCValue: x) as! T
+    } else {
+        return x as! T
+    }
+}
+
+internal func dynamicBridgeCast<T>(fromSwift x: T) -> Any {
+    if let x = x as? CustomObjectiveCBridgeable {
+        return x.objCValue
+    } else {
+        return x
+    }
+}
+
+// Used for conversion from Objective-C types to Swift types
+internal protocol CustomObjectiveCBridgeable {
+    static func bridging(objCValue: Any) -> Self
+    var objCValue: Any { get }
+}
+
+// FIXME: needed with swift 3.2
+// Double isn't though?
+extension Float: CustomObjectiveCBridgeable {
+    static func bridging(objCValue: Any) -> Float {
+        return (objCValue as! NSNumber).floatValue
+    }
+    var objCValue: Any {
+        return NSNumber(value: self)
+    }
+}
+
+extension Int8: CustomObjectiveCBridgeable {
+    static func bridging(objCValue: Any) -> Int8 {
+        return (objCValue as! NSNumber).int8Value
+    }
+    var objCValue: Any {
+        return NSNumber(value: self)
+    }
+}
+extension Int16: CustomObjectiveCBridgeable {
+    static func bridging(objCValue: Any) -> Int16 {
+        return (objCValue as! NSNumber).int16Value
+    }
+    var objCValue: Any {
+        return NSNumber(value: self)
+    }
+}
+extension Int32: CustomObjectiveCBridgeable {
+    static func bridging(objCValue: Any) -> Int32 {
+        return (objCValue as! NSNumber).int32Value
+    }
+    var objCValue: Any {
+        return NSNumber(value: self)
+    }
+}
+extension Int64: CustomObjectiveCBridgeable {
+    static func bridging(objCValue: Any) -> Int64 {
+        return (objCValue as! NSNumber).int64Value
+    }
+    var objCValue: Any {
+        return NSNumber(value: self)
+    }
+}
+extension Optional: CustomObjectiveCBridgeable {
+    static func bridging(objCValue: Any) -> Optional {
+        if objCValue is NSNull {
+            return nil
+        } else {
+            return .some(dynamicBridgeCast(fromObjectiveC: objCValue))
+        }
+    }
+    var objCValue: Any {
+        if let value = self {
+            return dynamicBridgeCast(fromSwift: value)
+        } else {
+            return NSNull()
+        }
+    }
+}
+
+// MARK: AssistedObjectiveCBridgeable
+
+internal protocol AssistedObjectiveCBridgeable {
+    static func bridging(from objectiveCValue: Any, with metadata: Any?) -> Self
+    var bridged: (objectiveCValue: Any, metadata: Any?) { get }
+}
diff --git a/iOS/Pods/RealmSwift/build.sh b/iOS/Pods/RealmSwift/build.sh
new file mode 100755 (executable)
index 0000000..04b73b4
--- /dev/null
@@ -0,0 +1,1516 @@
+#!/bin/bash
+
+##################################################################################
+# 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
+
+usage() {
+cat <<EOF
+Usage: sh $0 command [argument]
+
+command:
+  clean:                clean up/remove all generated files
+  download-core:        downloads core library (binary version)
+  download-object-server:  downloads and installs the Realm Object Server
+  download-sync:        downloads sync library (binary version, core+sync)
+  build:                builds all iOS  and OS X frameworks
+  ios-static:           builds fat iOS static framework
+  ios-dynamic:          builds iOS dynamic frameworks
+  ios-swift:            builds RealmSwift frameworks for iOS
+  watchos:              builds watchOS framwork
+  watchos-swift:        builds RealmSwift framework for watchOS
+  tvos:                 builds tvOS framework
+  tvos-swift:           builds RealmSwift framework for tvOS
+  osx:                  builds OS X framework
+  osx-swift:            builds RealmSwift framework for OS X
+  analyze-osx:          analyzes OS X framework
+  test:                 tests all iOS and OS X frameworks
+  test-all:             tests all iOS and OS X frameworks in both Debug and Release configurations
+  test-ios-static:      tests static iOS framework on 32-bit and 64-bit simulators
+  test-ios-dynamic:     tests dynamic iOS framework on 32-bit and 64-bit simulators
+  test-ios-swift:       tests RealmSwift iOS framework on 32-bit and 64-bit simulators
+  test-ios-devices:     tests ObjC & Swift iOS frameworks on all attached iOS devices
+  test-ios-devices-objc:  tests ObjC iOS framework on all attached iOS devices
+  test-ios-devices-swift: tests Swift iOS framework on all attached iOS devices
+  test-tvos:            tests tvOS framework
+  test-tvos-swift:      tests RealmSwift tvOS framework
+  test-tvos-devices:    tests ObjC & Swift tvOS frameworks on all attached tvOS devices
+  test-osx:             tests OS X framework
+  test-osx-swift:       tests RealmSwift OS X framework
+  verify:               verifies docs, osx, osx-swift, ios-static, ios-dynamic, ios-swift, ios-device in both Debug and Release configurations, swiftlint
+  verify-osx-object-server:  downloads the Realm Object Server and runs the Objective-C and Swift integration tests
+  docs:                 builds docs in docs/output
+  examples:             builds all examples
+  examples-ios:         builds all static iOS examples
+  examples-ios-swift:   builds all Swift iOS examples
+  examples-osx:         builds all OS X examples
+  get-version:          get the current version
+  set-version version:  set the version
+  cocoapods-setup:      download realm-core and create a stub RLMPlatform.h file to enable building via CocoaPods
+
+
+argument:
+  version: version in the x.y.z format
+
+environment variables:
+  XCMODE: xcodebuild (default), xcpretty or xctool
+  CONFIGURATION: Debug or Release (default)
+  REALM_CORE_VERSION: version in x.y.z format or "current" to use local build
+  REALM_EXTRA_BUILD_ARGUMENTS: additional arguments to pass to the build tool
+  REALM_XCODE_VERSION: the version number of Xcode to use (e.g.: 8.1)
+EOF
+}
+
+######################################
+# Xcode Helpers
+######################################
+
+xcode_version_major() {
+    echo "${REALM_XCODE_VERSION%%.*}"
+}
+
+xcode() {
+    mkdir -p build/DerivedData
+    CMD="xcodebuild -IDECustomDerivedDataLocation=build/DerivedData $@"
+    echo "Building with command:" $CMD
+    eval "$CMD"
+}
+
+xc() {
+    # Logs xcodebuild output in realtime
+    : ${NSUnbufferedIO:=YES}
+    args="$@ SWIFT_VERSION=$REALM_SWIFT_VERSION $REALM_EXTRA_BUILD_ARGUMENTS"
+    if [[ "$XCMODE" == "xcodebuild" ]]; then
+        xcode "$args"
+    elif [[ "$XCMODE" == "xcpretty" ]]; then
+        mkdir -p build
+        xcode "$args" | tee build/build.log | xcpretty -c ${XCPRETTY_PARAMS} || {
+            echo "The raw xcodebuild output is available in build/build.log"
+            exit 1
+        }
+    elif [[ "$XCMODE" == "xctool" ]]; then
+        xctool "$args"
+    fi
+}
+
+copy_bcsymbolmap() {
+    find "$1" -name '*.bcsymbolmap' -type f -exec cp {} "$2" \;
+}
+
+build_combined() {
+    local scheme="$1"
+    local module_name="$2"
+    local os="$3"
+    local simulator="$4"
+    local scope_suffix="$5"
+    local version_suffix="$6"
+    local config="$CONFIGURATION"
+
+    local destination=""
+    local os_name=""
+    if [[ "$os" == "iphoneos" ]]; then
+        os_name="ios"
+        destination="iPhone 6"
+    elif [[ "$os" == "watchos"  ]]; then
+        os_name="$os"
+        destination="Apple Watch - 42mm"
+    elif [[ "$os" == "appletvos"  ]]; then
+        os_name="tvos"
+        if (( $(xcode_version_major) >= 9 )); then
+            destination="Apple TV"
+        else
+            destination="Apple TV 1080p"
+        fi
+    fi
+
+    # Derive build paths
+    local build_products_path="build/DerivedData/Realm/Build/Products"
+    local product_name="$module_name.framework"
+    local binary_path="$module_name"
+    local os_path="$build_products_path/$config-$os$scope_suffix/$product_name"
+    local simulator_path="$build_products_path/$config-$simulator$scope_suffix/$product_name"
+    local out_path="build/$os_name$scope_suffix$version_suffix"
+
+    # Build for each platform
+    xc "-scheme '$scheme' -configuration $config -sdk $os"
+    xc "-scheme '$scheme' -configuration $config -sdk $simulator -destination 'name=$destination' ONLY_ACTIVE_ARCH=NO"
+
+    # Combine .swiftmodule
+    if [ -d $simulator_path/Modules/$module_name.swiftmodule ]; then
+      cp $simulator_path/Modules/$module_name.swiftmodule/* $os_path/Modules/$module_name.swiftmodule/
+    fi
+
+    # Copy *.bcsymbolmap to .framework for submitting app with bitcode
+    copy_bcsymbolmap "$build_products_path/$config-$os$scope_suffix" "$os_path"
+
+    # Retrieve build products
+    clean_retrieve $os_path $out_path $product_name
+
+    # Combine ar archives
+    LIPO_OUTPUT="$out_path/$product_name/$module_name"
+    xcrun lipo -create "$simulator_path/$binary_path" "$os_path/$binary_path" -output "$LIPO_OUTPUT"
+
+    if [[ "$destination" != "" && "$config" == "Release" ]]; then
+        sh build.sh binary-has-bitcode "$LIPO_OUTPUT"
+    fi
+}
+
+clean_retrieve() {
+  mkdir -p "$2"
+  rm -rf "$2/$3"
+  cp -R "$1" "$2"
+}
+
+move_to_clean_dir() {
+    rm -rf "$2"
+    mkdir -p "$2"
+    mv "$1" "$2"
+}
+
+test_ios_static() {
+    destination="$1"
+    xc "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' build"
+    if (( $(xcode_version_major) < 9 )); then
+        xc "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'"
+    fi
+
+    # Xcode's depending tracking is lacking and it doesn't realize that the Realm static framework's static library
+    # needs to be recreated when the active architectures change. Help Xcode out by removing the static library.
+    settings=$(xcode "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' -showBuildSettings")
+    path=$(echo "$settings" | awk '/CONFIGURATION_BUILD_DIR/ { cbd = $3; } /EXECUTABLE_PATH/ { ep = $3; } END { printf "%s/%s\n", cbd, ep; }')
+    rm "$path"
+
+    xc "-scheme 'Realm iOS static' -configuration $CONFIGURATION -sdk iphonesimulator -destination '$destination' test"
+}
+
+######################################
+# Device Test Helper
+######################################
+
+test_devices() {
+    local serial_numbers=()
+    local awk_script="
+    /^ +Vendor ID: / { is_apple = 0; }
+    /^ +Vendor ID: 0x05[aA][cC] / { is_apple = 1; }
+    /^ +Serial Number: / {
+        if (is_apple) {
+            match(\$0, /^ +Serial Number: /);
+            print substr(\$0, RLENGTH + 1);
+        }
+    }
+    "
+    local serial_numbers_text=$(/usr/sbin/system_profiler SPUSBDataType | /usr/bin/awk "$awk_script")
+    while read -r number; do
+        if [[ "$number" != "" ]]; then
+            serial_numbers+=("$number")
+        fi
+    done <<< "$serial_numbers_text"
+    if [[ ${#serial_numbers[@]} == 0 ]]; then
+        echo "At least one iOS/tvOS device must be connected to this computer to run device tests"
+        if [ -z "${JENKINS_HOME}" ]; then
+            # Don't fail if running locally and there's no device
+            exit 0
+        fi
+        exit 1
+    fi
+    local sdk="$1"
+    local scheme="$2"
+    local configuration="$3"
+    local failed=0
+    for device in "${serial_numbers[@]}"; do
+        xc "-scheme '$scheme' -configuration $configuration -destination 'id=$device' -sdk $sdk test" || failed=1
+    done
+    return $failed
+}
+
+######################################
+# Docs
+######################################
+
+build_docs() {
+    local language="$1"
+    local version=$(sh build.sh get-version)
+
+    local xcodebuild_arguments="--objc,Realm/Realm.h,--,-x,objective-c,-isysroot,$(xcrun --show-sdk-path),-I,$(pwd)"
+    local module="Realm"
+    local objc="--objc"
+
+    if [[ "$language" == "swift" ]]; then
+        sh build.sh set-swift-version
+        xcodebuild_arguments="-scheme,RealmSwift"
+        module="RealmSwift"
+        objc=""
+    fi
+
+    touch Realm/RLMPlatform.h # jazzy will fail if it can't find all public header files
+    jazzy \
+      ${objc} \
+      --clean \
+      --author Realm \
+      --author_url https://realm.io \
+      --github_url https://github.com/realm/realm-cocoa \
+      --github-file-prefix https://github.com/realm/realm-cocoa/tree/v${version} \
+      --module-version ${version} \
+      --xcodebuild-arguments ${xcodebuild_arguments} \
+      --module ${module} \
+      --root-url https://realm.io/docs/${language}/${version}/api/ \
+      --output docs/${language}_output \
+      --head "$(cat docs/custom_head.html)"
+
+    rm Realm/RLMPlatform.h
+}
+
+######################################
+# Input Validation
+######################################
+
+if [ "$#" -eq 0 -o "$#" -gt 3 ]; then
+    usage
+    exit 1
+fi
+
+######################################
+# Downloading
+######################################
+
+kill_object_server() {
+# Based on build.sh conventions we always run ROS from a path ending in 'ros/bin/ros'.
+    pkill -f ros/bin/ros\ start
+}
+
+download_object_server() {
+    rm -rf ./test-ros-instance
+    mkdir -p ./test-ros-instance/ros
+    chmod 777 ./test-ros-instance
+    /usr/local/bin/node /usr/local/bin/npm install --scripts-prepend-node-path=auto --prefix ./test-ros-instance/ros \
+        -g realm-object-server@${REALM_OBJECT_SERVER_VERSION}
+}
+
+download_common() {
+    local download_type=$1 tries_left=3 version url error temp_dir temp_path tar_path
+
+    if [ "$download_type" == "core" ]; then
+        version=$REALM_CORE_VERSION
+        url="https://static.realm.io/downloads/core/realm-core-${version}.tar.xz"
+    elif [ "$download_type" == "sync" ]; then
+        version=$REALM_SYNC_VERSION
+        url="https://static.realm.io/downloads/sync/realm-sync-cocoa-${version}.tar.xz"
+    else
+        echo "Unknown dowload_type: $download_type"
+        exit 1
+    fi
+
+    echo "Downloading dependency: ${download_type} ${version}"
+
+    if [ -z "$TMPDIR" ]; then
+        TMPDIR='/tmp'
+    fi
+    temp_dir=$(dirname "$TMPDIR/waste")/${download_type}_bin
+    mkdir -p "$temp_dir"
+    tar_path="${temp_dir}/${download_type}-${version}.tar.xz"
+    temp_path="${tar_path}.tmp"
+
+    while [ 0 -lt $tries_left ] && [ ! -f "$tar_path" ]; do
+        if ! error=$(/usr/bin/curl --fail --silent --show-error --location "$url" --output "$temp_path" 2>&1); then
+            tries_left=$[$tries_left-1]
+        else
+            mv "$temp_path" "$tar_path"
+        fi
+    done
+
+    if [ ! -f "$tar_path" ]; then
+        printf "Downloading ${download_type} failed:\n\t$url\n\t$error\n"
+        exit 1
+    fi
+
+    (
+        cd "$temp_dir"
+        rm -rf "$download_type"
+        tar xf "$tar_path" --xz
+        mv core "${download_type}-${version}"
+    )
+
+    rm -rf "${download_type}-${version}" core
+    mv "${temp_dir}/${download_type}-${version}" .
+    ln -s "${download_type}-${version}" core
+}
+
+download_core() {
+    download_common "core"
+}
+
+download_sync() {
+    download_common "sync"
+}
+
+######################################
+# Variables
+######################################
+
+COMMAND="$1"
+
+# Use Debug config if command ends with -debug, otherwise default to Release
+# Set IS_RUNNING_PACKAGING when running packaging steps to avoid running iOS static tests with Xcode 8.3.3
+case "$COMMAND" in
+    *-debug)
+        COMMAND="${COMMAND%-debug}"
+        CONFIGURATION="Debug"
+        ;;
+    package-*)
+        IS_RUNNING_PACKAGING=1
+        ;;
+esac
+export CONFIGURATION=${CONFIGURATION:-Release}
+export IS_RUNNING_PACKAGING=${IS_RUNNING_PACKAGING:-0}
+
+# Pre-choose Xcode and Swift versions for those operations that do not set them
+REALM_XCODE_VERSION=${xcode_version:-$REALM_XCODE_VERSION}
+REALM_SWIFT_VERSION=${swift_version:-$REALM_SWIFT_VERSION}
+source "${source_root}/scripts/swift-version.sh"
+set_xcode_and_swift_versions
+
+######################################
+# Commands
+######################################
+
+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
+        ;;
+
+    "reset-ros-server-state")
+        rm -rf "./test-ros-instance/data"
+        rm -rf "./test-ros-instance/realm-object-server"
+        exit 0
+        ;;
+
+    "reset-ros-client-state")
+        rm -rf ~/Library/Application\ Support/xctest
+        rm -rf ~/Library/Application\ Support/io.realm.TestHost
+        rm -rf ~/Library/Application\ Support/xctest-child
+        exit 0
+        ;;
+
+    "reset-object-server")
+        kill_object_server
+        # Add a short delay, so file system doesn't complain about files in use
+        sleep 1
+        sh build.sh reset-ros-server-state
+        sh build.sh reset-ros-client-state
+        # Add another delay to ensure files are actually gone from file system
+        sleep 1
+        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:-$REALM_SWIFT_VERSION}
+
+        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-ios-dynamic")
+        xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' build"
+        if (( $(xcode_version_major) < 9 )); then
+            xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'"
+        fi
+        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"
+        if (( $(xcode_version_major) < 9 )); then
+            xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'"
+        fi
+        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" "$CONFIGURATION"
+        exit $?
+        ;;
+
+    "test-ios-devices-swift")
+        test_devices iphoneos "RealmSwift" "$CONFIGURATION"
+        exit $?
+        ;;
+
+    "test-tvos")
+        if (( $(xcode_version_major) >= 9 )); then
+            destination="Apple TV"
+        else
+            destination="Apple TV 1080p"
+        fi
+        xc "-scheme Realm -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination' test"
+        exit $?
+        ;;
+
+    "test-tvos-swift")
+        if (( $(xcode_version_major) >= 9 )); then
+            destination="Apple TV"
+        else
+            destination="Apple TV 1080p"
+        fi
+        xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=$destination' 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-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-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
+        sh build.sh reset-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}"
+        xc "-workspace $workspace -scheme Draw -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")
+        sh build.sh prelaunch-simulator
+        workspace="examples/ios/swift/RealmExamples.xcworkspace"
+        if [[ ! -d "$workspace" ]]; then
+            workspace="${workspace/swift/swift-$REALM_SWIFT_VERSION}"
+        fi
+
+        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}"
+        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"
+        if (( $(xcode_version_major) >= 9 )); then
+            destination="Apple TV"
+        else
+            destination="Apple TV 1080p"
+        fi
+
+        xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}"
+        xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}"
+        exit 0
+        ;;
+
+    "examples-tvos-swift")
+        workspace="examples/tvos/swift/RealmExamples.xcworkspace"
+        if [[ ! -d "$workspace" ]]; then
+            workspace="${workspace/swift/swift-$REALM_SWIFT_VERSION}"
+        fi
+
+        if (( $(xcode_version_major) >= 9 )); then
+            destination="Apple TV"
+        else
+            destination="Apple TV 1080p"
+        fi
+
+        xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}"
+        xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=$destination' build ${CODESIGN_PARAMS}"
+        exit 0
+        ;;
+
+    ######################################
+    # Versioning
+    ######################################
+    "get-version")
+        version_file="Realm/Realm-Info.plist"
+        echo "$(PlistBuddy -c "Print :CFBundleShortVersionString" "$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
+        # The bundle version can contain only three groups of digits separated by periods,
+        # so strip off any -beta.x tag from the end of the version string.
+        bundle_version=$(echo "$realm_version" | cut -d - -f 1)
+        for version_file in $version_files; do
+            PlistBuddy -c "Set :CFBundleVersion $bundle_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[@]} -lt 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
+          mv core/librealm-ios.a core/librealmcore-ios.a
+          mv core/librealm-macosx.a core/librealmcore-macosx.a
+          mv core/librealm-tvos.a core/librealmcore-tvos.a
+          mv core/librealm-watchos.a core/librealmcore-watchos.a
+        fi
+
+        if [[ "$2" != "swift" ]]; then
+          if [ ! -d Realm/ObjectStore/src ]; then
+            cat >&2 <<EOM
+
+
+ERROR: One of Realm's submodules is missing!
+
+If you're using Realm and/or RealmSwift from a git branch, please add 'submodules: true' to
+their entries in your Podfile.
+
+
+EOM
+            exit 1
+          fi
+
+          rm -rf include
+          mkdir -p include
+          mv core/include include/core
+
+          mkdir -p include/impl/apple include/util/apple include/sync/impl/apple
+          cp Realm/*.hpp include
+          cp Realm/ObjectStore/src/*.hpp include
+          cp Realm/ObjectStore/src/sync/*.hpp include/sync
+          cp Realm/ObjectStore/src/sync/impl/*.hpp include/sync/impl
+          cp Realm/ObjectStore/src/sync/impl/apple/*.hpp include/sync/impl/apple
+          cp Realm/ObjectStore/src/impl/*.hpp include/impl
+          cp Realm/ObjectStore/src/impl/apple/*.hpp include/impl/apple
+          cp Realm/ObjectStore/src/util/*.hpp include/util
+          cp Realm/ObjectStore/src/util/apple/*.hpp include/util/apple
+
+          touch Realm/RLMPlatform.h
+          if [ -n "$COCOAPODS_VERSION" ]; then
+            # This variable is set for the prepare_command available
+            # from the 1.0 prereleases, which requires a different
+            # header layout within the header_mappings_dir.
+            cp Realm/*.h include
+          else
+            # For CocoaPods < 1.0, we need to scope the headers within
+            # the header_mappings_dir by another subdirectory to avoid
+            # Clang from complaining about non-modular headers.
+            mkdir -p include/Realm
+            cp Realm/*.h include/Realm
+          fi
+        else
+          sh build.sh set-swift-version
+        fi
+        ;;
+
+    ######################################
+    # Continuous Integration
+    ######################################
+
+    "ci-pr")
+        mkdir -p build/reports
+        # FIXME: Re-enable once CI can properly unlock the keychain
+        export REALM_DISABLE_METADATA_ENCRYPTION=1
+
+        # strip off the ios|tvos version specifier, e.g. the last part of: `ios-device-objc-ios8`
+        if [[ "$target" =~ ^((ios|tvos)-device(-(objc|swift))?)(-(ios|tvos)[[:digit:]]+)?$ ]]; then
+            export target=${BASH_REMATCH[1]}
+        fi
+
+        if [ "$target" = "docs" ]; then
+            sh build.sh set-swift-version
+            sh build.sh verify-docs
+        elif [ "$target" = "swiftlint" ]; then
+            sh build.sh verify-swiftlint
+        else
+            export sha=$GITHUB_PR_SOURCE_BRANCH
+            export CONFIGURATION=$configuration
+            export REALM_EXTRA_BUILD_ARGUMENTS='GCC_GENERATE_DEBUGGING_SYMBOLS=NO REALM_PREFIX_HEADER=Realm/RLMPrefix.h'
+            sh build.sh prelaunch-simulator
+
+            # Reset CoreSimulator.log
+            mkdir -p ~/Library/Logs/CoreSimulator
+            echo > ~/Library/Logs/CoreSimulator/CoreSimulator.log
+
+            if [ -d ~/Library/Developer/CoreSimulator/Devices/ ]; then
+                # Verify that no Realm files still exist
+                ! find ~/Library/Developer/CoreSimulator/Devices/ -name '*.realm' | grep -q .
+            fi
+
+            failed=0
+            sh build.sh verify-$target 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|operation never finished bootstrapping'; then
+                echo "Known Xcode error detected. Running job again."
+                if cat build/build.log | grep -E 'out of date and needs to be rebuilt'; then
+                    rm -rf build/DerivedData
+                fi
+                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 || true
+                echo "\n\n***\nCoreSimulator.log\n***\n\n" && cat ~/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/<source>\./<source>${WS}/" build/reports/coverage-report.xml
+        fi
+        ;;
+
+    ######################################
+    # Release packaging
+    ######################################
+
+    "package-examples")
+        ./scripts/package_examples.rb
+        zip --symlinks -r realm-examples.zip examples -x "examples/installation/*"
+        ;;
+
+    "package-test-examples")
+        if ! VERSION=$(echo realm-objc-*.zip | egrep -o '\d*\.\d*\.\d*-[a-z]*(\.\d*)?'); then
+            VERSION=$(echo realm-objc-*.zip | egrep -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")
+        sh build.sh prelaunch-simulator
+        sh build.sh ios-static
+
+        cd build/ios-static
+        zip --symlinks -r realm-framework-ios-static.zip Realm.framework
+        ;;
+
+    "package-ios")
+        sh build.sh prelaunch-simulator
+        sh build.sh ios-dynamic
+        cd build/ios
+        zip --symlinks -r realm-framework-ios.zip Realm.framework
+        ;;
+
+    "package-osx")
+        sh build.sh osx
+
+        cd build/DerivedData/Realm/Build/Products/Release
+        zip --symlinks -r realm-framework-osx.zip Realm.framework
+        ;;
+
+    "package-ios-swift")
+        for version in 8.3.3 9.0 9.1 9.2; do
+            REALM_XCODE_VERSION=$version
+            REALM_SWIFT_VERSION=
+            set_xcode_and_swift_versions
+            sh build.sh prelaunch-simulator
+            sh build.sh ios-swift
+        done
+
+        cd build/ios
+        ln -s swift-4.0 swift-3.2
+        ln -s swift-4.0.2 swift-3.2.2
+        ln -s swift-4.0.2 swift-3.2.3
+        ln -s swift-4.0.2 swift-4.0.3
+        zip --symlinks -r realm-swift-framework-ios.zip swift-3.1 swift-3.2 swift-3.2.2 swift-3.2.3 swift-4.0 swift-4.0.2 swift-4.0.3
+        ;;
+
+    "package-osx-swift")
+        for version in 8.3.3 9.0 9.1 9.2; do
+            REALM_XCODE_VERSION=$version
+            REALM_SWIFT_VERSION=
+            set_xcode_and_swift_versions
+            sh build.sh prelaunch-simulator
+            sh build.sh osx-swift
+        done
+
+        cd build/osx
+        ln -s swift-4.0 swift-3.2
+        ln -s swift-4.0.2 swift-3.2.2
+        ln -s swift-4.0.2 swift-3.2.3
+        ln -s swift-4.0.2 swift-4.0.3
+        zip --symlinks -r realm-swift-framework-osx.zip swift-3.1 swift-3.2 swift-3.2.2 swift-3.2.3 swift-4.0 swift-4.0.2 swift-4.0.3
+        ;;
+
+    "package-watchos")
+        sh build.sh prelaunch-simulator
+        sh build.sh watchos
+
+        cd build/watchos
+        zip --symlinks -r realm-framework-watchos.zip Realm.framework
+        ;;
+
+    "package-watchos-swift")
+        for version in 8.3.3 9.0 9.1 9.2; do
+            REALM_XCODE_VERSION=$version
+            REALM_SWIFT_VERSION=
+            set_xcode_and_swift_versions
+            sh build.sh prelaunch-simulator
+            sh build.sh watchos-swift
+        done
+
+        cd build/watchos
+        ln -s swift-4.0 swift-3.2
+        ln -s swift-4.0.2 swift-3.2.2
+        ln -s swift-4.0.2 swift-3.2.3
+        ln -s swift-4.0.2 swift-4.0.3
+        zip --symlinks -r realm-swift-framework-watchos.zip swift-3.1 swift-3.2 swift-3.2.2 swift-3.2.3 swift-4.0 swift-4.0.2 swift-4.0.3
+        ;;
+
+    "package-tvos")
+        sh build.sh prelaunch-simulator
+        sh build.sh tvos
+
+        cd build/tvos
+        zip --symlinks -r realm-framework-tvos.zip Realm.framework
+        ;;
+
+    "package-tvos-swift")
+        for version in 8.3.3 9.0 9.1 9.2; do
+            REALM_XCODE_VERSION=$version
+            REALM_SWIFT_VERSION=
+            set_xcode_and_swift_versions
+            sh build.sh prelaunch-simulator
+            sh build.sh tvos-swift
+        done
+
+        cd build/tvos
+        ln -s swift-4.0 swift-3.2
+        ln -s swift-4.0.2 swift-3.2.2
+        ln -s swift-4.0.2 swift-3.2.3
+        ln -s swift-4.0.2 swift-4.0.3
+        zip --symlinks -r realm-swift-framework-tvos.zip swift-3.1 swift-3.2 swift-3.2.2 swift-3.2.3 swift-4.0 swift-4.0.2 swift-4.0.3
+        ;;
+
+    package-*-swift-3.2)
+        PLATFORM=$(echo $COMMAND | cut -d - -f 2)
+        mkdir -p build/$PLATFORM
+        cd build/$PLATFORM
+        ln -s swift-4.0 swift-3.2
+        zip --symlinks -r realm-swift-framework-$PLATFORM-swift-3.2.zip swift-3.2
+        ;;
+
+    package-*-swift-3.2.2)
+        PLATFORM=$(echo $COMMAND | cut -d - -f 2)
+        mkdir -p build/$PLATFORM
+        cd build/$PLATFORM
+        ln -s swift-4.0.2 swift-3.2.2
+        zip --symlinks -r realm-swift-framework-$PLATFORM-swift-3.2.2.zip swift-3.2.2
+        ;;
+
+    package-*-swift-3.2.3)
+        PLATFORM=$(echo $COMMAND | cut -d - -f 2)
+        mkdir -p build/$PLATFORM
+        cd build/$PLATFORM
+        ln -s swift-4.0.2 swift-3.2.3
+        zip --symlinks -r realm-swift-framework-$PLATFORM-swift-3.2.3.zip swift-3.2.3
+        ;;
+
+    package-*-swift-4.0.3)
+        PLATFORM=$(echo $COMMAND | cut -d - -f 2)
+        mkdir -p build/$PLATFORM
+        cd build/$PLATFORM
+        ln -s swift-4.0.2 swift-4.0.3
+        zip --symlinks -r realm-swift-framework-$PLATFORM-swift-4.0.3.zip swift-4.0.3
+        ;;
+
+    package-*-swift-*)
+        PLATFORM=$(echo $COMMAND | cut -d - -f 2)
+        REALM_SWIFT_VERSION=$(echo $COMMAND | cut -d - -f 4)
+        REALM_XCODE_VERSION=
+
+        set_xcode_and_swift_versions
+        sh build.sh prelaunch-simulator
+        sh build.sh $PLATFORM-swift
+
+        cd build/$PLATFORM
+        zip --symlinks -r realm-swift-framework-$PLATFORM-swift-$REALM_SWIFT_VERSION.zip swift-$REALM_SWIFT_VERSION
+        ;;
+
+    "package-release")
+        LANG="$2"
+        TEMPDIR=$(mktemp -d $TMPDIR/realm-release-package-${LANG}.XXXX)
+
+        VERSION=$(sh build.sh get-version)
+
+        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-static.zip
+            )
+
+            (
+                cd ${FOLDER}/ios/dynamic
+                unzip ${WORKSPACE}/realm-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
+                for f in ${WORKSPACE}/realm-swift-framework-osx-swift-*.zip; do
+                    unzip "$f"
+                done
+            )
+
+            (
+                cd ${FOLDER}/ios
+                for f in ${WORKSPACE}/realm-swift-framework-ios-swift-*.zip; do
+                    unzip "$f"
+                done
+            )
+
+            (
+                cd ${FOLDER}/watchos
+                for f in ${WORKSPACE}/realm-swift-framework-watchos-swift-*.zip; do
+                    unzip "$f"
+                done
+            )
+
+            (
+                cd ${FOLDER}/tvos
+                for f in ${WORKSPACE}/realm-swift-framework-tvos-swift-*.zip; do
+                    unzip "$f"
+                done
+            )
+        fi
+
+        (
+            cd ${WORKSPACE}
+            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 <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>URL</key>
+    <string>https://realm.io/docs/${LANG}/${VERSION}</string>
+</dict>
+</plist>
+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 realm-cocoa
+        cd realm-cocoa
+
+        echo 'Packaging iOS'
+        sh build.sh package-ios-static
+        cp build/ios-static/realm-framework-ios-static.zip ..
+        sh build.sh package-ios
+        cp build/ios/realm-framework-ios.zip ..
+        sh build.sh package-ios-swift
+        cp build/ios/realm-swift-framework-ios.zip ..
+
+        echo 'Packaging OS X'
+        sh build.sh package-osx
+        cp build/DerivedData/Realm/Build/Products/Release/realm-framework-osx.zip ..
+        sh build.sh package-osx-swift
+        cp build/osx/realm-swift-framework-osx.zip ..
+
+        echo 'Packaging watchOS'
+        sh build.sh package-watchos
+        cp build/watchos/realm-framework-watchos.zip ..
+        sh build.sh package-watchos-swift
+        cp build/watchos/realm-swift-framework-watchos.zip ..
+
+        echo 'Packaging tvOS'
+        sh build.sh package-tvos
+        cp build/tvos/realm-framework-tvos.zip ..
+        sh build.sh package-tvos-swift
+        cp build/tvos/realm-swift-framework-tvos.zip ..
+
+        echo 'Packaging examples'
+        sh build.sh package-examples
+        cp realm-examples.zip ..
+
+        echo 'Building final release packages'
+        sh build.sh package-release objc
+        sh build.sh package-release swift
+
+        echo 'Testing packaged examples'
+        sh 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 <<EOS
+x.x.x Release notes (yyyy-MM-dd)
+=============================================================
+
+### Breaking Changes
+
+* None.
+
+### Enhancements
+
+* None.
+
+### Bugfixes
+
+* None.
+EOS)
+        changelog=$(cat CHANGELOG.md)
+        echo "$empty_section" > CHANGELOG.md
+        echo >> CHANGELOG.md
+        echo "$changelog" >> CHANGELOG.md
+        ;;
+
+    *)
+        echo "Unknown command '$COMMAND'"
+        usage
+        exit 1
+        ;;
+esac
diff --git a/iOS/Pods/SSZipArchive/LICENSE.txt b/iOS/Pods/SSZipArchive/LICENSE.txt
new file mode 100644 (file)
index 0000000..2229be4
--- /dev/null
@@ -0,0 +1,20 @@
+Copyright (c) 2010-2015, Sam Soffes, http://soff.es
+
+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/iOS/Pods/SSZipArchive/README.md b/iOS/Pods/SSZipArchive/README.md
new file mode 100644 (file)
index 0000000..2736bdb
--- /dev/null
@@ -0,0 +1,70 @@
+
+[![Build Status](https://travis-ci.org/ZipArchive/ZipArchive.svg?branch=master)](https://travis-ci.org/ZipArchive/ZipArchive)
+
+# SSZipArchive
+
+ZipArchive is a simple utility class for zipping and unzipping files on iOS, macOS and tvOS.
+
+- Unzip zip files;
+- Unzip password protected zip files;
+- Unzip AES encrypted zip files;
+- Create zip files;
+- Create password protected zip files;
+- Create AES encrypted zip files;
+- Choose compression level;
+- Append to existing zip files;
+- Zip-up NSData instances. (with a filename)
+
+## Installation and Setup
+
+*The main release branch is configured to support Objective C and Swift 3+.*
+
+SSZipArchive works on Xcode 7-9 and above, iOS 8-11 and above.
+
+### CocoaPods
+In your Podfile:  
+`pod 'SSZipArchive'`
+
+### Carthage
+In your Cartfile:  
+`github "ZipArchive/ZipArchive"`
+
+### Manual
+
+1. Add the `SSZipArchive` and `minizip` folders to your project.
+2. Add the `libz` library to your target
+
+SSZipArchive requires ARC.
+
+## Usage
+
+### Objective-C
+
+```objective-c
+// Create
+[SSZipArchive createZipFileAtPath:zipPath withContentsOfDirectory:sampleDataPath];
+
+// Unzip
+[SSZipArchive unzipFileAtPath:zipPath toDestination:unzipPath];
+```
+
+### Swift
+
+```swift
+// Create
+SSZipArchive.createZipFileAtPath(zipPath, withContentsOfDirectory: sampleDataPath)
+
+// Unzip
+SSZipArchive.unzipFileAtPath(zipPath, toDestination: unzipPath)
+```
+
+## License
+
+SSZipArchive is protected under the [MIT license](https://github.com/samsoffes/ssziparchive/raw/master/LICENSE) and our slightly modified version of [Minizip](https://github.com/nmoinvaz/minizip) 1.2 is licensed under the [Zlib license](http://www.zlib.net/zlib_license.html).
+
+## Acknowledgments
+
+* Big thanks to [aish](http://code.google.com/p/ziparchive) for creating [ZipArchive](http://code.google.com/p/ziparchive). The project that inspired SSZipArchive.
+* Thank you [@soffes](https://github.com/soffes) for the actual name of SSZipArchive.
+* Thank you [@randomsequence](https://github.com/randomsequence) for implementing the creation support tech.
+* Thank you [@johnezang](https://github.com/johnezang) for all his amazing help along the way.
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/SSZipArchive.h b/iOS/Pods/SSZipArchive/SSZipArchive/SSZipArchive.h
new file mode 100755 (executable)
index 0000000..e5768b1
--- /dev/null
@@ -0,0 +1,146 @@
+//
+//  SSZipArchive.h
+//  SSZipArchive
+//
+//  Created by Sam Soffes on 7/21/10.
+//  Copyright (c) Sam Soffes 2010-2015. All rights reserved.
+//
+
+#ifndef _SSZIPARCHIVE_H
+#define _SSZIPARCHIVE_H
+
+#import <Foundation/Foundation.h>
+#include "SSZipCommon.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+extern NSString *const SSZipArchiveErrorDomain;
+typedef NS_ENUM(NSInteger, SSZipArchiveErrorCode) {
+    SSZipArchiveErrorCodeFailedOpenZipFile      = -1,
+    SSZipArchiveErrorCodeFailedOpenFileInZip    = -2,
+    SSZipArchiveErrorCodeFileInfoNotLoadable    = -3,
+    SSZipArchiveErrorCodeFileContentNotReadable = -4,
+    SSZipArchiveErrorCodeFailedToWriteFile      = -5,
+    SSZipArchiveErrorCodeInvalidArguments       = -6,
+};
+
+@protocol SSZipArchiveDelegate;
+
+@interface SSZipArchive : NSObject
+
+// Password check
++ (BOOL)isFilePasswordProtectedAtPath:(NSString *)path;
++ (BOOL)isPasswordValidForArchiveAtPath:(NSString *)path password:(NSString *)pw error:(NSError * _Nullable * _Nullable)error NS_SWIFT_NOTHROW;
+
+// Unzip
++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination;
++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(nullable id<SSZipArchiveDelegate>)delegate;
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+              overwrite:(BOOL)overwrite
+               password:(nullable NSString *)password
+                  error:(NSError * *)error;
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+              overwrite:(BOOL)overwrite
+               password:(nullable NSString *)password
+                  error:(NSError * *)error
+               delegate:(nullable id<SSZipArchiveDelegate>)delegate NS_REFINED_FOR_SWIFT;
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+     preserveAttributes:(BOOL)preserveAttributes
+              overwrite:(BOOL)overwrite
+               password:(nullable NSString *)password
+                  error:(NSError * *)error
+               delegate:(nullable id<SSZipArchiveDelegate>)delegate;
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+        progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
+      completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler;
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+              overwrite:(BOOL)overwrite
+               password:(nullable NSString *)password
+        progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
+      completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler;
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+     preserveAttributes:(BOOL)preserveAttributes
+              overwrite:(BOOL)overwrite
+         nestedZipLevel:(NSInteger)nestedZipLevel
+               password:(nullable NSString *)password
+                  error:(NSError **)error
+               delegate:(nullable id<SSZipArchiveDelegate>)delegate
+        progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
+      completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler;
+
+// Zip
+// default compression level is Z_DEFAULT_COMPRESSION (from "zlib.h")
+
+// without password
++ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray<NSString *> *)paths;
++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath;
+
++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory;
+
+// with optional password, default encryption is AES
+// don't use AES if you need compatibility with native macOS unzip and Archive Utility
++ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray<NSString *> *)paths withPassword:(nullable NSString *)password;
++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath withPassword:(nullable NSString *)password;
++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory withPassword:(nullable NSString *)password;
++ (BOOL)createZipFileAtPath:(NSString *)path
+    withContentsOfDirectory:(NSString *)directoryPath
+        keepParentDirectory:(BOOL)keepParentDirectory
+               withPassword:(nullable NSString *)password
+         andProgressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler;
++ (BOOL)createZipFileAtPath:(NSString *)path
+    withContentsOfDirectory:(NSString *)directoryPath
+        keepParentDirectory:(BOOL)keepParentDirectory
+           compressionLevel:(int)compressionLevel
+                   password:(nullable NSString *)password
+                        AES:(BOOL)aes
+            progressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler;
+
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithPath:(NSString *)path NS_DESIGNATED_INITIALIZER;
+- (BOOL)open;
+
+/// write empty folder
+- (BOOL)writeFolderAtPath:(NSString *)path withFolderName:(NSString *)folderName withPassword:(nullable NSString *)password;
+/// write file
+- (BOOL)writeFile:(NSString *)path withPassword:(nullable NSString *)password;
+- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName withPassword:(nullable NSString *)password;
+- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes;
+/// write data
+- (BOOL)writeData:(NSData *)data filename:(nullable NSString *)filename withPassword:(nullable NSString *)password;
+- (BOOL)writeData:(NSData *)data filename:(nullable NSString *)filename compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes;
+
+- (BOOL)close;
+
+@end
+
+@protocol SSZipArchiveDelegate <NSObject>
+
+@optional
+
+- (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo;
+- (void)zipArchiveDidUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo unzippedPath:(NSString *)unzippedPath;
+
+- (BOOL)zipArchiveShouldUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo;
+- (void)zipArchiveWillUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo;
+- (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo;
+- (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath unzippedFilePath:(NSString *)unzippedFilePath;
+
+- (void)zipArchiveProgressEvent:(unsigned long long)loaded total:(unsigned long long)total;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif /* _SSZIPARCHIVE_H */
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/SSZipArchive.m b/iOS/Pods/SSZipArchive/SSZipArchive/SSZipArchive.m
new file mode 100755 (executable)
index 0000000..afcec71
--- /dev/null
@@ -0,0 +1,1059 @@
+//
+//  SSZipArchive.m
+//  SSZipArchive
+//
+//  Created by Sam Soffes on 7/21/10.
+//  Copyright (c) Sam Soffes 2010-2015. All rights reserved.
+//
+
+#import "SSZipArchive.h"
+#include "unzip.h"
+#include "zip.h"
+#include "minishared.h"
+
+#include <sys/stat.h>
+
+NSString *const SSZipArchiveErrorDomain = @"SSZipArchiveErrorDomain";
+
+#define CHUNK 16384
+
+int _zipOpenEntry(zipFile entry, NSString *name, const zip_fileinfo *zipfi, int level, NSString *password, BOOL aes);
+BOOL _fileIsSymbolicLink(const unz_file_info *fileInfo);
+
+#ifndef API_AVAILABLE
+// Xcode 7- compatibility
+#define API_AVAILABLE(...)
+#endif
+
+@interface NSData(SSZipArchive)
+- (NSString *)_base64RFC4648 API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0));
+- (NSString *)_hexString;
+@end
+
+@interface SSZipArchive ()
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+@end
+
+@implementation SSZipArchive
+{
+    /// path for zip file
+    NSString *_path;
+    zipFile _zip;
+}
+
+#pragma mark - Password check
+
++ (BOOL)isFilePasswordProtectedAtPath:(NSString *)path {
+    // Begin opening
+    zipFile zip = unzOpen(path.fileSystemRepresentation);
+    if (zip == NULL) {
+        return NO;
+    }
+    
+    int ret = unzGoToFirstFile(zip);
+    if (ret == UNZ_OK) {
+        do {
+            ret = unzOpenCurrentFile(zip);
+            if (ret != UNZ_OK) {
+                return NO;
+            }
+            unz_file_info fileInfo = {};
+            ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
+            if (ret != UNZ_OK) {
+                return NO;
+            } else if ((fileInfo.flag & 1) == 1) {
+                return YES;
+            }
+            
+            unzCloseCurrentFile(zip);
+            ret = unzGoToNextFile(zip);
+        } while (ret == UNZ_OK);
+    }
+    
+    return NO;
+}
+
++ (BOOL)isPasswordValidForArchiveAtPath:(NSString *)path password:(NSString *)pw error:(NSError **)error {
+    if (error) {
+        *error = nil;
+    }
+
+    zipFile zip = unzOpen(path.fileSystemRepresentation);
+    if (zip == NULL) {
+        if (error) {
+            *error = [NSError errorWithDomain:SSZipArchiveErrorDomain
+                                         code:SSZipArchiveErrorCodeFailedOpenZipFile
+                                     userInfo:@{NSLocalizedDescriptionKey: @"failed to open zip file"}];
+        }
+        return NO;
+    }
+
+    int ret = unzGoToFirstFile(zip);
+    if (ret == UNZ_OK) {
+        do {
+            if (pw.length == 0) {
+                ret = unzOpenCurrentFile(zip);
+            } else {
+                ret = unzOpenCurrentFilePassword(zip, [pw cStringUsingEncoding:NSUTF8StringEncoding]);
+            }
+            if (ret != UNZ_OK) {
+                if (ret != UNZ_BADPASSWORD) {
+                    if (error) {
+                        *error = [NSError errorWithDomain:SSZipArchiveErrorDomain
+                                                     code:SSZipArchiveErrorCodeFailedOpenFileInZip
+                                                 userInfo:@{NSLocalizedDescriptionKey: @"failed to open first file in zip file"}];
+                    }
+                }
+                return NO;
+            }
+            unz_file_info fileInfo = {};
+            ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
+            if (ret != UNZ_OK) {
+                if (error) {
+                    *error = [NSError errorWithDomain:SSZipArchiveErrorDomain
+                                                 code:SSZipArchiveErrorCodeFileInfoNotLoadable
+                                             userInfo:@{NSLocalizedDescriptionKey: @"failed to retrieve info for file"}];
+                }
+                return NO;
+            } else if ((fileInfo.flag & 1) == 1) {
+                unsigned char buffer[10] = {0};
+                int readBytes = unzReadCurrentFile(zip, buffer, (unsigned)MIN(10UL,fileInfo.uncompressed_size));
+                if (readBytes < 0) {
+                    // Let's assume error Z_DATA_ERROR is caused by an invalid password
+                    // Let's assume other errors are caused by Content Not Readable
+                    if (readBytes != Z_DATA_ERROR) {
+                        if (error) {
+                            *error = [NSError errorWithDomain:SSZipArchiveErrorDomain
+                                                         code:SSZipArchiveErrorCodeFileContentNotReadable
+                                                     userInfo:@{NSLocalizedDescriptionKey: @"failed to read contents of file entry"}];
+                        }
+                    }
+                    return NO;
+                }
+                return YES;
+            }
+            
+            unzCloseCurrentFile(zip);
+            ret = unzGoToNextFile(zip);
+        } while (ret == UNZ_OK);
+    }
+    
+    // No password required
+    return YES;
+}
+
+#pragma mark - Unzipping
+
++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination
+{
+    return [self unzipFileAtPath:path toDestination:destination delegate:nil];
+}
+
++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(nullable NSString *)password error:(NSError **)error
+{
+    return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:error delegate:nil progressHandler:nil completionHandler:nil];
+}
+
++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(nullable id<SSZipArchiveDelegate>)delegate
+{
+    return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:YES password:nil error:nil delegate:delegate progressHandler:nil completionHandler:nil];
+}
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+              overwrite:(BOOL)overwrite
+               password:(nullable NSString *)password
+                  error:(NSError **)error
+               delegate:(nullable id<SSZipArchiveDelegate>)delegate
+{
+    return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:error delegate:delegate progressHandler:nil completionHandler:nil];
+}
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+              overwrite:(BOOL)overwrite
+               password:(NSString *)password
+        progressHandler:(void (^)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
+      completionHandler:(void (^)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
+{
+    return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:nil delegate:nil progressHandler:progressHandler completionHandler:completionHandler];
+}
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+        progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
+      completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
+{
+    return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:YES password:nil error:nil delegate:nil progressHandler:progressHandler completionHandler:completionHandler];
+}
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+     preserveAttributes:(BOOL)preserveAttributes
+              overwrite:(BOOL)overwrite
+               password:(nullable NSString *)password
+                  error:(NSError * *)error
+               delegate:(nullable id<SSZipArchiveDelegate>)delegate
+{
+    return [self unzipFileAtPath:path toDestination:destination preserveAttributes:preserveAttributes overwrite:overwrite password:password error:error delegate:delegate progressHandler:nil completionHandler:nil];
+}
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+     preserveAttributes:(BOOL)preserveAttributes
+              overwrite:(BOOL)overwrite
+               password:(nullable NSString *)password
+                  error:(NSError **)error
+               delegate:(nullable id<SSZipArchiveDelegate>)delegate
+        progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
+      completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
+{
+    return [self unzipFileAtPath:path toDestination:destination preserveAttributes:preserveAttributes overwrite:overwrite nestedZipLevel:0 password:password error:error delegate:delegate progressHandler:progressHandler completionHandler:completionHandler];
+}
+
++ (BOOL)unzipFileAtPath:(NSString *)path
+          toDestination:(NSString *)destination
+     preserveAttributes:(BOOL)preserveAttributes
+              overwrite:(BOOL)overwrite
+         nestedZipLevel:(NSInteger)nestedZipLevel
+               password:(nullable NSString *)password
+                  error:(NSError **)error
+               delegate:(nullable id<SSZipArchiveDelegate>)delegate
+        progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
+      completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
+{
+    // Guard against empty strings
+    if (path.length == 0 || destination.length == 0)
+    {
+        NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"received invalid argument(s)"};
+        NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeInvalidArguments userInfo:userInfo];
+        if (error)
+        {
+            *error = err;
+        }
+        if (completionHandler)
+        {
+            completionHandler(nil, NO, err);
+        }
+        return NO;
+    }
+    
+    // Begin opening
+    zipFile zip = unzOpen(path.fileSystemRepresentation);
+    if (zip == NULL)
+    {
+        NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"failed to open zip file"};
+        NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFailedOpenZipFile userInfo:userInfo];
+        if (error)
+        {
+            *error = err;
+        }
+        if (completionHandler)
+        {
+            completionHandler(nil, NO, err);
+        }
+        return NO;
+    }
+    
+    NSDictionary * fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
+    unsigned long long fileSize = [fileAttributes[NSFileSize] unsignedLongLongValue];
+    unsigned long long currentPosition = 0;
+    
+    unz_global_info globalInfo = {};
+    unzGetGlobalInfo(zip, &globalInfo);
+    
+    // Begin unzipping
+    int ret = 0;
+    ret = unzGoToFirstFile(zip);
+    if (ret != UNZ_OK && ret != UNZ_END_OF_LIST_OF_FILE)
+    {
+        NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"failed to open first file in zip file"};
+        NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFailedOpenFileInZip userInfo:userInfo];
+        if (error)
+        {
+            *error = err;
+        }
+        if (completionHandler)
+        {
+            completionHandler(nil, NO, err);
+        }
+        return NO;
+    }
+    
+    BOOL success = YES;
+    BOOL canceled = NO;
+    int crc_ret = 0;
+    unsigned char buffer[4096] = {0};
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSMutableArray<NSDictionary *> *directoriesModificationDates = [[NSMutableArray alloc] init];
+    
+    // Message delegate
+    if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipArchiveAtPath:zipInfo:)]) {
+        [delegate zipArchiveWillUnzipArchiveAtPath:path zipInfo:globalInfo];
+    }
+    if ([delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) {
+        [delegate zipArchiveProgressEvent:currentPosition total:fileSize];
+    }
+    
+    NSInteger currentFileNumber = -1;
+    NSError *unzippingError;
+    do {
+        currentFileNumber++;
+        if (ret == UNZ_END_OF_LIST_OF_FILE)
+            break;
+        @autoreleasepool {
+            if (password.length == 0) {
+                ret = unzOpenCurrentFile(zip);
+            } else {
+                ret = unzOpenCurrentFilePassword(zip, [password cStringUsingEncoding:NSUTF8StringEncoding]);
+            }
+            
+            if (ret != UNZ_OK) {
+                unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFailedOpenFileInZip userInfo:@{NSLocalizedDescriptionKey: @"failed to open file in zip file"}];
+                success = NO;
+                break;
+            }
+            
+            // Reading data and write to file
+            unz_file_info fileInfo;
+            memset(&fileInfo, 0, sizeof(unz_file_info));
+            
+            ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
+            if (ret != UNZ_OK) {
+                unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFileInfoNotLoadable userInfo:@{NSLocalizedDescriptionKey: @"failed to retrieve info for file"}];
+                success = NO;
+                unzCloseCurrentFile(zip);
+                break;
+            }
+            
+            currentPosition += fileInfo.compressed_size;
+            
+            // Message delegate
+            if ([delegate respondsToSelector:@selector(zipArchiveShouldUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) {
+                if (![delegate zipArchiveShouldUnzipFileAtIndex:currentFileNumber
+                                                     totalFiles:(NSInteger)globalInfo.number_entry
+                                                    archivePath:path
+                                                       fileInfo:fileInfo]) {
+                    success = NO;
+                    canceled = YES;
+                    break;
+                }
+            }
+            if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) {
+                [delegate zipArchiveWillUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry
+                                             archivePath:path fileInfo:fileInfo];
+            }
+            if ([delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) {
+                [delegate zipArchiveProgressEvent:(NSInteger)currentPosition total:(NSInteger)fileSize];
+            }
+            
+            char *filename = (char *)malloc(fileInfo.size_filename + 1);
+            if (filename == NULL)
+            {
+                success = NO;
+                break;
+            }
+            
+            unzGetCurrentFileInfo(zip, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
+            filename[fileInfo.size_filename] = '\0';
+            
+            BOOL fileIsSymbolicLink = _fileIsSymbolicLink(&fileInfo);
+            
+            NSString * strPath = [SSZipArchive _filenameStringWithCString:filename size:fileInfo.size_filename];
+            if ([strPath hasPrefix:@"__MACOSX/"]) {
+                // ignoring resource forks: https://superuser.com/questions/104500/what-is-macosx-folder
+                unzCloseCurrentFile(zip);
+                ret = unzGoToNextFile(zip);
+                continue;
+            }
+            if (!strPath.length) {
+                // if filename data is unsalvageable, we default to currentFileNumber
+                strPath = @(currentFileNumber).stringValue;
+            }
+            
+            // Check if it contains directory
+            BOOL isDirectory = NO;
+            if (filename[fileInfo.size_filename-1] == '/' || filename[fileInfo.size_filename-1] == '\\') {
+                isDirectory = YES;
+            }
+            free(filename);
+            
+            // Contains a path
+            if ([strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location != NSNotFound) {
+                strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
+            }
+            
+            NSString *fullPath = [destination stringByAppendingPathComponent:strPath];
+            NSError *err = nil;
+            NSDictionary *directoryAttr;
+            if (preserveAttributes) {
+                NSDate *modDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dos_date];
+                directoryAttr = @{NSFileCreationDate: modDate, NSFileModificationDate: modDate};
+                [directoriesModificationDates addObject: @{@"path": fullPath, @"modDate": modDate}];
+            }
+            if (isDirectory) {
+                [fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:directoryAttr error:&err];
+            } else {
+                [fileManager createDirectoryAtPath:fullPath.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:directoryAttr error:&err];
+            }
+            if (nil != err) {
+                if ([err.domain isEqualToString:NSCocoaErrorDomain] &&
+                    err.code == 640) {
+                    unzippingError = err;
+                    unzCloseCurrentFile(zip);
+                    success = NO;
+                    break;
+                }
+                NSLog(@"[SSZipArchive] Error: %@", err.localizedDescription);
+            }
+            
+            if ([fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite) {
+                //FIXME: couldBe CRC Check?
+                unzCloseCurrentFile(zip);
+                ret = unzGoToNextFile(zip);
+                continue;
+            }
+            
+            if (!fileIsSymbolicLink) {
+                // ensure we are not creating stale file entries
+                int readBytes = unzReadCurrentFile(zip, buffer, 4096);
+                if (readBytes >= 0) {
+                    FILE *fp = fopen(fullPath.fileSystemRepresentation, "wb");
+                    while (fp) {
+                        if (readBytes > 0) {
+                            if (0 == fwrite(buffer, readBytes, 1, fp)) {
+                                if (ferror(fp)) {
+                                    NSString *message = [NSString stringWithFormat:@"Failed to write file (check your free space)"];
+                                    NSLog(@"[SSZipArchive] %@", message);
+                                    success = NO;
+                                    unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFailedToWriteFile userInfo:@{NSLocalizedDescriptionKey: message}];
+                                    break;
+                                }
+                            }
+                        } else {
+                            break;
+                        }
+                        readBytes = unzReadCurrentFile(zip, buffer, 4096);
+                        if (readBytes < 0) {
+                            // Let's assume error Z_DATA_ERROR is caused by an invalid password
+                            // Let's assume other errors are caused by Content Not Readable
+                            success = NO;
+                        }
+                    }
+                    
+                    if (fp) {
+                        fclose(fp);
+                        
+                        if (nestedZipLevel
+                            && [fullPath.pathExtension.lowercaseString isEqualToString:@"zip"]
+                            && [self unzipFileAtPath:fullPath
+                                       toDestination:fullPath.stringByDeletingLastPathComponent
+                                  preserveAttributes:preserveAttributes
+                                           overwrite:overwrite
+                                      nestedZipLevel:nestedZipLevel - 1
+                                            password:password
+                                               error:nil
+                                            delegate:nil
+                                     progressHandler:nil
+                                   completionHandler:nil]) {
+                            [directoriesModificationDates removeLastObject];
+                            [[NSFileManager defaultManager] removeItemAtPath:fullPath error:nil];
+                        } else if (preserveAttributes) {
+                            
+                            // Set the original datetime property
+                            if (fileInfo.dos_date != 0) {
+                                NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dos_date];
+                                NSDictionary *attr = @{NSFileModificationDate: orgDate};
+                                
+                                if (attr) {
+                                    if (![fileManager setAttributes:attr ofItemAtPath:fullPath error:nil]) {
+                                        // Can't set attributes
+                                        NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting modification date");
+                                    }
+                                }
+                            }
+                            
+                            // Set the original permissions on the file (+read/write to solve #293)
+                            uLong permissions = fileInfo.external_fa >> 16 | 0b110000000;
+                            if (permissions != 0) {
+                                // Store it into a NSNumber
+                                NSNumber *permissionsValue = @(permissions);
+                                
+                                // Retrieve any existing attributes
+                                NSMutableDictionary *attrs = [[NSMutableDictionary alloc] initWithDictionary:[fileManager attributesOfItemAtPath:fullPath error:nil]];
+                                
+                                // Set the value in the attributes dict
+                                attrs[NSFilePosixPermissions] = permissionsValue;
+                                
+                                // Update attributes
+                                if (![fileManager setAttributes:attrs ofItemAtPath:fullPath error:nil]) {
+                                    // Unable to set the permissions attribute
+                                    NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting permissions");
+                                }
+                            }
+                        }
+                    }
+                    else
+                    {
+                        // if we couldn't open file descriptor we can validate global errno to see the reason
+                        if (errno == ENOSPC) {
+                            NSError *enospcError = [NSError errorWithDomain:NSPOSIXErrorDomain
+                                                                       code:ENOSPC
+                                                                   userInfo:nil];
+                            unzippingError = enospcError;
+                            unzCloseCurrentFile(zip);
+                            success = NO;
+                            break;
+                        }
+                    }
+                } else {
+                    // Let's assume error Z_DATA_ERROR is caused by an invalid password
+                    // Let's assume other errors are caused by Content Not Readable
+                    success = NO;
+                    break;
+                }
+            }
+            else
+            {
+                // Assemble the path for the symbolic link
+                NSMutableString *destinationPath = [NSMutableString string];
+                int bytesRead = 0;
+                while ((bytesRead = unzReadCurrentFile(zip, buffer, 4096)) > 0)
+                {
+                    buffer[bytesRead] = 0;
+                    [destinationPath appendString:@((const char *)buffer)];
+                }
+                if (bytesRead < 0) {
+                    // Let's assume error Z_DATA_ERROR is caused by an invalid password
+                    // Let's assume other errors are caused by Content Not Readable
+                    success = NO;
+                    break;
+                }
+                
+                // Check if the symlink exists and delete it if we're overwriting
+                if (overwrite)
+                {
+                    if ([fileManager fileExistsAtPath:fullPath])
+                    {
+                        NSError *error = nil;
+                        BOOL removeSuccess = [fileManager removeItemAtPath:fullPath error:&error];
+                        if (!removeSuccess)
+                        {
+                            NSString *message = [NSString stringWithFormat:@"Failed to delete existing symbolic link at \"%@\"", error.localizedDescription];
+                            NSLog(@"[SSZipArchive] %@", message);
+                            success = NO;
+                            unzippingError = [NSError errorWithDomain:SSZipArchiveErrorDomain code:error.code userInfo:@{NSLocalizedDescriptionKey: message}];
+                        }
+                    }
+                }
+                
+                // Create the symbolic link (making sure it stays relative if it was relative before)
+                int symlinkError = symlink([destinationPath cStringUsingEncoding:NSUTF8StringEncoding],
+                                           [fullPath cStringUsingEncoding:NSUTF8StringEncoding]);
+                
+                if (symlinkError != 0)
+                {
+                    // Bubble the error up to the completion handler
+                    NSString *message = [NSString stringWithFormat:@"Failed to create symbolic link at \"%@\" to \"%@\" - symlink() error code: %d", fullPath, destinationPath, errno];
+                    NSLog(@"[SSZipArchive] %@", message);
+                    success = NO;
+                    unzippingError = [NSError errorWithDomain:NSPOSIXErrorDomain code:symlinkError userInfo:@{NSLocalizedDescriptionKey: message}];
+                }
+            }
+            
+            crc_ret = unzCloseCurrentFile(zip);
+            if (crc_ret == UNZ_CRCERROR) {
+                // CRC ERROR
+                success = NO;
+                break;
+            }
+            ret = unzGoToNextFile(zip);
+            
+            // Message delegate
+            if ([delegate respondsToSelector:@selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) {
+                [delegate zipArchiveDidUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry
+                                            archivePath:path fileInfo:fileInfo];
+            } else if ([delegate respondsToSelector: @selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:unzippedFilePath:)]) {
+                [delegate zipArchiveDidUnzipFileAtIndex: currentFileNumber totalFiles: (NSInteger)globalInfo.number_entry
+                                            archivePath:path unzippedFilePath: fullPath];
+            }
+            
+            if (progressHandler)
+            {
+                progressHandler(strPath, fileInfo, currentFileNumber, globalInfo.number_entry);
+            }
+        }
+    } while (ret == UNZ_OK && success);
+    
+    // Close
+    unzClose(zip);
+    
+    // The process of decompressing the .zip archive causes the modification times on the folders
+    // to be set to the present time. So, when we are done, they need to be explicitly set.
+    // set the modification date on all of the directories.
+    if (success && preserveAttributes) {
+        NSError * err = nil;
+        for (NSDictionary * d in directoriesModificationDates) {
+            if (![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate: d[@"modDate"]} ofItemAtPath:d[@"path"] error:&err]) {
+                NSLog(@"[SSZipArchive] Set attributes failed for directory: %@.", d[@"path"]);
+            }
+            if (err) {
+                NSLog(@"[SSZipArchive] Error setting directory file modification date attribute: %@", err.localizedDescription);
+            }
+        }
+    }
+    
+    // Message delegate
+    if (success && [delegate respondsToSelector:@selector(zipArchiveDidUnzipArchiveAtPath:zipInfo:unzippedPath:)]) {
+        [delegate zipArchiveDidUnzipArchiveAtPath:path zipInfo:globalInfo unzippedPath:destination];
+    }
+    // final progress event = 100%
+    if (!canceled && [delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) {
+        [delegate zipArchiveProgressEvent:fileSize total:fileSize];
+    }
+    
+    NSError *retErr = nil;
+    if (crc_ret == UNZ_CRCERROR)
+    {
+        NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"crc check failed for file"};
+        retErr = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFileInfoNotLoadable userInfo:userInfo];
+    }
+    
+    if (error) {
+        if (unzippingError) {
+            *error = unzippingError;
+        }
+        else {
+            *error = retErr;
+        }
+    }
+    if (completionHandler)
+    {
+        if (unzippingError) {
+            completionHandler(path, success, unzippingError);
+        }
+        else
+        {
+            completionHandler(path, success, retErr);
+        }
+    }
+    return success;
+}
+
+#pragma mark - Zipping
++ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray<NSString *> *)paths
+{
+    return [SSZipArchive createZipFileAtPath:path withFilesAtPaths:paths withPassword:nil];
+}
++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath {
+    return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath withPassword:nil];
+}
+
++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory {
+    return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:keepParentDirectory withPassword:nil];
+}
+
++ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray<NSString *> *)paths withPassword:(NSString *)password
+{
+    SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path];
+    BOOL success = [zipArchive open];
+    if (success) {
+        for (NSString *filePath in paths) {
+            success &= [zipArchive writeFile:filePath withPassword:password];
+        }
+        success &= [zipArchive close];
+    }
+    return success;
+}
+
++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath withPassword:(nullable NSString *)password {
+    return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:NO withPassword:password];
+}
+
+
++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory withPassword:(nullable NSString *)password {
+    return [SSZipArchive createZipFileAtPath:path
+                     withContentsOfDirectory:directoryPath
+                         keepParentDirectory:keepParentDirectory
+                                withPassword:password
+                          andProgressHandler:nil
+            ];
+}
+
++ (BOOL)createZipFileAtPath:(NSString *)path
+    withContentsOfDirectory:(NSString *)directoryPath
+        keepParentDirectory:(BOOL)keepParentDirectory
+               withPassword:(nullable NSString *)password
+         andProgressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler {
+    return [self createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:keepParentDirectory compressionLevel:Z_DEFAULT_COMPRESSION password:password AES:YES progressHandler:progressHandler];
+}
+
++ (BOOL)createZipFileAtPath:(NSString *)path
+    withContentsOfDirectory:(NSString *)directoryPath
+        keepParentDirectory:(BOOL)keepParentDirectory
+           compressionLevel:(int)compressionLevel
+                   password:(nullable NSString *)password
+                        AES:(BOOL)aes
+            progressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler {
+    
+    SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path];
+    BOOL success = [zipArchive open];
+    if (success) {
+        // use a local fileManager (queue/thread compatibility)
+        NSFileManager *fileManager = [[NSFileManager alloc] init];
+        NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtPath:directoryPath];
+        NSArray<NSString *> *allObjects = dirEnumerator.allObjects;
+        NSUInteger total = allObjects.count, complete = 0;
+        NSString *fileName;
+        for (fileName in allObjects) {
+            BOOL isDir;
+            NSString *fullFilePath = [directoryPath stringByAppendingPathComponent:fileName];
+            [fileManager fileExistsAtPath:fullFilePath isDirectory:&isDir];
+            
+            if (keepParentDirectory)
+            {
+                fileName = [directoryPath.lastPathComponent stringByAppendingPathComponent:fileName];
+            }
+            
+            if (!isDir) {
+                success &= [zipArchive writeFileAtPath:fullFilePath withFileName:fileName compressionLevel:compressionLevel password:password AES:aes];
+            }
+            else
+            {
+                if ([[NSFileManager defaultManager] subpathsOfDirectoryAtPath:fullFilePath error:nil].count == 0)
+                {
+                    success &= [zipArchive writeFolderAtPath:fullFilePath withFolderName:fileName withPassword:password];
+                }
+            }
+            complete++;
+            if (progressHandler) {
+                progressHandler(complete, total);
+            }
+        }
+        success &= [zipArchive close];
+    }
+    return success;
+}
+
+// disabling `init` because designated initializer is `initWithPath:`
+- (instancetype)init { @throw nil; }
+
+// designated initializer
+- (instancetype)initWithPath:(NSString *)path
+{
+    if ((self = [super init])) {
+        _path = [path copy];
+    }
+    return self;
+}
+
+
+- (BOOL)open
+{
+    NSAssert((_zip == NULL), @"Attempting to open an archive which is already open");
+    _zip = zipOpen(_path.fileSystemRepresentation, APPEND_STATUS_CREATE);
+    return (NULL != _zip);
+}
+
+- (BOOL)writeFolderAtPath:(NSString *)path withFolderName:(NSString *)folderName withPassword:(nullable NSString *)password
+{
+    NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened");
+    
+    zip_fileinfo zipInfo = {};
+    
+    [SSZipArchive zipInfo:&zipInfo setAttributesOfItemAtPath:path];
+    
+    int error = _zipOpenEntry(_zip, [folderName stringByAppendingString:@"/"], &zipInfo, Z_NO_COMPRESSION, password, 0);
+    const void *buffer = NULL;
+    zipWriteInFileInZip(_zip, buffer, 0);
+    zipCloseFileInZip(_zip);
+    return error == ZIP_OK;
+}
+
+- (BOOL)writeFile:(NSString *)path withPassword:(nullable NSString *)password
+{
+    return [self writeFileAtPath:path withFileName:nil withPassword:password];
+}
+
+- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName withPassword:(nullable NSString *)password
+{
+    return [self writeFileAtPath:path withFileName:fileName compressionLevel:Z_DEFAULT_COMPRESSION password:password AES:YES];
+}
+
+// supports writing files with logical folder/directory structure
+// *path* is the absolute path of the file that will be compressed
+// *fileName* is the relative name of the file how it is stored within the zip e.g. /folder/subfolder/text1.txt
+- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes
+{
+    NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened");
+    
+    FILE *input = fopen(path.fileSystemRepresentation, "r");
+    if (NULL == input) {
+        return NO;
+    }
+    
+    if (!fileName) {
+        fileName = path.lastPathComponent;
+    }
+    
+    zip_fileinfo zipInfo = {};
+    
+    [SSZipArchive zipInfo:&zipInfo setAttributesOfItemAtPath:path];
+    
+    void *buffer = malloc(CHUNK);
+    if (buffer == NULL)
+    {
+        fclose(input);
+        return NO;
+    }
+    
+    int error = _zipOpenEntry(_zip, fileName, &zipInfo, compressionLevel, password, aes);
+    
+    while (!feof(input) && !ferror(input))
+    {
+        unsigned int len = (unsigned int) fread(buffer, 1, CHUNK, input);
+        zipWriteInFileInZip(_zip, buffer, len);
+    }
+    
+    zipCloseFileInZip(_zip);
+    free(buffer);
+    fclose(input);
+    return error == ZIP_OK;
+}
+
+- (BOOL)writeData:(NSData *)data filename:(nullable NSString *)filename withPassword:(nullable NSString *)password
+{
+    return [self writeData:data filename:filename compressionLevel:Z_DEFAULT_COMPRESSION password:password AES:YES];
+}
+
+- (BOOL)writeData:(NSData *)data filename:(nullable NSString *)filename compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes
+{
+    if (!_zip) {
+        return NO;
+    }
+    if (!data) {
+        return NO;
+    }
+    zip_fileinfo zipInfo = {};
+    [SSZipArchive zipInfo:&zipInfo setDate:[NSDate date]];
+    
+    int error = _zipOpenEntry(_zip, filename, &zipInfo, compressionLevel, password, aes);
+    
+    zipWriteInFileInZip(_zip, data.bytes, (unsigned int)data.length);
+    
+    zipCloseFileInZip(_zip);
+    return error == ZIP_OK;
+}
+
+- (BOOL)close
+{
+    NSAssert((_zip != NULL), @"[SSZipArchive] Attempting to close an archive which was never opened");
+    int error = zipClose(_zip, NULL);
+    _zip = nil;
+    return error == UNZ_OK;
+}
+
+#pragma mark - Private
+
++ (NSString *)_filenameStringWithCString:(const char *)filename size:(uint16_t)size_filename
+{
+    NSString * strPath = @(filename);
+    if (strPath) {
+        return strPath;
+    }
+    // if filename is non-unicode, detect and transform Encoding
+    NSData *data = [NSData dataWithBytes:(const void *)filename length:sizeof(unsigned char) * size_filename];
+#ifdef __MAC_10_13
+    // Xcode 9+
+    if (@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)) {
+        // supported encodings are in [NSString availableStringEncodings]
+        [NSString stringEncodingForData:data encodingOptions:nil convertedString:&strPath usedLossyConversion:nil];
+    }
+#else
+    // Xcode 8-
+    if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_9_2) {
+        // supported encodings are in [NSString availableStringEncodings]
+        [NSString stringEncodingForData:data encodingOptions:nil convertedString:&strPath usedLossyConversion:nil];
+    }
+#endif
+    else {
+        // fallback to a simple manual detect for macOS 10.9 or older
+        NSArray<NSNumber *> *encodings = @[@(kCFStringEncodingGB_18030_2000), @(kCFStringEncodingShiftJIS)];
+        for (NSNumber *encoding in encodings) {
+            strPath = [NSString stringWithCString:filename encoding:(NSStringEncoding)CFStringConvertEncodingToNSStringEncoding(encoding.unsignedIntValue)];
+            if (strPath) {
+                break;
+            }
+        }
+    }
+    if (!strPath) {
+        // if filename encoding is non-detected, we default to something based on data
+        // _hexString is more readable than _base64RFC4648 for debugging unknown encodings
+        strPath = [data _hexString];
+    }
+    return strPath;
+}
+
++ (void)zipInfo:(zip_fileinfo *)zipInfo setAttributesOfItemAtPath:(NSString *)path
+{
+    NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:path error: nil];
+    if (attr)
+    {
+        NSDate *fileDate = (NSDate *)attr[NSFileModificationDate];
+        if (fileDate)
+        {
+            [self zipInfo:zipInfo setDate:fileDate];
+        }
+        
+        // Write permissions into the external attributes, for details on this see here: http://unix.stackexchange.com/a/14727
+        // Get the permissions value from the files attributes
+        NSNumber *permissionsValue = (NSNumber *)attr[NSFilePosixPermissions];
+        if (permissionsValue != nil) {
+            // Get the short value for the permissions
+            short permissionsShort = permissionsValue.shortValue;
+            
+            // Convert this into an octal by adding 010000, 010000 being the flag for a regular file
+            NSInteger permissionsOctal = 0100000 + permissionsShort;
+            
+            // Convert this into a long value
+            uLong permissionsLong = @(permissionsOctal).unsignedLongValue;
+            
+            // Store this into the external file attributes once it has been shifted 16 places left to form part of the second from last byte
+            
+            // Casted back to an unsigned int to match type of external_fa in minizip
+            zipInfo->external_fa = (unsigned int)(permissionsLong << 16L);
+        }
+    }
+}
+
++ (void)zipInfo:(zip_fileinfo *)zipInfo setDate:(NSDate *)date
+{
+    NSCalendar *currentCalendar = SSZipArchive._gregorian;
+    NSCalendarUnit flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
+    NSDateComponents *components = [currentCalendar components:flags fromDate:date];
+    struct tm tmz_date;
+    tmz_date.tm_sec = (unsigned int)components.second;
+    tmz_date.tm_min = (unsigned int)components.minute;
+    tmz_date.tm_hour = (unsigned int)components.hour;
+    tmz_date.tm_mday = (unsigned int)components.day;
+    // ISO/IEC 9899 struct tm is 0-indexed for January but NSDateComponents for gregorianCalendar is 1-indexed for January
+    tmz_date.tm_mon = (unsigned int)components.month - 1;
+    // ISO/IEC 9899 struct tm is 0-indexed for AD 1900 but NSDateComponents for gregorianCalendar is 1-indexed for AD 1
+    tmz_date.tm_year = (unsigned int)components.year - 1900;
+    zipInfo->dos_date = tm_to_dosdate(&tmz_date);
+}
+
++ (NSCalendar *)_gregorian
+{
+    static NSCalendar *gregorian;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
+    });
+    
+    return gregorian;
+}
+
+// Format from http://newsgroups.derkeiler.com/Archive/Comp/comp.os.msdos.programmer/2009-04/msg00060.html
+// Two consecutive words, or a longword, YYYYYYYMMMMDDDDD hhhhhmmmmmmsssss
+// YYYYYYY is years from 1980 = 0
+// sssss is (seconds/2).
+//
+// 3658 = 0011 0110 0101 1000 = 0011011 0010 11000 = 27 2 24 = 2007-02-24
+// 7423 = 0111 0100 0010 0011 - 01110 100001 00011 = 14 33 3 = 14:33:06
++ (NSDate *)_dateWithMSDOSFormat:(UInt32)msdosDateTime
+{
+    // the whole `_dateWithMSDOSFormat:` method is equivalent but faster than this one line,
+    // essentially because `mktime` is slow:
+    //NSDate *date = [NSDate dateWithTimeIntervalSince1970:dosdate_to_time_t(msdosDateTime)];
+    static const UInt32 kYearMask = 0xFE000000;
+    static const UInt32 kMonthMask = 0x1E00000;
+    static const UInt32 kDayMask = 0x1F0000;
+    static const UInt32 kHourMask = 0xF800;
+    static const UInt32 kMinuteMask = 0x7E0;
+    static const UInt32 kSecondMask = 0x1F;
+    
+    NSAssert(0xFFFFFFFF == (kYearMask | kMonthMask | kDayMask | kHourMask | kMinuteMask | kSecondMask), @"[SSZipArchive] MSDOS date masks don't add up");
+    
+    NSDateComponents *components = [[NSDateComponents alloc] init];
+    components.year = 1980 + ((msdosDateTime & kYearMask) >> 25);
+    components.month = (msdosDateTime & kMonthMask) >> 21;
+    components.day = (msdosDateTime & kDayMask) >> 16;
+    components.hour = (msdosDateTime & kHourMask) >> 11;
+    components.minute = (msdosDateTime & kMinuteMask) >> 5;
+    components.second = (msdosDateTime & kSecondMask) * 2;
+    
+    NSDate *date = [self._gregorian dateFromComponents:components];
+    return date;
+}
+
+@end
+
+int _zipOpenEntry(zipFile entry, NSString *name, const zip_fileinfo *zipfi, int level, NSString *password, BOOL aes)
+{
+    return zipOpenNewFileInZip5(entry, name.fileSystemRepresentation, zipfi, NULL, 0, NULL, 0, NULL, 0, 0, Z_DEFLATED, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, password.UTF8String, aes);
+}
+
+#pragma mark - Private tools for file info
+
+BOOL _fileIsSymbolicLink(const unz_file_info *fileInfo)
+{
+    //
+    // Determine whether this is a symbolic link:
+    // - File is stored with 'version made by' value of UNIX (3),
+    //   as per http://www.pkware.com/documents/casestudies/APPNOTE.TXT
+    //   in the upper byte of the version field.
+    // - BSD4.4 st_mode constants are stored in the high 16 bits of the
+    //   external file attributes (defacto standard, verified against libarchive)
+    //
+    // The original constants can be found here:
+    //    http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.4BSD/usr/include/sys/stat.h
+    //
+    const uLong ZipUNIXVersion = 3;
+    const uLong BSD_SFMT = 0170000;
+    const uLong BSD_IFLNK = 0120000;
+    
+    BOOL fileIsSymbolicLink = ((fileInfo->version >> 8) == ZipUNIXVersion) && BSD_IFLNK == (BSD_SFMT & (fileInfo->external_fa >> 16));
+    return fileIsSymbolicLink;
+}
+
+#pragma mark - Private tools for unreadable encodings
+
+@implementation NSData (SSZipArchive)
+
+// `base64EncodedStringWithOptions` uses a base64 alphabet with '+' and '/'.
+// we got those alternatives to make it compatible with filenames: https://en.wikipedia.org/wiki/Base64
+// * modified Base64 encoding for IMAP mailbox names (RFC 3501): uses '+' and ','
+// * modified Base64 for URL and filenames (RFC 4648): uses '-' and '_'
+- (NSString *)_base64RFC4648
+{
+    NSString *strName = [self base64EncodedStringWithOptions:0];
+    strName = [strName stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
+    strName = [strName stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
+    return strName;
+}
+
+// initWithBytesNoCopy from NSProgrammer, Jan 25 '12: https://stackoverflow.com/a/9009321/1033581
+// hexChars from Peter, Aug 19 '14: https://stackoverflow.com/a/25378464/1033581
+// not implemented as too lengthy: a potential mapping improvement from Moose, Nov 3 '15: https://stackoverflow.com/a/33501154/1033581
+- (NSString *)_hexString
+{
+    const char *hexChars = "0123456789ABCDEF";
+    NSUInteger length = self.length;
+    const unsigned char *bytes = self.bytes;
+    char *chars = malloc(length * 2);
+    char *s = chars;
+    NSUInteger i = length;
+    while (i--) {
+        *s++ = hexChars[*bytes >> 4];
+        *s++ = hexChars[*bytes & 0xF];
+        bytes++;
+    }
+    NSString *str = [[NSString alloc] initWithBytesNoCopy:chars
+                                                   length:length * 2
+                                                 encoding:NSASCIIStringEncoding
+                                             freeWhenDone:YES];
+    return str;
+}
+
+@end
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/SSZipCommon.h b/iOS/Pods/SSZipArchive/SSZipArchive/SSZipCommon.h
new file mode 100644 (file)
index 0000000..75bf0fc
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef SSZipCommon
+#define SSZipCommon
+
+/* unz_global_info structure contain global data about the ZIPfile
+ These data comes from the end of central dir */
+typedef struct unz_global_info64_s
+{
+    uint64_t number_entry;          /* total number of entries in the central dir on this disk */
+    uint32_t number_disk_with_CD;   /* number the the disk with central dir, used for spanning ZIP*/
+    uint16_t size_comment;          /* size of the global comment of the zipfile */
+} unz_global_info64;
+
+typedef struct unz_global_info_s
+{
+    uint32_t number_entry;          /* total number of entries in the central dir on this disk */
+    uint32_t number_disk_with_CD;   /* number the the disk with central dir, used for spanning ZIP*/
+    uint16_t size_comment;          /* size of the global comment of the zipfile */
+} unz_global_info;
+
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_info64_s
+{
+    uint16_t version;               /* version made by                 2 bytes */
+    uint16_t version_needed;        /* version needed to extract       2 bytes */
+    uint16_t flag;                  /* general purpose bit flag        2 bytes */
+    uint16_t compression_method;    /* compression method              2 bytes */
+    uint32_t dos_date;              /* last mod file date in Dos fmt   4 bytes */
+    uint32_t crc;                   /* crc-32                          4 bytes */
+    uint64_t compressed_size;       /* compressed size                 8 bytes */
+    uint64_t uncompressed_size;     /* uncompressed size               8 bytes */
+    uint16_t size_filename;         /* filename length                 2 bytes */
+    uint16_t size_file_extra;       /* extra field length              2 bytes */
+    uint16_t size_file_comment;     /* file comment length             2 bytes */
+    
+    uint32_t disk_num_start;        /* disk number start               4 bytes */
+    uint16_t internal_fa;           /* internal file attributes        2 bytes */
+    uint32_t external_fa;           /* external file attributes        4 bytes */
+    
+    uint64_t disk_offset;
+    
+    uint16_t size_file_extra_internal;
+} unz_file_info64;
+
+typedef struct unz_file_info_s
+{
+    uint16_t version;               /* version made by                 2 bytes */
+    uint16_t version_needed;        /* version needed to extract       2 bytes */
+    uint16_t flag;                  /* general purpose bit flag        2 bytes */
+    uint16_t compression_method;    /* compression method              2 bytes */
+    uint32_t dos_date;              /* last mod file date in Dos fmt   4 bytes */
+    uint32_t crc;                   /* crc-32                          4 bytes */
+    uint32_t compressed_size;       /* compressed size                 4 bytes */
+    uint32_t uncompressed_size;     /* uncompressed size               4 bytes */
+    uint16_t size_filename;         /* filename length                 2 bytes */
+    uint16_t size_file_extra;       /* extra field length              2 bytes */
+    uint16_t size_file_comment;     /* file comment length             2 bytes */
+    
+    uint16_t disk_num_start;        /* disk number start               2 bytes */
+    uint16_t internal_fa;           /* internal file attributes        2 bytes */
+    uint32_t external_fa;           /* external file attributes        4 bytes */
+    
+    uint64_t disk_offset;
+} unz_file_info;
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/ZipArchive.h b/iOS/Pods/SSZipArchive/SSZipArchive/ZipArchive.h
new file mode 100644 (file)
index 0000000..7a53df8
--- /dev/null
@@ -0,0 +1,19 @@
+//
+//  ZipArchive.h
+//  ZipArchive
+//
+//  Created by Serhii Mumriak on 12/1/15.
+//  Copyright © 2015 smumryak. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for ZipArchive.
+FOUNDATION_EXPORT double ZipArchiveVersionNumber;
+
+//! Project version string for ZipArchive.
+FOUNDATION_EXPORT const unsigned char ZipArchiveVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <ZipArchive/SSZipArchive.h>
+
+#import "SSZipArchive.h"
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aes.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aes.h
new file mode 100644 (file)
index 0000000..92e23c2
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+
+ This file contains the definitions required to use AES in C. See aesopt.h
+ for optimisation details.
+*/
+
+#ifndef _AES_H
+#define _AES_H
+
+#include <stdlib.h>
+
+/*  This include is used to find 8 & 32 bit unsigned integer types  */
+#include "brg_types.h"
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#define AES_128     /* if a fast 128 bit key scheduler is needed    */
+#define AES_192     /* if a fast 192 bit key scheduler is needed    */
+#define AES_256     /* if a fast 256 bit key scheduler is needed    */
+#define AES_VAR     /* if variable key size scheduler is needed     */
+#define AES_MODES   /* if support is needed for modes               */
+
+/* The following must also be set in assembler files if being used  */
+
+#define AES_ENCRYPT /* if support for encryption is needed          */
+#define AES_DECRYPT /* if support for decryption is needed          */
+
+#define AES_BLOCK_SIZE  16  /* the AES block size in bytes          */
+#define N_COLS           4  /* the number of columns in the state   */
+
+/* The key schedule length is 11, 13 or 15 16-byte blocks for 128,  */
+/* 192 or 256-bit keys respectively. That is 176, 208 or 240 bytes  */
+/* or 44, 52 or 60 32-bit words.                                    */
+
+#if defined( AES_VAR ) || defined( AES_256 )
+#define KS_LENGTH       60
+#elif defined( AES_192 )
+#define KS_LENGTH       52
+#else
+#define KS_LENGTH       44
+#endif
+
+#define AES_RETURN INT_RETURN
+
+/* the character array 'inf' in the following structures is used    */
+/* to hold AES context information. This AES code uses cx->inf.b[0] */
+/* to hold the number of rounds multiplied by 16. The other three   */
+/* elements can be used by code that implements additional modes    */
+
+typedef union
+{   uint32_t l;
+    uint8_t b[4];
+} aes_inf;
+
+#ifdef _MSC_VER
+#  pragma warning( disable : 4324 )
+#endif
+
+#if defined(_MSC_VER) && defined(_WIN64)
+#define ALIGNED_(x) __declspec(align(x))
+#elif defined(__GNUC__) && defined(__x86_64__)
+#define ALIGNED_(x) __attribute__ ((aligned(x)))
+#else
+#define ALIGNED_(x)
+#endif
+
+typedef struct ALIGNED_(16)
+{   uint32_t ks[KS_LENGTH];
+    aes_inf inf;
+} aes_encrypt_ctx;
+
+typedef struct ALIGNED_(16)
+{   uint32_t ks[KS_LENGTH];
+    aes_inf inf;
+} aes_decrypt_ctx;
+
+#ifdef _MSC_VER
+#  pragma warning( default : 4324 )
+#endif
+
+/* This routine must be called before first use if non-static       */
+/* tables are being used                                            */
+
+AES_RETURN aes_init(void);
+
+/* Key lengths in the range 16 <= key_len <= 32 are given in bytes, */
+/* those in the range 128 <= key_len <= 256 are given in bits       */
+
+#if defined( AES_ENCRYPT )
+
+#if defined( AES_128 ) || defined( AES_VAR)
+AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]);
+#endif
+
+#if defined( AES_192 ) || defined( AES_VAR)
+AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]);
+#endif
+
+#if defined( AES_256 ) || defined( AES_VAR)
+AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]);
+#endif
+
+#if defined( AES_VAR )
+AES_RETURN aes_encrypt_key(const unsigned char *key, int key_len, aes_encrypt_ctx cx[1]);
+#endif
+
+AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]);
+
+#endif
+
+#if defined( AES_DECRYPT )
+
+#if defined( AES_128 ) || defined( AES_VAR)
+AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]);
+#endif
+
+#if defined( AES_192 ) || defined( AES_VAR)
+AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]);
+#endif
+
+#if defined( AES_256 ) || defined( AES_VAR)
+AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]);
+#endif
+
+#if defined( AES_VAR )
+AES_RETURN aes_decrypt_key(const unsigned char *key, int key_len, aes_decrypt_ctx cx[1]);
+#endif
+
+AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]);
+
+#endif
+
+#if defined( AES_MODES )
+
+/* Multiple calls to the following subroutines for multiple block   */
+/* ECB, CBC, CFB, OFB and CTR mode encryption can be used to handle */
+/* long messages incrementally provided that the context AND the iv */
+/* are preserved between all such calls.  For the ECB and CBC modes */
+/* each individual call within a series of incremental calls must   */
+/* process only full blocks (i.e. len must be a multiple of 16) but */
+/* the CFB, OFB and CTR mode calls can handle multiple incremental  */
+/* calls of any length.  Each mode is reset when a new AES key is   */
+/* set but ECB needs no reset and CBC can be reset without setting  */
+/* a new key by setting a new IV value.  To reset CFB, OFB and CTR  */
+/* without setting the key, aes_mode_reset() must be called and the */
+/* IV must be set.  NOTE: All these calls update the IV on exit so  */
+/* this has to be reset if a new operation with the same IV as the  */
+/* previous one is required (or decryption follows encryption with  */
+/* the same IV array).                                              */
+
+AES_RETURN aes_test_alignment_detection(unsigned int n);
+
+AES_RETURN aes_ecb_encrypt(const unsigned char *ibuf, unsigned char *obuf,
+                    int len, const aes_encrypt_ctx cx[1]);
+
+AES_RETURN aes_ecb_decrypt(const unsigned char *ibuf, unsigned char *obuf,
+                    int len, const aes_decrypt_ctx cx[1]);
+
+AES_RETURN aes_cbc_encrypt(const unsigned char *ibuf, unsigned char *obuf,
+                    int len, unsigned char *iv, const aes_encrypt_ctx cx[1]);
+
+AES_RETURN aes_cbc_decrypt(const unsigned char *ibuf, unsigned char *obuf,
+                    int len, unsigned char *iv, const aes_decrypt_ctx cx[1]);
+
+AES_RETURN aes_mode_reset(aes_encrypt_ctx cx[1]);
+
+AES_RETURN aes_cfb_encrypt(const unsigned char *ibuf, unsigned char *obuf,
+                    int len, unsigned char *iv, aes_encrypt_ctx cx[1]);
+
+AES_RETURN aes_cfb_decrypt(const unsigned char *ibuf, unsigned char *obuf,
+                    int len, unsigned char *iv, aes_encrypt_ctx cx[1]);
+
+#define aes_ofb_encrypt aes_ofb_crypt
+#define aes_ofb_decrypt aes_ofb_crypt
+
+AES_RETURN aes_ofb_crypt(const unsigned char *ibuf, unsigned char *obuf,
+                    int len, unsigned char *iv, aes_encrypt_ctx cx[1]);
+
+typedef void cbuf_inc(unsigned char *cbuf);
+
+#define aes_ctr_encrypt aes_ctr_crypt
+#define aes_ctr_decrypt aes_ctr_crypt
+
+AES_RETURN aes_ctr_crypt(const unsigned char *ibuf, unsigned char *obuf,
+            int len, unsigned char *cbuf, cbuf_inc ctr_inc, aes_encrypt_ctx cx[1]);
+
+#endif
+
+#if 0
+#  define ADD_AESNI_MODE_CALLS
+#endif
+
+#if 0 && defined( ADD_AESNI_MODE_CALLS )
+#  define USE_AES_CONTEXT
+#endif
+
+#ifdef ADD_AESNI_MODE_CALLS
+#  ifdef USE_AES_CONTEXT
+
+AES_RETURN aes_CBC_encrypt(const unsigned char *in,
+    unsigned char *out,
+    unsigned char ivec[16],
+    unsigned long length,
+    const aes_encrypt_ctx cx[1]);
+
+AES_RETURN aes_CBC_decrypt(const unsigned char *in,
+    unsigned char *out,
+    unsigned char ivec[16],
+    unsigned long length,
+    const aes_decrypt_ctx cx[1]);
+
+AES_RETURN AES_CTR_encrypt(const unsigned char *in,
+    unsigned char *out,
+    const unsigned char ivec[8],
+    const unsigned char nonce[4],
+    unsigned long length,
+    const aes_encrypt_ctx cx[1]);
+
+#  else
+
+void aes_CBC_encrypt(const unsigned char *in,
+    unsigned char *out,
+    unsigned char ivec[16],
+    unsigned long length,
+    unsigned char *key,
+    int number_of_rounds);
+
+void aes_CBC_decrypt(const unsigned char *in,
+    unsigned char *out,
+    unsigned char ivec[16],
+    unsigned long length,
+    unsigned char *key,
+    int number_of_rounds);
+
+void AES_CTR_encrypt(const unsigned char *in,
+    unsigned char *out,
+    const unsigned char ivec[8],
+    const unsigned char nonce[4],
+    unsigned long length,
+    const unsigned char *key,
+    int number_of_rounds);
+
+#  endif
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aes_ni.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aes_ni.c
new file mode 100644 (file)
index 0000000..8afae91
--- /dev/null
@@ -0,0 +1,687 @@
+/*
+Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 09/09/2014
+*/
+
+#include "aes_ni.h"
+
+#if defined( USE_INTEL_AES_IF_PRESENT )
+
+#if defined(_MSC_VER)
+
+#include <intrin.h>
+#pragma intrinsic(__cpuid)
+#define INLINE  __inline
+
+INLINE int has_aes_ni(void)
+{
+       static int test = -1;
+       if(test < 0)
+       {
+        int cpu_info[4];
+        __cpuid(cpu_info, 1);
+               test = cpu_info[2] & 0x02000000;
+       }
+       return test;
+}
+
+#elif defined( __GNUC__ )
+
+#include <cpuid.h>
+
+#if !defined(__clang__)
+#pragma GCC target ("ssse3")
+#pragma GCC target ("sse4.1")
+#pragma GCC target ("aes")
+#endif
+
+#include <x86intrin.h>
+#define INLINE  static __inline
+
+INLINE int has_aes_ni()
+{
+    static int test = -1;
+    if(test < 0)
+    {
+        unsigned int a, b, c, d;
+        if(!__get_cpuid(1, &a, &b, &c, &d))
+            test = 0;
+        else
+            test = (c & 0x2000000);
+    }
+    return test;
+}
+
+#else
+#error AES New Instructions require Microsoft, Intel, GNU C, or CLANG
+#endif
+
+INLINE __m128i aes_128_assist(__m128i t1, __m128i t2)
+{
+       __m128i t3;
+       t2 = _mm_shuffle_epi32(t2, 0xff);
+       t3 = _mm_slli_si128(t1, 0x4);
+       t1 = _mm_xor_si128(t1, t3);
+       t3 = _mm_slli_si128(t3, 0x4);
+       t1 = _mm_xor_si128(t1, t3);
+       t3 = _mm_slli_si128(t3, 0x4);
+       t1 = _mm_xor_si128(t1, t3);
+       t1 = _mm_xor_si128(t1, t2);
+       return t1;
+}
+
+AES_RETURN aes_ni(encrypt_key128)(const unsigned char *key, aes_encrypt_ctx cx[1])
+{
+       __m128i t1, t2;
+       __m128i *ks = (__m128i*)cx->ks;
+
+       if(!has_aes_ni())
+       {
+               return aes_xi(encrypt_key128)(key, cx);
+       }
+
+       t1 = _mm_loadu_si128((__m128i*)key);
+
+       ks[0] = t1;
+
+       t2 = _mm_aeskeygenassist_si128(t1, 0x1);
+       t1 = aes_128_assist(t1, t2);
+       ks[1] = t1;
+
+       t2 = _mm_aeskeygenassist_si128(t1, 0x2);
+       t1 = aes_128_assist(t1, t2);
+       ks[2] = t1;
+
+       t2 = _mm_aeskeygenassist_si128(t1, 0x4);
+       t1 = aes_128_assist(t1, t2);
+       ks[3] = t1;
+
+       t2 = _mm_aeskeygenassist_si128(t1, 0x8);
+       t1 = aes_128_assist(t1, t2);
+       ks[4] = t1;
+
+       t2 = _mm_aeskeygenassist_si128(t1, 0x10);
+       t1 = aes_128_assist(t1, t2);
+       ks[5] = t1;
+
+       t2 = _mm_aeskeygenassist_si128(t1, 0x20);
+       t1 = aes_128_assist(t1, t2);
+       ks[6] = t1;
+
+       t2 = _mm_aeskeygenassist_si128(t1, 0x40);
+       t1 = aes_128_assist(t1, t2);
+       ks[7] = t1;
+
+       t2 = _mm_aeskeygenassist_si128(t1, 0x80);
+       t1 = aes_128_assist(t1, t2);
+       ks[8] = t1;
+
+       t2 = _mm_aeskeygenassist_si128(t1, 0x1b);
+       t1 = aes_128_assist(t1, t2);
+       ks[9] = t1;
+
+       t2 = _mm_aeskeygenassist_si128(t1, 0x36);
+       t1 = aes_128_assist(t1, t2);
+       ks[10] = t1;
+
+       cx->inf.l = 0;
+       cx->inf.b[0] = 10 * 16;
+       return EXIT_SUCCESS;
+}
+
+INLINE void aes_192_assist(__m128i* t1, __m128i * t2, __m128i * t3)
+{
+       __m128i t4;
+       *t2 = _mm_shuffle_epi32(*t2, 0x55);
+       t4 = _mm_slli_si128(*t1, 0x4);
+       *t1 = _mm_xor_si128(*t1, t4);
+       t4 = _mm_slli_si128(t4, 0x4);
+       *t1 = _mm_xor_si128(*t1, t4);
+       t4 = _mm_slli_si128(t4, 0x4);
+       *t1 = _mm_xor_si128(*t1, t4);
+       *t1 = _mm_xor_si128(*t1, *t2);
+       *t2 = _mm_shuffle_epi32(*t1, 0xff);
+       t4 = _mm_slli_si128(*t3, 0x4);
+       *t3 = _mm_xor_si128(*t3, t4);
+       *t3 = _mm_xor_si128(*t3, *t2);
+}
+
+AES_RETURN aes_ni(encrypt_key192)(const unsigned char *key, aes_encrypt_ctx cx[1])
+{
+       __m128i t1, t2, t3;
+       __m128i *ks = (__m128i*)cx->ks;
+
+       if(!has_aes_ni())
+       {
+               return aes_xi(encrypt_key192)(key, cx);
+       }
+
+       t1 = _mm_loadu_si128((__m128i*)key);
+       t3 = _mm_loadu_si128((__m128i*)(key + 16));
+
+       ks[0] = t1;
+       ks[1] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x1);
+       aes_192_assist(&t1, &t2, &t3);
+
+       ks[1] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(ks[1]), _mm_castsi128_pd(t1), 0));
+       ks[2] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(t1), _mm_castsi128_pd(t3), 1));
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x2);
+       aes_192_assist(&t1, &t2, &t3);
+       ks[3] = t1;
+       ks[4] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x4);
+       aes_192_assist(&t1, &t2, &t3);
+       ks[4] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(ks[4]), _mm_castsi128_pd(t1), 0));
+       ks[5] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(t1), _mm_castsi128_pd(t3), 1));
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x8);
+       aes_192_assist(&t1, &t2, &t3);
+       ks[6] = t1;
+       ks[7] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x10);
+       aes_192_assist(&t1, &t2, &t3);
+       ks[7] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(ks[7]), _mm_castsi128_pd(t1), 0));
+       ks[8] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(t1), _mm_castsi128_pd(t3), 1));
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x20);
+       aes_192_assist(&t1, &t2, &t3);
+       ks[9] = t1;
+       ks[10] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x40);
+       aes_192_assist(&t1, &t2, &t3);
+       ks[10] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(ks[10]), _mm_castsi128_pd(t1), 0));
+       ks[11] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(t1), _mm_castsi128_pd(t3), 1));
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x80);
+       aes_192_assist(&t1, &t2, &t3);
+       ks[12] = t1;
+
+       cx->inf.l = 0;
+       cx->inf.b[0] = 12 * 16;
+       return EXIT_SUCCESS;
+}
+
+INLINE void aes_256_assist1(__m128i* t1, __m128i * t2)
+{
+       __m128i t4;
+       *t2 = _mm_shuffle_epi32(*t2, 0xff);
+       t4 = _mm_slli_si128(*t1, 0x4);
+       *t1 = _mm_xor_si128(*t1, t4);
+       t4 = _mm_slli_si128(t4, 0x4);
+       *t1 = _mm_xor_si128(*t1, t4);
+       t4 = _mm_slli_si128(t4, 0x4);
+       *t1 = _mm_xor_si128(*t1, t4);
+       *t1 = _mm_xor_si128(*t1, *t2);
+}
+
+INLINE void aes_256_assist2(__m128i* t1, __m128i * t3)
+{
+       __m128i t2, t4;
+       t4 = _mm_aeskeygenassist_si128(*t1, 0x0);
+       t2 = _mm_shuffle_epi32(t4, 0xaa);
+       t4 = _mm_slli_si128(*t3, 0x4);
+       *t3 = _mm_xor_si128(*t3, t4);
+       t4 = _mm_slli_si128(t4, 0x4);
+       *t3 = _mm_xor_si128(*t3, t4);
+       t4 = _mm_slli_si128(t4, 0x4);
+       *t3 = _mm_xor_si128(*t3, t4);
+       *t3 = _mm_xor_si128(*t3, t2);
+}
+
+AES_RETURN aes_ni(encrypt_key256)(const unsigned char *key, aes_encrypt_ctx cx[1])
+{
+       __m128i t1, t2, t3;
+       __m128i *ks = (__m128i*)cx->ks;
+
+       if(!has_aes_ni())
+       {
+               return aes_xi(encrypt_key256)(key, cx);
+       }
+
+       t1 = _mm_loadu_si128((__m128i*)key);
+       t3 = _mm_loadu_si128((__m128i*)(key + 16));
+
+       ks[0] = t1;
+       ks[1] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x01);
+       aes_256_assist1(&t1, &t2);
+       ks[2] = t1;
+       aes_256_assist2(&t1, &t3);
+       ks[3] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x02);
+       aes_256_assist1(&t1, &t2);
+       ks[4] = t1;
+       aes_256_assist2(&t1, &t3);
+       ks[5] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x04);
+       aes_256_assist1(&t1, &t2);
+       ks[6] = t1;
+       aes_256_assist2(&t1, &t3);
+       ks[7] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x08);
+       aes_256_assist1(&t1, &t2);
+       ks[8] = t1;
+       aes_256_assist2(&t1, &t3);
+       ks[9] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x10);
+       aes_256_assist1(&t1, &t2);
+       ks[10] = t1;
+       aes_256_assist2(&t1, &t3);
+       ks[11] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x20);
+       aes_256_assist1(&t1, &t2);
+       ks[12] = t1;
+       aes_256_assist2(&t1, &t3);
+       ks[13] = t3;
+
+       t2 = _mm_aeskeygenassist_si128(t3, 0x40);
+       aes_256_assist1(&t1, &t2);
+       ks[14] = t1;
+
+       cx->inf.l = 0;
+       cx->inf.b[0] = 14 * 16;
+       return EXIT_SUCCESS;
+}
+
+INLINE void enc_to_dec(aes_decrypt_ctx cx[1])
+{
+       __m128i *ks = (__m128i*)cx->ks;
+       int j;
+
+       for( j = 1 ; j < (cx->inf.b[0] >> 4) ; ++j )
+               ks[j] = _mm_aesimc_si128(ks[j]);
+}
+
+AES_RETURN aes_ni(decrypt_key128)(const unsigned char *key, aes_decrypt_ctx cx[1])
+{
+       if(!has_aes_ni())
+       {
+               return aes_xi(decrypt_key128)(key, cx);
+       }
+
+       if(aes_ni(encrypt_key128)(key, (aes_encrypt_ctx*)cx) == EXIT_SUCCESS)
+       {
+               enc_to_dec(cx);
+               return EXIT_SUCCESS;
+       }
+       else
+               return EXIT_FAILURE;
+
+}
+
+AES_RETURN aes_ni(decrypt_key192)(const unsigned char *key, aes_decrypt_ctx cx[1])
+{
+       if(!has_aes_ni())
+       {
+               return aes_xi(decrypt_key192)(key, cx);
+       }
+
+       if(aes_ni(encrypt_key192)(key, (aes_encrypt_ctx*)cx) == EXIT_SUCCESS)
+       {
+               enc_to_dec(cx);
+               return EXIT_SUCCESS;
+       }
+       else
+               return EXIT_FAILURE;
+}
+
+AES_RETURN aes_ni(decrypt_key256)(const unsigned char *key, aes_decrypt_ctx cx[1])
+{
+       if(!has_aes_ni())
+       {
+               return aes_xi(decrypt_key256)(key, cx);
+       }
+
+       if(aes_ni(encrypt_key256)(key, (aes_encrypt_ctx*)cx) == EXIT_SUCCESS)
+       {
+               enc_to_dec(cx);
+               return EXIT_SUCCESS;
+       }
+       else
+               return EXIT_FAILURE;
+}
+
+AES_RETURN aes_ni(encrypt)(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1])
+{
+       __m128i *key = (__m128i*)cx->ks, t;
+
+       if(cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16)
+               return EXIT_FAILURE;
+
+       if(!has_aes_ni())
+       {
+               return aes_xi(encrypt)(in, out, cx);
+       }
+
+       t = _mm_xor_si128(_mm_loadu_si128((__m128i*)in), *(__m128i*)key);
+
+       switch(cx->inf.b[0])
+       {
+       case 14 * 16:
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+       case 12 * 16:
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+       case 10 * 16:
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenc_si128(t, *(__m128i*)++key);
+               t = _mm_aesenclast_si128(t, *(__m128i*)++key);
+       }
+
+       _mm_storeu_si128(&((__m128i*)out)[0], t);
+       return EXIT_SUCCESS;
+}
+
+AES_RETURN aes_ni(decrypt)(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1])
+{
+       __m128i *key = (__m128i*)cx->ks + (cx->inf.b[0] >> 4), t;
+
+       if(cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16)
+               return EXIT_FAILURE;
+
+       if(!has_aes_ni())
+       {
+               return aes_xi(decrypt)(in, out, cx);
+       }
+
+       t = _mm_xor_si128(_mm_loadu_si128((__m128i*)in), *(__m128i*)key);
+
+       switch(cx->inf.b[0])
+       {
+       case 14 * 16:
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+       case 12 * 16:
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+       case 10 * 16:
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdec_si128(t, *(__m128i*)--key);
+               t = _mm_aesdeclast_si128(t, *(__m128i*)--key);
+       }
+
+       _mm_storeu_si128((__m128i*)out, t);
+       return EXIT_SUCCESS;
+}
+
+#ifdef ADD_AESNI_MODE_CALLS
+#ifdef USE_AES_CONTEXT
+
+AES_RETURN aes_CBC_encrypt(const unsigned char *in,
+       unsigned char *out,
+       unsigned char ivec[16],
+       unsigned long length,
+    const aes_encrypt_ctx cx[1])
+{
+       __m128i feedback, data, *key = (__m128i*)cx->ks;
+       int number_of_rounds = cx->inf.b[0] >> 4, j;
+    unsigned long i;
+    
+    if(number_of_rounds != 10 && number_of_rounds != 12 && number_of_rounds != 14)
+        return EXIT_FAILURE;
+
+    if(!has_aes_ni())
+    {
+        return aes_cbc_encrypt(in, out, length, ivec, cx);
+    }
+
+    if(length % 16)
+               length = length / 16 + 1;
+       else length /= 16;
+       feedback = _mm_loadu_si128((__m128i*)ivec);
+       for(i = 0; i < length; i++)
+       {
+               data = _mm_loadu_si128(&((__m128i*)in)[i]);
+               feedback = _mm_xor_si128(data, feedback);
+               feedback = _mm_xor_si128(feedback, ((__m128i*)key)[0]);
+               for(j = 1; j <number_of_rounds; j++)
+                       feedback = _mm_aesenc_si128(feedback, ((__m128i*)key)[j]);
+               feedback = _mm_aesenclast_si128(feedback, ((__m128i*)key)[j]);
+               _mm_storeu_si128(&((__m128i*)out)[i], feedback);
+       }
+    return EXIT_SUCCESS;
+}
+
+AES_RETURN aes_CBC_decrypt(const unsigned char *in,
+    unsigned char *out,
+    unsigned char ivec[16],
+    unsigned long length,
+    const aes_decrypt_ctx cx[1])
+{
+    __m128i data, feedback, last_in, *key = (__m128i*)cx->ks;
+    int number_of_rounds = cx->inf.b[0] >> 4, j;
+    unsigned long i;
+
+    if(number_of_rounds != 10 && number_of_rounds != 12 && number_of_rounds != 14)
+        return EXIT_FAILURE;
+
+    if(!has_aes_ni())
+    {
+        return aes_cbc_decrypt(in, out, length, ivec, cx);
+    }
+
+    if(length % 16)
+        length = length / 16 + 1;
+    else length /= 16;
+    feedback = _mm_loadu_si128((__m128i*)ivec);
+    for(i = 0; i < length; i++)
+    {
+        last_in = _mm_loadu_si128(&((__m128i*)in)[i]);
+        data = _mm_xor_si128(last_in, ((__m128i*)key)[number_of_rounds]);
+        for(j = number_of_rounds - 1; j > 0; j--)
+        {
+            data = _mm_aesdec_si128(data, ((__m128i*)key)[j]);
+        }
+        data = _mm_aesdeclast_si128(data, ((__m128i*)key)[0]);
+        data = _mm_xor_si128(data, feedback);
+        _mm_storeu_si128(&((__m128i*)out)[i], data);
+        feedback = last_in;
+    }
+    return EXIT_SUCCESS;
+}
+
+static void ctr_inc(unsigned char *ctr_blk)
+{
+    uint32_t c;
+
+    c = *(uint32_t*)(ctr_blk + 8);
+    c++;
+    *(uint32_t*)(ctr_blk + 8) = c;
+
+    if(!c)
+        *(uint32_t*)(ctr_blk + 12) = *(uint32_t*)(ctr_blk + 12) + 1;
+}
+
+AES_RETURN AES_CTR_encrypt(const unsigned char *in,
+    unsigned char *out,
+    const unsigned char ivec[8],
+    const unsigned char nonce[4],
+    unsigned long length,
+    const aes_encrypt_ctx cx[1])
+{
+    __m128i ctr_block = { 0 }, *key = (__m128i*)cx->ks, tmp, ONE, BSWAP_EPI64;
+    int number_of_rounds = cx->inf.b[0] >> 4, j;
+    unsigned long i;
+
+    if(number_of_rounds != 10 && number_of_rounds != 12 && number_of_rounds != 14)
+        return EXIT_FAILURE;
+
+    if(!has_aes_ni())
+    {
+        unsigned char ctr_blk[16];
+        *(uint64_t*)ctr_blk = *(uint64_t*)ivec;
+        *(uint32_t*)(ctr_blk + 8) = *(uint32_t*)nonce;
+        return aes_ctr_crypt(in, out, length, (unsigned char*)ctr_blk, ctr_inc, cx);
+    }
+
+    if(length % 16)
+        length = length / 16 + 1;
+    else length /= 16;
+    ONE = _mm_set_epi32(0, 1, 0, 0);
+    BSWAP_EPI64 = _mm_setr_epi8(7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8);
+#ifdef _MSC_VER
+    ctr_block = _mm_insert_epi64(ctr_block, *(long long*)ivec, 1);
+#else
+    ctr_block = _mm_set_epi64(*(__m64*)ivec, *(__m64*)&ctr_block);
+#endif
+    ctr_block = _mm_insert_epi32(ctr_block, *(long*)nonce, 1);
+    ctr_block = _mm_srli_si128(ctr_block, 4);
+    ctr_block = _mm_shuffle_epi8(ctr_block, BSWAP_EPI64);
+    ctr_block = _mm_add_epi64(ctr_block, ONE);
+    for(i = 0; i < length; i++)
+    {
+        tmp = _mm_shuffle_epi8(ctr_block, BSWAP_EPI64);
+        ctr_block = _mm_add_epi64(ctr_block, ONE);
+        tmp = _mm_xor_si128(tmp, ((__m128i*)key)[0]);
+        for(j = 1; j <number_of_rounds; j++)
+        {
+            tmp = _mm_aesenc_si128(tmp, ((__m128i*)key)[j]);
+        };
+        tmp = _mm_aesenclast_si128(tmp, ((__m128i*)key)[j]);
+        tmp = _mm_xor_si128(tmp, _mm_loadu_si128(&((__m128i*)in)[i]));
+        _mm_storeu_si128(&((__m128i*)out)[i], tmp);
+    }
+    return EXIT_SUCCESS;
+}
+
+#else
+
+void aes_CBC_encrypt(const unsigned char *in,
+    unsigned char *out,
+    unsigned char ivec[16],
+    unsigned long length,
+    unsigned char *key,
+    int number_of_rounds)
+{
+    __m128i feedback, data;
+    unsigned long i;
+    int j;
+    if(length % 16)
+        length = length / 16 + 1;
+    else length /= 16;
+    feedback = _mm_loadu_si128((__m128i*)ivec);
+    for(i = 0; i < length; i++)
+    {
+        data = _mm_loadu_si128(&((__m128i*)in)[i]);
+        feedback = _mm_xor_si128(data, feedback);
+        feedback = _mm_xor_si128(feedback, ((__m128i*)key)[0]);
+        for(j = 1; j <number_of_rounds; j++)
+            feedback = _mm_aesenc_si128(feedback, ((__m128i*)key)[j]);
+        feedback = _mm_aesenclast_si128(feedback, ((__m128i*)key)[j]);
+        _mm_storeu_si128(&((__m128i*)out)[i], feedback);
+    }
+}
+
+void aes_CBC_decrypt(const unsigned char *in,
+       unsigned char *out,
+       unsigned char ivec[16],
+       unsigned long length,
+       unsigned char *key,
+       int number_of_rounds)
+{
+       __m128i data, feedback, last_in;
+       unsigned long i;
+       int j;
+       if(length % 16)
+               length = length / 16 + 1;
+       else length /= 16;
+       feedback = _mm_loadu_si128((__m128i*)ivec);
+       for(i = 0; i < length; i++)
+       {
+               last_in = _mm_loadu_si128(&((__m128i*)in)[i]);
+               data = _mm_xor_si128(last_in, ((__m128i*)key)[0]);
+               for(j = 1; j <number_of_rounds; j++)
+               {
+                       data = _mm_aesdec_si128(data, ((__m128i*)key)[j]);
+               }
+               data = _mm_aesdeclast_si128(data, ((__m128i*)key)[j]);
+               data = _mm_xor_si128(data, feedback);
+               _mm_storeu_si128(&((__m128i*)out)[i], data);
+               feedback = last_in;
+       }
+}
+
+void AES_CTR_encrypt(const unsigned char *in,
+       unsigned char *out,
+       const unsigned char ivec[8],
+       const unsigned char nonce[4],
+       unsigned long length,
+       const unsigned char *key,
+       int number_of_rounds)
+{
+       __m128i ctr_block = { 0 }, tmp, ONE, BSWAP_EPI64;
+       unsigned long i;
+       int j;
+       if(length % 16)
+               length = length / 16 + 1;
+       else length /= 16;
+       ONE = _mm_set_epi32(0, 1, 0, 0);
+       BSWAP_EPI64 = _mm_setr_epi8(7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8);
+#ifdef _MSC_VER
+       ctr_block = _mm_insert_epi64(ctr_block, *(long long*)ivec, 1);
+#else
+       ctr_block = _mm_set_epi64(*(__m64*)ivec, *(__m64*)&ctr_block);
+#endif
+       ctr_block = _mm_insert_epi32(ctr_block, *(long*)nonce, 1);
+       ctr_block = _mm_srli_si128(ctr_block, 4);
+       ctr_block = _mm_shuffle_epi8(ctr_block, BSWAP_EPI64);
+       ctr_block = _mm_add_epi64(ctr_block, ONE);
+       for(i = 0; i < length; i++)
+       {
+               tmp = _mm_shuffle_epi8(ctr_block, BSWAP_EPI64);
+               ctr_block = _mm_add_epi64(ctr_block, ONE);
+               tmp = _mm_xor_si128(tmp, ((__m128i*)key)[0]);
+               for(j = 1; j <number_of_rounds; j++)
+               {
+                       tmp = _mm_aesenc_si128(tmp, ((__m128i*)key)[j]);
+               };
+               tmp = _mm_aesenclast_si128(tmp, ((__m128i*)key)[j]);
+               tmp = _mm_xor_si128(tmp, _mm_loadu_si128(&((__m128i*)in)[i]));
+               _mm_storeu_si128(&((__m128i*)out)[i], tmp);
+       }
+}
+#endif
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aes_ni.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aes_ni.h
new file mode 100644 (file)
index 0000000..962a067
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 13/11/2013
+*/
+
+#ifndef AES_NI_H
+#define AES_NI_H
+
+#define USE_AES_CONTEXT
+
+#include "aesopt.h"
+
+#if defined( USE_INTEL_AES_IF_PRESENT )
+
+/* map names in C code to make them internal ('name' -> 'aes_name_i') */
+#define aes_xi(x) aes_ ## x ## _i
+
+/* map names here to provide the external API ('name' -> 'aes_name') */
+#define aes_ni(x) aes_ ## x
+
+AES_RETURN aes_ni(encrypt_key128)(const unsigned char *key, aes_encrypt_ctx cx[1]);
+AES_RETURN aes_ni(encrypt_key192)(const unsigned char *key, aes_encrypt_ctx cx[1]);
+AES_RETURN aes_ni(encrypt_key256)(const unsigned char *key, aes_encrypt_ctx cx[1]);
+
+AES_RETURN aes_ni(decrypt_key128)(const unsigned char *key, aes_decrypt_ctx cx[1]);
+AES_RETURN aes_ni(decrypt_key192)(const unsigned char *key, aes_decrypt_ctx cx[1]);
+AES_RETURN aes_ni(decrypt_key256)(const unsigned char *key, aes_decrypt_ctx cx[1]);
+
+AES_RETURN aes_ni(encrypt)(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]);
+AES_RETURN aes_ni(decrypt)(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]);
+
+AES_RETURN aes_xi(encrypt_key128)(const unsigned char *key, aes_encrypt_ctx cx[1]);
+AES_RETURN aes_xi(encrypt_key192)(const unsigned char *key, aes_encrypt_ctx cx[1]);
+AES_RETURN aes_xi(encrypt_key256)(const unsigned char *key, aes_encrypt_ctx cx[1]);
+
+AES_RETURN aes_xi(decrypt_key128)(const unsigned char *key, aes_decrypt_ctx cx[1]);
+AES_RETURN aes_xi(decrypt_key192)(const unsigned char *key, aes_decrypt_ctx cx[1]);
+AES_RETURN aes_xi(decrypt_key256)(const unsigned char *key, aes_decrypt_ctx cx[1]);
+
+AES_RETURN aes_xi(encrypt)(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]);
+AES_RETURN aes_xi(decrypt)(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]);
+
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aescrypt.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aescrypt.c
new file mode 100644 (file)
index 0000000..bd647e4
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+*/
+
+#include "aesopt.h"
+#include "aestab.h"
+
+#if defined( USE_INTEL_AES_IF_PRESENT )
+#  include "aes_ni.h"
+#else
+/* map names here to provide the external API ('name' -> 'aes_name') */
+#  define aes_xi(x) aes_ ## x
+#endif
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#define si(y,x,k,c) (s(y,c) = word_in(x, c) ^ (k)[c])
+#define so(y,x,c)   word_out(y, c, s(x,c))
+
+#if defined(ARRAYS)
+#define locals(y,x)     x[4],y[4]
+#else
+#define locals(y,x)     x##0,x##1,x##2,x##3,y##0,y##1,y##2,y##3
+#endif
+
+#define l_copy(y, x)    s(y,0) = s(x,0); s(y,1) = s(x,1); \
+                        s(y,2) = s(x,2); s(y,3) = s(x,3);
+#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3)
+#define state_out(y,x)  so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3)
+#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3)
+
+#if ( FUNCS_IN_C & ENCRYPTION_IN_C )
+
+/* Visual C++ .Net v7.1 provides the fastest encryption code when using
+   Pentium optimiation with small code but this is poor for decryption
+   so we need to control this with the following VC++ pragmas
+*/
+
+#if defined( _MSC_VER ) && !defined( _WIN64 )
+#pragma optimize( "s", on )
+#endif
+
+/* Given the column (c) of the output state variable, the following
+   macros give the input state variables which are needed in its
+   computation for each row (r) of the state. All the alternative
+   macros give the same end values but expand into different ways
+   of calculating these values.  In particular the complex macro
+   used for dynamically variable block sizes is designed to expand
+   to a compile time constant whenever possible but will expand to
+   conditional clauses on some branches (I am grateful to Frank
+   Yellin for this construction)
+*/
+
+#define fwd_var(x,r,c)\
+ ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\
+ : r == 1 ? ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))\
+ : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\
+ :          ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2)))
+
+#if defined(FT4_SET)
+#undef  dec_fmvars
+#define fwd_rnd(y,x,k,c)    (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,n),fwd_var,rf1,c))
+#elif defined(FT1_SET)
+#undef  dec_fmvars
+#define fwd_rnd(y,x,k,c)    (s(y,c) = (k)[c] ^ one_table(x,upr,t_use(f,n),fwd_var,rf1,c))
+#else
+#define fwd_rnd(y,x,k,c)    (s(y,c) = (k)[c] ^ fwd_mcol(no_table(x,t_use(s,box),fwd_var,rf1,c)))
+#endif
+
+#if defined(FL4_SET)
+#define fwd_lrnd(y,x,k,c)   (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,l),fwd_var,rf1,c))
+#elif defined(FL1_SET)
+#define fwd_lrnd(y,x,k,c)   (s(y,c) = (k)[c] ^ one_table(x,ups,t_use(f,l),fwd_var,rf1,c))
+#else
+#define fwd_lrnd(y,x,k,c)   (s(y,c) = (k)[c] ^ no_table(x,t_use(s,box),fwd_var,rf1,c))
+#endif
+
+AES_RETURN aes_xi(encrypt)(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1])
+{   uint32_t         locals(b0, b1);
+    const uint32_t   *kp;
+#if defined( dec_fmvars )
+    dec_fmvars; /* declare variables for fwd_mcol() if needed */
+#endif
+
+       if(cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16)
+               return EXIT_FAILURE;
+
+       kp = cx->ks;
+    state_in(b0, in, kp);
+
+#if (ENC_UNROLL == FULL)
+
+    switch(cx->inf.b[0])
+    {
+    case 14 * 16:
+        round(fwd_rnd,  b1, b0, kp + 1 * N_COLS);
+        round(fwd_rnd,  b0, b1, kp + 2 * N_COLS);
+        kp += 2 * N_COLS;
+    case 12 * 16:
+        round(fwd_rnd,  b1, b0, kp + 1 * N_COLS);
+        round(fwd_rnd,  b0, b1, kp + 2 * N_COLS);
+        kp += 2 * N_COLS;
+    case 10 * 16:
+        round(fwd_rnd,  b1, b0, kp + 1 * N_COLS);
+        round(fwd_rnd,  b0, b1, kp + 2 * N_COLS);
+        round(fwd_rnd,  b1, b0, kp + 3 * N_COLS);
+        round(fwd_rnd,  b0, b1, kp + 4 * N_COLS);
+        round(fwd_rnd,  b1, b0, kp + 5 * N_COLS);
+        round(fwd_rnd,  b0, b1, kp + 6 * N_COLS);
+        round(fwd_rnd,  b1, b0, kp + 7 * N_COLS);
+        round(fwd_rnd,  b0, b1, kp + 8 * N_COLS);
+        round(fwd_rnd,  b1, b0, kp + 9 * N_COLS);
+        round(fwd_lrnd, b0, b1, kp +10 * N_COLS);
+    }
+
+#else
+
+#if (ENC_UNROLL == PARTIAL)
+    {   uint32_t    rnd;
+        for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd)
+        {
+            kp += N_COLS;
+            round(fwd_rnd, b1, b0, kp);
+            kp += N_COLS;
+            round(fwd_rnd, b0, b1, kp);
+        }
+        kp += N_COLS;
+        round(fwd_rnd,  b1, b0, kp);
+#else
+    {   uint32_t    rnd;
+        for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd)
+        {
+            kp += N_COLS;
+            round(fwd_rnd, b1, b0, kp);
+            l_copy(b0, b1);
+        }
+#endif
+        kp += N_COLS;
+        round(fwd_lrnd, b0, b1, kp);
+    }
+#endif
+
+    state_out(out, b0);
+    return EXIT_SUCCESS;
+}
+
+#endif
+
+#if ( FUNCS_IN_C & DECRYPTION_IN_C)
+
+/* Visual C++ .Net v7.1 provides the fastest encryption code when using
+   Pentium optimiation with small code but this is poor for decryption
+   so we need to control this with the following VC++ pragmas
+*/
+
+#if defined( _MSC_VER ) && !defined( _WIN64 )
+#pragma optimize( "t", on )
+#endif
+
+/* Given the column (c) of the output state variable, the following
+   macros give the input state variables which are needed in its
+   computation for each row (r) of the state. All the alternative
+   macros give the same end values but expand into different ways
+   of calculating these values.  In particular the complex macro
+   used for dynamically variable block sizes is designed to expand
+   to a compile time constant whenever possible but will expand to
+   conditional clauses on some branches (I am grateful to Frank
+   Yellin for this construction)
+*/
+
+#define inv_var(x,r,c)\
+ ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\
+ : r == 1 ? ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))\
+ : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\
+ :          ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0)))
+
+#if defined(IT4_SET)
+#undef  dec_imvars
+#define inv_rnd(y,x,k,c)    (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,n),inv_var,rf1,c))
+#elif defined(IT1_SET)
+#undef  dec_imvars
+#define inv_rnd(y,x,k,c)    (s(y,c) = (k)[c] ^ one_table(x,upr,t_use(i,n),inv_var,rf1,c))
+#else
+#define inv_rnd(y,x,k,c)    (s(y,c) = inv_mcol((k)[c] ^ no_table(x,t_use(i,box),inv_var,rf1,c)))
+#endif
+
+#if defined(IL4_SET)
+#define inv_lrnd(y,x,k,c)   (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,l),inv_var,rf1,c))
+#elif defined(IL1_SET)
+#define inv_lrnd(y,x,k,c)   (s(y,c) = (k)[c] ^ one_table(x,ups,t_use(i,l),inv_var,rf1,c))
+#else
+#define inv_lrnd(y,x,k,c)   (s(y,c) = (k)[c] ^ no_table(x,t_use(i,box),inv_var,rf1,c))
+#endif
+
+/* This code can work with the decryption key schedule in the   */
+/* order that is used for encrytpion (where the 1st decryption  */
+/* round key is at the high end ot the schedule) or with a key  */
+/* schedule that has been reversed to put the 1st decryption    */
+/* round key at the low end of the schedule in memory (when     */
+/* AES_REV_DKS is defined)                                      */
+
+#ifdef AES_REV_DKS
+#define key_ofs     0
+#define rnd_key(n)  (kp + n * N_COLS)
+#else
+#define key_ofs     1
+#define rnd_key(n)  (kp - n * N_COLS)
+#endif
+
+AES_RETURN aes_xi(decrypt)(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1])
+{   uint32_t        locals(b0, b1);
+#if defined( dec_imvars )
+    dec_imvars; /* declare variables for inv_mcol() if needed */
+#endif
+    const uint32_t *kp;
+
+    if(cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16)
+        return EXIT_FAILURE;
+
+    kp = cx->ks + (key_ofs ? (cx->inf.b[0] >> 2) : 0);
+    state_in(b0, in, kp);
+
+#if (DEC_UNROLL == FULL)
+
+    kp = cx->ks + (key_ofs ? 0 : (cx->inf.b[0] >> 2));
+    switch(cx->inf.b[0])
+    {
+    case 14 * 16:
+        round(inv_rnd,  b1, b0, rnd_key(-13));
+        round(inv_rnd,  b0, b1, rnd_key(-12));
+    case 12 * 16:
+        round(inv_rnd,  b1, b0, rnd_key(-11));
+        round(inv_rnd,  b0, b1, rnd_key(-10));
+    case 10 * 16:
+        round(inv_rnd,  b1, b0, rnd_key(-9));
+        round(inv_rnd,  b0, b1, rnd_key(-8));
+        round(inv_rnd,  b1, b0, rnd_key(-7));
+        round(inv_rnd,  b0, b1, rnd_key(-6));
+        round(inv_rnd,  b1, b0, rnd_key(-5));
+        round(inv_rnd,  b0, b1, rnd_key(-4));
+        round(inv_rnd,  b1, b0, rnd_key(-3));
+        round(inv_rnd,  b0, b1, rnd_key(-2));
+        round(inv_rnd,  b1, b0, rnd_key(-1));
+        round(inv_lrnd, b0, b1, rnd_key( 0));
+    }
+
+#else
+
+#if (DEC_UNROLL == PARTIAL)
+    {   uint32_t    rnd;
+        for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd)
+        {
+            kp = rnd_key(1);
+            round(inv_rnd, b1, b0, kp);
+            kp = rnd_key(1);
+            round(inv_rnd, b0, b1, kp);
+        }
+        kp = rnd_key(1);
+        round(inv_rnd, b1, b0, kp);
+#else
+    {   uint32_t    rnd;
+        for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd)
+        {
+            kp = rnd_key(1);
+            round(inv_rnd, b1, b0, kp);
+            l_copy(b0, b1);
+        }
+#endif
+        kp = rnd_key(1);
+        round(inv_lrnd, b0, b1, kp);
+        }
+#endif
+
+    state_out(out, b0);
+    return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aeskey.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aeskey.c
new file mode 100644 (file)
index 0000000..16e9607
--- /dev/null
@@ -0,0 +1,554 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+*/
+
+#include "aesopt.h"
+#include "aestab.h"
+
+#if defined( USE_INTEL_AES_IF_PRESENT )
+#  include "aes_ni.h"
+#else
+/* map names here to provide the external API ('name' -> 'aes_name') */
+#  define aes_xi(x) aes_ ## x
+#endif
+
+#ifdef USE_VIA_ACE_IF_PRESENT
+#  include "aes_via_ace.h"
+#endif
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/* Initialise the key schedule from the user supplied key. The key
+   length can be specified in bytes, with legal values of 16, 24
+   and 32, or in bits, with legal values of 128, 192 and 256. These
+   values correspond with Nk values of 4, 6 and 8 respectively.
+
+   The following macros implement a single cycle in the key
+   schedule generation process. The number of cycles needed
+   for each cx->n_col and nk value is:
+
+    nk =             4  5  6  7  8
+    ------------------------------
+    cx->n_col = 4   10  9  8  7  7
+    cx->n_col = 5   14 11 10  9  9
+    cx->n_col = 6   19 15 12 11 11
+    cx->n_col = 7   21 19 16 13 14
+    cx->n_col = 8   29 23 19 17 14
+*/
+
+#if defined( REDUCE_CODE_SIZE )
+#  define ls_box ls_sub
+   uint32_t ls_sub(const uint32_t t, const uint32_t n);
+#  define inv_mcol im_sub
+   uint32_t im_sub(const uint32_t x);
+#  ifdef ENC_KS_UNROLL
+#    undef ENC_KS_UNROLL
+#  endif
+#  ifdef DEC_KS_UNROLL
+#    undef DEC_KS_UNROLL
+#  endif
+#endif
+
+#if (FUNCS_IN_C & ENC_KEYING_IN_C)
+
+#if defined(AES_128) || defined( AES_VAR )
+
+#define ke4(k,i) \
+{   k[4*(i)+4] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; \
+    k[4*(i)+5] = ss[1] ^= ss[0]; \
+    k[4*(i)+6] = ss[2] ^= ss[1]; \
+    k[4*(i)+7] = ss[3] ^= ss[2]; \
+}
+
+AES_RETURN aes_xi(encrypt_key128)(const unsigned char *key, aes_encrypt_ctx cx[1])
+{   uint32_t    ss[4];
+
+    cx->ks[0] = ss[0] = word_in(key, 0);
+    cx->ks[1] = ss[1] = word_in(key, 1);
+    cx->ks[2] = ss[2] = word_in(key, 2);
+    cx->ks[3] = ss[3] = word_in(key, 3);
+
+#ifdef ENC_KS_UNROLL
+    ke4(cx->ks, 0);  ke4(cx->ks, 1);
+    ke4(cx->ks, 2);  ke4(cx->ks, 3);
+    ke4(cx->ks, 4);  ke4(cx->ks, 5);
+    ke4(cx->ks, 6);  ke4(cx->ks, 7);
+    ke4(cx->ks, 8);
+#else
+    {   uint32_t i;
+        for(i = 0; i < 9; ++i)
+            ke4(cx->ks, i);
+    }
+#endif
+    ke4(cx->ks, 9);
+    cx->inf.l = 0;
+    cx->inf.b[0] = 10 * 16;
+
+#ifdef USE_VIA_ACE_IF_PRESENT
+    if(VIA_ACE_AVAILABLE)
+        cx->inf.b[1] = 0xff;
+#endif
+    return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined(AES_192) || defined( AES_VAR )
+
+#define kef6(k,i) \
+{   k[6*(i)+ 6] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; \
+    k[6*(i)+ 7] = ss[1] ^= ss[0]; \
+    k[6*(i)+ 8] = ss[2] ^= ss[1]; \
+    k[6*(i)+ 9] = ss[3] ^= ss[2]; \
+}
+
+#define ke6(k,i) \
+{   kef6(k,i); \
+    k[6*(i)+10] = ss[4] ^= ss[3]; \
+    k[6*(i)+11] = ss[5] ^= ss[4]; \
+}
+
+AES_RETURN aes_xi(encrypt_key192)(const unsigned char *key, aes_encrypt_ctx cx[1])
+{   uint32_t    ss[6];
+
+       cx->ks[0] = ss[0] = word_in(key, 0);
+    cx->ks[1] = ss[1] = word_in(key, 1);
+    cx->ks[2] = ss[2] = word_in(key, 2);
+    cx->ks[3] = ss[3] = word_in(key, 3);
+    cx->ks[4] = ss[4] = word_in(key, 4);
+    cx->ks[5] = ss[5] = word_in(key, 5);
+
+#ifdef ENC_KS_UNROLL
+    ke6(cx->ks, 0);  ke6(cx->ks, 1);
+    ke6(cx->ks, 2);  ke6(cx->ks, 3);
+    ke6(cx->ks, 4);  ke6(cx->ks, 5);
+    ke6(cx->ks, 6);
+#else
+    {   uint32_t i;
+        for(i = 0; i < 7; ++i)
+            ke6(cx->ks, i);
+    }
+#endif
+    kef6(cx->ks, 7);
+    cx->inf.l = 0;
+    cx->inf.b[0] = 12 * 16;
+
+#ifdef USE_VIA_ACE_IF_PRESENT
+    if(VIA_ACE_AVAILABLE)
+        cx->inf.b[1] = 0xff;
+#endif
+    return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined(AES_256) || defined( AES_VAR )
+
+#define kef8(k,i) \
+{   k[8*(i)+ 8] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; \
+    k[8*(i)+ 9] = ss[1] ^= ss[0]; \
+    k[8*(i)+10] = ss[2] ^= ss[1]; \
+    k[8*(i)+11] = ss[3] ^= ss[2]; \
+}
+
+#define ke8(k,i) \
+{   kef8(k,i); \
+    k[8*(i)+12] = ss[4] ^= ls_box(ss[3],0); \
+    k[8*(i)+13] = ss[5] ^= ss[4]; \
+    k[8*(i)+14] = ss[6] ^= ss[5]; \
+    k[8*(i)+15] = ss[7] ^= ss[6]; \
+}
+
+AES_RETURN aes_xi(encrypt_key256)(const unsigned char *key, aes_encrypt_ctx cx[1])
+{   uint32_t    ss[8];
+
+    cx->ks[0] = ss[0] = word_in(key, 0);
+    cx->ks[1] = ss[1] = word_in(key, 1);
+    cx->ks[2] = ss[2] = word_in(key, 2);
+    cx->ks[3] = ss[3] = word_in(key, 3);
+    cx->ks[4] = ss[4] = word_in(key, 4);
+    cx->ks[5] = ss[5] = word_in(key, 5);
+    cx->ks[6] = ss[6] = word_in(key, 6);
+    cx->ks[7] = ss[7] = word_in(key, 7);
+
+#ifdef ENC_KS_UNROLL
+    ke8(cx->ks, 0); ke8(cx->ks, 1);
+    ke8(cx->ks, 2); ke8(cx->ks, 3);
+    ke8(cx->ks, 4); ke8(cx->ks, 5);
+#else
+    {   uint32_t i;
+        for(i = 0; i < 6; ++i)
+            ke8(cx->ks,  i);
+    }
+#endif
+    kef8(cx->ks, 6);
+    cx->inf.l = 0;
+    cx->inf.b[0] = 14 * 16;
+
+#ifdef USE_VIA_ACE_IF_PRESENT
+    if(VIA_ACE_AVAILABLE)
+        cx->inf.b[1] = 0xff;
+#endif
+    return EXIT_SUCCESS;
+}
+
+#endif
+
+#endif
+
+#if (FUNCS_IN_C & DEC_KEYING_IN_C)
+
+/* this is used to store the decryption round keys  */
+/* in forward or reverse order                      */
+
+#ifdef AES_REV_DKS
+#define v(n,i)  ((n) - (i) + 2 * ((i) & 3))
+#else
+#define v(n,i)  (i)
+#endif
+
+#if DEC_ROUND == NO_TABLES
+#define ff(x)   (x)
+#else
+#define ff(x)   inv_mcol(x)
+#if defined( dec_imvars )
+#define d_vars  dec_imvars
+#endif
+#endif
+
+#if defined(AES_128) || defined( AES_VAR )
+
+#define k4e(k,i) \
+{   k[v(40,(4*(i))+4)] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; \
+    k[v(40,(4*(i))+5)] = ss[1] ^= ss[0]; \
+    k[v(40,(4*(i))+6)] = ss[2] ^= ss[1]; \
+    k[v(40,(4*(i))+7)] = ss[3] ^= ss[2]; \
+}
+
+#if 1
+
+#define kdf4(k,i) \
+{   ss[0] = ss[0] ^ ss[2] ^ ss[1] ^ ss[3]; \
+    ss[1] = ss[1] ^ ss[3]; \
+    ss[2] = ss[2] ^ ss[3]; \
+    ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; \
+    ss[i % 4] ^= ss[4]; \
+    ss[4] ^= k[v(40,(4*(i)))];   k[v(40,(4*(i))+4)] = ff(ss[4]); \
+    ss[4] ^= k[v(40,(4*(i))+1)]; k[v(40,(4*(i))+5)] = ff(ss[4]); \
+    ss[4] ^= k[v(40,(4*(i))+2)]; k[v(40,(4*(i))+6)] = ff(ss[4]); \
+    ss[4] ^= k[v(40,(4*(i))+3)]; k[v(40,(4*(i))+7)] = ff(ss[4]); \
+}
+
+#define kd4(k,i) \
+{   ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; \
+    ss[i % 4] ^= ss[4]; ss[4] = ff(ss[4]); \
+    k[v(40,(4*(i))+4)] = ss[4] ^= k[v(40,(4*(i)))]; \
+    k[v(40,(4*(i))+5)] = ss[4] ^= k[v(40,(4*(i))+1)]; \
+    k[v(40,(4*(i))+6)] = ss[4] ^= k[v(40,(4*(i))+2)]; \
+    k[v(40,(4*(i))+7)] = ss[4] ^= k[v(40,(4*(i))+3)]; \
+}
+
+#define kdl4(k,i) \
+{   ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; ss[i % 4] ^= ss[4]; \
+    k[v(40,(4*(i))+4)] = (ss[0] ^= ss[1]) ^ ss[2] ^ ss[3]; \
+    k[v(40,(4*(i))+5)] = ss[1] ^ ss[3]; \
+    k[v(40,(4*(i))+6)] = ss[0]; \
+    k[v(40,(4*(i))+7)] = ss[1]; \
+}
+
+#else
+
+#define kdf4(k,i) \
+{   ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[v(40,(4*(i))+ 4)] = ff(ss[0]); \
+    ss[1] ^= ss[0]; k[v(40,(4*(i))+ 5)] = ff(ss[1]); \
+    ss[2] ^= ss[1]; k[v(40,(4*(i))+ 6)] = ff(ss[2]); \
+    ss[3] ^= ss[2]; k[v(40,(4*(i))+ 7)] = ff(ss[3]); \
+}
+
+#define kd4(k,i) \
+{   ss[4] = ls_box(ss[3],3) ^ t_use(r,c)[i]; \
+    ss[0] ^= ss[4]; ss[4] = ff(ss[4]); k[v(40,(4*(i))+ 4)] = ss[4] ^= k[v(40,(4*(i)))]; \
+    ss[1] ^= ss[0]; k[v(40,(4*(i))+ 5)] = ss[4] ^= k[v(40,(4*(i))+ 1)]; \
+    ss[2] ^= ss[1]; k[v(40,(4*(i))+ 6)] = ss[4] ^= k[v(40,(4*(i))+ 2)]; \
+    ss[3] ^= ss[2]; k[v(40,(4*(i))+ 7)] = ss[4] ^= k[v(40,(4*(i))+ 3)]; \
+}
+
+#define kdl4(k,i) \
+{   ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[v(40,(4*(i))+ 4)] = ss[0]; \
+    ss[1] ^= ss[0]; k[v(40,(4*(i))+ 5)] = ss[1]; \
+    ss[2] ^= ss[1]; k[v(40,(4*(i))+ 6)] = ss[2]; \
+    ss[3] ^= ss[2]; k[v(40,(4*(i))+ 7)] = ss[3]; \
+}
+
+#endif
+
+AES_RETURN aes_xi(decrypt_key128)(const unsigned char *key, aes_decrypt_ctx cx[1])
+{   uint32_t    ss[5];
+#if defined( d_vars )
+        d_vars;
+#endif
+
+       cx->ks[v(40,(0))] = ss[0] = word_in(key, 0);
+    cx->ks[v(40,(1))] = ss[1] = word_in(key, 1);
+    cx->ks[v(40,(2))] = ss[2] = word_in(key, 2);
+    cx->ks[v(40,(3))] = ss[3] = word_in(key, 3);
+
+#ifdef DEC_KS_UNROLL
+     kdf4(cx->ks, 0); kd4(cx->ks, 1);
+     kd4(cx->ks, 2);  kd4(cx->ks, 3);
+     kd4(cx->ks, 4);  kd4(cx->ks, 5);
+     kd4(cx->ks, 6);  kd4(cx->ks, 7);
+     kd4(cx->ks, 8);  kdl4(cx->ks, 9);
+#else
+    {   uint32_t i;
+        for(i = 0; i < 10; ++i)
+            k4e(cx->ks, i);
+#if !(DEC_ROUND == NO_TABLES)
+        for(i = N_COLS; i < 10 * N_COLS; ++i)
+            cx->ks[i] = inv_mcol(cx->ks[i]);
+#endif
+    }
+#endif
+    cx->inf.l = 0;
+    cx->inf.b[0] = 10 * 16;
+
+#ifdef USE_VIA_ACE_IF_PRESENT
+    if(VIA_ACE_AVAILABLE)
+        cx->inf.b[1] = 0xff;
+#endif
+    return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined(AES_192) || defined( AES_VAR )
+
+#define k6ef(k,i) \
+{   k[v(48,(6*(i))+ 6)] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; \
+    k[v(48,(6*(i))+ 7)] = ss[1] ^= ss[0]; \
+    k[v(48,(6*(i))+ 8)] = ss[2] ^= ss[1]; \
+    k[v(48,(6*(i))+ 9)] = ss[3] ^= ss[2]; \
+}
+
+#define k6e(k,i) \
+{   k6ef(k,i); \
+    k[v(48,(6*(i))+10)] = ss[4] ^= ss[3]; \
+    k[v(48,(6*(i))+11)] = ss[5] ^= ss[4]; \
+}
+
+#define kdf6(k,i) \
+{   ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[v(48,(6*(i))+ 6)] = ff(ss[0]); \
+    ss[1] ^= ss[0]; k[v(48,(6*(i))+ 7)] = ff(ss[1]); \
+    ss[2] ^= ss[1]; k[v(48,(6*(i))+ 8)] = ff(ss[2]); \
+    ss[3] ^= ss[2]; k[v(48,(6*(i))+ 9)] = ff(ss[3]); \
+    ss[4] ^= ss[3]; k[v(48,(6*(i))+10)] = ff(ss[4]); \
+    ss[5] ^= ss[4]; k[v(48,(6*(i))+11)] = ff(ss[5]); \
+}
+
+#define kd6(k,i) \
+{   ss[6] = ls_box(ss[5],3) ^ t_use(r,c)[i]; \
+    ss[0] ^= ss[6]; ss[6] = ff(ss[6]); k[v(48,(6*(i))+ 6)] = ss[6] ^= k[v(48,(6*(i)))]; \
+    ss[1] ^= ss[0]; k[v(48,(6*(i))+ 7)] = ss[6] ^= k[v(48,(6*(i))+ 1)]; \
+    ss[2] ^= ss[1]; k[v(48,(6*(i))+ 8)] = ss[6] ^= k[v(48,(6*(i))+ 2)]; \
+    ss[3] ^= ss[2]; k[v(48,(6*(i))+ 9)] = ss[6] ^= k[v(48,(6*(i))+ 3)]; \
+    ss[4] ^= ss[3]; k[v(48,(6*(i))+10)] = ss[6] ^= k[v(48,(6*(i))+ 4)]; \
+    ss[5] ^= ss[4]; k[v(48,(6*(i))+11)] = ss[6] ^= k[v(48,(6*(i))+ 5)]; \
+}
+
+#define kdl6(k,i) \
+{   ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[v(48,(6*(i))+ 6)] = ss[0]; \
+    ss[1] ^= ss[0]; k[v(48,(6*(i))+ 7)] = ss[1]; \
+    ss[2] ^= ss[1]; k[v(48,(6*(i))+ 8)] = ss[2]; \
+    ss[3] ^= ss[2]; k[v(48,(6*(i))+ 9)] = ss[3]; \
+}
+
+AES_RETURN aes_xi(decrypt_key192)(const unsigned char *key, aes_decrypt_ctx cx[1])
+{   uint32_t    ss[7];
+#if defined( d_vars )
+        d_vars;
+#endif
+
+    cx->ks[v(48,(0))] = ss[0] = word_in(key, 0);
+    cx->ks[v(48,(1))] = ss[1] = word_in(key, 1);
+    cx->ks[v(48,(2))] = ss[2] = word_in(key, 2);
+    cx->ks[v(48,(3))] = ss[3] = word_in(key, 3);
+
+#ifdef DEC_KS_UNROLL
+    cx->ks[v(48,(4))] = ff(ss[4] = word_in(key, 4));
+    cx->ks[v(48,(5))] = ff(ss[5] = word_in(key, 5));
+    kdf6(cx->ks, 0); kd6(cx->ks, 1);
+    kd6(cx->ks, 2);  kd6(cx->ks, 3);
+    kd6(cx->ks, 4);  kd6(cx->ks, 5);
+    kd6(cx->ks, 6);  kdl6(cx->ks, 7);
+#else
+    cx->ks[v(48,(4))] = ss[4] = word_in(key, 4);
+    cx->ks[v(48,(5))] = ss[5] = word_in(key, 5);
+    {   uint32_t i;
+
+        for(i = 0; i < 7; ++i)
+            k6e(cx->ks, i);
+        k6ef(cx->ks, 7);
+#if !(DEC_ROUND == NO_TABLES)
+        for(i = N_COLS; i < 12 * N_COLS; ++i)
+            cx->ks[i] = inv_mcol(cx->ks[i]);
+#endif
+    }
+#endif
+    cx->inf.l = 0;
+    cx->inf.b[0] = 12 * 16;
+
+#ifdef USE_VIA_ACE_IF_PRESENT
+    if(VIA_ACE_AVAILABLE)
+        cx->inf.b[1] = 0xff;
+#endif
+    return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined(AES_256) || defined( AES_VAR )
+
+#define k8ef(k,i) \
+{   k[v(56,(8*(i))+ 8)] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; \
+    k[v(56,(8*(i))+ 9)] = ss[1] ^= ss[0]; \
+    k[v(56,(8*(i))+10)] = ss[2] ^= ss[1]; \
+    k[v(56,(8*(i))+11)] = ss[3] ^= ss[2]; \
+}
+
+#define k8e(k,i) \
+{   k8ef(k,i); \
+    k[v(56,(8*(i))+12)] = ss[4] ^= ls_box(ss[3],0); \
+    k[v(56,(8*(i))+13)] = ss[5] ^= ss[4]; \
+    k[v(56,(8*(i))+14)] = ss[6] ^= ss[5]; \
+    k[v(56,(8*(i))+15)] = ss[7] ^= ss[6]; \
+}
+
+#define kdf8(k,i) \
+{   ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[v(56,(8*(i))+ 8)] = ff(ss[0]); \
+    ss[1] ^= ss[0]; k[v(56,(8*(i))+ 9)] = ff(ss[1]); \
+    ss[2] ^= ss[1]; k[v(56,(8*(i))+10)] = ff(ss[2]); \
+    ss[3] ^= ss[2]; k[v(56,(8*(i))+11)] = ff(ss[3]); \
+    ss[4] ^= ls_box(ss[3],0); k[v(56,(8*(i))+12)] = ff(ss[4]); \
+    ss[5] ^= ss[4]; k[v(56,(8*(i))+13)] = ff(ss[5]); \
+    ss[6] ^= ss[5]; k[v(56,(8*(i))+14)] = ff(ss[6]); \
+    ss[7] ^= ss[6]; k[v(56,(8*(i))+15)] = ff(ss[7]); \
+}
+
+#define kd8(k,i) \
+{   ss[8] = ls_box(ss[7],3) ^ t_use(r,c)[i]; \
+    ss[0] ^= ss[8]; ss[8] = ff(ss[8]); k[v(56,(8*(i))+ 8)] = ss[8] ^= k[v(56,(8*(i)))]; \
+    ss[1] ^= ss[0]; k[v(56,(8*(i))+ 9)] = ss[8] ^= k[v(56,(8*(i))+ 1)]; \
+    ss[2] ^= ss[1]; k[v(56,(8*(i))+10)] = ss[8] ^= k[v(56,(8*(i))+ 2)]; \
+    ss[3] ^= ss[2]; k[v(56,(8*(i))+11)] = ss[8] ^= k[v(56,(8*(i))+ 3)]; \
+    ss[8] = ls_box(ss[3],0); \
+    ss[4] ^= ss[8]; ss[8] = ff(ss[8]); k[v(56,(8*(i))+12)] = ss[8] ^= k[v(56,(8*(i))+ 4)]; \
+    ss[5] ^= ss[4]; k[v(56,(8*(i))+13)] = ss[8] ^= k[v(56,(8*(i))+ 5)]; \
+    ss[6] ^= ss[5]; k[v(56,(8*(i))+14)] = ss[8] ^= k[v(56,(8*(i))+ 6)]; \
+    ss[7] ^= ss[6]; k[v(56,(8*(i))+15)] = ss[8] ^= k[v(56,(8*(i))+ 7)]; \
+}
+
+#define kdl8(k,i) \
+{   ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[v(56,(8*(i))+ 8)] = ss[0]; \
+    ss[1] ^= ss[0]; k[v(56,(8*(i))+ 9)] = ss[1]; \
+    ss[2] ^= ss[1]; k[v(56,(8*(i))+10)] = ss[2]; \
+    ss[3] ^= ss[2]; k[v(56,(8*(i))+11)] = ss[3]; \
+}
+
+AES_RETURN aes_xi(decrypt_key256)(const unsigned char *key, aes_decrypt_ctx cx[1])
+{   uint32_t    ss[9];
+#if defined( d_vars )
+        d_vars;
+#endif
+
+    cx->ks[v(56,(0))] = ss[0] = word_in(key, 0);
+    cx->ks[v(56,(1))] = ss[1] = word_in(key, 1);
+    cx->ks[v(56,(2))] = ss[2] = word_in(key, 2);
+    cx->ks[v(56,(3))] = ss[3] = word_in(key, 3);
+
+#ifdef DEC_KS_UNROLL
+    cx->ks[v(56,(4))] = ff(ss[4] = word_in(key, 4));
+    cx->ks[v(56,(5))] = ff(ss[5] = word_in(key, 5));
+    cx->ks[v(56,(6))] = ff(ss[6] = word_in(key, 6));
+    cx->ks[v(56,(7))] = ff(ss[7] = word_in(key, 7));
+    kdf8(cx->ks, 0); kd8(cx->ks, 1);
+    kd8(cx->ks, 2);  kd8(cx->ks, 3);
+    kd8(cx->ks, 4);  kd8(cx->ks, 5);
+    kdl8(cx->ks, 6);
+#else
+    cx->ks[v(56,(4))] = ss[4] = word_in(key, 4);
+    cx->ks[v(56,(5))] = ss[5] = word_in(key, 5);
+    cx->ks[v(56,(6))] = ss[6] = word_in(key, 6);
+    cx->ks[v(56,(7))] = ss[7] = word_in(key, 7);
+    {   uint32_t i;
+
+        for(i = 0; i < 6; ++i)
+            k8e(cx->ks,  i);
+        k8ef(cx->ks,  6);
+#if !(DEC_ROUND == NO_TABLES)
+        for(i = N_COLS; i < 14 * N_COLS; ++i)
+            cx->ks[i] = inv_mcol(cx->ks[i]);
+#endif
+    }
+#endif
+    cx->inf.l = 0;
+    cx->inf.b[0] = 14 * 16;
+
+#ifdef USE_VIA_ACE_IF_PRESENT
+    if(VIA_ACE_AVAILABLE)
+        cx->inf.b[1] = 0xff;
+#endif
+    return EXIT_SUCCESS;
+}
+
+#endif
+
+#endif
+
+#if defined( AES_VAR )
+
+AES_RETURN aes_encrypt_key(const unsigned char *key, int key_len, aes_encrypt_ctx cx[1])
+{
+       switch(key_len)
+       {
+       case 16: case 128: return aes_encrypt_key128(key, cx);
+       case 24: case 192: return aes_encrypt_key192(key, cx);
+       case 32: case 256: return aes_encrypt_key256(key, cx);
+       default: return EXIT_FAILURE;
+       }
+}
+
+AES_RETURN aes_decrypt_key(const unsigned char *key, int key_len, aes_decrypt_ctx cx[1])
+{
+       switch(key_len)
+       {
+       case 16: case 128: return aes_decrypt_key128(key, cx);
+       case 24: case 192: return aes_decrypt_key192(key, cx);
+       case 32: case 256: return aes_decrypt_key256(key, cx);
+       default: return EXIT_FAILURE;
+       }
+}
+
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aesopt.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aesopt.h
new file mode 100644 (file)
index 0000000..3678e74
--- /dev/null
@@ -0,0 +1,776 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+
+ This file contains the compilation options for AES (Rijndael) and code
+ that is common across encryption, key scheduling and table generation.
+
+ OPERATION
+
+ These source code files implement the AES algorithm Rijndael designed by
+ Joan Daemen and Vincent Rijmen. This version is designed for the standard
+ block size of 16 bytes and for key sizes of 128, 192 and 256 bits (16, 24
+ and 32 bytes).
+
+ This version is designed for flexibility and speed using operations on
+ 32-bit words rather than operations on bytes.  It can be compiled with
+ either big or little endian internal byte order but is faster when the
+ native byte order for the processor is used.
+
+ THE CIPHER INTERFACE
+
+ The cipher interface is implemented as an array of bytes in which lower
+ AES bit sequence indexes map to higher numeric significance within bytes.
+
+  uint8_t                 (an unsigned  8-bit type)
+  uint32_t                (an unsigned 32-bit type)
+  struct aes_encrypt_ctx  (structure for the cipher encryption context)
+  struct aes_decrypt_ctx  (structure for the cipher decryption context)
+  AES_RETURN                the function return type
+
+  C subroutine calls:
+
+  AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]);
+  AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]);
+  AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]);
+  AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out,
+                                                  const aes_encrypt_ctx cx[1]);
+
+  AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]);
+  AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]);
+  AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]);
+  AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out,
+                                                  const aes_decrypt_ctx cx[1]);
+
+ IMPORTANT NOTE: If you are using this C interface with dynamic tables make sure that
+ you call aes_init() before AES is used so that the tables are initialised.
+
+ C++ aes class subroutines:
+
+     Class AESencrypt  for encryption
+
+      Construtors:
+          AESencrypt(void)
+          AESencrypt(const unsigned char *key) - 128 bit key
+      Members:
+          AES_RETURN key128(const unsigned char *key)
+          AES_RETURN key192(const unsigned char *key)
+          AES_RETURN key256(const unsigned char *key)
+          AES_RETURN encrypt(const unsigned char *in, unsigned char *out) const
+
+      Class AESdecrypt  for encryption
+      Construtors:
+          AESdecrypt(void)
+          AESdecrypt(const unsigned char *key) - 128 bit key
+      Members:
+          AES_RETURN key128(const unsigned char *key)
+          AES_RETURN key192(const unsigned char *key)
+          AES_RETURN key256(const unsigned char *key)
+          AES_RETURN decrypt(const unsigned char *in, unsigned char *out) const
+*/
+
+#if !defined( _AESOPT_H )
+#define _AESOPT_H
+
+#if defined( __cplusplus )
+#include "aescpp.h"
+#else
+#include "aes.h"
+#endif
+
+/*  PLATFORM SPECIFIC INCLUDES */
+
+#include "brg_endian.h"
+
+/*  CONFIGURATION - THE USE OF DEFINES
+
+    Later in this section there are a number of defines that control the
+    operation of the code.  In each section, the purpose of each define is
+    explained so that the relevant form can be included or excluded by
+    setting either 1's or 0's respectively on the branches of the related
+    #if clauses.  The following local defines should not be changed.
+*/
+
+#define ENCRYPTION_IN_C     1
+#define DECRYPTION_IN_C     2
+#define ENC_KEYING_IN_C     4
+#define DEC_KEYING_IN_C     8
+
+#define NO_TABLES           0
+#define ONE_TABLE           1
+#define FOUR_TABLES         4
+#define NONE                0
+#define PARTIAL             1
+#define FULL                2
+
+/*  --- START OF USER CONFIGURED OPTIONS --- */
+
+/*  1. BYTE ORDER WITHIN 32 BIT WORDS
+
+    The fundamental data processing units in Rijndael are 8-bit bytes. The
+    input, output and key input are all enumerated arrays of bytes in which
+    bytes are numbered starting at zero and increasing to one less than the
+    number of bytes in the array in question. This enumeration is only used
+    for naming bytes and does not imply any adjacency or order relationship
+    from one byte to another. When these inputs and outputs are considered
+    as bit sequences, bits 8*n to 8*n+7 of the bit sequence are mapped to
+    byte[n] with bit 8n+i in the sequence mapped to bit 7-i within the byte.
+    In this implementation bits are numbered from 0 to 7 starting at the
+    numerically least significant end of each byte (bit n represents 2^n).
+
+    However, Rijndael can be implemented more efficiently using 32-bit
+    words by packing bytes into words so that bytes 4*n to 4*n+3 are placed
+    into word[n]. While in principle these bytes can be assembled into words
+    in any positions, this implementation only supports the two formats in
+    which bytes in adjacent positions within words also have adjacent byte
+    numbers. This order is called big-endian if the lowest numbered bytes
+    in words have the highest numeric significance and little-endian if the
+    opposite applies.
+
+    This code can work in either order irrespective of the order used by the
+    machine on which it runs. Normally the internal byte order will be set
+    to the order of the processor on which the code is to be run but this
+    define can be used to reverse this in special situations
+
+    WARNING: Assembler code versions rely on PLATFORM_BYTE_ORDER being set.
+    This define will hence be redefined later (in section 4) if necessary
+*/
+
+#if 1
+#  define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER
+#elif 0
+#  define ALGORITHM_BYTE_ORDER IS_LITTLE_ENDIAN
+#elif 0
+#  define ALGORITHM_BYTE_ORDER IS_BIG_ENDIAN
+#else
+#  error The algorithm byte order is not defined
+#endif
+
+/*  2. Intel AES AND VIA ACE SUPPORT */
+
+#if defined( __GNUC__ ) && defined( __i386__ ) \
+ || defined( _WIN32 ) && defined( _M_IX86 ) && !(defined( _WIN64 ) \
+ || defined( _WIN32_WCE ) || defined( _MSC_VER ) && ( _MSC_VER <= 800 ))
+#  define VIA_ACE_POSSIBLE
+#endif
+
+#if (defined( _WIN64 ) && defined( _MSC_VER )) \
+ || (defined( __GNUC__ ) && defined( __x86_64__ )) && !(defined( __APPLE__ ))\
+ && !(defined( INTEL_AES_POSSIBLE ))
+#  define INTEL_AES_POSSIBLE
+#endif
+
+/*  Define this option if support for the Intel AESNI is required
+    If USE_INTEL_AES_IF_PRESENT is defined then AESNI will be used
+    if it is detected (both present and enabled).
+
+       AESNI uses a decryption key schedule with the first decryption
+       round key at the high end of the key scedule with the following
+       round keys at lower positions in memory.  So AES_REV_DKS must NOT
+       be defined when AESNI will be used.  ALthough it is unlikely that
+       assembler code will be used with an AESNI build, if it is then
+       AES_REV_DKS must NOT be defined when the assembler files are
+       built
+*/
+
+#if 1 && defined( INTEL_AES_POSSIBLE ) && !defined( USE_INTEL_AES_IF_PRESENT )
+#  define USE_INTEL_AES_IF_PRESENT
+#endif
+
+/*  Define this option if support for the VIA ACE is required. This uses
+    inline assembler instructions and is only implemented for the Microsoft,
+    Intel and GCC compilers.  If VIA ACE is known to be present, then defining
+    ASSUME_VIA_ACE_PRESENT will remove the ordinary encryption/decryption
+    code.  If USE_VIA_ACE_IF_PRESENT is defined then VIA ACE will be used if
+    it is detected (both present and enabled) but the normal AES code will
+    also be present.
+
+    When VIA ACE is to be used, all AES encryption contexts MUST be 16 byte
+    aligned; other input/output buffers do not need to be 16 byte aligned
+    but there are very large performance gains if this can be arranged.
+    VIA ACE also requires the decryption key schedule to be in reverse
+    order (which later checks below ensure).
+
+       AES_REV_DKS must be set for assembler code used with a VIA ACE build
+*/
+
+#if 0 && defined( VIA_ACE_POSSIBLE ) && !defined( USE_VIA_ACE_IF_PRESENT )
+#  define USE_VIA_ACE_IF_PRESENT
+#endif
+
+#if 0 && defined( VIA_ACE_POSSIBLE ) && !defined( ASSUME_VIA_ACE_PRESENT )
+#  define ASSUME_VIA_ACE_PRESENT
+#  endif
+
+/*  3. ASSEMBLER SUPPORT
+
+    This define (which can be on the command line) enables the use of the
+    assembler code routines for encryption, decryption and key scheduling
+    as follows:
+
+    ASM_X86_V1C uses the assembler (aes_x86_v1.asm) with large tables for
+                encryption and decryption and but with key scheduling in C
+    ASM_X86_V2  uses assembler (aes_x86_v2.asm) with compressed tables for
+                encryption, decryption and key scheduling
+    ASM_X86_V2C uses assembler (aes_x86_v2.asm) with compressed tables for
+                encryption and decryption and but with key scheduling in C
+    ASM_AMD64_C uses assembler (aes_amd64.asm) with compressed tables for
+                encryption and decryption and but with key scheduling in C
+
+    Change one 'if 0' below to 'if 1' to select the version or define
+    as a compilation option.
+*/
+
+#if 0 && !defined( ASM_X86_V1C )
+#  define ASM_X86_V1C
+#elif 0 && !defined( ASM_X86_V2  )
+#  define ASM_X86_V2
+#elif 0 && !defined( ASM_X86_V2C )
+#  define ASM_X86_V2C
+#elif 0 && !defined( ASM_AMD64_C )
+#  define ASM_AMD64_C
+#endif
+
+#if defined( __i386 ) || defined( _M_IX86 )
+#  define A32_
+#elif defined( __x86_64__ ) || defined( _M_X64 )
+#  define A64_
+#endif
+
+#if (defined ( ASM_X86_V1C ) || defined( ASM_X86_V2 ) || defined( ASM_X86_V2C )) \
+       && !defined( A32_ )  || defined( ASM_AMD64_C ) && !defined( A64_ )
+#  error Assembler code is only available for x86 and AMD64 systems
+#endif
+
+/*  4. FAST INPUT/OUTPUT OPERATIONS.
+
+    On some machines it is possible to improve speed by transferring the
+    bytes in the input and output arrays to and from the internal 32-bit
+    variables by addressing these arrays as if they are arrays of 32-bit
+    words.  On some machines this will always be possible but there may
+    be a large performance penalty if the byte arrays are not aligned on
+    the normal word boundaries. On other machines this technique will
+    lead to memory access errors when such 32-bit word accesses are not
+    properly aligned. The option SAFE_IO avoids such problems but will
+    often be slower on those machines that support misaligned access
+    (especially so if care is taken to align the input  and output byte
+    arrays on 32-bit word boundaries). If SAFE_IO is not defined it is
+    assumed that access to byte arrays as if they are arrays of 32-bit
+    words will not cause problems when such accesses are misaligned.
+*/
+#if 1 && !defined( _MSC_VER )
+#  define SAFE_IO
+#endif
+
+/*  5. LOOP UNROLLING
+
+    The code for encryption and decrytpion cycles through a number of rounds
+    that can be implemented either in a loop or by expanding the code into a
+    long sequence of instructions, the latter producing a larger program but
+    one that will often be much faster. The latter is called loop unrolling.
+    There are also potential speed advantages in expanding two iterations in
+    a loop with half the number of iterations, which is called partial loop
+    unrolling.  The following options allow partial or full loop unrolling
+    to be set independently for encryption and decryption
+*/
+#if 1
+#  define ENC_UNROLL  FULL
+#elif 0
+#  define ENC_UNROLL  PARTIAL
+#else
+#  define ENC_UNROLL  NONE
+#endif
+
+#if 1
+#  define DEC_UNROLL  FULL
+#elif 0
+#  define DEC_UNROLL  PARTIAL
+#else
+#  define DEC_UNROLL  NONE
+#endif
+
+#if 1
+#  define ENC_KS_UNROLL
+#endif
+
+#if 1
+#  define DEC_KS_UNROLL
+#endif
+
+/*  6. FAST FINITE FIELD OPERATIONS
+
+    If this section is included, tables are used to provide faster finite
+    field arithmetic (this has no effect if STATIC_TABLES is defined).
+*/
+#if 1
+#  define FF_TABLES
+#endif
+
+/*  7. INTERNAL STATE VARIABLE FORMAT
+
+    The internal state of Rijndael is stored in a number of local 32-bit
+    word varaibles which can be defined either as an array or as individual
+    names variables. Include this section if you want to store these local
+    varaibles in arrays. Otherwise individual local variables will be used.
+*/
+#if 1
+#  define ARRAYS
+#endif
+
+/*  8. FIXED OR DYNAMIC TABLES
+
+    When this section is included the tables used by the code are compiled
+    statically into the binary file.  Otherwise the subroutine aes_init()
+    must be called to compute them before the code is first used.
+*/
+#if 1 && !(defined( _MSC_VER ) && ( _MSC_VER <= 800 ))
+#  define STATIC_TABLES
+#endif
+
+/*  9. MASKING OR CASTING FROM LONGER VALUES TO BYTES
+
+    In some systems it is better to mask longer values to extract bytes
+    rather than using a cast. This option allows this choice.
+*/
+#if 0
+#  define to_byte(x)  ((uint8_t)(x))
+#else
+#  define to_byte(x)  ((x) & 0xff)
+#endif
+
+/*  10. TABLE ALIGNMENT
+
+    On some sytsems speed will be improved by aligning the AES large lookup
+    tables on particular boundaries. This define should be set to a power of
+    two giving the desired alignment. It can be left undefined if alignment
+    is not needed.  This option is specific to the Microsft VC++ compiler -
+    it seems to sometimes cause trouble for the VC++ version 6 compiler.
+*/
+
+#if 1 && defined( _MSC_VER ) && ( _MSC_VER >= 1300 )
+#  define TABLE_ALIGN 32
+#endif
+
+/*  11.  REDUCE CODE AND TABLE SIZE
+
+    This replaces some expanded macros with function calls if AES_ASM_V2 or
+    AES_ASM_V2C are defined
+*/
+
+#if 1 && (defined( ASM_X86_V2 ) || defined( ASM_X86_V2C ))
+#  define REDUCE_CODE_SIZE
+#endif
+
+/*  12. TABLE OPTIONS
+
+    This cipher proceeds by repeating in a number of cycles known as 'rounds'
+    which are implemented by a round function which can optionally be speeded
+    up using tables.  The basic tables are each 256 32-bit words, with either
+    one or four tables being required for each round function depending on
+    how much speed is required. The encryption and decryption round functions
+    are different and the last encryption and decrytpion round functions are
+    different again making four different round functions in all.
+
+    This means that:
+      1. Normal encryption and decryption rounds can each use either 0, 1
+         or 4 tables and table spaces of 0, 1024 or 4096 bytes each.
+      2. The last encryption and decryption rounds can also use either 0, 1
+         or 4 tables and table spaces of 0, 1024 or 4096 bytes each.
+
+    Include or exclude the appropriate definitions below to set the number
+    of tables used by this implementation.
+*/
+
+#if 1   /* set tables for the normal encryption round */
+#  define ENC_ROUND   FOUR_TABLES
+#elif 0
+#  define ENC_ROUND   ONE_TABLE
+#else
+#  define ENC_ROUND   NO_TABLES
+#endif
+
+#if 1   /* set tables for the last encryption round */
+#  define LAST_ENC_ROUND  FOUR_TABLES
+#elif 0
+#  define LAST_ENC_ROUND  ONE_TABLE
+#else
+#  define LAST_ENC_ROUND  NO_TABLES
+#endif
+
+#if 1   /* set tables for the normal decryption round */
+#  define DEC_ROUND   FOUR_TABLES
+#elif 0
+#  define DEC_ROUND   ONE_TABLE
+#else
+#  define DEC_ROUND   NO_TABLES
+#endif
+
+#if 1   /* set tables for the last decryption round */
+#  define LAST_DEC_ROUND  FOUR_TABLES
+#elif 0
+#  define LAST_DEC_ROUND  ONE_TABLE
+#else
+#  define LAST_DEC_ROUND  NO_TABLES
+#endif
+
+/*  The decryption key schedule can be speeded up with tables in the same
+    way that the round functions can.  Include or exclude the following
+    defines to set this requirement.
+*/
+#if 1
+#  define KEY_SCHED   FOUR_TABLES
+#elif 0
+#  define KEY_SCHED   ONE_TABLE
+#else
+#  define KEY_SCHED   NO_TABLES
+#endif
+
+/*  ---- END OF USER CONFIGURED OPTIONS ---- */
+
+/* VIA ACE support is only available for VC++ and GCC */
+
+#if !defined( _MSC_VER ) && !defined( __GNUC__ )
+#  if defined( ASSUME_VIA_ACE_PRESENT )
+#    undef ASSUME_VIA_ACE_PRESENT
+#  endif
+#  if defined( USE_VIA_ACE_IF_PRESENT )
+#    undef USE_VIA_ACE_IF_PRESENT
+#  endif
+#endif
+
+#if defined( ASSUME_VIA_ACE_PRESENT ) && !defined( USE_VIA_ACE_IF_PRESENT )
+#  define USE_VIA_ACE_IF_PRESENT
+#endif
+
+/* define to reverse decryption key schedule    */
+#if 1 || defined( USE_VIA_ACE_IF_PRESENT ) && !defined ( AES_REV_DKS )
+#  define AES_REV_DKS
+#endif
+
+/* Intel AESNI uses a decryption key schedule in the encryption order */
+#if defined( USE_INTEL_AES_IF_PRESENT ) && defined ( AES_REV_DKS )
+#  undef AES_REV_DKS
+#endif
+
+/* Assembler support requires the use of platform byte order */
+
+#if ( defined( ASM_X86_V1C ) || defined( ASM_X86_V2C ) || defined( ASM_AMD64_C ) ) \
+    && (ALGORITHM_BYTE_ORDER != PLATFORM_BYTE_ORDER)
+#  undef  ALGORITHM_BYTE_ORDER
+#  define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER
+#endif
+
+/* In this implementation the columns of the state array are each held in
+   32-bit words. The state array can be held in various ways: in an array
+   of words, in a number of individual word variables or in a number of
+   processor registers. The following define maps a variable name x and
+   a column number c to the way the state array variable is to be held.
+   The first define below maps the state into an array x[c] whereas the
+   second form maps the state into a number of individual variables x0,
+   x1, etc.  Another form could map individual state colums to machine
+   register names.
+*/
+
+#if defined( ARRAYS )
+#  define s(x,c) x[c]
+#else
+#  define s(x,c) x##c
+#endif
+
+/*  This implementation provides subroutines for encryption, decryption
+    and for setting the three key lengths (separately) for encryption
+    and decryption. Since not all functions are needed, masks are set
+    up here to determine which will be implemented in C
+*/
+
+#if !defined( AES_ENCRYPT )
+#  define EFUNCS_IN_C   0
+#elif defined( ASSUME_VIA_ACE_PRESENT ) || defined( ASM_X86_V1C ) \
+    || defined( ASM_X86_V2C ) || defined( ASM_AMD64_C )
+#  define EFUNCS_IN_C   ENC_KEYING_IN_C
+#elif !defined( ASM_X86_V2 )
+#  define EFUNCS_IN_C   ( ENCRYPTION_IN_C | ENC_KEYING_IN_C )
+#else
+#  define EFUNCS_IN_C   0
+#endif
+
+#if !defined( AES_DECRYPT )
+#  define DFUNCS_IN_C   0
+#elif defined( ASSUME_VIA_ACE_PRESENT ) || defined( ASM_X86_V1C ) \
+    || defined( ASM_X86_V2C ) || defined( ASM_AMD64_C )
+#  define DFUNCS_IN_C   DEC_KEYING_IN_C
+#elif !defined( ASM_X86_V2 )
+#  define DFUNCS_IN_C   ( DECRYPTION_IN_C | DEC_KEYING_IN_C )
+#else
+#  define DFUNCS_IN_C   0
+#endif
+
+#define FUNCS_IN_C  ( EFUNCS_IN_C | DFUNCS_IN_C )
+
+/* END OF CONFIGURATION OPTIONS */
+
+#define RC_LENGTH   (5 * (AES_BLOCK_SIZE / 4 - 2))
+
+/* Disable or report errors on some combinations of options */
+
+#if ENC_ROUND == NO_TABLES && LAST_ENC_ROUND != NO_TABLES
+#  undef  LAST_ENC_ROUND
+#  define LAST_ENC_ROUND  NO_TABLES
+#elif ENC_ROUND == ONE_TABLE && LAST_ENC_ROUND == FOUR_TABLES
+#  undef  LAST_ENC_ROUND
+#  define LAST_ENC_ROUND  ONE_TABLE
+#endif
+
+#if ENC_ROUND == NO_TABLES && ENC_UNROLL != NONE
+#  undef  ENC_UNROLL
+#  define ENC_UNROLL  NONE
+#endif
+
+#if DEC_ROUND == NO_TABLES && LAST_DEC_ROUND != NO_TABLES
+#  undef  LAST_DEC_ROUND
+#  define LAST_DEC_ROUND  NO_TABLES
+#elif DEC_ROUND == ONE_TABLE && LAST_DEC_ROUND == FOUR_TABLES
+#  undef  LAST_DEC_ROUND
+#  define LAST_DEC_ROUND  ONE_TABLE
+#endif
+
+#if DEC_ROUND == NO_TABLES && DEC_UNROLL != NONE
+#  undef  DEC_UNROLL
+#  define DEC_UNROLL  NONE
+#endif
+
+#if defined( bswap32 )
+#  define aes_sw32    bswap32
+#elif defined( bswap_32 )
+#  define aes_sw32    bswap_32
+#else
+#  define brot(x,n)   (((uint32_t)(x) <<  n) | ((uint32_t)(x) >> (32 - n)))
+#  define aes_sw32(x) ((brot((x),8) & 0x00ff00ff) | (brot((x),24) & 0xff00ff00))
+#endif
+
+/*  upr(x,n):  rotates bytes within words by n positions, moving bytes to
+               higher index positions with wrap around into low positions
+    ups(x,n):  moves bytes by n positions to higher index positions in
+               words but without wrap around
+    bval(x,n): extracts a byte from a word
+
+    WARNING:   The definitions given here are intended only for use with
+               unsigned variables and with shift counts that are compile
+               time constants
+*/
+
+#if ( ALGORITHM_BYTE_ORDER == IS_LITTLE_ENDIAN )
+#  define upr(x,n)      (((uint32_t)(x) << (8 * (n))) | ((uint32_t)(x) >> (32 - 8 * (n))))
+#  define ups(x,n)      ((uint32_t) (x) << (8 * (n)))
+#  define bval(x,n)     to_byte((x) >> (8 * (n)))
+#  define bytes2word(b0, b1, b2, b3)  \
+        (((uint32_t)(b3) << 24) | ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0))
+#endif
+
+#if ( ALGORITHM_BYTE_ORDER == IS_BIG_ENDIAN )
+#  define upr(x,n)      (((uint32_t)(x) >> (8 * (n))) | ((uint32_t)(x) << (32 - 8 * (n))))
+#  define ups(x,n)      ((uint32_t) (x) >> (8 * (n)))
+#  define bval(x,n)     to_byte((x) >> (24 - 8 * (n)))
+#  define bytes2word(b0, b1, b2, b3)  \
+        (((uint32_t)(b0) << 24) | ((uint32_t)(b1) << 16) | ((uint32_t)(b2) << 8) | (b3))
+#endif
+
+#if defined( SAFE_IO )
+#  define word_in(x,c)    bytes2word(((const uint8_t*)(x)+4*c)[0], ((const uint8_t*)(x)+4*c)[1], \
+                                   ((const uint8_t*)(x)+4*c)[2], ((const uint8_t*)(x)+4*c)[3])
+#  define word_out(x,c,v) { ((uint8_t*)(x)+4*c)[0] = bval(v,0); ((uint8_t*)(x)+4*c)[1] = bval(v,1); \
+                          ((uint8_t*)(x)+4*c)[2] = bval(v,2); ((uint8_t*)(x)+4*c)[3] = bval(v,3); }
+#elif ( ALGORITHM_BYTE_ORDER == PLATFORM_BYTE_ORDER )
+#  define word_in(x,c)    (*((uint32_t*)(x)+(c)))
+#  define word_out(x,c,v) (*((uint32_t*)(x)+(c)) = (v))
+#else
+#  define word_in(x,c)    aes_sw32(*((uint32_t*)(x)+(c)))
+#  define word_out(x,c,v) (*((uint32_t*)(x)+(c)) = aes_sw32(v))
+#endif
+
+/* the finite field modular polynomial and elements */
+
+#define WPOLY   0x011b
+#define BPOLY     0x1b
+
+/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+#define gf_c1  0x80808080
+#define gf_c2  0x7f7f7f7f
+#define gf_mulx(x)  ((((x) & gf_c2) << 1) ^ ((((x) & gf_c1) >> 7) * BPOLY))
+
+/* The following defines provide alternative definitions of gf_mulx that might
+   give improved performance if a fast 32-bit multiply is not available. Note
+   that a temporary variable u needs to be defined where gf_mulx is used.
+
+#define gf_mulx(x) (u = (x) & gf_c1, u |= (u >> 1), ((x) & gf_c2) << 1) ^ ((u >> 3) | (u >> 6))
+#define gf_c4  (0x01010101 * BPOLY)
+#define gf_mulx(x) (u = (x) & gf_c1, ((x) & gf_c2) << 1) ^ ((u - (u >> 7)) & gf_c4)
+*/
+
+/* Work out which tables are needed for the different options   */
+
+#if defined( ASM_X86_V1C )
+#  if defined( ENC_ROUND )
+#    undef  ENC_ROUND
+#  endif
+#  define ENC_ROUND   FOUR_TABLES
+#  if defined( LAST_ENC_ROUND )
+#    undef  LAST_ENC_ROUND
+#  endif
+#  define LAST_ENC_ROUND  FOUR_TABLES
+#  if defined( DEC_ROUND )
+#    undef  DEC_ROUND
+#  endif
+#  define DEC_ROUND   FOUR_TABLES
+#  if defined( LAST_DEC_ROUND )
+#    undef  LAST_DEC_ROUND
+#  endif
+#  define LAST_DEC_ROUND  FOUR_TABLES
+#  if defined( KEY_SCHED )
+#    undef  KEY_SCHED
+#    define KEY_SCHED   FOUR_TABLES
+#  endif
+#endif
+
+#if ( FUNCS_IN_C & ENCRYPTION_IN_C ) || defined( ASM_X86_V1C )
+#  if ENC_ROUND == ONE_TABLE
+#    define FT1_SET
+#  elif ENC_ROUND == FOUR_TABLES
+#    define FT4_SET
+#  else
+#    define SBX_SET
+#  endif
+#  if LAST_ENC_ROUND == ONE_TABLE
+#    define FL1_SET
+#  elif LAST_ENC_ROUND == FOUR_TABLES
+#    define FL4_SET
+#  elif !defined( SBX_SET )
+#    define SBX_SET
+#  endif
+#endif
+
+#if ( FUNCS_IN_C & DECRYPTION_IN_C ) || defined( ASM_X86_V1C )
+#  if DEC_ROUND == ONE_TABLE
+#    define IT1_SET
+#  elif DEC_ROUND == FOUR_TABLES
+#    define IT4_SET
+#  else
+#    define ISB_SET
+#  endif
+#  if LAST_DEC_ROUND == ONE_TABLE
+#    define IL1_SET
+#  elif LAST_DEC_ROUND == FOUR_TABLES
+#    define IL4_SET
+#  elif !defined(ISB_SET)
+#    define ISB_SET
+#  endif
+#endif
+
+#if !(defined( REDUCE_CODE_SIZE ) && (defined( ASM_X86_V2 ) || defined( ASM_X86_V2C )))
+#  if ((FUNCS_IN_C & ENC_KEYING_IN_C) || (FUNCS_IN_C & DEC_KEYING_IN_C))
+#    if KEY_SCHED == ONE_TABLE
+#      if !defined( FL1_SET )  && !defined( FL4_SET )
+#        define LS1_SET
+#      endif
+#    elif KEY_SCHED == FOUR_TABLES
+#      if !defined( FL4_SET )
+#        define LS4_SET
+#      endif
+#    elif !defined( SBX_SET )
+#      define SBX_SET
+#    endif
+#  endif
+#  if (FUNCS_IN_C & DEC_KEYING_IN_C)
+#    if KEY_SCHED == ONE_TABLE
+#      define IM1_SET
+#    elif KEY_SCHED == FOUR_TABLES
+#      define IM4_SET
+#    elif !defined( SBX_SET )
+#      define SBX_SET
+#    endif
+#  endif
+#endif
+
+/* generic definitions of Rijndael macros that use tables    */
+
+#define no_table(x,box,vf,rf,c) bytes2word( \
+    box[bval(vf(x,0,c),rf(0,c))], \
+    box[bval(vf(x,1,c),rf(1,c))], \
+    box[bval(vf(x,2,c),rf(2,c))], \
+    box[bval(vf(x,3,c),rf(3,c))])
+
+#define one_table(x,op,tab,vf,rf,c) \
+ (     tab[bval(vf(x,0,c),rf(0,c))] \
+  ^ op(tab[bval(vf(x,1,c),rf(1,c))],1) \
+  ^ op(tab[bval(vf(x,2,c),rf(2,c))],2) \
+  ^ op(tab[bval(vf(x,3,c),rf(3,c))],3))
+
+#define four_tables(x,tab,vf,rf,c) \
+ (  tab[0][bval(vf(x,0,c),rf(0,c))] \
+  ^ tab[1][bval(vf(x,1,c),rf(1,c))] \
+  ^ tab[2][bval(vf(x,2,c),rf(2,c))] \
+  ^ tab[3][bval(vf(x,3,c),rf(3,c))])
+
+#define vf1(x,r,c)  (x)
+#define rf1(r,c)    (r)
+#define rf2(r,c)    ((8+r-c)&3)
+
+/* perform forward and inverse column mix operation on four bytes in long word x in */
+/* parallel. NOTE: x must be a simple variable, NOT an expression in these macros.  */
+
+#if !(defined( REDUCE_CODE_SIZE ) && (defined( ASM_X86_V2 ) || defined( ASM_X86_V2C )))
+
+#if defined( FM4_SET )      /* not currently used */
+#  define fwd_mcol(x)       four_tables(x,t_use(f,m),vf1,rf1,0)
+#elif defined( FM1_SET )    /* not currently used */
+#  define fwd_mcol(x)       one_table(x,upr,t_use(f,m),vf1,rf1,0)
+#else
+#  define dec_fmvars        uint32_t g2
+#  define fwd_mcol(x)       (g2 = gf_mulx(x), g2 ^ upr((x) ^ g2, 3) ^ upr((x), 2) ^ upr((x), 1))
+#endif
+
+#if defined( IM4_SET )
+#  define inv_mcol(x)       four_tables(x,t_use(i,m),vf1,rf1,0)
+#elif defined( IM1_SET )
+#  define inv_mcol(x)       one_table(x,upr,t_use(i,m),vf1,rf1,0)
+#else
+#  define dec_imvars        uint32_t g2, g4, g9
+#  define inv_mcol(x)       (g2 = gf_mulx(x), g4 = gf_mulx(g2), g9 = (x) ^ gf_mulx(g4), g4 ^= g9, \
+                            (x) ^ g2 ^ g4 ^ upr(g2 ^ g9, 3) ^ upr(g4, 2) ^ upr(g9, 1))
+#endif
+
+#if defined( FL4_SET )
+#  define ls_box(x,c)       four_tables(x,t_use(f,l),vf1,rf2,c)
+#elif defined( LS4_SET )
+#  define ls_box(x,c)       four_tables(x,t_use(l,s),vf1,rf2,c)
+#elif defined( FL1_SET )
+#  define ls_box(x,c)       one_table(x,upr,t_use(f,l),vf1,rf2,c)
+#elif defined( LS1_SET )
+#  define ls_box(x,c)       one_table(x,upr,t_use(l,s),vf1,rf2,c)
+#else
+#  define ls_box(x,c)       no_table(x,t_use(s,box),vf1,rf2,c)
+#endif
+
+#endif
+
+#if defined( ASM_X86_V1C ) && defined( AES_DECRYPT ) && !defined( ISB_SET )
+#  define ISB_SET
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aestab.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aestab.c
new file mode 100644 (file)
index 0000000..3d48edf
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+*/
+
+#define DO_TABLES
+
+#include "aes.h"
+#include "aesopt.h"
+
+#if defined(STATIC_TABLES)
+
+#define sb_data(w) {\
+    w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\
+    w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\
+    w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\
+    w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\
+    w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\
+    w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\
+    w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\
+    w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\
+    w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\
+    w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\
+    w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\
+    w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\
+    w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\
+    w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\
+    w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\
+    w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\
+    w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\
+    w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\
+    w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\
+    w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\
+    w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\
+    w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\
+    w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\
+    w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\
+    w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\
+    w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\
+    w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\
+    w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\
+    w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\
+    w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\
+    w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\
+    w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) }
+
+#define isb_data(w) {\
+    w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\
+    w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\
+    w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\
+    w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\
+    w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\
+    w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\
+    w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\
+    w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\
+    w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\
+    w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\
+    w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\
+    w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\
+    w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\
+    w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\
+    w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\
+    w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\
+    w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\
+    w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\
+    w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\
+    w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\
+    w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\
+    w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\
+    w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\
+    w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\
+    w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\
+    w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\
+    w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\
+    w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\
+    w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\
+    w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\
+    w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\
+    w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) }
+
+#define mm_data(w) {\
+    w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\
+    w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\
+    w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\
+    w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\
+    w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\
+    w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\
+    w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\
+    w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\
+    w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\
+    w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\
+    w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\
+    w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\
+    w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\
+    w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\
+    w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\
+    w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\
+    w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\
+    w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\
+    w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\
+    w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\
+    w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\
+    w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\
+    w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\
+    w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\
+    w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\
+    w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\
+    w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\
+    w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\
+    w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\
+    w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\
+    w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\
+    w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) }
+
+#define rc_data(w) {\
+    w(0x01), w(0x02), w(0x04), w(0x08), w(0x10),w(0x20), w(0x40), w(0x80),\
+    w(0x1b), w(0x36) }
+
+#define h0(x)   (x)
+
+#define w0(p)   bytes2word(p, 0, 0, 0)
+#define w1(p)   bytes2word(0, p, 0, 0)
+#define w2(p)   bytes2word(0, 0, p, 0)
+#define w3(p)   bytes2word(0, 0, 0, p)
+
+#define u0(p)   bytes2word(f2(p), p, p, f3(p))
+#define u1(p)   bytes2word(f3(p), f2(p), p, p)
+#define u2(p)   bytes2word(p, f3(p), f2(p), p)
+#define u3(p)   bytes2word(p, p, f3(p), f2(p))
+
+#define v0(p)   bytes2word(fe(p), f9(p), fd(p), fb(p))
+#define v1(p)   bytes2word(fb(p), fe(p), f9(p), fd(p))
+#define v2(p)   bytes2word(fd(p), fb(p), fe(p), f9(p))
+#define v3(p)   bytes2word(f9(p), fd(p), fb(p), fe(p))
+
+#endif
+
+#if defined(STATIC_TABLES) || !defined(FF_TABLES)
+
+#define f2(x)   ((x<<1) ^ (((x>>7) & 1) * WPOLY))
+#define f4(x)   ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY))
+#define f8(x)   ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) \
+                        ^ (((x>>5) & 4) * WPOLY))
+#define f3(x)   (f2(x) ^ x)
+#define f9(x)   (f8(x) ^ x)
+#define fb(x)   (f8(x) ^ f2(x) ^ x)
+#define fd(x)   (f8(x) ^ f4(x) ^ x)
+#define fe(x)   (f8(x) ^ f4(x) ^ f2(x))
+
+#else
+
+#define f2(x) ((x) ? pow[log[x] + 0x19] : 0)
+#define f3(x) ((x) ? pow[log[x] + 0x01] : 0)
+#define f9(x) ((x) ? pow[log[x] + 0xc7] : 0)
+#define fb(x) ((x) ? pow[log[x] + 0x68] : 0)
+#define fd(x) ((x) ? pow[log[x] + 0xee] : 0)
+#define fe(x) ((x) ? pow[log[x] + 0xdf] : 0)
+
+#endif
+
+#include "aestab.h"
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#if defined(STATIC_TABLES)
+
+/* implemented in case of wrong call for fixed tables */
+
+AES_RETURN aes_init(void)
+{
+    return EXIT_SUCCESS;
+}
+
+#else   /*  Generate the tables for the dynamic table option */
+
+#if defined(FF_TABLES)
+
+#define gf_inv(x)   ((x) ? pow[ 255 - log[x]] : 0)
+
+#else
+
+/*  It will generally be sensible to use tables to compute finite
+    field multiplies and inverses but where memory is scarse this
+    code might sometimes be better. But it only has effect during
+    initialisation so its pretty unimportant in overall terms.
+*/
+
+/*  return 2 ^ (n - 1) where n is the bit number of the highest bit
+    set in x with x in the range 1 < x < 0x00000200.   This form is
+    used so that locals within fi can be bytes rather than words
+*/
+
+static uint8_t hibit(const uint32_t x)
+{   uint8_t r = (uint8_t)((x >> 1) | (x >> 2));
+
+    r |= (r >> 2);
+    r |= (r >> 4);
+    return (r + 1) >> 1;
+}
+
+/* return the inverse of the finite field element x */
+
+static uint8_t gf_inv(const uint8_t x)
+{   uint8_t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0;
+
+    if(x < 2)
+        return x;
+
+    for( ; ; )
+    {
+        if(n1)
+            while(n2 >= n1)             /* divide polynomial p2 by p1    */
+            {
+                n2 /= n1;               /* shift smaller polynomial left */
+                p2 ^= (p1 * n2) & 0xff; /* and remove from larger one    */
+                v2 ^= v1 * n2;          /* shift accumulated value and   */
+                n2 = hibit(p2);         /* add into result               */
+            }
+        else
+            return v1;
+
+        if(n2)                          /* repeat with values swapped    */
+            while(n1 >= n2)
+            {
+                n1 /= n2;
+                p1 ^= p2 * n1;
+                v1 ^= v2 * n1;
+                n1 = hibit(p1);
+            }
+        else
+            return v2;
+    }
+}
+
+#endif
+
+/* The forward and inverse affine transformations used in the S-box */
+uint8_t fwd_affine(const uint8_t x)
+{   uint32_t w = x;
+    w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4);
+    return 0x63 ^ ((w ^ (w >> 8)) & 0xff);
+}
+
+uint8_t inv_affine(const uint8_t x)
+{   uint32_t w = x;
+    w = (w << 1) ^ (w << 3) ^ (w << 6);
+    return 0x05 ^ ((w ^ (w >> 8)) & 0xff);
+}
+
+static int init = 0;
+
+AES_RETURN aes_init(void)
+{   uint32_t  i, w;
+
+#if defined(FF_TABLES)
+
+    uint8_t  pow[512], log[256];
+
+    if(init)
+        return EXIT_SUCCESS;
+    /*  log and power tables for GF(2^8) finite field with
+        WPOLY as modular polynomial - the simplest primitive
+        root is 0x03, used here to generate the tables
+    */
+
+    i = 0; w = 1;
+    do
+    {
+        pow[i] = (uint8_t)w;
+        pow[i + 255] = (uint8_t)w;
+        log[w] = (uint8_t)i++;
+        w ^=  (w << 1) ^ (w & 0x80 ? WPOLY : 0);
+    }
+    while (w != 1);
+
+#else
+    if(init)
+        return EXIT_SUCCESS;
+#endif
+
+    for(i = 0, w = 1; i < RC_LENGTH; ++i)
+    {
+        t_set(r,c)[i] = bytes2word(w, 0, 0, 0);
+        w = f2(w);
+    }
+
+    for(i = 0; i < 256; ++i)
+    {   uint8_t    b;
+
+        b = fwd_affine(gf_inv((uint8_t)i));
+        w = bytes2word(f2(b), b, b, f3(b));
+
+#if defined( SBX_SET )
+        t_set(s,box)[i] = b;
+#endif
+
+#if defined( FT1_SET )                 /* tables for a normal encryption round */
+        t_set(f,n)[i] = w;
+#endif
+#if defined( FT4_SET )
+        t_set(f,n)[0][i] = w;
+        t_set(f,n)[1][i] = upr(w,1);
+        t_set(f,n)[2][i] = upr(w,2);
+        t_set(f,n)[3][i] = upr(w,3);
+#endif
+        w = bytes2word(b, 0, 0, 0);
+
+#if defined( FL1_SET )            /* tables for last encryption round (may also   */
+        t_set(f,l)[i] = w;        /* be used in the key schedule)                 */
+#endif
+#if defined( FL4_SET )
+        t_set(f,l)[0][i] = w;
+        t_set(f,l)[1][i] = upr(w,1);
+        t_set(f,l)[2][i] = upr(w,2);
+        t_set(f,l)[3][i] = upr(w,3);
+#endif
+
+#if defined( LS1_SET )                 /* table for key schedule if t_set(f,l) above is*/
+        t_set(l,s)[i] = w;      /* not of the required form                     */
+#endif
+#if defined( LS4_SET )
+        t_set(l,s)[0][i] = w;
+        t_set(l,s)[1][i] = upr(w,1);
+        t_set(l,s)[2][i] = upr(w,2);
+        t_set(l,s)[3][i] = upr(w,3);
+#endif
+
+        b = gf_inv(inv_affine((uint8_t)i));
+        w = bytes2word(fe(b), f9(b), fd(b), fb(b));
+
+#if defined( IM1_SET )                 /* tables for the inverse mix column operation  */
+        t_set(i,m)[b] = w;
+#endif
+#if defined( IM4_SET )
+        t_set(i,m)[0][b] = w;
+        t_set(i,m)[1][b] = upr(w,1);
+        t_set(i,m)[2][b] = upr(w,2);
+        t_set(i,m)[3][b] = upr(w,3);
+#endif
+
+#if defined( ISB_SET )
+        t_set(i,box)[i] = b;
+#endif
+#if defined( IT1_SET )                 /* tables for a normal decryption round */
+        t_set(i,n)[i] = w;
+#endif
+#if defined( IT4_SET )
+        t_set(i,n)[0][i] = w;
+        t_set(i,n)[1][i] = upr(w,1);
+        t_set(i,n)[2][i] = upr(w,2);
+        t_set(i,n)[3][i] = upr(w,3);
+#endif
+        w = bytes2word(b, 0, 0, 0);
+#if defined( IL1_SET )                 /* tables for last decryption round */
+        t_set(i,l)[i] = w;
+#endif
+#if defined( IL4_SET )
+        t_set(i,l)[0][i] = w;
+        t_set(i,l)[1][i] = upr(w,1);
+        t_set(i,l)[2][i] = upr(w,2);
+        t_set(i,l)[3][i] = upr(w,3);
+#endif
+    }
+    init = 1;
+    return EXIT_SUCCESS;
+}
+
+/* 
+   Automatic code initialisation (suggested by by Henrik S. Gaßmann)
+   based on code provided by Joe Lowe and placed in the public domain at:
+   http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc
+*/
+
+#ifdef _MSC_VER
+
+#pragma section(".CRT$XCU", read)
+
+__declspec(allocate(".CRT$XCU")) void (__cdecl *aes_startup)(void) = aes_init;
+
+#elif defined(__GNUC__)
+
+static void aes_startup(void) __attribute__((constructor));
+
+static void aes_startup(void)
+{
+    aes_init();
+}
+
+#else
+
+#pragma message( "dynamic tables must be initialised manually on your system" )
+
+#endif
+
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aestab.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/aestab.h
new file mode 100644 (file)
index 0000000..8fe32d1
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+
+ This file contains the code for declaring the tables needed to implement
+ AES. The file aesopt.h is assumed to be included before this header file.
+ If there are no global variables, the definitions here can be used to put
+ the AES tables in a structure so that a pointer can then be added to the
+ AES context to pass them to the AES routines that need them.   If this
+ facility is used, the calling program has to ensure that this pointer is
+ managed appropriately.  In particular, the value of the t_dec(in,it) item
+ in the table structure must be set to zero in order to ensure that the
+ tables are initialised. In practice the three code sequences in aeskey.c
+ that control the calls to aes_init() and the aes_init() routine itself will
+ have to be changed for a specific implementation. If global variables are
+ available it will generally be preferable to use them with the precomputed
+ STATIC_TABLES option that uses static global tables.
+
+ The following defines can be used to control the way the tables
+ are defined, initialised and used in embedded environments that
+ require special features for these purposes
+
+    the 't_dec' construction is used to declare fixed table arrays
+    the 't_set' construction is used to set fixed table values
+    the 't_use' construction is used to access fixed table values
+
+    256 byte tables:
+
+        t_xxx(s,box)    => forward S box
+        t_xxx(i,box)    => inverse S box
+
+    256 32-bit word OR 4 x 256 32-bit word tables:
+
+        t_xxx(f,n)      => forward normal round
+        t_xxx(f,l)      => forward last round
+        t_xxx(i,n)      => inverse normal round
+        t_xxx(i,l)      => inverse last round
+        t_xxx(l,s)      => key schedule table
+        t_xxx(i,m)      => key schedule table
+
+    Other variables and tables:
+
+        t_xxx(r,c)      => the rcon table
+*/
+
+#if !defined( _AESTAB_H )
+#define _AESTAB_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define t_dec(m,n) t_##m##n
+#define t_set(m,n) t_##m##n
+#define t_use(m,n) t_##m##n
+
+#if defined(STATIC_TABLES)
+#  if !defined( __GNUC__ ) && (defined( __MSDOS__ ) || defined( __WIN16__ ))
+/*   make tables far data to avoid using too much DGROUP space (PG) */
+#    define CONST const far
+#  else
+#    define CONST const
+#  endif
+#else
+#  define CONST
+#endif
+
+#if defined(DO_TABLES)
+#  define EXTERN
+#else
+#  define EXTERN extern
+#endif
+
+#if defined(_MSC_VER) && defined(TABLE_ALIGN)
+#define ALIGN __declspec(align(TABLE_ALIGN))
+#else
+#define ALIGN
+#endif
+
+#if defined( __WATCOMC__ ) && ( __WATCOMC__ >= 1100 )
+#  define XP_DIR __cdecl
+#else
+#  define XP_DIR
+#endif
+
+#if defined(DO_TABLES) && defined(STATIC_TABLES)
+#define d_1(t,n,b,e)       EXTERN ALIGN CONST XP_DIR t n[256]    =   b(e)
+#define d_4(t,n,b,e,f,g,h) EXTERN ALIGN CONST XP_DIR t n[4][256] = { b(e), b(f), b(g), b(h) }
+EXTERN ALIGN CONST uint32_t t_dec(r,c)[RC_LENGTH] = rc_data(w0);
+#else
+#define d_1(t,n,b,e)       EXTERN ALIGN CONST XP_DIR t n[256]
+#define d_4(t,n,b,e,f,g,h) EXTERN ALIGN CONST XP_DIR t n[4][256]
+EXTERN ALIGN CONST uint32_t t_dec(r,c)[RC_LENGTH];
+#endif
+
+#if defined( SBX_SET )
+    d_1(uint8_t, t_dec(s,box), sb_data, h0);
+#endif
+#if defined( ISB_SET )
+    d_1(uint8_t, t_dec(i,box), isb_data, h0);
+#endif
+
+#if defined( FT1_SET )
+    d_1(uint32_t, t_dec(f,n), sb_data, u0);
+#endif
+#if defined( FT4_SET )
+    d_4(uint32_t, t_dec(f,n), sb_data, u0, u1, u2, u3);
+#endif
+
+#if defined( FL1_SET )
+    d_1(uint32_t, t_dec(f,l), sb_data, w0);
+#endif
+#if defined( FL4_SET )
+    d_4(uint32_t, t_dec(f,l), sb_data, w0, w1, w2, w3);
+#endif
+
+#if defined( IT1_SET )
+    d_1(uint32_t, t_dec(i,n), isb_data, v0);
+#endif
+#if defined( IT4_SET )
+    d_4(uint32_t, t_dec(i,n), isb_data, v0, v1, v2, v3);
+#endif
+
+#if defined( IL1_SET )
+    d_1(uint32_t, t_dec(i,l), isb_data, w0);
+#endif
+#if defined( IL4_SET )
+    d_4(uint32_t, t_dec(i,l), isb_data, w0, w1, w2, w3);
+#endif
+
+#if defined( LS1_SET )
+#if defined( FL1_SET )
+#undef  LS1_SET
+#else
+    d_1(uint32_t, t_dec(l,s), sb_data, w0);
+#endif
+#endif
+
+#if defined( LS4_SET )
+#if defined( FL4_SET )
+#undef  LS4_SET
+#else
+    d_4(uint32_t, t_dec(l,s), sb_data, w0, w1, w2, w3);
+#endif
+#endif
+
+#if defined( IM1_SET )
+    d_1(uint32_t, t_dec(i,m), mm_data, v0);
+#endif
+#if defined( IM4_SET )
+    d_4(uint32_t, t_dec(i,m), mm_data, v0, v1, v2, v3);
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/brg_endian.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/brg_endian.h
new file mode 100644 (file)
index 0000000..9ef4af5
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+*/
+
+#ifndef _BRG_ENDIAN_H
+#define _BRG_ENDIAN_H
+
+#define IS_BIG_ENDIAN      4321 /* byte 0 is most significant (mc68k) */
+#define IS_LITTLE_ENDIAN   1234 /* byte 0 is least significant (i386) */
+
+/* Include files where endian defines and byteswap functions may reside */
+#if defined( __sun )
+#  include <sys/isa_defs.h>
+#elif defined( __FreeBSD__ ) || defined( __OpenBSD__ ) || defined( __NetBSD__ )
+#  include <sys/endian.h>
+#elif defined( BSD ) && ( BSD >= 199103 ) || defined( __APPLE__ ) || \
+      defined( __CYGWIN32__ ) || defined( __DJGPP__ ) || defined( __osf__ ) || \
+      defined(__pnacl__)
+#  include <machine/endian.h>
+#elif defined( __linux__ ) || defined( __GNUC__ ) || defined( __GNU_LIBRARY__ )
+#  if !defined( __MINGW32__ ) && !defined( _AIX )
+#    include <endian.h>
+#    if !defined( __BEOS__ )
+#      include <byteswap.h>
+#    endif
+#  endif
+#endif
+
+/* Now attempt to set the define for platform byte order using any  */
+/* of the four forms SYMBOL, _SYMBOL, __SYMBOL & __SYMBOL__, which  */
+/* seem to encompass most endian symbol definitions                 */
+
+#if defined( BIG_ENDIAN ) && defined( LITTLE_ENDIAN )
+#  if defined( BYTE_ORDER ) && BYTE_ORDER == BIG_ENDIAN
+#    define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#  elif defined( BYTE_ORDER ) && BYTE_ORDER == LITTLE_ENDIAN
+#    define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#  endif
+#elif defined( BIG_ENDIAN )
+#  define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#elif defined( LITTLE_ENDIAN )
+#  define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#endif
+
+#if defined( _BIG_ENDIAN ) && defined( _LITTLE_ENDIAN )
+#  if defined( _BYTE_ORDER ) && _BYTE_ORDER == _BIG_ENDIAN
+#    define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#  elif defined( _BYTE_ORDER ) && _BYTE_ORDER == _LITTLE_ENDIAN
+#    define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#  endif
+#elif defined( _BIG_ENDIAN )
+#  define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#elif defined( _LITTLE_ENDIAN )
+#  define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#endif
+
+#if defined( __BIG_ENDIAN ) && defined( __LITTLE_ENDIAN )
+#  if defined( __BYTE_ORDER ) && __BYTE_ORDER == __BIG_ENDIAN
+#    define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#  elif defined( __BYTE_ORDER ) && __BYTE_ORDER == __LITTLE_ENDIAN
+#    define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#  endif
+#elif defined( __BIG_ENDIAN )
+#  define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#elif defined( __LITTLE_ENDIAN )
+#  define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#endif
+
+#if defined( __BIG_ENDIAN__ ) && defined( __LITTLE_ENDIAN__ )
+#  if defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __BIG_ENDIAN__
+#    define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#  elif defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __LITTLE_ENDIAN__
+#    define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#  endif
+#elif defined( __BIG_ENDIAN__ )
+#  define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#elif defined( __LITTLE_ENDIAN__ )
+#  define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#endif
+
+/*  if the platform byte order could not be determined, then try to */
+/*  set this define using common machine defines                    */
+#if !defined(PLATFORM_BYTE_ORDER)
+
+#if   defined( __alpha__ ) || defined( __alpha ) || defined( i386 )       || \
+      defined( __i386__ )  || defined( _M_I86 )  || defined( _M_IX86 )    || \
+      defined( __OS2__ )   || defined( sun386 )  || defined( __TURBOC__ ) || \
+      defined( vax )       || defined( vms )     || defined( VMS )        || \
+      defined( __VMS )     || defined( _M_X64 )
+#  define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+
+#elif defined( AMIGA )   || defined( applec )    || defined( __AS400__ )  || \
+      defined( _CRAY )   || defined( __hppa )    || defined( __hp9000 )   || \
+      defined( ibm370 )  || defined( mc68000 )   || defined( m68k )       || \
+      defined( __MRC__ ) || defined( __MVS__ )   || defined( __MWERKS__ ) || \
+      defined( sparc )   || defined( __sparc)    || defined( SYMANTEC_C ) || \
+      defined( __VOS__ ) || defined( __TIGCC__ ) || defined( __TANDEM )   || \
+      defined( THINK_C ) || defined( __VMCMS__ ) || defined( _AIX )
+#  define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+
+#elif 0     /* **** EDIT HERE IF NECESSARY **** */
+#  define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#elif 0     /* **** EDIT HERE IF NECESSARY **** */
+#  define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#else
+#  error Please edit lines 126 or 128 in brg_endian.h to set the platform byte order
+#endif
+
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/brg_types.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/brg_types.h
new file mode 100644 (file)
index 0000000..307319b
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+
+ The unsigned integer types defined here are of the form uint_<nn>t where
+ <nn> is the length of the type; for example, the unsigned 32-bit type is
+ 'uint32_t'.  These are NOT the same as the 'C99 integer types' that are
+ defined in the inttypes.h and stdint.h headers since attempts to use these
+ types have shown that support for them is still highly variable.  However,
+ since the latter are of the form uint<nn>_t, a regular expression search
+ and replace (in VC++ search on 'uint_{:z}t' and replace with 'uint\1_t')
+ can be used to convert the types used here to the C99 standard types.
+*/
+
+#ifndef _BRG_TYPES_H
+#define _BRG_TYPES_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include <limits.h>
+#include <stdint.h>
+
+#if defined( _MSC_VER ) && ( _MSC_VER >= 1300 )
+#  include <stddef.h>
+#  define ptrint_t intptr_t
+#elif defined( __ECOS__ )
+#  define intptr_t unsigned int
+#  define ptrint_t intptr_t
+#elif defined( __GNUC__ ) && ( __GNUC__ >= 3 )
+#  define ptrint_t intptr_t
+#else
+#  define ptrint_t int
+#endif
+
+#ifndef BRG_UI32
+#  define BRG_UI32
+#  if UINT_MAX == 4294967295u
+#    define li_32(h) 0x##h##u
+#  elif ULONG_MAX == 4294967295u
+#    define li_32(h) 0x##h##ul
+#  elif defined( _CRAY )
+#    error This code needs 32-bit data types, which Cray machines do not provide
+#  else
+#    error Please define uint32_t as a 32-bit unsigned integer type in brg_types.h
+#  endif
+#endif
+
+#ifndef BRG_UI64
+#  if defined( __BORLANDC__ ) && !defined( __MSDOS__ )
+#    define BRG_UI64
+#    define li_64(h) 0x##h##ui64
+#  elif defined( _MSC_VER ) && ( _MSC_VER < 1300 )    /* 1300 == VC++ 7.0 */
+#    define BRG_UI64
+#    define li_64(h) 0x##h##ui64
+#  elif defined( __sun ) && defined( ULONG_MAX ) && ULONG_MAX == 0xfffffffful
+#    define BRG_UI64
+#    define li_64(h) 0x##h##ull
+#  elif defined( __MVS__ )
+#    define BRG_UI64
+#    define li_64(h) 0x##h##ull
+#  elif defined( UINT_MAX ) && UINT_MAX > 4294967295u
+#    if UINT_MAX == 18446744073709551615u
+#      define BRG_UI64
+#      define li_64(h) 0x##h##u
+#    endif
+#  elif defined( ULONG_MAX ) && ULONG_MAX > 4294967295u
+#    if ULONG_MAX == 18446744073709551615ul
+#      define BRG_UI64
+#      define li_64(h) 0x##h##ul
+#    endif
+#  elif defined( ULLONG_MAX ) && ULLONG_MAX > 4294967295u
+#    if ULLONG_MAX == 18446744073709551615ull
+#      define BRG_UI64
+#      define li_64(h) 0x##h##ull
+#    endif
+#  elif defined( ULONG_LONG_MAX ) && ULONG_LONG_MAX > 4294967295u
+#    if ULONG_LONG_MAX == 18446744073709551615ull
+#      define BRG_UI64
+#      define li_64(h) 0x##h##ull
+#    endif
+#  endif
+#endif
+
+#if !defined( BRG_UI64 )
+#  if defined( NEED_UINT_64T )
+#    error Please define uint64_t as an unsigned 64 bit type in brg_types.h
+#  endif
+#endif
+
+#ifndef RETURN_VALUES
+#  define RETURN_VALUES
+#  if defined( DLL_EXPORT )
+#    if defined( _MSC_VER ) || defined ( __INTEL_COMPILER )
+#      define VOID_RETURN    __declspec( dllexport ) void __stdcall
+#      define INT_RETURN     __declspec( dllexport ) int  __stdcall
+#    elif defined( __GNUC__ )
+#      define VOID_RETURN    __declspec( __dllexport__ ) void
+#      define INT_RETURN     __declspec( __dllexport__ ) int
+#    else
+#      error Use of the DLL is only available on the Microsoft, Intel and GCC compilers
+#    endif
+#  elif defined( DLL_IMPORT )
+#    if defined( _MSC_VER ) || defined ( __INTEL_COMPILER )
+#      define VOID_RETURN    __declspec( dllimport ) void __stdcall
+#      define INT_RETURN     __declspec( dllimport ) int  __stdcall
+#    elif defined( __GNUC__ )
+#      define VOID_RETURN    __declspec( __dllimport__ ) void
+#      define INT_RETURN     __declspec( __dllimport__ ) int
+#    else
+#      error Use of the DLL is only available on the Microsoft, Intel and GCC compilers
+#    endif
+#  elif defined( __WATCOMC__ )
+#    define VOID_RETURN  void __cdecl
+#    define INT_RETURN   int  __cdecl
+#  else
+#    define VOID_RETURN  void
+#    define INT_RETURN   int
+#  endif
+#endif
+
+/*     These defines are used to detect and set the memory alignment of pointers.
+    Note that offsets are in bytes.
+
+    ALIGN_OFFSET(x,n)                  return the positive or zero offset of 
+                                the memory addressed by the pointer 'x' 
+                                from an address that is aligned on an 
+                                'n' byte boundary ('n' is a power of 2)
+
+    ALIGN_FLOOR(x,n)                   return a pointer that points to memory
+                                that is aligned on an 'n' byte boundary 
+                                and is not higher than the memory address
+                                pointed to by 'x' ('n' is a power of 2)
+
+    ALIGN_CEIL(x,n)                            return a pointer that points to memory
+                                that is aligned on an 'n' byte boundary 
+                                and is not lower than the memory address
+                                pointed to by 'x' ('n' is a power of 2)
+*/
+
+#define ALIGN_OFFSET(x,n)      (((ptrint_t)(x)) & ((n) - 1))
+#define ALIGN_FLOOR(x,n)       ((uint8_t*)(x) - ( ((ptrint_t)(x)) & ((n) - 1)))
+#define ALIGN_CEIL(x,n)                ((uint8_t*)(x) + (-((ptrint_t)(x)) & ((n) - 1)))
+
+/*  These defines are used to declare buffers in a way that allows
+    faster operations on longer variables to be used.  In all these
+    defines 'size' must be a power of 2 and >= 8. NOTE that the 
+    buffer size is in bytes but the type length is in bits
+
+    UNIT_TYPEDEF(x,size)        declares a variable 'x' of length 
+                                'size' bits
+
+    BUFR_TYPEDEF(x,size,bsize)  declares a buffer 'x' of length 'bsize' 
+                                bytes defined as an array of variables
+                                each of 'size' bits (bsize must be a 
+                                multiple of size / 8)
+
+    UNIT_CAST(x,size)           casts a variable to a type of 
+                                length 'size' bits
+
+    UPTR_CAST(x,size)           casts a pointer to a pointer to a 
+                                varaiable of length 'size' bits
+*/
+
+#define UI_TYPE(size)               uint##size##_t
+#define UNIT_TYPEDEF(x,size)        typedef UI_TYPE(size) x
+#define BUFR_TYPEDEF(x,size,bsize)  typedef UI_TYPE(size) x[bsize / (size >> 3)]
+#define UNIT_CAST(x,size)           ((UI_TYPE(size) )(x))  
+#define UPTR_CAST(x,size)           ((UI_TYPE(size)*)(x))
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/fileenc.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/fileenc.c
new file mode 100644 (file)
index 0000000..31a8994
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2002, Dr Brian Gladman <                 >, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+   1. distributions of this source code include the above copyright
+      notice, this list of conditions and the following disclaimer;
+
+   2. distributions in binary form include the above copyright
+      notice, this list of conditions and the following disclaimer
+      in the documentation and/or other associated materials;
+
+   3. the copyright holder's name is not used to endorse products
+      built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ -------------------------------------------------------------------------
+ Issue Date: 24/01/2003
+
+ This file implements password based file encryption and authentication 
+ using AES in CTR mode, HMAC-SHA1 authentication and RFC2898 password 
+ based key derivation.
+
+*/
+
+#include <string.h>
+
+#include "fileenc.h"
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/* subroutine for data encryption/decryption    */
+/* this could be speeded up a lot by aligning   */
+/* buffers and using 32 bit operations          */
+
+static void encr_data(unsigned char data[], unsigned long d_len, fcrypt_ctx cx[1])
+{
+    unsigned int i = 0, pos = cx->encr_pos;
+
+    while (i < d_len)
+    {
+        if (pos == AES_BLOCK_SIZE)
+        {
+            unsigned int j = 0;
+            /* increment encryption nonce   */
+            while (j < 8 && !++cx->nonce[j])
+                ++j;
+            /* encrypt the nonce to form next xor buffer    */
+            aes_encrypt(cx->nonce, cx->encr_bfr, cx->encr_ctx);
+            pos = 0;
+        }
+
+        data[i++] ^= cx->encr_bfr[pos++];
+    }
+
+    cx->encr_pos = pos;
+}
+
+int fcrypt_init(
+    int mode,                               /* the mode to be used (input)          */
+    const unsigned char pwd[],              /* the user specified password (input)  */
+    unsigned int pwd_len,                   /* the length of the password (input)   */
+    const unsigned char salt[],             /* the salt (input)                     */
+#ifdef PASSWORD_VERIFIER
+    unsigned char pwd_ver[PWD_VER_LENGTH],  /* 2 byte password verifier (output)    */
+#endif
+    fcrypt_ctx      cx[1])                  /* the file encryption context (output) */
+{   unsigned char kbuf[2 * MAX_KEY_LENGTH + PWD_VER_LENGTH];
+
+    if (pwd_len > MAX_PWD_LENGTH)
+        return PASSWORD_TOO_LONG;
+
+    if (mode < 1 || mode > 3)
+        return BAD_MODE;
+
+    cx->mode = mode;
+    cx->pwd_len = pwd_len;
+
+    /* derive the encryption and authentication keys and the password verifier   */
+    derive_key(pwd, pwd_len, salt, SALT_LENGTH(mode), KEYING_ITERATIONS,
+                        kbuf, 2 * KEY_LENGTH(mode) + PWD_VER_LENGTH);
+
+    /* initialise the encryption nonce and buffer pos   */
+    cx->encr_pos = AES_BLOCK_SIZE;
+    /* if we need a random component in the encryption  */
+    /* nonce, this is where it would have to be set     */
+    memset(cx->nonce, 0, AES_BLOCK_SIZE * sizeof(unsigned char));
+
+    /* initialise for encryption using key 1            */
+    aes_encrypt_key(kbuf, KEY_LENGTH(mode), cx->encr_ctx);
+
+    /* initialise for authentication using key 2        */
+    hmac_sha_begin(HMAC_SHA1, cx->auth_ctx);
+    hmac_sha_key(kbuf + KEY_LENGTH(mode), KEY_LENGTH(mode), cx->auth_ctx);
+
+#ifdef PASSWORD_VERIFIER
+    memcpy(pwd_ver, kbuf + 2 * KEY_LENGTH(mode), PWD_VER_LENGTH);
+#endif
+
+    return GOOD_RETURN;
+}
+
+/* perform 'in place' encryption and authentication */
+
+void fcrypt_encrypt(unsigned char data[], unsigned int data_len, fcrypt_ctx cx[1])
+{
+    encr_data(data, data_len, cx);
+    hmac_sha_data(data, data_len, cx->auth_ctx);
+}
+
+/* perform 'in place' authentication and decryption */
+
+void fcrypt_decrypt(unsigned char data[], unsigned int data_len, fcrypt_ctx cx[1])
+{
+    hmac_sha_data(data, data_len, cx->auth_ctx);
+    encr_data(data, data_len, cx);
+}
+
+/* close encryption/decryption and return the MAC value */
+
+int fcrypt_end(unsigned char mac[], fcrypt_ctx cx[1])
+{
+    hmac_sha_end(mac, MAC_LENGTH(cx->mode), cx->auth_ctx);
+    return MAC_LENGTH(cx->mode);    /* return MAC length in bytes   */
+}
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/fileenc.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/fileenc.h
new file mode 100644 (file)
index 0000000..9c0250e
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2002, Dr Brian Gladman <                 >, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+   1. distributions of this source code include the above copyright
+      notice, this list of conditions and the following disclaimer;
+
+   2. distributions in binary form include the above copyright
+      notice, this list of conditions and the following disclaimer
+      in the documentation and/or other associated materials;
+
+   3. the copyright holder's name is not used to endorse products
+      built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 24/01/2003
+
+ This file contains the header file for fileenc.c, which implements password
+ based file encryption and authentication using AES in CTR mode, HMAC-SHA1 
+ authentication and RFC2898 password based key derivation.
+*/
+
+#ifndef _FENC_H
+#define _FENC_H
+
+#include "aes.h"
+#include "hmac.h"
+#include "pwd2key.h"
+
+#define PASSWORD_VERIFIER
+
+#define MAX_KEY_LENGTH        32
+#define MAX_PWD_LENGTH       128
+#define MAX_SALT_LENGTH       16
+#define KEYING_ITERATIONS   1000
+
+#ifdef  PASSWORD_VERIFIER
+#define PWD_VER_LENGTH         2
+#else
+#define PWD_VER_LENGTH         0
+#endif
+
+#define GOOD_RETURN            0
+#define PASSWORD_TOO_LONG   -100
+#define BAD_MODE            -101
+
+/*
+    Field lengths (in bytes) versus File Encryption Mode (0 < mode < 4)
+
+    Mode Key Salt  MAC Overhead
+       1  16    8   10       18
+       2  24   12   10       22
+       3  32   16   10       26
+
+   The following macros assume that the mode value is correct.
+*/
+
+#define KEY_LENGTH(mode)        (8 * (mode & 3) + 8)
+#define SALT_LENGTH(mode)       (4 * (mode & 3) + 4)
+#define MAC_LENGTH(mode)        (10)
+
+/* the context for file encryption   */
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+typedef struct
+{   unsigned char   nonce[AES_BLOCK_SIZE];      /* the CTR nonce          */
+    unsigned char   encr_bfr[AES_BLOCK_SIZE];   /* encrypt buffer         */
+    aes_encrypt_ctx encr_ctx[1];                /* encryption context     */
+    hmac_ctx        auth_ctx[1];                /* authentication context */
+    unsigned int    encr_pos;                   /* block position (enc)   */
+    unsigned int    pwd_len;                    /* password length        */
+    unsigned int    mode;                       /* File encryption mode   */
+} fcrypt_ctx;
+
+/* initialise file encryption or decryption */
+
+int fcrypt_init(
+    int mode,                               /* the mode to be used (input)          */
+    const unsigned char pwd[],              /* the user specified password (input)  */
+    unsigned int pwd_len,                   /* the length of the password (input)   */
+    const unsigned char salt[],             /* the salt (input)                     */
+#ifdef PASSWORD_VERIFIER
+    unsigned char pwd_ver[PWD_VER_LENGTH],  /* 2 byte password verifier (output)    */
+#endif
+    fcrypt_ctx      cx[1]);                 /* the file encryption context (output) */
+
+/* perform 'in place' encryption or decryption and authentication               */
+
+void fcrypt_encrypt(unsigned char data[], unsigned int data_len, fcrypt_ctx cx[1]);
+void fcrypt_decrypt(unsigned char data[], unsigned int data_len, fcrypt_ctx cx[1]);
+
+/* close encryption/decryption and return the MAC value */
+/* the return value is the length of the MAC            */
+
+int fcrypt_end(unsigned char mac[],     /* the MAC value (output)   */
+               fcrypt_ctx cx[1]);       /* the context (input)      */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/hmac.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/hmac.c
new file mode 100644 (file)
index 0000000..face1fb
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+
+This is an implementation of HMAC, the FIPS standard keyed hash function
+*/
+
+#include "hmac.h"
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/* initialise the HMAC context to zero */
+int hmac_sha_begin(enum hmac_hash hash, hmac_ctx cx[1])
+{
+    memset(cx, 0, sizeof(hmac_ctx));
+    switch(hash)
+    {
+#ifdef SHA_1
+    case HMAC_SHA1:
+        cx->f_begin = (hf_begin *)sha1_begin;
+        cx->f_hash  = (hf_hash *)sha1_hash;
+        cx->f_end   = (hf_end *)sha1_end;
+        cx->input_len = SHA1_BLOCK_SIZE;
+        cx->output_len = SHA1_DIGEST_SIZE;
+        break;
+#endif
+#ifdef SHA_224
+    case HMAC_SHA224:
+        cx->f_begin = (hf_begin *)sha224_begin;
+        cx->f_hash  = (hf_hash *)sha224_hash;
+        cx->f_end   = (hf_end *)sha224_end;
+        cx->input_len = SHA224_BLOCK_SIZE;
+        cx->output_len = SHA224_DIGEST_SIZE;
+        break;
+#endif
+#ifdef SHA_256
+    case HMAC_SHA256:
+        cx->f_begin = (hf_begin *)sha256_begin;
+        cx->f_hash  = (hf_hash *)sha256_hash;
+        cx->f_end   = (hf_end *)sha256_end;
+        cx->input_len = SHA256_BLOCK_SIZE;
+        cx->output_len = SHA256_DIGEST_SIZE;
+        break;
+#endif
+#ifdef SHA_384
+    case HMAC_SHA384:
+        cx->f_begin = (hf_begin *)sha384_begin;
+        cx->f_hash  = (hf_hash *)sha384_hash;
+        cx->f_end   = (hf_end *)sha384_end;
+        cx->input_len = SHA384_BLOCK_SIZE;
+        cx->output_len = SHA384_DIGEST_SIZE;
+        break;
+#endif
+#ifdef SHA_512
+    case HMAC_SHA512:
+        cx->f_begin = (hf_begin *)sha512_begin;
+        cx->f_hash  = (hf_hash *)sha512_hash;
+        cx->f_end   = (hf_end *)sha512_end;
+        cx->input_len = SHA512_BLOCK_SIZE;
+        cx->output_len = SHA512_DIGEST_SIZE;
+        break;
+    case HMAC_SHA512_256:
+        cx->f_begin = (hf_begin *)sha512_256_begin;
+        cx->f_hash  = (hf_hash *)sha512_256_hash;
+        cx->f_end   = (hf_end *)sha512_256_end;
+        cx->input_len = SHA512_256_BLOCK_SIZE;
+        cx->output_len = SHA512_256_DIGEST_SIZE;
+        break;
+    case HMAC_SHA512_224:
+        cx->f_begin = (hf_begin *)sha512_224_begin;
+        cx->f_hash  = (hf_hash *)sha512_224_hash;
+        cx->f_end   = (hf_end *)sha512_224_end;
+        cx->input_len = SHA512_224_BLOCK_SIZE;
+        cx->output_len = SHA512_224_DIGEST_SIZE;
+        break;
+    case HMAC_SHA512_192:
+        cx->f_begin = (hf_begin *)sha512_192_begin;
+        cx->f_hash  = (hf_hash *)sha512_192_hash;
+        cx->f_end   = (hf_end *)sha512_192_end;
+        cx->input_len = SHA512_192_BLOCK_SIZE;
+        cx->output_len = SHA512_192_DIGEST_SIZE;
+        break;
+    case HMAC_SHA512_128:
+        cx->f_begin = (hf_begin *)sha512_128_begin;
+        cx->f_hash  = (hf_hash *)sha512_128_hash;
+        cx->f_end   = (hf_begin *)sha512_128_end;
+        cx->input_len = SHA512_128_BLOCK_SIZE;
+        cx->output_len = SHA512_128_DIGEST_SIZE;
+        break;
+#endif
+    }
+    return (int)cx->output_len;
+}
+
+/* input the HMAC key (can be called multiple times)    */
+int hmac_sha_key(const unsigned char key[], unsigned long key_len, hmac_ctx cx[1])
+{
+    if(cx->klen == HMAC_IN_DATA)                /* error if further key input   */
+        return EXIT_FAILURE;                    /* is attempted in data mode    */
+
+    if(cx->klen + key_len > cx->input_len)      /* if the key has to be hashed  */
+    {
+        if(cx->klen <= cx->input_len)           /* if the hash has not yet been */
+        {                                       /* started, initialise it and   */
+            cx->f_begin(cx->sha_ctx);           /* hash stored key characters   */
+            cx->f_hash(cx->key, cx->klen, cx->sha_ctx);
+        }
+
+        cx->f_hash(key, key_len, cx->sha_ctx);  /* hash long key data into hash */
+    }
+    else                                        /* otherwise store key data     */
+        memcpy(cx->key + cx->klen, key, key_len);
+
+    cx->klen += key_len;                        /* update the key length count  */
+    return EXIT_SUCCESS;
+}
+
+/* input the HMAC data (can be called multiple times) - */
+/* note that this call terminates the key input phase   */
+void hmac_sha_data(const unsigned char data[], unsigned long data_len, hmac_ctx cx[1])
+{   unsigned int i;
+
+    if(cx->klen != HMAC_IN_DATA)                /* if not yet in data phase */
+    {
+        if(cx->klen > cx->input_len)            /* if key is being hashed   */
+        {                                       /* complete the hash and    */
+            cx->f_end(cx->key, cx->sha_ctx);    /* store the result as the  */
+            cx->klen = cx->output_len;          /* key and set new length   */
+        }
+
+        /* pad the key if necessary */
+        memset(cx->key + cx->klen, 0, cx->input_len - cx->klen);
+
+        /* xor ipad into key value  */
+        for(i = 0; i < (cx->input_len >> 2); ++i)
+            ((uint32_t*)cx->key)[i] ^= 0x36363636;
+
+        /* and start hash operation */
+        cx->f_begin(cx->sha_ctx);
+        cx->f_hash(cx->key, cx->input_len, cx->sha_ctx);
+
+        /* mark as now in data mode */
+        cx->klen = HMAC_IN_DATA;
+    }
+
+    /* hash the data (if any)       */
+    if(data_len)
+        cx->f_hash(data, data_len, cx->sha_ctx);
+}
+
+/* compute and output the MAC value */
+void hmac_sha_end(unsigned char mac[], unsigned long mac_len, hmac_ctx cx[1])
+{   unsigned char dig[HMAC_MAX_OUTPUT_SIZE];
+    unsigned int i;
+
+    /* if no data has been entered perform a null data phase        */
+    if(cx->klen != HMAC_IN_DATA)
+        hmac_sha_data((const unsigned char*)0, 0, cx);
+
+    cx->f_end(dig, cx->sha_ctx);        /* complete the inner hash       */
+
+    /* set outer key value using opad and removing ipad */
+    for(i = 0; i < (cx->input_len >> 2); ++i)
+        ((uint32_t*)cx->key)[i] ^= 0x36363636 ^ 0x5c5c5c5c;
+
+    /* perform the outer hash operation */
+    cx->f_begin(cx->sha_ctx);
+    cx->f_hash(cx->key, cx->input_len, cx->sha_ctx);
+    cx->f_hash(dig, cx->output_len, cx->sha_ctx);
+    cx->f_end(dig, cx->sha_ctx);
+
+    /* output the hash value            */
+    for(i = 0; i < mac_len; ++i)
+        mac[i] = dig[i];
+}
+
+/* 'do it all in one go' subroutine     */
+void hmac_sha(enum hmac_hash hash, const unsigned char key[], unsigned long key_len,
+          const unsigned char data[], unsigned long data_len,
+          unsigned char mac[], unsigned long mac_len)
+{   hmac_ctx    cx[1];
+
+    hmac_sha_begin(hash, cx);
+    hmac_sha_key(key, key_len, cx);
+    hmac_sha_data(data, data_len, cx);
+    hmac_sha_end(mac, mac_len, cx);
+}
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/hmac.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/hmac.h
new file mode 100644 (file)
index 0000000..46956c4
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+
+This is an implementation of HMAC, the FIPS standard keyed hash function
+*/
+
+#ifndef _HMAC2_H
+#define _HMAC2_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#include "sha1.h"
+
+#if defined(SHA_224) || defined(SHA_256) || defined(SHA_384) || defined(SHA_512)
+#define HMAC_MAX_OUTPUT_SIZE SHA2_MAX_DIGEST_SIZE
+#define HMAC_MAX_BLOCK_SIZE SHA2_MAX_BLOCK_SIZE
+#else 
+#define HMAC_MAX_OUTPUT_SIZE SHA1_DIGEST_SIZE
+#define HMAC_MAX_BLOCK_SIZE SHA1_BLOCK_SIZE
+#endif
+
+#define HMAC_IN_DATA  0xffffffff
+
+enum hmac_hash  
+{ 
+#ifdef SHA_1
+    HMAC_SHA1, 
+#endif
+#ifdef SHA_224 
+    HMAC_SHA224, 
+#endif
+#ifdef SHA_256
+    HMAC_SHA256, 
+#endif
+#ifdef SHA_384
+    HMAC_SHA384, 
+#endif
+#ifdef SHA_512
+    HMAC_SHA512, 
+    HMAC_SHA512_256,
+    HMAC_SHA512_224,
+    HMAC_SHA512_192,
+    HMAC_SHA512_128
+#endif
+};
+
+typedef VOID_RETURN hf_begin(void*);
+typedef VOID_RETURN hf_hash(const void*, unsigned long len, void*);
+typedef VOID_RETURN hf_end(void*, void*);
+
+typedef struct
+{   hf_begin        *f_begin;
+    hf_hash         *f_hash;
+    hf_end          *f_end;
+    unsigned char   key[HMAC_MAX_BLOCK_SIZE];
+    union
+    {
+#ifdef SHA_1
+       sha1_ctx    u_sha1;
+#endif
+#ifdef SHA_224
+        sha224_ctx  u_sha224;
+#endif
+#ifdef SHA_256
+        sha256_ctx  u_sha256;
+#endif
+#ifdef SHA_384
+        sha384_ctx  u_sha384;
+#endif
+#ifdef SHA_512
+        sha512_ctx  u_sha512;
+#endif
+    } sha_ctx[1];
+    unsigned long   input_len;
+    unsigned long   output_len;
+    unsigned long   klen;
+} hmac_ctx;
+
+/* returns the length of hash digest for the hash used  */
+/* mac_len must not be greater than this                */
+int hmac_sha_begin(enum hmac_hash hash, hmac_ctx cx[1]);
+
+int  hmac_sha_key(const unsigned char key[], unsigned long key_len, hmac_ctx cx[1]);
+
+void hmac_sha_data(const unsigned char data[], unsigned long data_len, hmac_ctx cx[1]);
+
+void hmac_sha_end(unsigned char mac[], unsigned long mac_len, hmac_ctx cx[1]);
+
+void hmac_sha(enum hmac_hash hash, const unsigned char key[], unsigned long key_len,
+          const unsigned char data[], unsigned long data_len,
+          unsigned char mac[], unsigned long mac_len);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/prng.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/prng.c
new file mode 100644 (file)
index 0000000..b90fcae
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2002, Dr Brian Gladman <                 >, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+   1. distributions of this source code include the above copyright
+      notice, this list of conditions and the following disclaimer;
+
+   2. distributions in binary form include the above copyright
+      notice, this list of conditions and the following disclaimer
+      in the documentation and/or other associated materials;
+
+   3. the copyright holder's name is not used to endorse products
+      built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 24/01/2003
+
+ This file implements a random data pool based on the use of an external
+ entropy function.  It is based on the ideas advocated by Peter Gutmann in
+ his work on pseudo random sequence generators.  It is not a 'paranoid'
+ random sequence generator and no attempt is made to protect the pool
+ from prying eyes either by memory locking or by techniques to obscure
+ its location in memory.
+*/
+
+#include <string.h>
+#include "prng.h"
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/* mix a random data pool using the SHA1 compression function (as   */
+/* suggested by Peter Gutmann in his paper on random pools)         */
+
+static void prng_mix(unsigned char buf[])
+{   unsigned int    i, len;
+    sha1_ctx        ctx[1];
+
+    /*lint -e{663}  unusual array to pointer conversion */
+    for(i = 0; i < PRNG_POOL_SIZE; i += SHA1_DIGEST_SIZE)
+    {
+        /* copy digest size pool block into SHA1 hash block */
+        memcpy(ctx->hash, buf + (i ? i : PRNG_POOL_SIZE)
+                            - SHA1_DIGEST_SIZE, SHA1_DIGEST_SIZE);
+
+        /* copy data from pool into the SHA1 data buffer    */
+        len = PRNG_POOL_SIZE - i;
+        memcpy(ctx->wbuf, buf + i, (len > SHA1_BLOCK_SIZE ? SHA1_BLOCK_SIZE : len));
+
+        if(len < SHA1_BLOCK_SIZE)
+            memcpy(((char*)ctx->wbuf) + len, buf, SHA1_BLOCK_SIZE - len);
+
+        /* compress using the SHA1 compression function     */
+        sha1_compile(ctx);
+
+        /* put digest size block back into the random pool  */
+        memcpy(buf + i, ctx->hash, SHA1_DIGEST_SIZE);
+    }
+}
+
+/* refresh the output buffer and update the random pool by adding   */
+/* entropy and remixing                                             */
+
+static void update_pool(prng_ctx ctx[1])
+{   unsigned int    i = 0;
+
+    /* transfer random pool data to the output buffer   */
+    memcpy(ctx->obuf, ctx->rbuf, PRNG_POOL_SIZE);
+
+    /* enter entropy data into the pool */
+    while(i < PRNG_POOL_SIZE)
+        i += ctx->entropy(ctx->rbuf + i, PRNG_POOL_SIZE - i);
+
+    /* invert and xor the original pool data into the pool  */
+    for(i = 0; i < PRNG_POOL_SIZE; ++i)
+        ctx->rbuf[i] ^= ~ctx->obuf[i];
+
+    /* mix the pool and the output buffer   */
+    prng_mix(ctx->rbuf);
+    prng_mix(ctx->obuf);
+}
+
+void prng_init(prng_entropy_fn fun, prng_ctx ctx[1])
+{   int i;
+
+    /* clear the buffers and the counter in the context     */
+    memset(ctx, 0, sizeof(prng_ctx));
+
+    /* set the pointer to the entropy collection function   */
+    ctx->entropy = fun;
+
+    /* initialise the random data pool                      */
+    update_pool(ctx);
+
+    /* mix the pool a minimum number of times               */
+    for(i = 0; i < PRNG_MIN_MIX; ++i)
+        prng_mix(ctx->rbuf);
+
+    /* update the pool to prime the pool output buffer      */
+    update_pool(ctx);
+}
+
+/* provide random bytes from the random data pool   */
+
+void prng_rand(unsigned char data[], unsigned int data_len, prng_ctx ctx[1])
+{   unsigned char   *rp = data;
+    unsigned int    len, pos = ctx->pos;
+
+    while(data_len)
+    {
+        /* transfer 'data_len' bytes (or the number of bytes remaining  */
+        /* the pool output buffer if less) into the output              */
+        len = (data_len < PRNG_POOL_SIZE - pos ? data_len : PRNG_POOL_SIZE - pos);
+        memcpy(rp, ctx->obuf + pos, len);
+        rp += len;          /* update ouput buffer position pointer     */
+        pos += len;         /* update pool output buffer pointer        */
+        data_len -= len;    /* update the remaining data count          */
+
+        /* refresh the random pool if necessary */
+        if(pos == PRNG_POOL_SIZE)
+        {
+            update_pool(ctx); pos = 0;
+        }
+    }
+
+    ctx->pos = pos;
+}
+
+void prng_end(prng_ctx ctx[1])
+{
+    /* ensure the data in the context is destroyed  */
+    memset(ctx, 0, sizeof(prng_ctx));
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/prng.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/prng.h
new file mode 100644 (file)
index 0000000..9a77426
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2002, Dr Brian Gladman <                 >, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+   1. distributions of this source code include the above copyright
+      notice, this list of conditions and the following disclaimer;
+
+   2. distributions in binary form include the above copyright
+      notice, this list of conditions and the following disclaimer
+      in the documentation and/or other associated materials;
+
+   3. the copyright holder's name is not used to endorse products
+      built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 24/01/2003
+
+ This is the header file for an implementation of a random data pool based on
+ the use of an external entropy function (inspired by Peter Gutmann's work).
+*/
+
+#ifndef _PRNG_H
+#define _PRNG_H
+
+#include "sha1.h"
+
+#define PRNG_POOL_LEN    256    /* minimum random pool size             */
+#define PRNG_MIN_MIX      20    /* min initial pool mixing iterations   */
+
+/* ensure that pool length is a multiple of the SHA1 digest size        */
+
+#define PRNG_POOL_SIZE  (SHA1_DIGEST_SIZE * (1 + (PRNG_POOL_LEN - 1) / SHA1_DIGEST_SIZE))
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/* A function for providing entropy is a parameter in the prng_init()   */
+/* call.  This function has the following form and returns a maximum    */
+/* of 'len' bytes of pseudo random data in the buffer 'buf'.  It can    */
+/* return less than 'len' bytes but will be repeatedly called for more  */
+/* data in this case.                                                   */
+
+typedef int (*prng_entropy_fn)(unsigned char buf[], unsigned int len);
+
+typedef struct
+{   unsigned char   rbuf[PRNG_POOL_SIZE];   /* the random pool          */
+    unsigned char   obuf[PRNG_POOL_SIZE];   /* pool output buffer       */
+    unsigned int    pos;                    /* output buffer position   */
+    prng_entropy_fn entropy;                /* entropy function pointer */
+} prng_ctx;
+
+/* initialise the random stream generator   */
+void prng_init(prng_entropy_fn fun, prng_ctx ctx[1]);
+
+/* obtain random bytes from the generator   */
+void prng_rand(unsigned char data[], unsigned int data_len, prng_ctx ctx[1]);
+
+/* close the random stream generator        */
+void prng_end(prng_ctx ctx[1]);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/pwd2key.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/pwd2key.c
new file mode 100644 (file)
index 0000000..86c97c4
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+
+This is an implementation of RFC2898, which specifies key derivation from
+a password and a salt value.
+*/
+
+#include <string.h>
+#include "hmac.h"
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+void derive_key(const unsigned char pwd[],  /* the PASSWORD     */
+               unsigned int pwd_len,        /* and its length   */
+               const unsigned char salt[],  /* the SALT and its */
+               unsigned int salt_len,       /* length           */
+               unsigned int iter,   /* the number of iterations */
+               unsigned char key[], /* space for the output key */
+               unsigned int key_len)/* and its required length  */
+{
+    unsigned int    i, j, k, n_blk, h_size;
+    unsigned char uu[HMAC_MAX_OUTPUT_SIZE], ux[HMAC_MAX_OUTPUT_SIZE];
+    hmac_ctx c1[1], c2[1], c3[1];
+
+    /* set HMAC context (c1) for password               */
+    h_size = hmac_sha_begin(HMAC_SHA1, c1);
+    hmac_sha_key(pwd, pwd_len, c1);
+
+    /* set HMAC context (c2) for password and salt      */
+    memcpy(c2, c1, sizeof(hmac_ctx));
+    hmac_sha_data(salt, salt_len, c2);
+
+    /* find the number of SHA blocks in the key         */
+    n_blk = 1 + (key_len - 1) / h_size;
+
+    for(i = 0; i < n_blk; ++i) /* for each block in key */
+    {
+        /* ux[] holds the running xor value             */
+        memset(ux, 0, h_size);
+
+        /* set HMAC context (c3) for password and salt  */
+        memcpy(c3, c2, sizeof(hmac_ctx));
+
+        /* enter additional data for 1st block into uu  */
+        uu[0] = (unsigned char)((i + 1) >> 24);
+        uu[1] = (unsigned char)((i + 1) >> 16);
+        uu[2] = (unsigned char)((i + 1) >> 8);
+        uu[3] = (unsigned char)(i + 1);
+
+        /* this is the key mixing iteration         */
+        for(j = 0, k = 4; j < iter; ++j)
+        {
+            /* add previous round data to HMAC      */
+            hmac_sha_data(uu, k, c3);
+
+            /* obtain HMAC for uu[]                 */
+            hmac_sha_end(uu, h_size, c3);
+
+            /* xor into the running xor block       */
+            for(k = 0; k < h_size; ++k)
+                ux[k] ^= uu[k];
+
+            /* set HMAC context (c3) for password   */
+            memcpy(c3, c1, sizeof(hmac_ctx));
+        }
+
+        /* compile key blocks into the key output   */
+        j = 0; k = i * h_size;
+        while(j < h_size && k < key_len)
+            key[k++] = ux[j++];
+    }
+}
+
+#ifdef TEST
+
+#include <stdio.h>
+
+struct
+{   unsigned int    pwd_len;
+    unsigned int    salt_len;
+    unsigned int    it_count;
+    unsigned char   *pwd;
+    unsigned char   salt[32];
+    unsigned char   key[32];
+} tests[] =
+{
+    {   8, 4, 5, (unsigned char*)"password",
+        {   
+            0x12, 0x34, 0x56, 0x78 
+        },
+        {   
+            0x5c, 0x75, 0xce, 0xf0, 0x1a, 0x96, 0x0d, 0xf7,
+            0x4c, 0xb6, 0xb4, 0x9b, 0x9e, 0x38, 0xe6, 0xb5 
+        }
+    },
+    {   8, 8, 5, (unsigned char*)"password",
+        {   
+            0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0x12 
+        },
+        {   
+            0xd1, 0xda, 0xa7, 0x86, 0x15, 0xf2, 0x87, 0xe6,
+            0xa1, 0xc8, 0xb1, 0x20, 0xd7, 0x06, 0x2a, 0x49 
+        }
+    },
+    {   8, 21, 1, (unsigned char*)"password",
+        {
+            "ATHENA.MIT.EDUraeburn"
+        },
+        {
+            0xcd, 0xed, 0xb5, 0x28, 0x1b, 0xb2, 0xf8, 0x01,
+            0x56, 0x5a, 0x11, 0x22, 0xb2, 0x56, 0x35, 0x15
+        }
+    },
+    {   8, 21, 2, (unsigned char*)"password",
+        {
+            "ATHENA.MIT.EDUraeburn"
+        },
+        {
+            0x01, 0xdb, 0xee, 0x7f, 0x4a, 0x9e, 0x24, 0x3e, 
+            0x98, 0x8b, 0x62, 0xc7, 0x3c, 0xda, 0x93, 0x5d
+        }
+    },
+    {   8, 21, 1200, (unsigned char*)"password",
+        {
+            "ATHENA.MIT.EDUraeburn"
+        },
+        {
+            0x5c, 0x08, 0xeb, 0x61, 0xfd, 0xf7, 0x1e, 0x4e, 
+            0x4e, 0xc3, 0xcf, 0x6b, 0xa1, 0xf5, 0x51, 0x2b
+        }
+    }
+};
+
+int main()
+{   unsigned int    i, j, key_len = 256;
+    unsigned char   key[256];
+
+    printf("\nTest of RFC2898 Password Based Key Derivation");
+    for(i = 0; i < 5; ++i)
+    {
+        derive_key(tests[i].pwd, tests[i].pwd_len, tests[i].salt,
+                    tests[i].salt_len, tests[i].it_count, key, key_len);
+
+        printf("\ntest %i: ", i + 1);
+        printf("key %s", memcmp(tests[i].key, key, 16) ? "is bad" : "is good");
+        for(j = 0; j < key_len && j < 64; j += 4)
+        {
+            if(j % 16 == 0)
+                printf("\n");
+            printf("0x%02x%02x%02x%02x ", key[j], key[j + 1], key[j + 2], key[j + 3]);
+        }
+        printf(j < key_len ? " ... \n" : "\n");
+    }
+    printf("\n");
+    return 0;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/pwd2key.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/pwd2key.h
new file mode 100644 (file)
index 0000000..b9ba421
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+
+This is an implementation of RFC2898, which specifies key derivation from
+a password and a salt value.
+*/
+
+#ifndef PWD2KEY_H
+#define PWD2KEY_H
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+void derive_key(
+        const unsigned char pwd[],   /* the PASSWORD, and   */
+        unsigned int pwd_len,        /*    its length       */ 
+        const unsigned char salt[],  /* the SALT and its    */
+        unsigned int salt_len,       /*    length           */
+        unsigned int iter,      /* the number of iterations */
+        unsigned char key[],    /* space for the output key */
+        unsigned int key_len);  /* and its required length  */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/sha1.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/sha1.c
new file mode 100644 (file)
index 0000000..3300363
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+*/
+
+#include <string.h>     /* for memcpy() etc.        */
+
+#include "sha1.h"
+#include "brg_endian.h"
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#if defined( _MSC_VER ) && ( _MSC_VER > 800 )
+#pragma intrinsic(memcpy)
+#pragma intrinsic(memset)
+#endif
+
+#if 0 && defined(_MSC_VER)
+#define rotl32  _lrotl
+#define rotr32  _lrotr
+#else
+#define rotl32(x,n)   (((x) << n) | ((x) >> (32 - n)))
+#define rotr32(x,n)   (((x) >> n) | ((x) << (32 - n)))
+#endif
+
+#if !defined(bswap_32)
+#define bswap_32(x) ((rotr32((x), 24) & 0x00ff00ff) | (rotr32((x), 8) & 0xff00ff00))
+#endif
+
+#if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN)
+#define SWAP_BYTES
+#else
+#undef  SWAP_BYTES
+#endif
+
+#if defined(SWAP_BYTES)
+#define bsw_32(p,n) \
+    { int _i = (n); while(_i--) ((uint32_t*)p)[_i] = bswap_32(((uint32_t*)p)[_i]); }
+#else
+#define bsw_32(p,n)
+#endif
+
+#define SHA1_MASK   (SHA1_BLOCK_SIZE - 1)
+
+#if 0
+
+#define ch(x,y,z)       (((x) & (y)) ^ (~(x) & (z)))
+#define parity(x,y,z)   ((x) ^ (y) ^ (z))
+#define maj(x,y,z)      (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+#else   /* Discovered by Rich Schroeppel and Colin Plumb   */
+
+#define ch(x,y,z)       ((z) ^ ((x) & ((y) ^ (z))))
+#define parity(x,y,z)   ((x) ^ (y) ^ (z))
+#define maj(x,y,z)      (((x) & (y)) | ((z) & ((x) ^ (y))))
+
+#endif
+
+/* Compile 64 bytes of hash data into SHA1 context. Note    */
+/* that this routine assumes that the byte order in the     */
+/* ctx->wbuf[] at this point is in such an order that low   */
+/* address bytes in the ORIGINAL byte stream will go in     */
+/* this buffer to the high end of 32-bit words on BOTH big  */
+/* and little endian systems                                */
+
+#ifdef ARRAY
+#define q(v,n)  v[n]
+#else
+#define q(v,n)  v##n
+#endif
+
+#ifdef SHA_1
+
+#define one_cycle(v,a,b,c,d,e,f,k,h)            \
+    q(v,e) += rotr32(q(v,a),27) +               \
+              f(q(v,b),q(v,c),q(v,d)) + k + h;  \
+    q(v,b)  = rotr32(q(v,b), 2)
+
+#define five_cycle(v,f,k,i)                 \
+    one_cycle(v, 0,1,2,3,4, f,k,hf(i  ));   \
+    one_cycle(v, 4,0,1,2,3, f,k,hf(i+1));   \
+    one_cycle(v, 3,4,0,1,2, f,k,hf(i+2));   \
+    one_cycle(v, 2,3,4,0,1, f,k,hf(i+3));   \
+    one_cycle(v, 1,2,3,4,0, f,k,hf(i+4))
+
+VOID_RETURN sha1_compile(sha1_ctx ctx[1])
+{   uint32_t    *w = ctx->wbuf;
+
+#ifdef ARRAY
+    uint32_t    v[5];
+    memcpy(v, ctx->hash, sizeof(ctx->hash));
+#else
+    uint32_t    v0, v1, v2, v3, v4;
+    v0 = ctx->hash[0]; v1 = ctx->hash[1];
+    v2 = ctx->hash[2]; v3 = ctx->hash[3];
+    v4 = ctx->hash[4];
+#endif
+
+#define hf(i)   w[i]
+
+    five_cycle(v, ch, 0x5a827999,  0);
+    five_cycle(v, ch, 0x5a827999,  5);
+    five_cycle(v, ch, 0x5a827999, 10);
+    one_cycle(v,0,1,2,3,4, ch, 0x5a827999, hf(15)); \
+
+#undef  hf
+#define hf(i) (w[(i) & 15] = rotl32(                    \
+                 w[((i) + 13) & 15] ^ w[((i) + 8) & 15] \
+               ^ w[((i) +  2) & 15] ^ w[(i) & 15], 1))
+
+    one_cycle(v,4,0,1,2,3, ch, 0x5a827999, hf(16));
+    one_cycle(v,3,4,0,1,2, ch, 0x5a827999, hf(17));
+    one_cycle(v,2,3,4,0,1, ch, 0x5a827999, hf(18));
+    one_cycle(v,1,2,3,4,0, ch, 0x5a827999, hf(19));
+
+    five_cycle(v, parity, 0x6ed9eba1,  20);
+    five_cycle(v, parity, 0x6ed9eba1,  25);
+    five_cycle(v, parity, 0x6ed9eba1,  30);
+    five_cycle(v, parity, 0x6ed9eba1,  35);
+
+    five_cycle(v, maj, 0x8f1bbcdc,  40);
+    five_cycle(v, maj, 0x8f1bbcdc,  45);
+    five_cycle(v, maj, 0x8f1bbcdc,  50);
+    five_cycle(v, maj, 0x8f1bbcdc,  55);
+
+    five_cycle(v, parity, 0xca62c1d6,  60);
+    five_cycle(v, parity, 0xca62c1d6,  65);
+    five_cycle(v, parity, 0xca62c1d6,  70);
+    five_cycle(v, parity, 0xca62c1d6,  75);
+
+#ifdef ARRAY
+    ctx->hash[0] += v[0]; ctx->hash[1] += v[1];
+    ctx->hash[2] += v[2]; ctx->hash[3] += v[3];
+    ctx->hash[4] += v[4];
+#else
+    ctx->hash[0] += v0; ctx->hash[1] += v1;
+    ctx->hash[2] += v2; ctx->hash[3] += v3;
+    ctx->hash[4] += v4;
+#endif
+}
+
+VOID_RETURN sha1_begin(sha1_ctx ctx[1])
+{
+    memset(ctx, 0, sizeof(sha1_ctx));
+    ctx->hash[0] = 0x67452301;
+    ctx->hash[1] = 0xefcdab89;
+    ctx->hash[2] = 0x98badcfe;
+    ctx->hash[3] = 0x10325476;
+    ctx->hash[4] = 0xc3d2e1f0;
+}
+
+/* SHA1 hash data in an array of bytes into hash buffer and */
+/* call the hash_compile function as required. For both the */
+/* bit and byte orientated versions, the block length 'len' */
+/* must not be greater than 2^32 - 1 bits (2^29 - 1 bytes)  */ 
+
+VOID_RETURN sha1_hash(const unsigned char data[], unsigned long len, sha1_ctx ctx[1])
+{   uint32_t pos = (uint32_t)((ctx->count[0] >> 3) & SHA1_MASK);
+    const unsigned char *sp = data;
+    unsigned char *w = (unsigned char*)ctx->wbuf;
+#if SHA1_BITS == 1
+    uint32_t ofs = (ctx->count[0] & 7);
+#else
+    len <<= 3;
+#endif
+    if((ctx->count[0] += len) < len)
+        ++(ctx->count[1]);
+#if SHA1_BITS == 1
+    if(ofs)                 /* if not on a byte boundary    */
+    {
+        if(ofs + len < 8)   /* if no added bytes are needed */
+        {
+            w[pos] |= (*sp >> ofs);
+        }
+        else                /* otherwise and add bytes      */
+        {   unsigned char part = w[pos];
+
+            while((int)(ofs + (len -= 8)) >= 0)
+            {
+                w[pos++] = part | (*sp >> ofs);
+                part = *sp++ << (8 - ofs);
+                if(pos == SHA1_BLOCK_SIZE)
+                {
+                    bsw_32(w, SHA1_BLOCK_SIZE >> 2);
+                    sha1_compile(ctx); pos = 0;
+                }
+            }
+
+            w[pos] = part;
+        }
+    }
+    else    /* data is byte aligned */
+#endif
+    {   uint32_t space = SHA1_BLOCK_SIZE - pos;
+
+        while(len >= (space << 3))
+        {
+            memcpy(w + pos, sp, space);
+            bsw_32(w, SHA1_BLOCK_SIZE >> 2);
+            sha1_compile(ctx); 
+            sp += space; len -= (space << 3); 
+            space = SHA1_BLOCK_SIZE; pos = 0;
+        }
+        memcpy(w + pos, sp, (len + 7 * SHA1_BITS) >> 3);
+    }
+}
+
+/* SHA1 final padding and digest calculation  */
+
+VOID_RETURN sha1_end(unsigned char hval[], sha1_ctx ctx[1])
+{   uint32_t    i = (uint32_t)((ctx->count[0] >> 3) & SHA1_MASK), m1;
+
+    /* put bytes in the buffer in an order in which references to   */
+    /* 32-bit words will put bytes with lower addresses into the    */
+    /* top of 32 bit words on BOTH big and little endian machines   */
+    bsw_32(ctx->wbuf, (i + 3 + SHA1_BITS) >> 2);
+
+    /* we now need to mask valid bytes and add the padding which is */
+    /* a single 1 bit and as many zero bits as necessary. Note that */
+    /* we can always add the first padding byte here because the    */
+    /* buffer always has at least one empty slot                    */
+    m1 = (unsigned char)0x80 >> (ctx->count[0] & 7);
+    ctx->wbuf[i >> 2] &= ((0xffffff00 | (~m1 + 1)) << 8 * (~i & 3));
+    ctx->wbuf[i >> 2] |= (m1 << 8 * (~i & 3));
+
+    /* we need 9 or more empty positions, one for the padding byte  */
+    /* (above) and eight for the length count. If there is not      */
+    /* enough space, pad and empty the buffer                       */
+    if(i > SHA1_BLOCK_SIZE - 9)
+    {
+        if(i < 60) ctx->wbuf[15] = 0;
+        sha1_compile(ctx);
+        i = 0;
+    }
+    else    /* compute a word index for the empty buffer positions  */
+        i = (i >> 2) + 1;
+
+    while(i < 14) /* and zero pad all but last two positions        */
+        ctx->wbuf[i++] = 0;
+
+    /* the following 32-bit length fields are assembled in the      */
+    /* wrong byte order on little endian machines but this is       */
+    /* corrected later since they are only ever used as 32-bit      */
+    /* word values.                                                 */
+    ctx->wbuf[14] = ctx->count[1];
+    ctx->wbuf[15] = ctx->count[0];
+    sha1_compile(ctx);
+
+    /* extract the hash value as bytes in case the hash buffer is   */
+    /* misaligned for 32-bit words                                  */
+    for(i = 0; i < SHA1_DIGEST_SIZE; ++i)
+        hval[i] = ((ctx->hash[i >> 2] >> (8 * (~i & 3))) & 0xff);
+}
+
+VOID_RETURN sha1(unsigned char hval[], const unsigned char data[], unsigned long len)
+{   sha1_ctx    cx[1];
+
+    sha1_begin(cx); sha1_hash(data, len, cx); sha1_end(hval, cx);
+}
+
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/sha1.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/aes/sha1.h
new file mode 100644 (file)
index 0000000..e805ad9
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+---------------------------------------------------------------------------
+Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved.
+
+The redistribution and use of this software (with or without changes)
+is allowed without the payment of fees or royalties provided that:
+
+  source code distributions include the above copyright notice, this
+  list of conditions and the following disclaimer;
+
+  binary distributions include the above copyright notice, this list
+  of conditions and the following disclaimer in their documentation.
+
+This software is provided 'as is' with no explicit or implied warranties
+in respect of its operation, including, but not limited to, correctness
+and fitness for purpose.
+---------------------------------------------------------------------------
+Issue Date: 20/12/2007
+*/
+
+#ifndef _SHA1_H
+#define _SHA1_H
+
+#define SHA_1
+
+/* define for bit or byte oriented SHA   */
+#if 1
+#  define SHA1_BITS 0   /* byte oriented */
+#else
+#  define SHA1_BITS 1   /* bit oriented  */
+#endif
+
+#include <stdlib.h>
+#include "brg_types.h"
+
+#define SHA1_BLOCK_SIZE  64
+#define SHA1_DIGEST_SIZE 20
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/* type to hold the SHA256 context  */
+
+typedef struct
+{   uint32_t count[2];
+    uint32_t hash[SHA1_DIGEST_SIZE >> 2];
+    uint32_t wbuf[SHA1_BLOCK_SIZE >> 2];
+} sha1_ctx;
+
+/* Note that these prototypes are the same for both bit and */
+/* byte oriented implementations. However the length fields */
+/* are in bytes or bits as appropriate for the version used */
+/* and bit sequences are input as arrays of bytes in which  */
+/* bit sequences run from the most to the least significant */
+/* end of each byte. The value 'len' in sha1_hash for the   */
+/* byte oriented version of SHA1 is limited to 2^29 bytes,  */
+/* but multiple calls will handle longer data blocks.       */
+
+VOID_RETURN sha1_compile(sha1_ctx ctx[1]);
+
+VOID_RETURN sha1_begin(sha1_ctx ctx[1]);
+VOID_RETURN sha1_hash(const unsigned char data[], unsigned long len, sha1_ctx ctx[1]);
+VOID_RETURN sha1_end(unsigned char hval[], sha1_ctx ctx[1]);
+VOID_RETURN sha1(unsigned char hval[], const unsigned char data[], unsigned long len);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/crypt.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/crypt.c
new file mode 100644 (file)
index 0000000..9c1203b
--- /dev/null
@@ -0,0 +1,144 @@
+/* crypt.c -- base code for traditional PKWARE encryption
+   Version 1.2.0, September 16th, 2017
+
+   Copyright (C) 2012-2017 Nathan Moinvaziri
+     https://github.com/nmoinvaz/minizip
+   Copyright (C) 1998-2005 Gilles Vollant
+     Modifications for Info-ZIP crypting
+     http://www.winimage.com/zLibDll/minizip.html
+   Copyright (C) 2003 Terry Thorsen
+
+   This code is a modified version of crypting code in Info-ZIP distribution
+
+   Copyright (C) 1990-2000 Info-ZIP.  All rights reserved.
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+
+   This encryption code is a direct transcription of the algorithm from
+   Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+   file (appnote.txt) is distributed with the PKZIP program (even in the
+   version without encryption capabilities).
+
+   If you don't need crypting in your application, just define symbols
+   NOCRYPT and NOUNCRYPT.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+
+#ifdef _WIN32
+#  include <windows.h>
+#  include <wincrypt.h>
+#else
+#  include <sys/stat.h>
+#  include <fcntl.h>
+#  include <unistd.h>
+#endif
+
+#include "zlib.h"
+
+#include "crypt.h"
+
+/***************************************************************************/
+
+#define CRC32(c, b) ((*(pcrc_32_tab+(((uint32_t)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
+
+/***************************************************************************/
+
+uint8_t decrypt_byte(uint32_t *pkeys)
+{
+    unsigned temp;  /* POTENTIAL BUG:  temp*(temp^1) may overflow in an
+                     * unpredictable manner on 16-bit systems; not a problem
+                     * with any known compiler so far, though */
+
+    temp = ((uint32_t)(*(pkeys+2)) & 0xffff) | 2;
+    return (uint8_t)(((temp * (temp ^ 1)) >> 8) & 0xff);
+}
+
+uint8_t update_keys(uint32_t *pkeys, const z_crc_t *pcrc_32_tab, int32_t c)
+{
+    (*(pkeys+0)) = (uint32_t)CRC32((*(pkeys+0)), c);
+    (*(pkeys+1)) += (*(pkeys+0)) & 0xff;
+    (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
+    {
+        register int32_t keyshift = (int32_t)((*(pkeys + 1)) >> 24);
+        (*(pkeys+2)) = (uint32_t)CRC32((*(pkeys+2)), keyshift);
+    }
+    return c;
+}
+
+void init_keys(const char *passwd, uint32_t *pkeys, const z_crc_t *pcrc_32_tab)
+{
+    *(pkeys+0) = 305419896L;
+    *(pkeys+1) = 591751049L;
+    *(pkeys+2) = 878082192L;
+    while (*passwd != 0)
+    {
+        update_keys(pkeys, pcrc_32_tab, *passwd);
+        passwd += 1;
+    }
+}
+
+/***************************************************************************/
+
+int cryptrand(unsigned char *buf, unsigned int len)
+{
+#ifdef _WIN32
+    HCRYPTPROV provider;
+    unsigned __int64 pentium_tsc[1];
+    int rlen = 0;
+    int result = 0;
+
+
+    if (CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+    {
+        result = CryptGenRandom(provider, len, buf);
+        CryptReleaseContext(provider, 0);
+        if (result)
+            return len;
+    }
+
+    for (rlen = 0; rlen < (int)len; ++rlen)
+    {
+        if (rlen % 8 == 0)
+            QueryPerformanceCounter((LARGE_INTEGER *)pentium_tsc);
+        buf[rlen] = ((unsigned char*)pentium_tsc)[rlen % 8];
+    }
+
+    return rlen;
+#else
+    arc4random_buf(buf, len);
+    return len;
+#endif
+}
+
+int crypthead(const char *passwd, uint8_t *buf, int buf_size, uint32_t *pkeys,
+              const z_crc_t *pcrc_32_tab, uint8_t verify1, uint8_t verify2)
+{
+    uint8_t n = 0;                      /* index in random header */
+    uint8_t header[RAND_HEAD_LEN-2];    /* random header */
+    uint16_t t = 0;                     /* temporary */
+
+    if (buf_size < RAND_HEAD_LEN)
+        return 0;
+
+    init_keys(passwd, pkeys, pcrc_32_tab);
+
+    /* First generate RAND_HEAD_LEN-2 random bytes. */
+    cryptrand(header, RAND_HEAD_LEN-2);
+
+    /* Encrypt random header (last two bytes is high word of crc) */
+    init_keys(passwd, pkeys, pcrc_32_tab);
+
+    for (n = 0; n < RAND_HEAD_LEN-2; n++)
+        buf[n] = (uint8_t)zencode(pkeys, pcrc_32_tab, header[n], t);
+
+    buf[n++] = (uint8_t)zencode(pkeys, pcrc_32_tab, verify1, t);
+    buf[n++] = (uint8_t)zencode(pkeys, pcrc_32_tab, verify2, t);
+    return n;
+}
+
+/***************************************************************************/
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/crypt.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/crypt.h
new file mode 100644 (file)
index 0000000..0ba8260
--- /dev/null
@@ -0,0 +1,64 @@
+/* crypt.h -- base code for traditional PKWARE encryption
+   Version 1.2.0, September 16th, 2017
+
+   Copyright (C) 2012-2017 Nathan Moinvaziri
+     https://github.com/nmoinvaz/minizip
+   Copyright (C) 1998-2005 Gilles Vollant
+     Modifications for Info-ZIP crypting
+     http://www.winimage.com/zLibDll/minizip.html
+   Copyright (C) 2003 Terry Thorsen
+
+   This code is a modified version of crypting code in Info-ZIP distribution
+
+   Copyright (C) 1990-2000 Info-ZIP.  All rights reserved.
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+*/
+
+#ifndef _MINICRYPT_H
+#define _MINICRYPT_H
+
+#if ZLIB_VERNUM < 0x1270
+typedef unsigned long z_crc_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RAND_HEAD_LEN  12
+
+/***************************************************************************/
+
+#define zdecode(pkeys,pcrc_32_tab,c) \
+    (update_keys(pkeys,pcrc_32_tab, c ^= decrypt_byte(pkeys)))
+
+#define zencode(pkeys,pcrc_32_tab,c,t) \
+    (t = decrypt_byte(pkeys), update_keys(pkeys,pcrc_32_tab,c), t^(c))
+
+/***************************************************************************/
+
+/* Return the next byte in the pseudo-random sequence */
+uint8_t decrypt_byte(uint32_t *pkeys);
+
+/* Update the encryption keys with the next byte of plain text */
+uint8_t update_keys(uint32_t *pkeys, const z_crc_t *pcrc_32_tab, int32_t c);
+
+/* Initialize the encryption keys and the random header according to the given password. */
+void init_keys(const char *passwd, uint32_t *pkeys, const z_crc_t *pcrc_32_tab);
+
+/* Generate cryptographically secure random numbers */
+int cryptrand(unsigned char *buf, unsigned int len);
+
+/* Create encryption header */
+int crypthead(const char *passwd, uint8_t *buf, int buf_size, uint32_t *pkeys,
+    const z_crc_t *pcrc_32_tab, uint8_t verify1, uint8_t verify2);
+
+/***************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi.c
new file mode 100755 (executable)
index 0000000..239bb05
--- /dev/null
@@ -0,0 +1,353 @@
+/* ioapi.c -- IO base function header for compress/uncompress .zip
+   part of the MiniZip project
+
+   Copyright (C) 2012-2017 Nathan Moinvaziri
+     https://github.com/nmoinvaz/minizip
+   Modifications for Zip64 support
+     Copyright (C) 2009-2010 Mathias Svensson
+     http://result42.com
+   Copyright (C) 1998-2010 Gilles Vollant
+     http://www.winimage.com/zLibDll/minizip.html
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#if defined unix || defined __APPLE__
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include "ioapi.h"
+
+#if defined(_WIN32)
+#  define snprintf _snprintf
+#endif
+
+voidpf call_zopen64(const zlib_filefunc64_32_def *pfilefunc, const void *filename, int mode)
+{
+    if (pfilefunc->zfile_func64.zopen64_file != NULL)
+        return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque, filename, mode);
+    return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque, (const char*)filename, mode);
+}
+
+voidpf call_zopendisk64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint32_t number_disk, int mode)
+{
+    if (pfilefunc->zfile_func64.zopendisk64_file != NULL)
+        return (*(pfilefunc->zfile_func64.zopendisk64_file)) (pfilefunc->zfile_func64.opaque, filestream, number_disk, mode);
+    return (*(pfilefunc->zopendisk32_file))(pfilefunc->zfile_func64.opaque, filestream, number_disk, mode);
+}
+
+long call_zseek64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint64_t offset, int origin)
+{
+    uint32_t offset_truncated = 0;
+    if (pfilefunc->zfile_func64.zseek64_file != NULL)
+        return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin);
+    offset_truncated = (uint32_t)offset;
+    if (offset_truncated != offset)
+        return -1;
+    return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream, offset_truncated, origin);
+}
+
+uint64_t call_ztell64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream)
+{
+    uint64_t position;
+    if (pfilefunc->zfile_func64.zseek64_file != NULL)
+        return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque, filestream);
+    position = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque, filestream);
+    if ((position) == UINT32_MAX)
+        return (uint64_t)-1;
+    return position;
+}
+
+void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def *p_filefunc64_32, const zlib_filefunc_def *p_filefunc32)
+{
+    p_filefunc64_32->zfile_func64.zopen64_file = NULL;
+    p_filefunc64_32->zfile_func64.zopendisk64_file = NULL;
+    p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file;
+    p_filefunc64_32->zopendisk32_file = p_filefunc32->zopendisk_file;
+    p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
+    p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file;
+    p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file;
+    p_filefunc64_32->zfile_func64.ztell64_file = NULL;
+    p_filefunc64_32->zfile_func64.zseek64_file = NULL;
+    p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file;
+    p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
+    p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque;
+    p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file;
+    p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file;
+}
+
+static voidpf   ZCALLBACK fopen_file_func(ZIP_UNUSED voidpf opaque, const char *filename, int mode);
+static uint32_t ZCALLBACK fread_file_func(voidpf opaque, voidpf stream, void* buf, uint32_t size);
+static uint32_t ZCALLBACK fwrite_file_func(voidpf opaque, voidpf stream, const void *buf, uint32_t size);
+static uint64_t ZCALLBACK ftell64_file_func(voidpf opaque, voidpf stream);
+static long     ZCALLBACK fseek64_file_func(voidpf opaque, voidpf stream, uint64_t offset, int origin);
+static int      ZCALLBACK fclose_file_func(voidpf opaque, voidpf stream);
+static int      ZCALLBACK ferror_file_func(voidpf opaque, voidpf stream);
+
+typedef struct 
+{
+    FILE *file;
+    int filenameLength;
+    void *filename;
+} FILE_IOPOSIX;
+
+static voidpf file_build_ioposix(FILE *file, const char *filename)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    if (file == NULL)
+        return NULL;
+    ioposix = (FILE_IOPOSIX*)malloc(sizeof(FILE_IOPOSIX));
+    ioposix->file = file;
+    ioposix->filenameLength = (int)strlen(filename) + 1;
+    ioposix->filename = (char*)malloc(ioposix->filenameLength * sizeof(char));
+    strncpy((char*)ioposix->filename, filename, ioposix->filenameLength);
+    return (voidpf)ioposix;
+}
+
+static voidpf ZCALLBACK fopen_file_func(ZIP_UNUSED voidpf opaque, const char *filename, int mode)
+{
+    FILE* file = NULL;
+    const char *mode_fopen = NULL;
+    if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
+        mode_fopen = "rb";
+    else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
+        mode_fopen = "r+b";
+    else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+        mode_fopen = "wb";
+
+    if ((filename != NULL) && (mode_fopen != NULL))
+    {
+        file = fopen(filename, mode_fopen);
+        return file_build_ioposix(file, filename);
+    }
+    return file;
+}
+
+static voidpf ZCALLBACK fopen64_file_func(ZIP_UNUSED voidpf opaque, const void *filename, int mode)
+{
+    FILE* file = NULL;
+    const char *mode_fopen = NULL;
+    if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
+        mode_fopen = "rb";
+    else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
+        mode_fopen = "r+b";
+    else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+        mode_fopen = "wb";
+
+    if ((filename != NULL) && (mode_fopen != NULL))
+    {
+        file = fopen64((const char*)filename, mode_fopen);
+        return file_build_ioposix(file, (const char*)filename);
+    }
+    return file;
+}
+
+static voidpf ZCALLBACK fopendisk64_file_func(voidpf opaque, voidpf stream, uint32_t number_disk, int mode)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    char *diskFilename = NULL;
+    voidpf ret = NULL;
+    int i = 0;
+
+    if (stream == NULL)
+        return NULL;
+    ioposix = (FILE_IOPOSIX*)stream;
+    diskFilename = (char*)malloc(ioposix->filenameLength * sizeof(char));
+    strncpy(diskFilename, (const char*)ioposix->filename, ioposix->filenameLength);
+    for (i = ioposix->filenameLength - 1; i >= 0; i -= 1)
+    {
+        if (diskFilename[i] != '.')
+            continue;
+        snprintf(&diskFilename[i], ioposix->filenameLength - i, ".z%02u", number_disk + 1);
+        break;
+    }
+    if (i >= 0)
+        ret = fopen64_file_func(opaque, diskFilename, mode);
+    free(diskFilename);
+    return ret;
+}
+
+static voidpf ZCALLBACK fopendisk_file_func(voidpf opaque, voidpf stream, uint32_t number_disk, int mode)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    char *diskFilename = NULL;
+    voidpf ret = NULL;
+    int i = 0;
+
+    if (stream == NULL)
+        return NULL;
+    ioposix = (FILE_IOPOSIX*)stream;
+    diskFilename = (char*)malloc(ioposix->filenameLength * sizeof(char));
+    strncpy(diskFilename, (const char*)ioposix->filename, ioposix->filenameLength);
+    for (i = ioposix->filenameLength - 1; i >= 0; i -= 1)
+    {
+        if (diskFilename[i] != '.')
+            continue;
+        snprintf(&diskFilename[i], ioposix->filenameLength - i, ".z%02u", number_disk + 1);
+        break;
+    }
+    if (i >= 0)
+        ret = fopen_file_func(opaque, diskFilename, mode);
+    free(diskFilename);
+    return ret;
+}
+
+static uint32_t ZCALLBACK fread_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, void* buf, uint32_t size)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    uint32_t read = (uint32_t)-1;
+    if (stream == NULL)
+        return read;
+    ioposix = (FILE_IOPOSIX*)stream;
+    read = (uint32_t)fread(buf, 1, (size_t)size, ioposix->file);
+    return read;
+}
+
+static uint32_t ZCALLBACK fwrite_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, const void *buf, uint32_t size)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    uint32_t written = (uint32_t)-1;
+    if (stream == NULL)
+        return written;
+    ioposix = (FILE_IOPOSIX*)stream;
+    written = (uint32_t)fwrite(buf, 1, (size_t)size, ioposix->file);
+    return written;
+}
+
+static long ZCALLBACK ftell_file_func(ZIP_UNUSED voidpf opaque, voidpf stream)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    long ret = -1;
+    if (stream == NULL)
+        return ret;
+    ioposix = (FILE_IOPOSIX*)stream;
+    ret = ftell(ioposix->file);
+    return ret;
+}
+
+static uint64_t ZCALLBACK ftell64_file_func(ZIP_UNUSED voidpf opaque, voidpf stream)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    uint64_t ret = (uint64_t)-1;
+    if (stream == NULL)
+        return ret;
+    ioposix = (FILE_IOPOSIX*)stream;
+    ret = ftello64(ioposix->file);
+    return ret;
+}
+
+static long ZCALLBACK fseek_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint32_t offset, int origin)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    int fseek_origin = 0;
+    long ret = 0;
+
+    if (stream == NULL)
+        return -1;
+    ioposix = (FILE_IOPOSIX*)stream;
+
+    switch (origin)
+    {
+        case ZLIB_FILEFUNC_SEEK_CUR:
+            fseek_origin = SEEK_CUR;
+            break;
+        case ZLIB_FILEFUNC_SEEK_END:
+            fseek_origin = SEEK_END;
+            break;
+        case ZLIB_FILEFUNC_SEEK_SET:
+            fseek_origin = SEEK_SET;
+            break;
+        default:
+            return -1;
+    }
+    if (fseek(ioposix->file, offset, fseek_origin) != 0)
+        ret = -1;
+    return ret;
+}
+
+static long ZCALLBACK fseek64_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint64_t offset, int origin)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    int fseek_origin = 0;
+    long ret = 0;
+
+    if (stream == NULL)
+        return -1;
+    ioposix = (FILE_IOPOSIX*)stream;
+
+    switch (origin)
+    {
+        case ZLIB_FILEFUNC_SEEK_CUR:
+            fseek_origin = SEEK_CUR;
+            break;
+        case ZLIB_FILEFUNC_SEEK_END:
+            fseek_origin = SEEK_END;
+            break;
+        case ZLIB_FILEFUNC_SEEK_SET:
+            fseek_origin = SEEK_SET;
+            break;
+        default:
+            return -1;
+    }
+
+    if (fseeko64(ioposix->file, offset, fseek_origin) != 0)
+        ret = -1;
+
+    return ret;
+}
+
+static int ZCALLBACK fclose_file_func(ZIP_UNUSED voidpf opaque, voidpf stream)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    int ret = -1;
+    if (stream == NULL)
+        return ret;
+    ioposix = (FILE_IOPOSIX*)stream;
+    if (ioposix->filename != NULL)
+        free(ioposix->filename);
+    ret = fclose(ioposix->file);
+    free(ioposix);
+    return ret;
+}
+
+static int ZCALLBACK ferror_file_func(ZIP_UNUSED voidpf opaque, voidpf stream)
+{
+    FILE_IOPOSIX *ioposix = NULL;
+    int ret = -1;
+    if (stream == NULL)
+        return ret;
+    ioposix = (FILE_IOPOSIX*)stream;
+    ret = ferror(ioposix->file);
+    return ret;
+}
+
+void fill_fopen_filefunc(zlib_filefunc_def *pzlib_filefunc_def)
+{
+    pzlib_filefunc_def->zopen_file = fopen_file_func;
+    pzlib_filefunc_def->zopendisk_file = fopendisk_file_func;
+    pzlib_filefunc_def->zread_file = fread_file_func;
+    pzlib_filefunc_def->zwrite_file = fwrite_file_func;
+    pzlib_filefunc_def->ztell_file = ftell_file_func;
+    pzlib_filefunc_def->zseek_file = fseek_file_func;
+    pzlib_filefunc_def->zclose_file = fclose_file_func;
+    pzlib_filefunc_def->zerror_file = ferror_file_func;
+    pzlib_filefunc_def->opaque = NULL;
+}
+
+void fill_fopen64_filefunc(zlib_filefunc64_def *pzlib_filefunc_def)
+{
+    pzlib_filefunc_def->zopen64_file = fopen64_file_func;
+    pzlib_filefunc_def->zopendisk64_file = fopendisk64_file_func;
+    pzlib_filefunc_def->zread_file = fread_file_func;
+    pzlib_filefunc_def->zwrite_file = fwrite_file_func;
+    pzlib_filefunc_def->ztell64_file = ftell64_file_func;
+    pzlib_filefunc_def->zseek64_file = fseek64_file_func;
+    pzlib_filefunc_def->zclose_file = fclose_file_func;
+    pzlib_filefunc_def->zerror_file = ferror_file_func;
+    pzlib_filefunc_def->opaque = NULL;
+}
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi.h
new file mode 100755 (executable)
index 0000000..c5698ad
--- /dev/null
@@ -0,0 +1,154 @@
+/* ioapi.h -- IO base function header for compress/uncompress .zip
+   part of the MiniZip project
+
+   Copyright (C) 2012-2017 Nathan Moinvaziri
+     https://github.com/nmoinvaz/minizip
+   Copyright (C) 2009-2010 Mathias Svensson
+     Modifications for Zip64 support
+     http://result42.com
+   Copyright (C) 1998-2010 Gilles Vollant
+     http://www.winimage.com/zLibDll/minizip.html
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+*/
+
+#ifndef _ZLIBIOAPI64_H
+#define _ZLIBIOAPI64_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "zlib.h"
+
+#ifdef __GNUC__
+#  define ZIP_UNUSED __attribute__((__unused__))
+#else
+#  define ZIP_UNUSED
+#endif
+
+#if defined(USE_FILE32API)
+#  define fopen64 fopen
+#  define ftello64 ftell
+#  define fseeko64 fseek
+#else
+#  if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__ANDROID__)
+#    define fopen64 fopen
+#    define ftello64 ftello
+#    define fseeko64 fseeko
+#  endif
+#  ifdef _MSC_VER
+#    define fopen64 fopen
+#    if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC)))
+#      define ftello64 _ftelli64
+#      define fseeko64 _fseeki64
+#    else /* old MSC */
+#      define ftello64 ftell
+#      define fseeko64 fseek
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_FILEFUNC_SEEK_CUR (1)
+#define ZLIB_FILEFUNC_SEEK_END (2)
+#define ZLIB_FILEFUNC_SEEK_SET (0)
+
+#define ZLIB_FILEFUNC_MODE_READ             (1)
+#define ZLIB_FILEFUNC_MODE_WRITE            (2)
+#define ZLIB_FILEFUNC_MODE_READWRITEFILTER  (3)
+#define ZLIB_FILEFUNC_MODE_EXISTING         (4)
+#define ZLIB_FILEFUNC_MODE_CREATE           (8)
+
+#ifndef ZCALLBACK
+#  if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || \
+       defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
+#    define ZCALLBACK CALLBACK
+#  else
+#    define ZCALLBACK
+#  endif
+#endif
+
+typedef voidpf   (ZCALLBACK *open_file_func)     (voidpf opaque, const char *filename, int mode);
+typedef voidpf   (ZCALLBACK *opendisk_file_func) (voidpf opaque, voidpf stream, uint32_t number_disk, int mode);
+typedef uint32_t (ZCALLBACK *read_file_func)     (voidpf opaque, voidpf stream, void* buf, uint32_t size);
+typedef uint32_t (ZCALLBACK *write_file_func)    (voidpf opaque, voidpf stream, const void *buf, uint32_t size);
+typedef int      (ZCALLBACK *close_file_func)    (voidpf opaque, voidpf stream);
+typedef int      (ZCALLBACK *error_file_func)    (voidpf opaque, voidpf stream);
+
+typedef long     (ZCALLBACK *tell_file_func)     (voidpf opaque, voidpf stream);
+typedef long     (ZCALLBACK *seek_file_func)     (voidpf opaque, voidpf stream, uint32_t offset, int origin);
+
+/* here is the "old" 32 bits structure structure */
+typedef struct zlib_filefunc_def_s
+{
+    open_file_func      zopen_file;
+    opendisk_file_func  zopendisk_file;
+    read_file_func      zread_file;
+    write_file_func     zwrite_file;
+    tell_file_func      ztell_file;
+    seek_file_func      zseek_file;
+    close_file_func     zclose_file;
+    error_file_func     zerror_file;
+    voidpf              opaque;
+} zlib_filefunc_def;
+
+typedef uint64_t (ZCALLBACK *tell64_file_func)    (voidpf opaque, voidpf stream);
+typedef long     (ZCALLBACK *seek64_file_func)    (voidpf opaque, voidpf stream, uint64_t offset, int origin);
+typedef voidpf   (ZCALLBACK *open64_file_func)    (voidpf opaque, const void *filename, int mode);
+typedef voidpf   (ZCALLBACK *opendisk64_file_func)(voidpf opaque, voidpf stream, uint32_t number_disk, int mode);
+
+typedef struct zlib_filefunc64_def_s
+{
+    open64_file_func     zopen64_file;
+    opendisk64_file_func zopendisk64_file;
+    read_file_func       zread_file;
+    write_file_func      zwrite_file;
+    tell64_file_func     ztell64_file;
+    seek64_file_func     zseek64_file;
+    close_file_func      zclose_file;
+    error_file_func      zerror_file;
+    voidpf               opaque;
+} zlib_filefunc64_def;
+
+void fill_fopen_filefunc(zlib_filefunc_def *pzlib_filefunc_def);
+void fill_fopen64_filefunc(zlib_filefunc64_def *pzlib_filefunc_def);
+
+/* now internal definition, only for zip.c and unzip.h */
+typedef struct zlib_filefunc64_32_def_s
+{
+    zlib_filefunc64_def zfile_func64;
+    open_file_func      zopen32_file;
+    opendisk_file_func  zopendisk32_file;
+    tell_file_func      ztell32_file;
+    seek_file_func      zseek32_file;
+} zlib_filefunc64_32_def;
+
+#define ZREAD64(filefunc,filestream,buf,size)       ((*((filefunc).zfile_func64.zread_file))        ((filefunc).zfile_func64.opaque,filestream,buf,size))
+#define ZWRITE64(filefunc,filestream,buf,size)      ((*((filefunc).zfile_func64.zwrite_file))       ((filefunc).zfile_func64.opaque,filestream,buf,size))
+/*#define ZTELL64(filefunc,filestream)                ((*((filefunc).ztell64_file))                   ((filefunc).opaque,filestream))*/
+/*#define ZSEEK64(filefunc,filestream,pos,mode)       ((*((filefunc).zseek64_file))                   ((filefunc).opaque,filestream,pos,mode))*/
+#define ZCLOSE64(filefunc,filestream)               ((*((filefunc).zfile_func64.zclose_file))       ((filefunc).zfile_func64.opaque,filestream))
+#define ZERROR64(filefunc,filestream)               ((*((filefunc).zfile_func64.zerror_file))       ((filefunc).zfile_func64.opaque,filestream))
+
+voidpf   call_zopen64(const zlib_filefunc64_32_def *pfilefunc,const void*filename, int mode);
+voidpf   call_zopendisk64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint32_t number_disk, int mode);
+long     call_zseek64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint64_t offset, int origin);
+uint64_t call_ztell64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream);
+
+void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def *p_filefunc64_32, const zlib_filefunc_def *p_filefunc32);
+
+#define ZOPEN64(filefunc,filename,mode)             (call_zopen64((&(filefunc)),(filename),(mode)))
+#define ZOPENDISK64(filefunc,filestream,diskn,mode) (call_zopendisk64((&(filefunc)),(filestream),(diskn),(mode)))
+#define ZTELL64(filefunc,filestream)                (call_ztell64((&(filefunc)),(filestream)))
+#define ZSEEK64(filefunc,filestream,pos,mode)       (call_zseek64((&(filefunc)),(filestream),(pos),(mode)))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_buf.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_buf.c
new file mode 100755 (executable)
index 0000000..074a930
--- /dev/null
@@ -0,0 +1,461 @@
+/* ioapi_buf.c -- IO base function header for compress/uncompress .zip
+   files using zlib + zip or unzip API
+
+   This version of ioapi is designed to buffer IO.
+
+   Copyright (C) 2012-2017 Nathan Moinvaziri
+      https://github.com/nmoinvaz/minizip
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+
+#include "ioapi_buf.h"
+
+#ifndef IOBUF_BUFFERSIZE
+#  define IOBUF_BUFFERSIZE (UINT16_MAX)
+#endif
+
+#if defined(_WIN32)
+#  include <conio.h>
+#  define PRINTF  _cprintf
+#  define VPRINTF _vcprintf
+#else
+#  define PRINTF  printf
+#  define VPRINTF vprintf
+#endif
+
+//#define IOBUF_VERBOSE
+
+#ifdef __GNUC__
+#ifndef max
+#define max(x,y) ({ \
+const __typeof__(x) _x = (x);  \
+const __typeof__(y) _y = (y);  \
+(void) (&_x == &_y);           \
+_x > _y ? _x : _y; })
+#endif /* __GNUC__ */
+
+#ifndef min
+#define min(x,y) ({ \
+const __typeof__(x) _x = (x);  \
+const __typeof__(y) _y = (y);  \
+(void) (&_x == &_y);           \
+_x < _y ? _x : _y; })
+#endif
+#endif
+
+typedef struct ourstream_s {
+  char      readbuf[IOBUF_BUFFERSIZE];
+  uint32_t  readbuf_len;
+  uint32_t  readbuf_pos;
+  uint32_t  readbuf_hits;
+  uint32_t  readbuf_misses;
+  char      writebuf[IOBUF_BUFFERSIZE];
+  uint32_t  writebuf_len;
+  uint32_t  writebuf_pos;
+  uint32_t  writebuf_hits;
+  uint32_t  writebuf_misses;
+  uint64_t  position;
+  voidpf    stream;
+} ourstream_t;
+
+#if defined(IOBUF_VERBOSE)
+#  define print_buf(o,s,f,...) print_buf_internal(o,s,f,__VA_ARGS__);
+#else
+#  define print_buf(o,s,f,...)
+#endif
+
+void print_buf_internal(ZIP_UNUSED voidpf opaque, voidpf stream, char *format, ...)
+{
+    ourstream_t *streamio = (ourstream_t *)stream;
+    va_list arglist;
+    PRINTF("Buf stream %p - ", streamio);
+    va_start(arglist, format);
+    VPRINTF(format, arglist);
+    va_end(arglist);
+}
+
+voidpf fopen_buf_internal_func(ZIP_UNUSED voidpf opaque, voidpf stream, ZIP_UNUSED uint32_t number_disk, ZIP_UNUSED int mode)
+{
+    ourstream_t *streamio = NULL;
+    if (stream == NULL)
+        return NULL;
+    streamio = (ourstream_t *)malloc(sizeof(ourstream_t));
+    if (streamio == NULL)
+        return NULL;
+    memset(streamio, 0, sizeof(ourstream_t));
+    streamio->stream = stream;
+    print_buf(opaque, streamio, "open [num %d mode %d]\n", number_disk, mode);
+    return streamio;
+}
+
+voidpf ZCALLBACK fopen_buf_func(voidpf opaque, const char *filename, int mode)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    voidpf stream = bufio->filefunc.zopen_file(bufio->filefunc.opaque, filename, mode);
+    return fopen_buf_internal_func(opaque, stream, 0, mode);
+}
+
+voidpf ZCALLBACK fopen64_buf_func(voidpf opaque, const void *filename, int mode)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    voidpf stream = bufio->filefunc64.zopen64_file(bufio->filefunc64.opaque, filename, mode);
+    return fopen_buf_internal_func(opaque, stream, 0, mode);
+}
+
+voidpf ZCALLBACK fopendisk_buf_func(voidpf opaque, voidpf stream_cd, uint32_t number_disk, int mode)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream_cd;
+    voidpf *stream = bufio->filefunc.zopendisk_file(bufio->filefunc.opaque, streamio->stream, number_disk, mode);
+    return fopen_buf_internal_func(opaque, stream, number_disk, mode);
+}
+
+voidpf ZCALLBACK fopendisk64_buf_func(voidpf opaque, voidpf stream_cd, uint32_t number_disk, int mode)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream_cd;
+    voidpf stream = bufio->filefunc64.zopendisk64_file(bufio->filefunc64.opaque, streamio->stream, number_disk, mode);
+    return fopen_buf_internal_func(opaque, stream, number_disk, mode);
+}
+
+long fflush_buf(voidpf opaque, voidpf stream)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream;
+    uint32_t total_bytes_to_write = 0;
+    uint32_t bytes_to_write = streamio->writebuf_len;
+    uint32_t bytes_left_to_write = streamio->writebuf_len;
+    long bytes_written = 0;
+
+    while (bytes_left_to_write > 0)
+    {
+        if (bufio->filefunc64.zwrite_file != NULL)
+            bytes_written = bufio->filefunc64.zwrite_file(bufio->filefunc64.opaque, streamio->stream, streamio->writebuf + (bytes_to_write - bytes_left_to_write), bytes_left_to_write);
+        else
+            bytes_written = bufio->filefunc.zwrite_file(bufio->filefunc.opaque, streamio->stream, streamio->writebuf + (bytes_to_write - bytes_left_to_write), bytes_left_to_write);
+
+        streamio->writebuf_misses += 1;
+
+        print_buf(opaque, stream, "write flush [%d:%d len %d]\n", bytes_to_write, bytes_left_to_write, streamio->writebuf_len);
+
+        if (bytes_written < 0)
+            return bytes_written;
+
+        total_bytes_to_write += bytes_written;
+        bytes_left_to_write -= bytes_written;
+        streamio->position += bytes_written;
+    }
+    streamio->writebuf_len = 0;
+    streamio->writebuf_pos = 0;
+    return total_bytes_to_write;
+}
+
+uint32_t ZCALLBACK fread_buf_func(voidpf opaque, voidpf stream, void *buf, uint32_t size)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream;
+    uint32_t buf_len = 0;
+    uint32_t bytes_to_read = 0;
+    uint32_t bytes_to_copy = 0;
+    uint32_t bytes_left_to_read = size;
+    uint32_t bytes_read = 0;
+
+    print_buf(opaque, stream, "read [size %ld pos %lld]\n", size, streamio->position);
+
+    if (streamio->writebuf_len > 0)
+    {
+        print_buf(opaque, stream, "switch from write to read, not yet supported [%lld]\n", streamio->position);
+    }
+
+    while (bytes_left_to_read > 0)
+    {
+        if ((streamio->readbuf_len == 0) || (streamio->readbuf_pos == streamio->readbuf_len))
+        {
+            if (streamio->readbuf_len == IOBUF_BUFFERSIZE)
+            {
+                streamio->readbuf_pos = 0;
+                streamio->readbuf_len = 0;
+            }
+
+            bytes_to_read = IOBUF_BUFFERSIZE - (streamio->readbuf_len - streamio->readbuf_pos);
+
+            if (bufio->filefunc64.zread_file != NULL)
+                bytes_read = bufio->filefunc64.zread_file(bufio->filefunc64.opaque, streamio->stream, streamio->readbuf + streamio->readbuf_pos, bytes_to_read);
+            else
+                bytes_read = bufio->filefunc.zread_file(bufio->filefunc.opaque, streamio->stream, streamio->readbuf + streamio->readbuf_pos, bytes_to_read);
+
+            streamio->readbuf_misses += 1;
+            streamio->readbuf_len += bytes_read;
+            streamio->position += bytes_read;
+
+            print_buf(opaque, stream, "filled [read %d/%d buf %d:%d pos %lld]\n", bytes_read, bytes_to_read, streamio->readbuf_pos, streamio->readbuf_len, streamio->position);
+
+            if (bytes_read == 0)
+                break;
+        }
+
+        if ((streamio->readbuf_len - streamio->readbuf_pos) > 0)
+        {
+            bytes_to_copy = min(bytes_left_to_read, (uint32_t)(streamio->readbuf_len - streamio->readbuf_pos));
+            memcpy((char *)buf + buf_len, streamio->readbuf + streamio->readbuf_pos, bytes_to_copy);
+
+            buf_len += bytes_to_copy;
+            bytes_left_to_read -= bytes_to_copy;
+
+            streamio->readbuf_hits += 1;
+            streamio->readbuf_pos += bytes_to_copy;
+
+            print_buf(opaque, stream, "emptied [copied %d remaining %d buf %d:%d pos %lld]\n", bytes_to_copy, bytes_left_to_read, streamio->readbuf_pos, streamio->readbuf_len, streamio->position);
+        }
+    }
+
+    return size - bytes_left_to_read;
+}
+
+uint32_t ZCALLBACK fwrite_buf_func(voidpf opaque, voidpf stream, const void *buf, uint32_t size)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream;
+    uint32_t bytes_to_write = size;
+    uint32_t bytes_left_to_write = size;
+    uint32_t bytes_to_copy = 0;
+    int64_t ret = 0;
+
+    print_buf(opaque, stream, "write [size %ld len %d pos %lld]\n", size, streamio->writebuf_len, streamio->position);
+
+    if (streamio->readbuf_len > 0)
+    {
+        streamio->position -= streamio->readbuf_len;
+        streamio->position += streamio->readbuf_pos;
+
+        streamio->readbuf_len = 0;
+        streamio->readbuf_pos = 0;
+
+        print_buf(opaque, stream, "switch from read to write [%lld]\n", streamio->position);
+
+        if (bufio->filefunc64.zseek64_file != NULL)
+            ret = bufio->filefunc64.zseek64_file(bufio->filefunc64.opaque, streamio->stream, streamio->position, ZLIB_FILEFUNC_SEEK_SET);
+        else
+            ret = bufio->filefunc.zseek_file(bufio->filefunc.opaque, streamio->stream, (uint32_t)streamio->position, ZLIB_FILEFUNC_SEEK_SET);
+
+        if (ret != 0)
+            return (uint32_t)-1;
+    }
+
+    while (bytes_left_to_write > 0)
+    {
+        bytes_to_copy = min(bytes_left_to_write, (uint32_t)(IOBUF_BUFFERSIZE - min(streamio->writebuf_len, streamio->writebuf_pos)));
+
+        if (bytes_to_copy == 0)
+        {
+            if (fflush_buf(opaque, stream) <= 0)
+                return 0;
+
+            continue;
+        }
+
+        memcpy(streamio->writebuf + streamio->writebuf_pos, (char *)buf + (bytes_to_write - bytes_left_to_write), bytes_to_copy);
+
+        print_buf(opaque, stream, "write copy [remaining %d write %d:%d len %d]\n", bytes_to_copy, bytes_to_write, bytes_left_to_write, streamio->writebuf_len);
+
+        bytes_left_to_write -= bytes_to_copy;
+
+        streamio->writebuf_pos += bytes_to_copy;
+        streamio->writebuf_hits += 1;
+        if (streamio->writebuf_pos > streamio->writebuf_len)
+            streamio->writebuf_len += streamio->writebuf_pos - streamio->writebuf_len;
+    }
+
+    return size - bytes_left_to_write;
+}
+
+uint64_t ftell_buf_internal_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint64_t position)
+{
+    ourstream_t *streamio = (ourstream_t *)stream;
+    streamio->position = position;
+    print_buf(opaque, stream, "tell [pos %llu readpos %d writepos %d err %d]\n", streamio->position, streamio->readbuf_pos, streamio->writebuf_pos, errno);
+    if (streamio->readbuf_len > 0)
+        position -= (streamio->readbuf_len - streamio->readbuf_pos);
+    if (streamio->writebuf_len > 0)
+        position += streamio->writebuf_pos;
+    return position;
+}
+
+long ZCALLBACK ftell_buf_func(voidpf opaque, voidpf stream)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream;
+    uint64_t position = bufio->filefunc.ztell_file(bufio->filefunc.opaque, streamio->stream);
+    return (long)ftell_buf_internal_func(opaque, stream, position);
+}
+
+uint64_t ZCALLBACK ftell64_buf_func(voidpf opaque, voidpf stream)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream;
+    uint64_t position = bufio->filefunc64.ztell64_file(bufio->filefunc64.opaque, streamio->stream);
+    return ftell_buf_internal_func(opaque, stream, position);
+}
+
+int fseek_buf_internal_func(voidpf opaque, voidpf stream, uint64_t offset, int origin)
+{
+    ourstream_t *streamio = (ourstream_t *)stream;
+
+    print_buf(opaque, stream, "seek [origin %d offset %llu pos %lld]\n", origin, offset, streamio->position);
+
+    switch (origin)
+    {
+        case ZLIB_FILEFUNC_SEEK_SET:
+
+            if (streamio->writebuf_len > 0)
+            {
+                if ((offset >= streamio->position) && (offset <= streamio->position + streamio->writebuf_len))
+                {
+                    streamio->writebuf_pos = (uint32_t)(offset - streamio->position);
+                    return 0;
+                }
+            }
+            if ((streamio->readbuf_len > 0) && (offset < streamio->position) && (offset >= streamio->position - streamio->readbuf_len))
+            {
+                streamio->readbuf_pos = (uint32_t)(offset - (streamio->position - streamio->readbuf_len));
+                return 0;
+            }
+            if (fflush_buf(opaque, stream) < 0)
+                return -1;
+            streamio->position = offset;
+            break;
+
+        case ZLIB_FILEFUNC_SEEK_CUR:
+
+            if (streamio->readbuf_len > 0)
+            {
+                if (offset <= (streamio->readbuf_len - streamio->readbuf_pos))
+                {
+                    streamio->readbuf_pos += (uint32_t)offset;
+                    return 0;
+                }
+                offset -= (streamio->readbuf_len - streamio->readbuf_pos);
+                streamio->position += offset;
+            }
+            if (streamio->writebuf_len > 0)
+            {
+                if (offset <= (streamio->writebuf_len - streamio->writebuf_pos))
+                {
+                    streamio->writebuf_pos += (uint32_t)offset;
+                    return 0;
+                }
+                //offset -= (streamio->writebuf_len - streamio->writebuf_pos);
+            }
+
+            if (fflush_buf(opaque, stream) < 0)
+                return -1;
+
+            break;
+
+        case ZLIB_FILEFUNC_SEEK_END:
+
+            if (streamio->writebuf_len > 0)
+            {
+                streamio->writebuf_pos = streamio->writebuf_len;
+                return 0;
+            }
+            break;
+    }
+
+    streamio->readbuf_len = 0;
+    streamio->readbuf_pos = 0;
+    streamio->writebuf_len = 0;
+    streamio->writebuf_pos = 0;
+    return 1;
+}
+
+long ZCALLBACK fseek_buf_func(voidpf opaque, voidpf stream, uint32_t offset, int origin)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream;
+    long ret = -1;
+    if (bufio->filefunc.zseek_file == NULL)
+        return ret;
+    ret = fseek_buf_internal_func(opaque, stream, offset, origin);
+    if (ret == 1)
+        ret = bufio->filefunc.zseek_file(bufio->filefunc.opaque, streamio->stream, offset, origin);
+    return ret;
+}
+
+long ZCALLBACK fseek64_buf_func(voidpf opaque, voidpf stream, uint64_t offset, int origin)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream;
+    long ret = -1;
+    if (bufio->filefunc64.zseek64_file == NULL)
+        return ret;
+    ret = fseek_buf_internal_func(opaque, stream, offset, origin);
+    if (ret == 1)
+        ret = bufio->filefunc64.zseek64_file(bufio->filefunc64.opaque, streamio->stream, offset, origin);
+    return ret;
+}
+
+int ZCALLBACK fclose_buf_func(voidpf opaque, voidpf stream)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream;
+    int ret = 0;
+    fflush_buf(opaque, stream);
+    print_buf(opaque, stream, "close\n");
+    if (streamio->readbuf_hits + streamio->readbuf_misses > 0)
+        print_buf(opaque, stream, "read efficency %.02f%%\n", (streamio->readbuf_hits / ((float)streamio->readbuf_hits + streamio->readbuf_misses)) * 100);
+    if (streamio->writebuf_hits + streamio->writebuf_misses > 0)
+        print_buf(opaque, stream, "write efficency %.02f%%\n", (streamio->writebuf_hits / ((float)streamio->writebuf_hits + streamio->writebuf_misses)) * 100);
+    if (bufio->filefunc64.zclose_file != NULL)
+        ret = bufio->filefunc64.zclose_file(bufio->filefunc64.opaque, streamio->stream);
+    else
+        ret = bufio->filefunc.zclose_file(bufio->filefunc.opaque, streamio->stream);
+    free(streamio);
+    return ret;
+}
+
+int ZCALLBACK ferror_buf_func(voidpf opaque, voidpf stream)
+{
+    ourbuffer_t *bufio = (ourbuffer_t *)opaque;
+    ourstream_t *streamio = (ourstream_t *)stream;
+    if (bufio->filefunc64.zerror_file != NULL)
+        return bufio->filefunc64.zerror_file(bufio->filefunc64.opaque, streamio->stream);
+    return bufio->filefunc.zerror_file(bufio->filefunc.opaque, streamio->stream);
+}
+
+void fill_buffer_filefunc(zlib_filefunc_def *pzlib_filefunc_def, ourbuffer_t *ourbuf)
+{
+    pzlib_filefunc_def->zopen_file = fopen_buf_func;
+    pzlib_filefunc_def->zopendisk_file = fopendisk_buf_func;
+    pzlib_filefunc_def->zread_file = fread_buf_func;
+    pzlib_filefunc_def->zwrite_file = fwrite_buf_func;
+    pzlib_filefunc_def->ztell_file = ftell_buf_func;
+    pzlib_filefunc_def->zseek_file = fseek_buf_func;
+    pzlib_filefunc_def->zclose_file = fclose_buf_func;
+    pzlib_filefunc_def->zerror_file = ferror_buf_func;
+    pzlib_filefunc_def->opaque = ourbuf;
+}
+
+void fill_buffer_filefunc64(zlib_filefunc64_def *pzlib_filefunc_def, ourbuffer_t *ourbuf)
+{
+    pzlib_filefunc_def->zopen64_file = fopen64_buf_func;
+    pzlib_filefunc_def->zopendisk64_file = fopendisk64_buf_func;
+    pzlib_filefunc_def->zread_file = fread_buf_func;
+    pzlib_filefunc_def->zwrite_file = fwrite_buf_func;
+    pzlib_filefunc_def->ztell64_file = ftell64_buf_func;
+    pzlib_filefunc_def->zseek64_file = fseek64_buf_func;
+    pzlib_filefunc_def->zclose_file = fclose_buf_func;
+    pzlib_filefunc_def->zerror_file = ferror_buf_func;
+    pzlib_filefunc_def->opaque = ourbuf;
+}
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_buf.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_buf.h
new file mode 100644 (file)
index 0000000..b0e7698
--- /dev/null
@@ -0,0 +1,52 @@
+/* ioapi_buf.h -- IO base function header for compress/uncompress .zip
+   files using zlib + zip or unzip API
+
+   This version of ioapi is designed to buffer IO.
+
+   Copyright (C) 2012-2017 Nathan Moinvaziri
+      https://github.com/nmoinvaz/minizip
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+*/
+
+#ifndef _IOAPI_BUF_H
+#define _IOAPI_BUF_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+voidpf   ZCALLBACK fopen_buf_func(voidpf opaque, const char* filename, int mode);
+voidpf   ZCALLBACK fopen64_buf_func(voidpf opaque, const void* filename, int mode);
+voidpf   ZCALLBACK fopendisk_buf_func(voidpf opaque, voidpf stream_cd, uint32_t number_disk, int mode);
+voidpf   ZCALLBACK fopendisk64_buf_func(voidpf opaque, voidpf stream_cd, uint32_t number_disk, int mode);
+uint32_t ZCALLBACK fread_buf_func(voidpf opaque, voidpf stream, void* buf, uint32_t size);
+uint32_t ZCALLBACK fwrite_buf_func(voidpf opaque, voidpf stream, const void* buf, uint32_t size);
+long     ZCALLBACK ftell_buf_func(voidpf opaque, voidpf stream);
+uint64_t ZCALLBACK ftell64_buf_func(voidpf opaque, voidpf stream);
+long     ZCALLBACK fseek_buf_func(voidpf opaque, voidpf stream, uint32_t offset, int origin);
+long     ZCALLBACK fseek64_buf_func(voidpf opaque, voidpf stream, uint64_t offset, int origin);
+int      ZCALLBACK fclose_buf_func(voidpf opaque,voidpf stream);
+int      ZCALLBACK ferror_buf_func(voidpf opaque,voidpf stream);
+
+typedef struct ourbuffer_s {
+  zlib_filefunc_def   filefunc;
+  zlib_filefunc64_def filefunc64;
+} ourbuffer_t;
+
+void fill_buffer_filefunc(zlib_filefunc_def* pzlib_filefunc_def, ourbuffer_t *ourbuf);
+void fill_buffer_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def, ourbuffer_t *ourbuf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_mem.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_mem.c
new file mode 100644 (file)
index 0000000..ed53345
--- /dev/null
@@ -0,0 +1,167 @@
+/* ioapi_mem.c -- IO base function header for compress/uncompress .zip
+   files using zlib + zip or unzip API
+
+   This version of ioapi is designed to access memory rather than files.
+   We do use a region of memory to put data in to and take it out of. We do
+   not have auto-extending buffers and do not inform anyone else that the
+   data has been written. It is really intended for accessing a zip archive
+   embedded in an application such that I can write an installer with no
+   external files. Creation of archives has not been attempted, although
+   parts of the framework are present.
+
+   Based on Unzip ioapi.c version 0.22, May 19th, 2003
+
+   Copyright (C) 2012-2017 Nathan Moinvaziri
+     https://github.com/nmoinvaz/minizip
+   Copyright (C) 2003 Justin Fletcher
+   Copyright (C) 1998-2003 Gilles Vollant
+     http://www.winimage.com/zLibDll/minizip.html
+
+   This file is under the same license as the Unzip tool it is distributed
+   with.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+
+#include "ioapi_mem.h"
+
+#ifndef IOMEM_BUFFERSIZE
+#  define IOMEM_BUFFERSIZE (UINT16_MAX)
+#endif
+
+voidpf ZCALLBACK fopen_mem_func(voidpf opaque, ZIP_UNUSED const char *filename, int mode)
+{
+    ourmemory_t *mem = (ourmemory_t *)opaque;
+    if (mem == NULL)
+        return NULL; /* Mem structure passed in was null */
+
+    if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+    {
+        if (mem->grow)
+        {
+            mem->size = IOMEM_BUFFERSIZE;
+            mem->base = (char *)malloc(mem->size);
+        }
+
+        mem->limit = 0; /* When writing we start with 0 bytes written */
+    }
+    else
+        mem->limit = mem->size;
+
+    mem->cur_offset = 0;
+
+    return mem;
+}
+
+voidpf ZCALLBACK fopendisk_mem_func(ZIP_UNUSED voidpf opaque, ZIP_UNUSED voidpf stream, ZIP_UNUSED uint32_t number_disk, ZIP_UNUSED int mode)
+{
+    /* Not used */
+    return NULL;
+}
+
+uint32_t ZCALLBACK fread_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream, void *buf, uint32_t size)
+{
+    ourmemory_t *mem = (ourmemory_t *)stream;
+
+    if (size > mem->size - mem->cur_offset)
+        size = mem->size - mem->cur_offset;
+
+    memcpy(buf, mem->base + mem->cur_offset, size);
+    mem->cur_offset += size;
+
+    return size;
+}
+
+uint32_t ZCALLBACK fwrite_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream, const void *buf, uint32_t size)
+{
+    ourmemory_t *mem = (ourmemory_t *)stream;
+    uint32_t newmemsize = 0;
+    char *newbase = NULL;
+
+    if (size > mem->size - mem->cur_offset)
+    {
+        if (mem->grow)
+        {
+            newmemsize = mem->size;
+            if (size < IOMEM_BUFFERSIZE)
+                newmemsize += IOMEM_BUFFERSIZE;
+            else
+                newmemsize += size;
+            newbase = (char *)malloc(newmemsize);
+            memcpy(newbase, mem->base, mem->size);
+            free(mem->base);
+            mem->base = newbase;
+            mem->size = newmemsize;
+        }
+        else
+            size = mem->size - mem->cur_offset;
+    }
+    memcpy(mem->base + mem->cur_offset, buf, size);
+    mem->cur_offset += size;
+    if (mem->cur_offset > mem->limit)
+        mem->limit = mem->cur_offset;
+
+    return size;
+}
+
+long ZCALLBACK ftell_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream)
+{
+    ourmemory_t *mem = (ourmemory_t *)stream;
+    return mem->cur_offset;
+}
+
+long ZCALLBACK fseek_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint32_t offset, int origin)
+{
+    ourmemory_t *mem = (ourmemory_t *)stream;
+    uint32_t new_pos = 0;
+    switch (origin)
+    {
+        case ZLIB_FILEFUNC_SEEK_CUR:
+            new_pos = mem->cur_offset + offset;
+            break;
+        case ZLIB_FILEFUNC_SEEK_END:
+            new_pos = mem->limit + offset;
+            break;
+        case ZLIB_FILEFUNC_SEEK_SET:
+            new_pos = offset;
+            break;
+        default:
+            return -1;
+    }
+
+    if (new_pos > mem->size)
+        return 1; /* Failed to seek that far */
+    mem->cur_offset = new_pos;
+    return 0;
+}
+
+int ZCALLBACK fclose_mem_func(ZIP_UNUSED voidpf opaque, ZIP_UNUSED voidpf stream)
+{
+    /* Even with grow = 1, caller must always free() memory */
+    return 0;
+}
+
+int ZCALLBACK ferror_mem_func(ZIP_UNUSED voidpf opaque, ZIP_UNUSED voidpf stream)
+{
+    /* We never return errors */
+    return 0;
+}
+
+void fill_memory_filefunc(zlib_filefunc_def *pzlib_filefunc_def, ourmemory_t *ourmem)
+{
+    pzlib_filefunc_def->zopen_file = fopen_mem_func;
+    pzlib_filefunc_def->zopendisk_file = fopendisk_mem_func;
+    pzlib_filefunc_def->zread_file = fread_mem_func;
+    pzlib_filefunc_def->zwrite_file = fwrite_mem_func;
+    pzlib_filefunc_def->ztell_file = ftell_mem_func;
+    pzlib_filefunc_def->zseek_file = fseek_mem_func;
+    pzlib_filefunc_def->zclose_file = fclose_mem_func;
+    pzlib_filefunc_def->zerror_file = ferror_mem_func;
+    pzlib_filefunc_def->opaque = ourmem;
+}
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_mem.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/ioapi_mem.h
new file mode 100644 (file)
index 0000000..7061d6f
--- /dev/null
@@ -0,0 +1,52 @@
+/* ioapi_mem.h -- IO base function header for compress/uncompress .zip
+   files using zlib + zip or unzip API
+
+   This version of ioapi is designed to access memory rather than files.
+   We do use a region of memory to put data in to and take it out of.
+
+   Copyright (C) 2012-2017 Nathan Moinvaziri (https://github.com/nmoinvaz/minizip)
+             (C) 2003 Justin Fletcher
+             (C) 1998-2003 Gilles Vollant
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+*/
+
+#ifndef _IOAPI_MEM_H
+#define _IOAPI_MEM_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+voidpf   ZCALLBACK fopen_mem_func(voidpf opaque, const char* filename, int mode);
+voidpf   ZCALLBACK fopendisk_mem_func(voidpf opaque, voidpf stream, uint32_t number_disk, int mode);
+uint32_t ZCALLBACK fread_mem_func(voidpf opaque, voidpf stream, void* buf, uint32_t size);
+uint32_t ZCALLBACK fwrite_mem_func(voidpf opaque, voidpf stream, const void* buf, uint32_t size);
+long     ZCALLBACK ftell_mem_func(voidpf opaque, voidpf stream);
+long     ZCALLBACK fseek_mem_func(voidpf opaque, voidpf stream, uint32_t offset, int origin);
+int      ZCALLBACK fclose_mem_func(voidpf opaque, voidpf stream);
+int      ZCALLBACK ferror_mem_func(voidpf opaque, voidpf stream);
+
+typedef struct ourmemory_s {
+    char *base;          /* Base of the region of memory we're using */
+    uint32_t size;       /* Size of the region of memory we're using */
+    uint32_t limit;      /* Furthest we've written */
+    uint32_t cur_offset; /* Current offset in the area */
+    int grow;            /* Growable memory buffer */
+} ourmemory_t;
+
+void fill_memory_filefunc(zlib_filefunc_def* pzlib_filefunc_def, ourmemory_t *ourmem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/minishared.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/minishared.c
new file mode 100644 (file)
index 0000000..beabddc
--- /dev/null
@@ -0,0 +1,292 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+
+#ifdef _WIN32
+#  include <direct.h>
+#  include <io.h>
+#else
+#  include <unistd.h>
+#  include <utime.h>
+#  include <sys/types.h>
+#  include <sys/stat.h>
+#endif
+
+#include "minishared.h"
+
+#ifdef _WIN32
+#  define USEWIN32IOAPI
+#  include "iowin32.h"
+#endif
+
+uint32_t get_file_date(const char *path, uint32_t *dos_date)
+{
+    int ret = 0;
+#ifdef _WIN32
+    FILETIME ftm_local;
+    HANDLE find = NULL;
+    WIN32_FIND_DATAA ff32;
+
+    find = FindFirstFileA(path, &ff32);
+    if (find != INVALID_HANDLE_VALUE)
+    {
+        FileTimeToLocalFileTime(&(ff32.ftLastWriteTime), &ftm_local);
+        FileTimeToDosDateTime(&ftm_local, ((LPWORD)dos_date) + 1, ((LPWORD)dos_date) + 0);
+        FindClose(find);
+        ret = 1;
+    }
+#else
+    struct stat s;
+    struct tm *filedate = NULL;
+    time_t tm_t = 0;
+
+    memset(&s, 0, sizeof(s));
+
+    if (strcmp(path, "-") != 0)
+    {
+        size_t len = strlen(path);
+        char *name = (char *)malloc(len + 1);
+        strncpy(name, path, len + 1);
+        name[len] = 0;
+        if (name[len - 1] == '/')
+            name[len - 1] = 0;
+
+        /* Not all systems allow stat'ing a file with / appended */
+        if (stat(name, &s) == 0)
+        {
+            tm_t = s.st_mtime;
+            ret = 1;
+        }
+        free(name);
+    }
+
+    filedate = localtime(&tm_t);
+    *dos_date = tm_to_dosdate(filedate);
+#endif
+    return ret;
+}
+
+void change_file_date(const char *path, uint32_t dos_date)
+{
+#ifdef _WIN32
+    HANDLE handle = NULL;
+    FILETIME ftm, ftm_local, ftm_create, ftm_access, ftm_modified;
+
+    handle = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+    if (handle != INVALID_HANDLE_VALUE)
+    {
+        GetFileTime(handle, &ftm_create, &ftm_access, &ftm_modified);
+        DosDateTimeToFileTime((WORD)(dos_date >> 16), (WORD)dos_date, &ftm_local);
+        LocalFileTimeToFileTime(&ftm_local, &ftm);
+        SetFileTime(handle, &ftm, &ftm_access, &ftm);
+        CloseHandle(handle);
+    }
+#else
+    struct utimbuf ut;
+    ut.actime = ut.modtime = dosdate_to_time_t(dos_date);
+    utime(path, &ut);
+#endif
+}
+
+int invalid_date(const struct tm *ptm)
+{
+#define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
+    return (!datevalue_in_range(0, 207, ptm->tm_year) ||
+            !datevalue_in_range(0, 11, ptm->tm_mon) ||
+            !datevalue_in_range(1, 31, ptm->tm_mday) ||
+            !datevalue_in_range(0, 23, ptm->tm_hour) ||
+            !datevalue_in_range(0, 59, ptm->tm_min) ||
+            !datevalue_in_range(0, 59, ptm->tm_sec));
+#undef datevalue_in_range
+}
+
+// Conversion without validation
+void dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
+{
+    uint64_t date = (uint64_t)(dos_date >> 16);
+
+    ptm->tm_mday = (uint16_t)(date & 0x1f);
+    ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
+    ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
+    ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
+    ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
+    ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
+    ptm->tm_isdst = -1;
+}
+
+int dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
+{
+    dosdate_to_raw_tm(dos_date, ptm);
+
+    if (invalid_date(ptm))
+    {
+        // Invalid date stored, so don't return it.
+        memset(ptm, 0, sizeof(struct tm));
+        return -1;
+    }
+    return 0;
+}
+
+time_t dosdate_to_time_t(uint64_t dos_date)
+{
+    struct tm ptm;
+    dosdate_to_raw_tm(dos_date, &ptm);
+    return mktime(&ptm);
+}
+
+uint32_t tm_to_dosdate(const struct tm *ptm)
+{
+    struct tm fixed_tm;
+
+    /* Years supported:
+    * [00, 79]      (assumed to be between 2000 and 2079)
+    * [80, 207]     (assumed to be between 1980 and 2107, typical output of old
+                     software that does 'year-1900' to get a double digit year)
+    * [1980, 2107]  (due to the date format limitations, only years between 1980 and 2107 can be stored.)
+    */
+
+    memcpy(&fixed_tm, ptm, sizeof(struct tm));
+    if (fixed_tm.tm_year >= 1980) /* range [1980, 2107] */
+        fixed_tm.tm_year -= 1980;
+    else if (fixed_tm.tm_year >= 80) /* range [80, 99] */
+        fixed_tm.tm_year -= 80;
+    else /* range [00, 79] */
+        fixed_tm.tm_year += 20;
+
+    if (invalid_date(ptm))
+        return 0;
+
+    return (uint32_t)(((fixed_tm.tm_mday) + (32 * (fixed_tm.tm_mon + 1)) + (512 * fixed_tm.tm_year)) << 16) |
+        ((fixed_tm.tm_sec / 2) + (32 * fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour));
+}
+
+int makedir(const char *newdir)
+{
+    char *buffer = NULL;
+    char *p = NULL;
+    int len = (int)strlen(newdir);
+
+    if (len <= 0)
+        return 0;
+
+    buffer = (char*)malloc(len + 1);
+    if (buffer == NULL)
+    {
+        printf("Error allocating memory\n");
+        return -1;
+    }
+
+    strcpy(buffer, newdir);
+
+    if (buffer[len - 1] == '/')
+        buffer[len - 1] = 0;
+
+    if (MKDIR(buffer) == 0)
+    {
+        free(buffer);
+        return 1;
+    }
+
+    p = buffer + 1;
+    while (1)
+    {
+        char hold;
+        while (*p && *p != '\\' && *p != '/')
+            p++;
+        hold = *p;
+        *p = 0;
+
+        if ((MKDIR(buffer) == -1) && (errno == ENOENT))
+        {
+            printf("couldn't create directory %s (%d)\n", buffer, errno);
+            free(buffer);
+            return 0;
+        }
+
+        if (hold == 0)
+            break;
+
+        *p++ = hold;
+    }
+
+    free(buffer);
+    return 1;
+}
+
+FILE *get_file_handle(const char *path)
+{
+    FILE *handle = NULL;
+#if defined(WIN32)
+    wchar_t *pathWide = NULL;
+    int pathLength = 0;
+
+    pathLength = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) + 1;
+    pathWide = (wchar_t*)calloc(pathLength, sizeof(wchar_t));
+    MultiByteToWideChar(CP_UTF8, 0, path, -1, pathWide, pathLength);
+    handle = _wfopen((const wchar_t*)pathWide, L"rb");
+    free(pathWide);
+#else
+    handle = fopen64(path, "rb");
+#endif
+
+    return handle;
+}
+
+int check_file_exists(const char *path)
+{
+    FILE *handle = get_file_handle(path);
+    if (handle == NULL)
+        return 0;
+    fclose(handle);
+    return 1;
+}
+
+int is_large_file(const char *path)
+{
+    FILE* handle = NULL;
+    uint64_t pos = 0;
+
+    handle = get_file_handle(path);
+    if (handle == NULL)
+        return 0;
+
+    fseeko64(handle, 0, SEEK_END);
+    pos = ftello64(handle);
+    fclose(handle);
+
+    printf("file : %s is %lld bytes\n", path, pos);
+
+    return (pos >= UINT32_MAX);
+}
+
+void display_zpos64(uint64_t n, int size_char)
+{
+    /* To avoid compatibility problem we do here the conversion */
+    char number[21] = { 0 };
+    int offset = 19;
+    int pos_string = 19;
+    int size_display_string = 19;
+
+    while (1)
+    {
+        number[offset] = (char)((n % 10) + '0');
+        if (number[offset] != '0')
+            pos_string = offset;
+        n /= 10;
+        if (offset == 0)
+            break;
+        offset--;
+    }
+
+    size_display_string -= pos_string;
+    while (size_char-- > size_display_string)
+        printf(" ");
+    printf("%s", &number[pos_string]);
+}
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/minishared.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/minishared.h
new file mode 100644 (file)
index 0000000..32fccb6
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _MINISHARED_H
+#define _MINISHARED_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+#  define MKDIR(d) _mkdir(d)
+#  define CHDIR(d) _chdir(d)
+#else
+#  define MKDIR(d) mkdir(d, 0775)
+#  define CHDIR(d) chdir(d)
+#endif
+
+/***************************************************************************/
+
+/* Get a file's date and time in dos format */
+uint32_t get_file_date(const char *path, uint32_t *dos_date);
+
+/* Sets a file's date and time in dos format */
+void change_file_date(const char *path, uint32_t dos_date);
+
+/* Convert dos date/time format to struct tm */
+int dosdate_to_tm(uint64_t dos_date, struct tm *ptm);
+
+/* Convert dos date/time format to time_t */
+time_t dosdate_to_time_t(uint64_t dos_date);
+
+/* Convert struct tm to dos date/time format */
+uint32_t tm_to_dosdate(const struct tm *ptm);
+
+/* Create a directory and all subdirectories */
+int makedir(const char *newdir);
+
+/* Check to see if a file exists */
+int check_file_exists(const char *path);
+
+/* Check to see if a file is over 4GB and needs ZIP64 extension */
+int is_large_file(const char *path);
+
+/* Print a 64-bit number for compatibility */
+void display_zpos64(uint64_t n, int size_char);
+
+/***************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MINISHARED_H */
\ No newline at end of file
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/unzip.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/unzip.c
new file mode 100644 (file)
index 0000000..2aadf9d
--- /dev/null
@@ -0,0 +1,2001 @@
+/* unzip.c -- IO for uncompress .zip files using zlib
+   Version 1.2.0, September 16th, 2017
+   part of the MiniZip project
+
+   Copyright (C) 2010-2017 Nathan Moinvaziri
+     Modifications for AES, PKWARE disk spanning
+     https://github.com/nmoinvaz/minizip
+   Copyright (C) 2009-2010 Mathias Svensson
+     Modifications for Zip64 support on both zip and unzip
+     http://result42.com
+   Copyright (C) 2007-2008 Even Rouault
+     Modifications of Unzip for Zip64
+   Copyright (C) 1998-2010 Gilles Vollant
+     http://www.winimage.com/zLibDll/minizip.html
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include "zlib.h"
+#include "unzip.h"
+
+#ifdef HAVE_AES
+#  define AES_METHOD          (99)
+#  define AES_PWVERIFYSIZE    (2)
+#  define AES_MAXSALTLENGTH   (16)
+#  define AES_AUTHCODESIZE    (10)
+#  define AES_HEADERSIZE      (11)
+#  define AES_KEYSIZE(mode)   (64 + (mode * 64))
+
+#  include "aes/aes.h"
+#  include "aes/fileenc.h"
+#endif
+#ifdef HAVE_APPLE_COMPRESSION
+#  include <compression.h>
+#endif
+
+#ifndef NOUNCRYPT
+#  include "crypt.h"
+#endif
+
+#define DISKHEADERMAGIC             (0x08074b50)
+#define LOCALHEADERMAGIC            (0x04034b50)
+#define CENTRALHEADERMAGIC          (0x02014b50)
+#define ENDHEADERMAGIC              (0x06054b50)
+#define ZIP64ENDHEADERMAGIC         (0x06064b50)
+#define ZIP64ENDLOCHEADERMAGIC      (0x07064b50)
+
+#define SIZECENTRALDIRITEM          (0x2e)
+#define SIZECENTRALHEADERLOCATOR    (0x14)
+#define SIZEZIPLOCALHEADER          (0x1e)
+
+#ifndef BUFREADCOMMENT
+#  define BUFREADCOMMENT            (0x400)
+#endif
+
+#ifndef UNZ_BUFSIZE
+#  define UNZ_BUFSIZE               (UINT16_MAX)
+#endif
+#ifndef UNZ_MAXFILENAMEINZIP
+#  define UNZ_MAXFILENAMEINZIP      (256)
+#endif
+
+#ifndef ALLOC
+#  define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+#  define TRYFREE(p) {if (p) free(p);}
+#endif
+
+const char unz_copyright[] = " unzip 1.2.0 Copyright 1998-2017 - https://github.com/nmoinvaz/minizip";
+
+/* unz_file_info_internal contain internal info about a file in zipfile*/
+typedef struct unz_file_info64_internal_s
+{
+    uint64_t offset_curfile;            /* relative offset of local header 8 bytes */
+    uint64_t byte_before_the_zipfile;   /* byte before the zipfile, (>0 for sfx) */
+#ifdef HAVE_AES
+    uint8_t  aes_encryption_mode;
+    uint16_t aes_compression_method;
+    uint16_t aes_version;
+#endif
+} unz_file_info64_internal;
+
+/* file_in_zip_read_info_s contain internal information about a file in zipfile */
+typedef struct
+{
+    uint8_t *read_buffer;               /* internal buffer for compressed data */
+    z_stream stream;                    /* zLib stream structure for inflate */
+#ifdef HAVE_BZIP2
+    bz_stream bstream;                  /* bzLib stream structure for bziped */
+#endif
+#ifdef HAVE_APPLE_COMPRESSION
+    compression_stream astream;         /* libcompression stream structure */
+#endif
+#ifdef HAVE_AES
+    fcrypt_ctx aes_ctx;
+#endif
+    uint64_t pos_in_zipfile;            /* position in byte on the zipfile, for fseek */
+    uint8_t  stream_initialised;        /* flag set if stream structure is initialised */
+
+    uint64_t offset_local_extrafield;   /* offset of the local extra field */
+    uint16_t size_local_extrafield;     /* size of the local extra field */
+    uint64_t pos_local_extrafield;      /* position in the local extra field in read */
+    uint64_t total_out_64;
+
+    uint32_t crc32;                     /* crc32 of all data uncompressed */
+    uint32_t crc32_expected;            /* crc32 we must obtain after decompress all */
+    uint64_t rest_read_compressed;      /* number of byte to be decompressed */
+    uint64_t rest_read_uncompressed;    /* number of byte to be obtained after decomp */
+
+    zlib_filefunc64_32_def z_filefunc;
+
+    voidpf   filestream;                /* io structore of the zipfile */
+    uint16_t compression_method;        /* compression method (0==store) */
+    uint64_t byte_before_the_zipfile;   /* byte before the zipfile, (>0 for sfx) */
+    int      raw;
+} file_in_zip64_read_info_s;
+
+/* unz64_s contain internal information about the zipfile */
+typedef struct
+{
+    zlib_filefunc64_32_def z_filefunc;
+
+    voidpf filestream;                  /* io structure of the current zipfile */
+    voidpf filestream_with_CD;          /* io structure of the disk with the central directory */
+
+    unz_global_info64 gi;               /* public global information */
+
+    uint64_t byte_before_the_zipfile;   /* byte before the zipfile, (>0 for sfx) */
+    uint64_t num_file;                  /* number of the current file in the zipfile */
+    uint64_t pos_in_central_dir;        /* pos of the current file in the central dir */
+    uint64_t current_file_ok;           /* flag about the usability of the current file */
+    uint64_t central_pos;               /* position of the beginning of the central dir */
+    uint32_t number_disk;               /* number of the current disk, used for spanning ZIP */
+    uint64_t size_central_dir;          /* size of the central directory */
+    uint64_t offset_central_dir;        /* offset of start of central directory with
+                                           respect to the starting disk number */
+
+    unz_file_info64 cur_file_info;      /* public info about the current file in zip*/
+    unz_file_info64_internal cur_file_info_internal;
+                                        /* private info about it*/
+    file_in_zip64_read_info_s *pfile_in_zip_read;
+                                        /* structure about the current file if we are decompressing it */
+    int is_zip64;                       /* is the current file zip64 */
+#ifndef NOUNCRYPT
+    uint32_t keys[3];                   /* keys defining the pseudo-random sequence */
+    const z_crc_t *pcrc_32_tab;
+#endif
+} unz64_internal;
+
+/* Read a byte from a gz_stream; Return EOF for end of file. */
+static int unzReadUInt8(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint8_t *value)
+{
+    uint8_t c = 0;
+    if (ZREAD64(*pzlib_filefunc_def, filestream, &c, 1) == 1)
+    {
+        *value = (uint8_t)c;
+        return UNZ_OK;
+    }
+    *value = 0;
+    if (ZERROR64(*pzlib_filefunc_def, filestream))
+        return UNZ_ERRNO;
+    return UNZ_EOF;
+}
+
+static int unzReadUInt16(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint16_t *value)
+{
+    uint16_t x;
+    uint8_t c = 0;
+    int err = UNZ_OK;
+
+    err = unzReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x = (uint16_t)c;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x |= ((uint16_t)c) << 8;
+
+    if (err == UNZ_OK)
+        *value = x;
+    else
+        *value = 0;
+    return err;
+}
+
+static int unzReadUInt32(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint32_t *value)
+{
+    uint32_t x = 0;
+    uint8_t c = 0;
+    int err = UNZ_OK;
+
+    err = unzReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x = (uint32_t)c;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x |= ((uint32_t)c) << 8;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x |= ((uint32_t)c) << 16;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint32_t)c) << 24;
+
+    if (err == UNZ_OK)
+        *value = x;
+    else
+        *value = 0;
+    return err;
+}
+
+static int unzReadUInt64(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint64_t *value)
+{
+    uint64_t x = 0;
+    uint8_t i = 0;
+    int err = UNZ_OK;
+
+    err = unzReadUInt8(pzlib_filefunc_def, filestream, &i);
+    x = (uint64_t)i;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &i);
+    x |= ((uint64_t)i) << 8;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &i);
+    x |= ((uint64_t)i) << 16;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &i);
+    x |= ((uint64_t)i) << 24;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &i);
+    x |= ((uint64_t)i) << 32;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &i);
+    x |= ((uint64_t)i) << 40;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &i);
+    x |= ((uint64_t)i) << 48;
+    if (err == UNZ_OK)
+        err = unzReadUInt8(pzlib_filefunc_def, filestream, &i);
+    x |= ((uint64_t)i) << 56;
+
+    if (err == UNZ_OK)
+        *value = x;
+    else
+        *value = 0;
+    return err;
+}
+
+/* Locate the Central directory of a zip file (at the end, just before the global comment) */
+static int unzSearchCentralDir(const zlib_filefunc64_32_def *pzlib_filefunc_def, uint64_t *pos_found, voidpf filestream)
+{
+    uint8_t buf[BUFREADCOMMENT + 4];
+    uint64_t file_size = 0;
+    uint64_t back_read = 4;
+    uint64_t max_back = UINT16_MAX; /* maximum size of global comment */
+    uint32_t read_size = 0;
+    uint64_t read_pos = 0;
+    uint32_t i = 0;
+    *pos_found = 0;
+
+    if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) != 0)
+        return UNZ_ERRNO;
+
+    file_size = ZTELL64(*pzlib_filefunc_def, filestream);
+
+    if (max_back > file_size)
+        max_back = file_size;
+
+    while (back_read < max_back)
+    {
+        if (back_read + BUFREADCOMMENT > max_back)
+            back_read = max_back;
+        else
+            back_read += BUFREADCOMMENT;
+
+        read_pos = file_size - back_read;
+        read_size = ((BUFREADCOMMENT + 4) < (file_size - read_pos)) ?
+                     (BUFREADCOMMENT + 4) : (uint32_t)(file_size - read_pos);
+
+        if (ZSEEK64(*pzlib_filefunc_def, filestream, read_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
+            break;
+        if (ZREAD64(*pzlib_filefunc_def, filestream, buf, read_size) != read_size)
+            break;
+
+        for (i = read_size - 3; (i--) > 0;)
+            if (((*(buf+i)) == (ENDHEADERMAGIC & 0xff)) &&
+                ((*(buf+i+1)) == (ENDHEADERMAGIC >> 8 & 0xff)) &&
+                ((*(buf+i+2)) == (ENDHEADERMAGIC >> 16 & 0xff)) &&
+                ((*(buf+i+3)) == (ENDHEADERMAGIC >> 24 & 0xff)))
+            {
+                *pos_found = read_pos+i;
+                return UNZ_OK;
+            }
+    }
+
+    return UNZ_ERRNO;
+}
+
+/* Locate the Central directory 64 of a zipfile (at the end, just before the global comment) */
+static int unzSearchCentralDir64(const zlib_filefunc64_32_def *pzlib_filefunc_def, uint64_t *offset, voidpf filestream,
+    const uint64_t endcentraloffset)
+{
+    uint32_t value32 = 0;
+    *offset = 0;
+
+    /* Zip64 end of central directory locator */
+    if (ZSEEK64(*pzlib_filefunc_def, filestream, endcentraloffset - SIZECENTRALHEADERLOCATOR, ZLIB_FILEFUNC_SEEK_SET) != 0)
+        return UNZ_ERRNO;
+
+    /* Read locator signature */
+    if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK)
+        return UNZ_ERRNO;
+    if (value32 != ZIP64ENDLOCHEADERMAGIC)
+        return UNZ_ERRNO;
+    /* Number of the disk with the start of the zip64 end of  central directory */
+    if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK)
+        return UNZ_ERRNO;
+    /* Relative offset of the zip64 end of central directory record */
+    if (unzReadUInt64(pzlib_filefunc_def, filestream, offset) != UNZ_OK)
+        return UNZ_ERRNO;
+    /* Total number of disks */
+    if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK)
+        return UNZ_ERRNO;
+    /* Goto end of central directory record */
+    if (ZSEEK64(*pzlib_filefunc_def, filestream, *offset, ZLIB_FILEFUNC_SEEK_SET) != 0)
+        return UNZ_ERRNO;
+     /* The signature */
+    if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK)
+        return UNZ_ERRNO;
+    if (value32 != ZIP64ENDHEADERMAGIC)
+        return UNZ_ERRNO;
+
+    return UNZ_OK;
+}
+
+static unzFile unzOpenInternal(const void *path, zlib_filefunc64_32_def *pzlib_filefunc64_32_def)
+{
+    unz64_internal us;
+    unz64_internal *s = NULL;
+    uint64_t central_pos = 0;
+    uint64_t central_pos64 = 0;
+    uint64_t number_entry_CD = 0;
+    uint16_t value16 = 0;
+    uint32_t value32 = 0;
+    uint64_t value64 = 0;
+    voidpf filestream = NULL;
+    int err = UNZ_OK;
+    int err64 = UNZ_OK;
+
+    us.filestream = NULL;
+    us.filestream_with_CD = NULL;
+    us.z_filefunc.zseek32_file = NULL;
+    us.z_filefunc.ztell32_file = NULL;
+
+    if (pzlib_filefunc64_32_def == NULL)
+        fill_fopen64_filefunc(&us.z_filefunc.zfile_func64);
+    else
+        us.z_filefunc = *pzlib_filefunc64_32_def;
+
+    us.filestream = ZOPEN64(us.z_filefunc, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING);
+
+    if (us.filestream == NULL)
+        return NULL;
+
+    us.filestream_with_CD = us.filestream;
+    us.is_zip64 = 0;
+
+    /* Search for end of central directory header */
+    err = unzSearchCentralDir(&us.z_filefunc, &central_pos, us.filestream);
+    if (err == UNZ_OK)
+    {
+        if (ZSEEK64(us.z_filefunc, us.filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
+            err = UNZ_ERRNO;
+
+        /* The signature, already checked */
+        if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK)
+            err = UNZ_ERRNO;
+        /* Number of this disk */
+        if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK)
+            err = UNZ_ERRNO;
+        us.number_disk = value16;
+        /* Number of the disk with the start of the central directory */
+        if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK)
+            err = UNZ_ERRNO;
+        us.gi.number_disk_with_CD = value16;
+        /* Total number of entries in the central directory on this disk */
+        if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK)
+            err = UNZ_ERRNO;
+        us.gi.number_entry = value16;
+        /* Total number of entries in the central directory */
+        if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK)
+            err = UNZ_ERRNO;
+        number_entry_CD = value16;
+        if (number_entry_CD != us.gi.number_entry)
+            err = UNZ_BADZIPFILE;
+        /* Size of the central directory */
+        if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK)
+            err = UNZ_ERRNO;
+        us.size_central_dir = value32;
+        /* Offset of start of central directory with respect to the starting disk number */
+        if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK)
+            err = UNZ_ERRNO;
+        us.offset_central_dir = value32;
+        /* Zipfile comment length */
+        if (unzReadUInt16(&us.z_filefunc, us.filestream, &us.gi.size_comment) != UNZ_OK)
+            err = UNZ_ERRNO;
+
+        if (err == UNZ_OK)
+        {
+            /* Search for Zip64 end of central directory header */
+            err64 = unzSearchCentralDir64(&us.z_filefunc, &central_pos64, us.filestream, central_pos);
+            if (err64 == UNZ_OK)
+            {
+                central_pos = central_pos64;
+                us.is_zip64 = 1;
+
+                if (ZSEEK64(us.z_filefunc, us.filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
+                    err = UNZ_ERRNO;
+
+                /* the signature, already checked */
+                if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                /* size of zip64 end of central directory record */
+                if (unzReadUInt64(&us.z_filefunc, us.filestream, &value64) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                /* version made by */
+                if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                /* version needed to extract */
+                if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                /* number of this disk */
+                if (unzReadUInt32(&us.z_filefunc, us.filestream, &us.number_disk) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                /* number of the disk with the start of the central directory */
+                if (unzReadUInt32(&us.z_filefunc, us.filestream, &us.gi.number_disk_with_CD) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                /* total number of entries in the central directory on this disk */
+                if (unzReadUInt64(&us.z_filefunc, us.filestream, &us.gi.number_entry) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                /* total number of entries in the central directory */
+                if (unzReadUInt64(&us.z_filefunc, us.filestream, &number_entry_CD) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                if (number_entry_CD != us.gi.number_entry)
+                    err = UNZ_BADZIPFILE;
+                /* size of the central directory */
+                if (unzReadUInt64(&us.z_filefunc, us.filestream, &us.size_central_dir) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                /* offset of start of central directory with respect to the starting disk number */
+                if (unzReadUInt64(&us.z_filefunc, us.filestream, &us.offset_central_dir) != UNZ_OK)
+                    err = UNZ_ERRNO;
+            }
+            else if ((us.size_central_dir == UINT16_MAX) || (us.offset_central_dir == UINT32_MAX))
+                err = UNZ_BADZIPFILE;
+        }
+    }
+    else
+        err = UNZ_ERRNO;
+
+    if ((err == UNZ_OK) && (central_pos < us.offset_central_dir + us.size_central_dir))
+        err = UNZ_BADZIPFILE;
+
+    if (err != UNZ_OK)
+    {
+        ZCLOSE64(us.z_filefunc, us.filestream);
+        return NULL;
+    }
+
+    if (us.gi.number_disk_with_CD == 0)
+    {
+        /* If there is only one disk open another stream so we don't have to seek between the CD
+           and the file headers constantly */
+        filestream = ZOPEN64(us.z_filefunc, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING);
+        if (filestream != NULL)
+            us.filestream = filestream;
+    }
+
+    /* Hack for zip files that have no respect for zip64
+    if ((central_pos > 0xffffffff) && (us.offset_central_dir < 0xffffffff))
+        us.offset_central_dir = central_pos - us.size_central_dir;*/
+
+    us.byte_before_the_zipfile = central_pos - (us.offset_central_dir + us.size_central_dir);
+    us.central_pos = central_pos;
+    us.pfile_in_zip_read = NULL;
+
+    s = (unz64_internal*)ALLOC(sizeof(unz64_internal));
+    if (s != NULL)
+    {
+        *s = us;
+        if (err64 != UNZ_OK)
+            // workaround incorrect count #184
+            s->gi.number_entry = unzCountEntries(s);
+        
+        unzGoToFirstFile((unzFile)s);
+    }
+    return (unzFile)s;
+}
+
+extern unzFile ZEXPORT unzOpen2(const char *path, zlib_filefunc_def *pzlib_filefunc32_def)
+{
+    if (pzlib_filefunc32_def != NULL)
+    {
+        zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
+        fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill, pzlib_filefunc32_def);
+        return unzOpenInternal(path, &zlib_filefunc64_32_def_fill);
+    }
+    return unzOpenInternal(path, NULL);
+}
+
+extern unzFile ZEXPORT unzOpen2_64(const void *path, zlib_filefunc64_def *pzlib_filefunc_def)
+{
+    if (pzlib_filefunc_def != NULL)
+    {
+        zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
+        zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def;
+        zlib_filefunc64_32_def_fill.ztell32_file = NULL;
+        zlib_filefunc64_32_def_fill.zseek32_file = NULL;
+        return unzOpenInternal(path, &zlib_filefunc64_32_def_fill);
+    }
+    return unzOpenInternal(path, NULL);
+}
+
+extern unzFile ZEXPORT unzOpen(const char *path)
+{
+    return unzOpenInternal(path, NULL);
+}
+
+extern unzFile ZEXPORT unzOpen64(const void *path)
+{
+    return unzOpenInternal(path, NULL);
+}
+
+extern int ZEXPORT unzClose(unzFile file)
+{
+    unz64_internal *s;
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+
+    if (s->pfile_in_zip_read != NULL)
+        unzCloseCurrentFile(file);
+
+    if ((s->filestream != NULL) && (s->filestream != s->filestream_with_CD))
+        ZCLOSE64(s->z_filefunc, s->filestream);
+    if (s->filestream_with_CD != NULL)
+        ZCLOSE64(s->z_filefunc, s->filestream_with_CD);
+
+    s->filestream = NULL;
+    s->filestream_with_CD = NULL;
+    TRYFREE(s);
+    return UNZ_OK;
+}
+
+/* Goto to the next available disk for spanned archives */
+static int unzGoToNextDisk(unzFile file)
+{
+    unz64_internal *s;
+    uint32_t number_disk_next = 0;
+
+    s = (unz64_internal*)file;
+    if (s == NULL)
+        return UNZ_PARAMERROR;
+    number_disk_next = s->number_disk;
+
+    if ((s->pfile_in_zip_read != NULL) && (s->pfile_in_zip_read->rest_read_uncompressed > 0))
+        /* We are currently reading a file and we need the next sequential disk */
+        number_disk_next += 1;
+    else
+        /* Goto the disk for the current file */
+        number_disk_next = s->cur_file_info.disk_num_start;
+
+    if (number_disk_next != s->number_disk)
+    {
+        /* Switch disks */
+        if ((s->filestream != NULL) && (s->filestream != s->filestream_with_CD))
+            ZCLOSE64(s->z_filefunc, s->filestream);
+
+        if (number_disk_next == s->gi.number_disk_with_CD)
+        {
+            s->filestream = s->filestream_with_CD;
+        }
+        else
+        {
+            s->filestream = ZOPENDISK64(s->z_filefunc, s->filestream_with_CD, number_disk_next,
+                ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING);
+        }
+
+        if (s->filestream == NULL)
+            return UNZ_ERRNO;
+
+        s->number_disk = number_disk_next;
+    }
+
+    return UNZ_OK;
+}
+
+extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info* pglobal_info32)
+{
+    unz64_internal *s = NULL;
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+
+    pglobal_info32->number_entry = (uint32_t)s->gi.number_entry;
+    pglobal_info32->size_comment = s->gi.size_comment;
+    pglobal_info32->number_disk_with_CD = s->gi.number_disk_with_CD;
+    return UNZ_OK;
+}
+
+extern int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64 *pglobal_info)
+{
+    unz64_internal *s = NULL;
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    *pglobal_info = s->gi;
+    return UNZ_OK;
+}
+
+extern int ZEXPORT unzGetGlobalComment(unzFile file, char *comment, uint16_t comment_size)
+{
+    unz64_internal *s = NULL;
+    uint16_t bytes_to_read = comment_size;
+    if (file == NULL)
+        return (int)UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+
+    if (bytes_to_read > s->gi.size_comment)
+        bytes_to_read = s->gi.size_comment;
+
+    if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, s->central_pos + 22, ZLIB_FILEFUNC_SEEK_SET) != 0)
+        return UNZ_ERRNO;
+
+    if (bytes_to_read > 0)
+    {
+        *comment = 0;
+        if (ZREAD64(s->z_filefunc, s->filestream_with_CD, comment, bytes_to_read) != bytes_to_read)
+            return UNZ_ERRNO;
+    }
+
+    if ((comment != NULL) && (comment_size > s->gi.size_comment))
+        *(comment + s->gi.size_comment) = 0;
+
+    return (int)bytes_to_read;
+}
+
+static int unzGetCurrentFileInfoField(unzFile file, uint32_t *seek, void *field, uint16_t field_size, uint16_t size_file_field, int null_terminated_field)
+{
+    unz64_internal *s = NULL;
+    uint32_t bytes_to_read = 0;
+    int err = UNZ_OK;
+
+    if (file == NULL)
+        return (int)UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+
+    /* Read field */
+    if (field != NULL)
+    {
+        if (size_file_field < field_size)
+        {
+            if (null_terminated_field)
+                *((char *)field+size_file_field) = 0;
+
+            bytes_to_read = size_file_field;
+        }
+        else
+            bytes_to_read = field_size;
+        
+        if (*seek != 0)
+        {
+            if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, *seek, ZLIB_FILEFUNC_SEEK_CUR) == 0)
+                *seek = 0;
+            else
+                err = UNZ_ERRNO;
+        }
+        
+        if ((size_file_field > 0) && (field_size > 0))
+        {
+            if (ZREAD64(s->z_filefunc, s->filestream_with_CD, field, bytes_to_read) != bytes_to_read)
+                err = UNZ_ERRNO;
+        }
+        *seek += size_file_field - bytes_to_read;
+    }
+    else
+    {
+        *seek += size_file_field;
+    }
+
+    return err;
+}
+
+/* Get info about the current file in the zipfile, with internal only info */
+static int unzGetCurrentFileInfoInternal(unzFile file, unz_file_info64 *pfile_info,
+    unz_file_info64_internal *pfile_info_internal, char *filename, uint16_t filename_size, void *extrafield,
+    uint16_t extrafield_size, char *comment, uint16_t comment_size)
+{
+    unz64_internal *s = NULL;
+    unz_file_info64 file_info;
+    unz_file_info64_internal file_info_internal;
+    uint32_t magic = 0;
+    uint64_t current_pos = 0;
+    uint32_t seek = 0;
+    uint32_t extra_pos = 0;
+    uint16_t extra_header_id = 0;
+    uint16_t extra_data_size = 0;
+    uint16_t value16 = 0;
+    uint32_t value32 = 0;
+    uint64_t value64 = 0;
+    int err = UNZ_OK;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+
+    if (ZSEEK64(s->z_filefunc, s->filestream_with_CD,
+            s->pos_in_central_dir + s->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0)
+        err = UNZ_ERRNO;
+
+    /* Check the magic */
+    if (err == UNZ_OK)
+    {
+        if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &magic) != UNZ_OK)
+            err = UNZ_ERRNO;
+        else if (magic != CENTRALHEADERMAGIC)
+            err = UNZ_BADZIPFILE;
+    }
+
+    /* Read central directory header */
+    if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.version) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.version_needed) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.flag) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.compression_method) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.dos_date) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.crc) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &value32) != UNZ_OK)
+        err = UNZ_ERRNO;
+    file_info.compressed_size = value32;
+    if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &value32) != UNZ_OK)
+        err = UNZ_ERRNO;
+    file_info.uncompressed_size = value32;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.size_filename) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.size_file_extra) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.size_file_comment) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &value16) != UNZ_OK)
+        err = UNZ_ERRNO;
+    file_info.disk_num_start = value16;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.internal_fa) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.external_fa) != UNZ_OK)
+        err = UNZ_ERRNO;
+    /* Relative offset of local header */
+    if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &value32) != UNZ_OK)
+        err = UNZ_ERRNO;
+
+    file_info.size_file_extra_internal = 0;
+    file_info.disk_offset = value32;
+    file_info_internal.offset_curfile = value32;
+#ifdef HAVE_AES
+    file_info_internal.aes_compression_method = 0;
+    file_info_internal.aes_encryption_mode = 0;
+    file_info_internal.aes_version = 0;
+#endif
+
+    if (err == UNZ_OK)
+        err = unzGetCurrentFileInfoField(file, &seek, filename, filename_size, file_info.size_filename, 1);
+
+    /* Read extrafield */
+    if (err == UNZ_OK)
+        err = unzGetCurrentFileInfoField(file, &seek, extrafield, extrafield_size, file_info.size_file_extra, 0);
+
+    if ((err == UNZ_OK) && (file_info.size_file_extra != 0))
+    {
+        if (seek != 0)
+        {
+            if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, seek, ZLIB_FILEFUNC_SEEK_CUR) == 0)
+                seek = 0;
+            else
+                err = UNZ_ERRNO;
+        }
+
+        /* We are going to parse the extra field so we need to move back */
+        current_pos = ZTELL64(s->z_filefunc, s->filestream_with_CD);
+        if (current_pos < file_info.size_file_extra)
+            err = UNZ_ERRNO;
+        current_pos -= file_info.size_file_extra;
+        if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, current_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
+            err = UNZ_ERRNO;
+
+        while ((err != UNZ_ERRNO) && (extra_pos < file_info.size_file_extra))
+        {
+            if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &extra_header_id) != UNZ_OK)
+                err = UNZ_ERRNO;
+            if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &extra_data_size) != UNZ_OK)
+                err = UNZ_ERRNO;
+
+            /* ZIP64 extra fields */
+            if (extra_header_id == 0x0001)
+            {
+                /* Subtract size of ZIP64 field, since ZIP64 is handled internally */
+                file_info.size_file_extra_internal += 2 + 2 + extra_data_size;
+
+                if (file_info.uncompressed_size == UINT32_MAX)
+                {
+                    if (unzReadUInt64(&s->z_filefunc, s->filestream_with_CD, &file_info.uncompressed_size) != UNZ_OK)
+                        err = UNZ_ERRNO;
+                }
+                if (file_info.compressed_size == UINT32_MAX)
+                {
+                    if (unzReadUInt64(&s->z_filefunc, s->filestream_with_CD, &file_info.compressed_size) != UNZ_OK)
+                        err = UNZ_ERRNO;
+                }
+                if (file_info_internal.offset_curfile == UINT32_MAX)
+                {
+                    /* Relative Header offset */
+                    if (unzReadUInt64(&s->z_filefunc, s->filestream_with_CD, &value64) != UNZ_OK)
+                        err = UNZ_ERRNO;
+                    file_info_internal.offset_curfile = value64;
+                    file_info.disk_offset = value64;
+                }
+                if (file_info.disk_num_start == UINT32_MAX)
+                {
+                    /* Disk Start Number */
+                    if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.disk_num_start) != UNZ_OK)
+                        err = UNZ_ERRNO;
+                }
+            }
+#ifdef HAVE_AES
+            /* AES header */
+            else if (extra_header_id == 0x9901)
+            {
+                uint8_t value8 = 0;
+
+                /* Subtract size of AES field, since AES is handled internally */
+                file_info.size_file_extra_internal += 2 + 2 + extra_data_size;
+
+                /* Verify version info */
+                if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &value16) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                /* Support AE-1 and AE-2 */
+                if (value16 != 1 && value16 != 2)
+                    err = UNZ_ERRNO;
+                file_info_internal.aes_version = value16;
+                if (unzReadUInt8(&s->z_filefunc, s->filestream_with_CD, &value8) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                if ((char)value8 != 'A')
+                    err = UNZ_ERRNO;
+                if (unzReadUInt8(&s->z_filefunc, s->filestream_with_CD, &value8) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                if ((char)value8 != 'E')
+                    err = UNZ_ERRNO;
+                /* Get AES encryption strength and actual compression method */
+                if (unzReadUInt8(&s->z_filefunc, s->filestream_with_CD, &value8) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                file_info_internal.aes_encryption_mode = value8;
+                if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &value16) != UNZ_OK)
+                    err = UNZ_ERRNO;
+                file_info_internal.aes_compression_method = value16;
+            }
+#endif
+            else
+            {
+                if (ZSEEK64(s->z_filefunc, s->filestream_with_CD,extra_data_size, ZLIB_FILEFUNC_SEEK_CUR) != 0)
+                    err = UNZ_ERRNO;
+            }
+
+            extra_pos += 2 + 2 + extra_data_size;
+        }
+    }
+
+    if (file_info.disk_num_start == s->gi.number_disk_with_CD)
+        file_info_internal.byte_before_the_zipfile = s->byte_before_the_zipfile;
+    else
+        file_info_internal.byte_before_the_zipfile = 0;
+
+    if (err == UNZ_OK)
+        err = unzGetCurrentFileInfoField(file, &seek, comment, comment_size, file_info.size_file_comment, 1);
+
+    if ((err == UNZ_OK) && (pfile_info != NULL))
+        *pfile_info = file_info;
+    if ((err == UNZ_OK) && (pfile_info_internal != NULL))
+        *pfile_info_internal = file_info_internal;
+
+    return err;
+}
+
+extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *filename,
+    uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size)
+{
+    unz_file_info64 file_info64;
+    int err = UNZ_OK;
+
+    err = unzGetCurrentFileInfoInternal(file, &file_info64, NULL, filename, filename_size,
+                extrafield, extrafield_size, comment, comment_size);
+
+    if ((err == UNZ_OK) && (pfile_info != NULL))
+    {
+        pfile_info->version = file_info64.version;
+        pfile_info->version_needed = file_info64.version_needed;
+        pfile_info->flag = file_info64.flag;
+        pfile_info->compression_method = file_info64.compression_method;
+        pfile_info->dos_date = file_info64.dos_date;
+        pfile_info->crc = file_info64.crc;
+
+        pfile_info->size_filename = file_info64.size_filename;
+        pfile_info->size_file_extra = file_info64.size_file_extra - file_info64.size_file_extra_internal;
+        pfile_info->size_file_comment = file_info64.size_file_comment;
+
+        pfile_info->disk_num_start = (uint16_t)file_info64.disk_num_start;
+        pfile_info->internal_fa = file_info64.internal_fa;
+        pfile_info->external_fa = file_info64.external_fa;
+
+        pfile_info->compressed_size = (uint32_t)file_info64.compressed_size;
+        pfile_info->uncompressed_size = (uint32_t)file_info64.uncompressed_size;
+    }
+    return err;
+}
+
+extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 * pfile_info, char *filename,
+    uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size)
+{
+    return unzGetCurrentFileInfoInternal(file, pfile_info, NULL, filename, filename_size,
+        extrafield, extrafield_size, comment,comment_size);
+}
+
+/* Read the local header of the current zipfile. Check the coherency of the local header and info in the
+   end of central directory about this file store in *piSizeVar the size of extra info in local header
+   (filename and size of extra field data) */
+static int unzCheckCurrentFileCoherencyHeader(unz64_internal *s, uint32_t *psize_variable, uint64_t *poffset_local_extrafield,
+    uint16_t *psize_local_extrafield)
+{
+    uint32_t magic = 0;
+    uint16_t value16 = 0;
+    uint32_t value32 = 0;
+    uint32_t flags = 0;
+    uint16_t size_filename = 0;
+    uint16_t size_extra_field = 0;
+    uint16_t compression_method = 0;
+    int err = UNZ_OK;
+
+    if (psize_variable == NULL)
+        return UNZ_PARAMERROR;
+    *psize_variable = 0;
+    if (poffset_local_extrafield == NULL)
+        return UNZ_PARAMERROR;
+    *poffset_local_extrafield = 0;
+    if (psize_local_extrafield == NULL)
+        return UNZ_PARAMERROR;
+    *psize_local_extrafield = 0;
+
+    err = unzGoToNextDisk((unzFile)s);
+    if (err != UNZ_OK)
+        return err;
+
+    if (ZSEEK64(s->z_filefunc, s->filestream, s->cur_file_info_internal.offset_curfile +
+        s->cur_file_info_internal.byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0)
+        return UNZ_ERRNO;
+
+    if (err == UNZ_OK)
+    {
+        if (unzReadUInt32(&s->z_filefunc, s->filestream, &magic) != UNZ_OK)
+            err = UNZ_ERRNO;
+        else if (magic != LOCALHEADERMAGIC)
+            err = UNZ_BADZIPFILE;
+    }
+
+    if (unzReadUInt16(&s->z_filefunc, s->filestream, &value16) != UNZ_OK)
+        err = UNZ_ERRNO;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream, &value16) != UNZ_OK)
+        err = UNZ_ERRNO;
+    flags = value16;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream, &value16) != UNZ_OK)
+        err = UNZ_ERRNO;
+    else if ((err == UNZ_OK) && (value16 != s->cur_file_info.compression_method))
+        err = UNZ_BADZIPFILE;
+
+    compression_method = s->cur_file_info.compression_method;
+#ifdef HAVE_AES
+    if (compression_method == AES_METHOD)
+        compression_method = s->cur_file_info_internal.aes_compression_method;
+#endif
+
+    if ((err == UNZ_OK) && (compression_method != 0) && (compression_method != Z_DEFLATED))
+    {
+#ifdef HAVE_BZIP2
+        if (compression_method != Z_BZIP2ED)
+#endif
+            err = UNZ_BADZIPFILE;
+    }
+
+    if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* date/time */
+        err = UNZ_ERRNO;
+    if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* crc */
+        err = UNZ_ERRNO;
+    else if ((err == UNZ_OK) && (value32 != s->cur_file_info.crc) && ((flags & 8) == 0))
+        err = UNZ_BADZIPFILE;
+    if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* size compr */
+        err = UNZ_ERRNO;
+    else if ((value32 != UINT32_MAX) && (err == UNZ_OK) && (value32 != s->cur_file_info.compressed_size) && ((flags & 8) == 0))
+        err = UNZ_BADZIPFILE;
+    if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* size uncompr */
+        err = UNZ_ERRNO;
+    else if ((value32 != UINT32_MAX) && (err == UNZ_OK) && (value32 != s->cur_file_info.uncompressed_size) && ((flags & 8) == 0))
+        err = UNZ_BADZIPFILE;
+    if (unzReadUInt16(&s->z_filefunc, s->filestream, &size_filename) != UNZ_OK)
+        err = UNZ_ERRNO;
+
+    *psize_variable += size_filename;
+
+    if (unzReadUInt16(&s->z_filefunc, s->filestream, &size_extra_field) != UNZ_OK)
+        err = UNZ_ERRNO;
+
+    *poffset_local_extrafield = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename;
+    *psize_local_extrafield = size_extra_field;
+    *psize_variable += size_extra_field;
+
+    return err;
+}
+
+extern uint64_t ZEXPORT unzCountEntries(const unzFile file)
+{
+    if (file == NULL)
+        return 0;
+    
+    unz64_internal s = *(unz64_internal*)file;
+    
+    s.pos_in_central_dir = s.offset_central_dir;
+    s.num_file = 0;
+    while (UNZ_OK == unzGetCurrentFileInfoInternal(&s,
+                                                   &s.cur_file_info,
+                                                   &s.cur_file_info_internal,
+                                                   NULL, 0, NULL, 0, NULL, 0))
+    {
+        s.pos_in_central_dir += SIZECENTRALDIRITEM
+        + s.cur_file_info.size_filename
+        + s.cur_file_info.size_file_extra
+        + s.cur_file_info.size_file_comment;
+        s.num_file += 1;
+    }
+    return s.num_file;
+}
+
+/*
+  Open for reading data the current file in the zipfile.
+  If there is no error and the file is opened, the return value is UNZ_OK.
+*/
+extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int *method, int *level, int raw, const char *password)
+{
+    unz64_internal *s = NULL;
+    file_in_zip64_read_info_s *pfile_in_zip_read_info = NULL;
+    uint16_t compression_method = 0;
+    uint64_t offset_local_extrafield = 0;
+    uint16_t size_local_extrafield = 0;
+    uint32_t size_variable = 0;
+    int err = UNZ_OK;
+#ifndef NOUNCRYPT
+    char source[12];
+#else
+    if (password != NULL)
+        return UNZ_PARAMERROR;
+#endif
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    if (!s->current_file_ok)
+        return UNZ_PARAMERROR;
+
+    if (s->pfile_in_zip_read != NULL)
+        unzCloseCurrentFile(file);
+
+    if (unzCheckCurrentFileCoherencyHeader(s, &size_variable, &offset_local_extrafield, &size_local_extrafield) != UNZ_OK)
+        return UNZ_BADZIPFILE;
+    
+    compression_method = s->cur_file_info.compression_method;
+#ifdef HAVE_AES
+    if (compression_method == AES_METHOD)
+    {
+        compression_method = s->cur_file_info_internal.aes_compression_method;
+        if (password == NULL)
+        {
+            return UNZ_PARAMERROR;
+        }
+    }
+#endif
+
+    if (method != NULL)
+        *method = compression_method;
+
+    if (level != NULL)
+    {
+        *level = 6;
+        switch (s->cur_file_info.flag & 0x06)
+        {
+          case 6 : *level = 1; break;
+          case 4 : *level = 2; break;
+          case 2 : *level = 9; break;
+        }
+    }
+
+    if ((compression_method != 0) && (compression_method != Z_DEFLATED))
+    {
+#ifdef HAVE_BZIP2
+        if (compression_method != Z_BZIP2ED)
+#endif
+        {
+            return UNZ_BADZIPFILE;
+        }
+    }
+    
+    pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s));
+    if (pfile_in_zip_read_info == NULL)
+        return UNZ_INTERNALERROR;
+
+    pfile_in_zip_read_info->read_buffer = (uint8_t*)ALLOC(UNZ_BUFSIZE);
+    if (pfile_in_zip_read_info->read_buffer == NULL)
+    {
+        TRYFREE(pfile_in_zip_read_info);
+        return UNZ_INTERNALERROR;
+    }
+    
+    pfile_in_zip_read_info->stream_initialised = 0;
+
+    pfile_in_zip_read_info->filestream = s->filestream;
+    pfile_in_zip_read_info->z_filefunc = s->z_filefunc;
+
+    pfile_in_zip_read_info->raw = raw;
+    pfile_in_zip_read_info->crc32 = 0;
+    pfile_in_zip_read_info->crc32_expected = s->cur_file_info.crc;
+    pfile_in_zip_read_info->total_out_64 = 0;
+    pfile_in_zip_read_info->compression_method = compression_method;
+    
+    pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield;
+    pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield;
+    pfile_in_zip_read_info->pos_local_extrafield = 0;
+
+    pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size;
+    pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size;
+    pfile_in_zip_read_info->byte_before_the_zipfile = 0;
+
+    if (s->number_disk == s->gi.number_disk_with_CD)
+        pfile_in_zip_read_info->byte_before_the_zipfile = s->byte_before_the_zipfile;
+        
+    pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_variable;
+
+    pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
+    pfile_in_zip_read_info->stream.zfree = (free_func)0;
+    pfile_in_zip_read_info->stream.opaque = (voidpf)s;
+    pfile_in_zip_read_info->stream.total_out = 0;
+    pfile_in_zip_read_info->stream.total_in = 0;
+    pfile_in_zip_read_info->stream.next_in = NULL;
+    pfile_in_zip_read_info->stream.avail_in = 0;
+
+    if (!raw)
+    {
+        if (compression_method == Z_BZIP2ED)
+        {
+#ifdef HAVE_BZIP2
+            pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0;
+            pfile_in_zip_read_info->bstream.bzfree = (free_func)0;
+            pfile_in_zip_read_info->bstream.opaque = (voidpf)0;
+            pfile_in_zip_read_info->bstream.state = (voidpf)0;
+
+            err = BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0);
+            if (err == Z_OK)
+            {
+                pfile_in_zip_read_info->stream_initialised = Z_BZIP2ED;
+            }
+            else
+            {
+                TRYFREE(pfile_in_zip_read_info);
+                return err;
+            }
+#else
+            pfile_in_zip_read_info->raw = 1;
+#endif
+        }
+        else if (compression_method == Z_DEFLATED)
+        {
+#ifdef HAVE_APPLE_COMPRESSION
+            err = compression_stream_init(&pfile_in_zip_read_info->astream, COMPRESSION_STREAM_DECODE, COMPRESSION_ZLIB);
+            if (err == COMPRESSION_STATUS_ERROR)
+                err = UNZ_INTERNALERROR;
+            else
+                err = Z_OK;
+#else
+            err = inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
+#endif
+            if (err == Z_OK)
+            {
+                pfile_in_zip_read_info->stream_initialised = Z_DEFLATED;
+            }
+            else
+            {
+                TRYFREE(pfile_in_zip_read_info);
+                return err;
+            }
+            /* windowBits is passed < 0 to tell that there is no zlib header.
+             * Note that in this case inflate *requires* an extra "dummy" byte
+             * after the compressed stream in order to complete decompression and
+             * return Z_STREAM_END.
+             * In unzip, i don't wait absolutely Z_STREAM_END because I known the
+             * size of both compressed and uncompressed data
+             */
+        }
+    }
+
+    s->pfile_in_zip_read = pfile_in_zip_read_info;
+
+#ifndef NOUNCRYPT
+    s->pcrc_32_tab = NULL;
+
+    if ((password != NULL) && ((s->cur_file_info.flag & 1) != 0))
+    {
+        if (ZSEEK64(s->z_filefunc, s->filestream,
+                  s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile,
+                  ZLIB_FILEFUNC_SEEK_SET) != 0)
+            return UNZ_INTERNALERROR;
+#ifdef HAVE_AES
+        if (s->cur_file_info.compression_method == AES_METHOD)
+        {
+            unsigned char passverify_archive[AES_PWVERIFYSIZE];
+            unsigned char passverify_password[AES_PWVERIFYSIZE];
+            unsigned char salt_value[AES_MAXSALTLENGTH];
+            uint32_t salt_length = 0;
+
+            if ((s->cur_file_info_internal.aes_encryption_mode < 1) ||
+                (s->cur_file_info_internal.aes_encryption_mode > 3))
+                return UNZ_INTERNALERROR;
+
+            salt_length = SALT_LENGTH(s->cur_file_info_internal.aes_encryption_mode);
+
+            if (ZREAD64(s->z_filefunc, s->filestream, salt_value, salt_length) != salt_length)
+                return UNZ_INTERNALERROR;
+            if (ZREAD64(s->z_filefunc, s->filestream, passverify_archive, AES_PWVERIFYSIZE) != AES_PWVERIFYSIZE)
+                return UNZ_INTERNALERROR;
+
+            fcrypt_init(s->cur_file_info_internal.aes_encryption_mode, (uint8_t *)password,
+                (uint32_t)strlen(password), salt_value, passverify_password, &s->pfile_in_zip_read->aes_ctx);
+
+            if (memcmp(passverify_archive, passverify_password, AES_PWVERIFYSIZE) != 0)
+                return UNZ_BADPASSWORD;
+
+            s->pfile_in_zip_read->rest_read_compressed -= salt_length + AES_PWVERIFYSIZE;
+            s->pfile_in_zip_read->rest_read_compressed -= AES_AUTHCODESIZE;
+
+            s->pfile_in_zip_read->pos_in_zipfile += salt_length + AES_PWVERIFYSIZE;
+        }
+        else
+#endif
+        {
+            int i;
+            s->pcrc_32_tab = (const z_crc_t*)get_crc_table();
+            init_keys(password, s->keys, s->pcrc_32_tab);
+
+            if (ZREAD64(s->z_filefunc, s->filestream, source, 12) < 12)
+                return UNZ_INTERNALERROR;
+
+            for (i = 0; i < 12; i++)
+                zdecode(s->keys, s->pcrc_32_tab, source[i]);
+
+            s->pfile_in_zip_read->rest_read_compressed -= 12;
+            s->pfile_in_zip_read->pos_in_zipfile += 12;
+        }
+    }
+#endif
+
+    return UNZ_OK;
+}
+
+extern int ZEXPORT unzOpenCurrentFile(unzFile file)
+{
+    return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL);
+}
+
+extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char *password)
+{
+    return unzOpenCurrentFile3(file, NULL, NULL, 0, password);
+}
+
+extern int ZEXPORT unzOpenCurrentFile2(unzFile file, int *method, int *level, int raw)
+{
+    return unzOpenCurrentFile3(file, method, level, raw, NULL);
+}
+
+/* Read bytes from the current file.
+   buf contain buffer where data must be copied
+   len the size of buf.
+
+   return the number of byte copied if some bytes are copied
+   return 0 if the end of file was reached
+   return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */
+extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, uint32_t len)
+{
+    unz64_internal *s = NULL;
+    uint32_t read = 0;
+    int err = UNZ_OK;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+
+    if (s->pfile_in_zip_read == NULL)
+        return UNZ_PARAMERROR;
+    if (s->pfile_in_zip_read->read_buffer == NULL)
+        return UNZ_END_OF_LIST_OF_FILE;
+    if (len == 0)
+        return 0;
+    if (len > UINT16_MAX)
+        return UNZ_PARAMERROR;
+
+    s->pfile_in_zip_read->stream.next_out = (uint8_t*)buf;
+    s->pfile_in_zip_read->stream.avail_out = (uint16_t)len;
+
+    if ((s->pfile_in_zip_read->compression_method == 0) || (s->pfile_in_zip_read->raw))
+    {
+        if (len > s->pfile_in_zip_read->rest_read_compressed + s->pfile_in_zip_read->stream.avail_in)
+            s->pfile_in_zip_read->stream.avail_out = (uint16_t)s->pfile_in_zip_read->rest_read_compressed +
+            s->pfile_in_zip_read->stream.avail_in;
+    }
+
+    do
+    {
+        if (s->pfile_in_zip_read->stream.avail_in == 0)
+        {
+            uint32_t bytes_to_read = UNZ_BUFSIZE;
+            uint32_t bytes_not_read = 0;
+            uint32_t bytes_read = 0;
+            uint32_t total_bytes_read = 0;
+
+            if (s->pfile_in_zip_read->stream.next_in != NULL)
+                bytes_not_read = (uint32_t)(s->pfile_in_zip_read->read_buffer + UNZ_BUFSIZE -
+                    s->pfile_in_zip_read->stream.next_in);
+            bytes_to_read -= bytes_not_read;
+            if (bytes_not_read > 0)
+                memcpy(s->pfile_in_zip_read->read_buffer, s->pfile_in_zip_read->stream.next_in, bytes_not_read);
+            if (s->pfile_in_zip_read->rest_read_compressed < bytes_to_read)
+                bytes_to_read = (uint16_t)s->pfile_in_zip_read->rest_read_compressed;
+
+            while (total_bytes_read != bytes_to_read)
+            {
+                if (ZSEEK64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream,
+                        s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile,
+                        ZLIB_FILEFUNC_SEEK_SET) != 0)
+                    return UNZ_ERRNO;
+
+                bytes_read = ZREAD64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream,
+                          s->pfile_in_zip_read->read_buffer + bytes_not_read + total_bytes_read,
+                          bytes_to_read - total_bytes_read);
+
+                total_bytes_read += bytes_read;
+                s->pfile_in_zip_read->pos_in_zipfile += bytes_read;
+
+                if (bytes_read == 0)
+                {
+                    if (ZERROR64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream))
+                        return UNZ_ERRNO;
+
+                    err = unzGoToNextDisk(file);
+                    if (err != UNZ_OK)
+                        return err;
+
+                    s->pfile_in_zip_read->pos_in_zipfile = 0;
+                    s->pfile_in_zip_read->filestream = s->filestream;
+                }
+            }
+
+#ifndef NOUNCRYPT
+            if ((s->cur_file_info.flag & 1) != 0)
+            {
+#ifdef HAVE_AES
+                if (s->cur_file_info.compression_method == AES_METHOD)
+                {
+                    fcrypt_decrypt(s->pfile_in_zip_read->read_buffer, bytes_to_read, &s->pfile_in_zip_read->aes_ctx);
+                }
+                else
+#endif
+                if (s->pcrc_32_tab != NULL)
+                {
+                    uint32_t i = 0;
+
+                    for (i = 0; i < total_bytes_read; i++)
+                      s->pfile_in_zip_read->read_buffer[i] =
+                          zdecode(s->keys, s->pcrc_32_tab, s->pfile_in_zip_read->read_buffer[i]);
+                }
+            }
+#endif
+
+            s->pfile_in_zip_read->rest_read_compressed -= total_bytes_read;
+            s->pfile_in_zip_read->stream.next_in = (uint8_t*)s->pfile_in_zip_read->read_buffer;
+            s->pfile_in_zip_read->stream.avail_in = (uint16_t)(bytes_not_read + total_bytes_read);
+        }
+
+        if ((s->pfile_in_zip_read->compression_method == 0) || (s->pfile_in_zip_read->raw))
+        {
+            uint32_t i = 0;
+            uint32_t copy = 0;
+
+            if ((s->pfile_in_zip_read->stream.avail_in == 0) &&
+                (s->pfile_in_zip_read->rest_read_compressed == 0))
+                return (read == 0) ? UNZ_EOF : read;
+
+            if (s->pfile_in_zip_read->stream.avail_out < s->pfile_in_zip_read->stream.avail_in)
+                copy = s->pfile_in_zip_read->stream.avail_out;
+            else
+                copy = s->pfile_in_zip_read->stream.avail_in;
+
+            for (i = 0; i < copy; i++)
+                *(s->pfile_in_zip_read->stream.next_out + i) =
+                        *(s->pfile_in_zip_read->stream.next_in + i);
+
+            s->pfile_in_zip_read->total_out_64 = s->pfile_in_zip_read->total_out_64 + copy;
+            s->pfile_in_zip_read->rest_read_uncompressed -= copy;
+            s->pfile_in_zip_read->crc32 = (uint32_t)crc32(s->pfile_in_zip_read->crc32,
+                                s->pfile_in_zip_read->stream.next_out, copy);
+
+            s->pfile_in_zip_read->stream.avail_in -= copy;
+            s->pfile_in_zip_read->stream.avail_out -= copy;
+            s->pfile_in_zip_read->stream.next_out += copy;
+            s->pfile_in_zip_read->stream.next_in += copy;
+            s->pfile_in_zip_read->stream.total_out += copy;
+
+            read += copy;
+        }
+        else if (s->pfile_in_zip_read->compression_method == Z_BZIP2ED)
+        {
+#ifdef HAVE_BZIP2
+            uint64_t total_out_before = 0;
+            uint64_t total_out_after = 0;
+            uint64_t out_bytes = 0;
+            const uint8_t *buf_before = NULL;
+
+            s->pfile_in_zip_read->bstream.next_in        = (char*)s->pfile_in_zip_read->stream.next_in;
+            s->pfile_in_zip_read->bstream.avail_in       = s->pfile_in_zip_read->stream.avail_in;
+            s->pfile_in_zip_read->bstream.total_in_lo32  = (uint32_t)s->pfile_in_zip_read->stream.total_in;
+            s->pfile_in_zip_read->bstream.total_in_hi32  = s->pfile_in_zip_read->stream.total_in >> 32;
+            
+            s->pfile_in_zip_read->bstream.next_out       = (char*)s->pfile_in_zip_read->stream.next_out;
+            s->pfile_in_zip_read->bstream.avail_out      = s->pfile_in_zip_read->stream.avail_out;
+            s->pfile_in_zip_read->bstream.total_out_lo32 = (uint32_t)s->pfile_in_zip_read->stream.total_out;
+            s->pfile_in_zip_read->bstream.total_out_hi32 = s->pfile_in_zip_read->stream.total_out >> 32;
+
+            total_out_before = s->pfile_in_zip_read->bstream.total_out_lo32 + 
+                (((uint32_t)s->pfile_in_zip_read->bstream.total_out_hi32) << 32);
+            buf_before = (const uint8_t*)s->pfile_in_zip_read->bstream.next_out;
+
+            err = BZ2_bzDecompress(&s->pfile_in_zip_read->bstream);
+
+            total_out_after = s->pfile_in_zip_read->bstream.total_out_lo32 + 
+                (((uint32_t)s->pfile_in_zip_read->bstream.total_out_hi32) << 32);
+
+            out_bytes = total_out_after - total_out_before;
+
+            s->pfile_in_zip_read->total_out_64 = s->pfile_in_zip_read->total_out_64 + out_bytes;
+            s->pfile_in_zip_read->rest_read_uncompressed -= out_bytes;
+            s->pfile_in_zip_read->crc32 = crc32(s->pfile_in_zip_read->crc32, buf_before, (uint32_t)out_bytes);
+
+            read += (uint32_t)out_bytes;
+
+            s->pfile_in_zip_read->stream.next_in   = (uint8_t*)s->pfile_in_zip_read->bstream.next_in;
+            s->pfile_in_zip_read->stream.avail_in  = s->pfile_in_zip_read->bstream.avail_in;
+            s->pfile_in_zip_read->stream.total_in  = s->pfile_in_zip_read->bstream.total_in_lo32;
+            s->pfile_in_zip_read->stream.next_out  = (uint8_t*)s->pfile_in_zip_read->bstream.next_out;
+            s->pfile_in_zip_read->stream.avail_out = s->pfile_in_zip_read->bstream.avail_out;
+            s->pfile_in_zip_read->stream.total_out = s->pfile_in_zip_read->bstream.total_out_lo32;
+
+            if (err == BZ_STREAM_END)
+                return (read == 0) ? UNZ_EOF : read;
+            if (err != BZ_OK)
+                break;
+#endif
+        }
+#ifdef HAVE_APPLE_COMPRESSION
+        else
+        {
+            uint64_t total_out_before = 0;
+            uint64_t total_out_after = 0;
+            uint64_t out_bytes = 0;
+            const uint8_t *buf_before = NULL;
+
+            s->pfile_in_zip_read->astream.src_ptr = s->pfile_in_zip_read->stream.next_in;
+            s->pfile_in_zip_read->astream.src_size = s->pfile_in_zip_read->stream.avail_in;
+            s->pfile_in_zip_read->astream.dst_ptr = s->pfile_in_zip_read->stream.next_out;
+            s->pfile_in_zip_read->astream.dst_size = len;
+
+            total_out_before = s->pfile_in_zip_read->stream.total_out;
+            buf_before = s->pfile_in_zip_read->stream.next_out;
+
+            compression_status status;
+            compression_stream_flags flags;
+
+            if (s->pfile_in_zip_read->stream.avail_in == 0)
+            {
+                flags = COMPRESSION_STREAM_FINALIZE;
+            }
+
+            status = compression_stream_process(&s->pfile_in_zip_read->astream, flags);
+
+            total_out_after = len - s->pfile_in_zip_read->astream.dst_size;
+            out_bytes = total_out_after - total_out_before;
+
+            s->pfile_in_zip_read->total_out_64 += out_bytes;
+            s->pfile_in_zip_read->rest_read_uncompressed -= out_bytes;
+            s->pfile_in_zip_read->crc32 =
+                crc32(s->pfile_in_zip_read->crc32, buf_before, (uint32_t)out_bytes);
+
+            read += (uint32_t)out_bytes;
+
+            s->pfile_in_zip_read->stream.next_in = s->pfile_in_zip_read->astream.src_ptr;
+            s->pfile_in_zip_read->stream.avail_in = s->pfile_in_zip_read->astream.src_size;
+            s->pfile_in_zip_read->stream.next_out = s->pfile_in_zip_read->astream.dst_ptr;
+            s->pfile_in_zip_read->stream.avail_out = s->pfile_in_zip_read->astream.dst_size;
+
+            if (status == COMPRESSION_STATUS_END)
+                return (read == 0) ? UNZ_EOF : read;
+            if (status == COMPRESSION_STATUS_ERROR)
+                return Z_DATA_ERROR;
+            return read;
+        }
+#else
+        else
+        {
+            uint64_t total_out_before = 0;
+            uint64_t total_out_after = 0;
+            uint64_t out_bytes = 0;
+            const uint8_t *buf_before = NULL;
+            int flush = Z_SYNC_FLUSH;
+
+            total_out_before = s->pfile_in_zip_read->stream.total_out;
+            buf_before = s->pfile_in_zip_read->stream.next_out;
+
+            /*
+            if ((pfile_in_zip_read_info->rest_read_uncompressed ==
+                     pfile_in_zip_read_info->stream.avail_out) &&
+                (pfile_in_zip_read_info->rest_read_compressed == 0))
+                flush = Z_FINISH;
+            */
+            err = inflate(&s->pfile_in_zip_read->stream, flush);
+
+            if ((err >= 0) && (s->pfile_in_zip_read->stream.msg != NULL))
+                err = Z_DATA_ERROR;
+
+            total_out_after = s->pfile_in_zip_read->stream.total_out;
+            out_bytes = total_out_after - total_out_before;
+
+            s->pfile_in_zip_read->total_out_64 += out_bytes;
+            s->pfile_in_zip_read->rest_read_uncompressed -= out_bytes;
+            s->pfile_in_zip_read->crc32 =
+                (uint32_t)crc32(s->pfile_in_zip_read->crc32,buf_before, (uint32_t)out_bytes);
+
+            read += (uint32_t)out_bytes;
+
+            if (err == Z_STREAM_END)
+                return (read == 0) ? UNZ_EOF : read;
+            if (err != Z_OK)
+                break;
+        }
+#endif
+    }
+    while (s->pfile_in_zip_read->stream.avail_out > 0);
+
+    if (err == Z_OK)
+        return read;
+    return err;
+}
+
+extern int ZEXPORT unzGetLocalExtrafield(unzFile file, voidp buf, uint32_t len)
+{
+    unz64_internal *s = NULL;
+    uint64_t size_to_read = 0;
+    uint32_t read_now = 0;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    if (s->pfile_in_zip_read == NULL)
+        return UNZ_PARAMERROR;
+
+    size_to_read = s->pfile_in_zip_read->size_local_extrafield - s->pfile_in_zip_read->pos_local_extrafield;
+
+    if (buf == NULL)
+        return (int)size_to_read;
+
+    if (len > size_to_read)
+        read_now = (uint32_t)size_to_read;
+    else
+        read_now = len;
+
+    if (read_now == 0)
+        return 0;
+
+    if (ZSEEK64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream,
+        s->pfile_in_zip_read->offset_local_extrafield + s->pfile_in_zip_read->pos_local_extrafield,
+        ZLIB_FILEFUNC_SEEK_SET) != 0)
+        return UNZ_ERRNO;
+
+    if (ZREAD64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream, buf, read_now) != read_now)
+        return UNZ_ERRNO;
+
+    return (int)read_now;
+}
+
+extern int ZEXPORT unzCloseCurrentFile(unzFile file)
+{
+    unz64_internal *s = NULL;
+    file_in_zip64_read_info_s *pfile_in_zip_read_info = NULL;
+    int err = UNZ_OK;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    pfile_in_zip_read_info = s->pfile_in_zip_read;
+    if (pfile_in_zip_read_info == NULL)
+        return UNZ_PARAMERROR;
+
+#ifdef HAVE_AES
+    if (s->cur_file_info.compression_method == AES_METHOD)
+    {
+        unsigned char authcode[AES_AUTHCODESIZE];
+        unsigned char rauthcode[AES_AUTHCODESIZE];
+
+        if (ZREAD64(s->z_filefunc, s->filestream, authcode, AES_AUTHCODESIZE) != AES_AUTHCODESIZE)
+            return UNZ_ERRNO;
+
+        if (fcrypt_end(rauthcode, &s->pfile_in_zip_read->aes_ctx) != AES_AUTHCODESIZE)
+            err = UNZ_CRCERROR;
+        if (memcmp(authcode, rauthcode, AES_AUTHCODESIZE) != 0)
+            err = UNZ_CRCERROR;
+    }
+    /* AES zip version AE-1 will expect a valid crc as well */
+    if ((s->cur_file_info.compression_method != AES_METHOD) ||
+        (s->cur_file_info_internal.aes_version == 0x0001))
+#endif
+    {
+        if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) &&
+            (!pfile_in_zip_read_info->raw))
+        {
+            if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_expected)
+                err = UNZ_CRCERROR;
+        }
+    }
+
+    TRYFREE(pfile_in_zip_read_info->read_buffer);
+    pfile_in_zip_read_info->read_buffer = NULL;
+    if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED)
+    {
+#ifdef HAVE_APPLE_COMPRESSION
+        if (compression_stream_destroy)
+            compression_stream_destroy(&pfile_in_zip_read_info->astream);
+#else
+        inflateEnd(&pfile_in_zip_read_info->stream);
+#endif
+        
+    }
+#ifdef HAVE_BZIP2
+    else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED)
+        BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream);
+#endif
+
+    pfile_in_zip_read_info->stream_initialised = 0;
+    TRYFREE(pfile_in_zip_read_info);
+
+    s->pfile_in_zip_read = NULL;
+
+    return err;
+}
+
+extern int ZEXPORT unzGoToFirstFile2(unzFile file, unz_file_info64 *pfile_info, char *filename,
+    uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size)
+{
+    unz64_internal *s = NULL;
+    int err = UNZ_OK;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+
+    if (s->gi.number_entry == 0)
+        return UNZ_END_OF_LIST_OF_FILE;
+
+    s->pos_in_central_dir = s->offset_central_dir;
+    s->num_file = 0;
+
+    err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal,
+            filename, filename_size, extrafield, extrafield_size, comment,comment_size);
+
+    s->current_file_ok = (err == UNZ_OK);
+    if ((err == UNZ_OK) && (pfile_info != NULL))
+        memcpy(pfile_info, &s->cur_file_info, sizeof(unz_file_info64));
+
+    return err;
+}
+
+extern int ZEXPORT unzGoToFirstFile(unzFile file)
+{
+    return unzGoToFirstFile2(file, NULL, NULL, 0, NULL, 0, NULL, 0);
+}
+
+extern int ZEXPORT unzGoToNextFile2(unzFile file, unz_file_info64 *pfile_info, char *filename,
+    uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size)
+{
+    unz64_internal *s = NULL;
+    int err = UNZ_OK;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+
+    if (!s->current_file_ok)
+        return UNZ_END_OF_LIST_OF_FILE;
+    if (s->gi.number_entry != UINT16_MAX)    /* 2^16 files overflow hack */
+    {
+        if (s->num_file+1 == s->gi.number_entry)
+            return UNZ_END_OF_LIST_OF_FILE;
+    }
+
+    s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename +
+            s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment;
+    s->num_file += 1;
+
+    err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal,
+            filename, filename_size, extrafield,extrafield_size, comment, comment_size);
+
+    s->current_file_ok = (err == UNZ_OK);
+    if ((err == UNZ_OK) && (pfile_info != NULL))
+        memcpy(pfile_info, &s->cur_file_info, sizeof(unz_file_info64));
+
+    return err;
+}
+
+extern int ZEXPORT unzGoToNextFile(unzFile file)
+{
+    return unzGoToNextFile2(file, NULL, NULL, 0, NULL, 0, NULL, 0);
+}
+
+extern int ZEXPORT unzLocateFile(unzFile file, const char *filename, unzFileNameComparer filename_compare_func)
+{
+    unz64_internal *s = NULL;
+    unz_file_info64 cur_file_info_saved;
+    unz_file_info64_internal cur_file_info_internal_saved;
+    uint64_t num_file_saved = 0;
+    uint64_t pos_in_central_dir_saved = 0;
+    char current_filename[UNZ_MAXFILENAMEINZIP+1];
+    int err = UNZ_OK;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    if (strlen(filename) >= UNZ_MAXFILENAMEINZIP)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    if (!s->current_file_ok)
+        return UNZ_END_OF_LIST_OF_FILE;
+
+    /* Save the current state */
+    num_file_saved = s->num_file;
+    pos_in_central_dir_saved = s->pos_in_central_dir;
+    cur_file_info_saved = s->cur_file_info;
+    cur_file_info_internal_saved = s->cur_file_info_internal;
+
+    err = unzGoToFirstFile2(file, NULL, current_filename, sizeof(current_filename)-1, NULL, 0, NULL, 0);
+
+    while (err == UNZ_OK)
+    {
+        if (filename_compare_func != NULL)
+            err = filename_compare_func(file, current_filename, filename);
+        else
+            err = strcmp(current_filename, filename);
+        if (err == 0)
+            return UNZ_OK;
+        err = unzGoToNextFile2(file, NULL, current_filename, sizeof(current_filename)-1, NULL, 0, NULL, 0);
+    }
+
+    /* We failed, so restore the state of the 'current file' to where we were. */
+    s->num_file = num_file_saved;
+    s->pos_in_central_dir = pos_in_central_dir_saved;
+    s->cur_file_info = cur_file_info_saved;
+    s->cur_file_info_internal = cur_file_info_internal_saved;
+    return err;
+}
+
+extern int ZEXPORT unzGetFilePos(unzFile file, unz_file_pos *file_pos)
+{
+    unz64_file_pos file_pos64;
+    int err = unzGetFilePos64(file, &file_pos64);
+    if (err == UNZ_OK)
+    {
+        file_pos->pos_in_zip_directory = (uint32_t)file_pos64.pos_in_zip_directory;
+        file_pos->num_of_file = (uint32_t)file_pos64.num_of_file;
+    }
+    return err;
+}
+
+extern int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos *file_pos)
+{
+    unz64_file_pos file_pos64;
+    if (file_pos == NULL)
+        return UNZ_PARAMERROR;
+    file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory;
+    file_pos64.num_of_file = file_pos->num_of_file;
+    return unzGoToFilePos64(file, &file_pos64);
+}
+
+extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos *file_pos)
+{
+    unz64_internal *s = NULL;
+
+    if (file == NULL || file_pos == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    if (!s->current_file_ok)
+        return UNZ_END_OF_LIST_OF_FILE;
+
+    file_pos->pos_in_zip_directory  = s->pos_in_central_dir;
+    file_pos->num_of_file = s->num_file;
+    return UNZ_OK;
+}
+
+extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos *file_pos)
+{
+    unz64_internal *s = NULL;
+    int err = UNZ_OK;
+
+    if (file == NULL || file_pos == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+
+    /* Jump to the right spot */
+    s->pos_in_central_dir = file_pos->pos_in_zip_directory;
+    s->num_file = file_pos->num_of_file;
+
+    /* Set the current file */
+    err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal, NULL, 0, NULL, 0, NULL, 0);
+    /* Return results */
+    s->current_file_ok = (err == UNZ_OK);
+    return err;
+}
+
+extern int32_t ZEXPORT unzGetOffset(unzFile file)
+{
+    uint64_t offset64 = 0;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    offset64 = unzGetOffset64(file);
+    return (int32_t)offset64;
+}
+
+extern int64_t ZEXPORT unzGetOffset64(unzFile file)
+{
+    unz64_internal *s = NULL;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    if (!s->current_file_ok)
+        return 0;
+    if (s->gi.number_entry != 0 && s->gi.number_entry != UINT16_MAX)
+    {
+        if (s->num_file == s->gi.number_entry)
+            return 0;
+    }
+    return s->pos_in_central_dir;
+}
+
+extern int ZEXPORT unzSetOffset(unzFile file, uint32_t pos)
+{
+    return unzSetOffset64(file, pos);
+}
+
+extern int ZEXPORT unzSetOffset64(unzFile file, uint64_t pos)
+{
+    unz64_internal *s = NULL;
+    int err = UNZ_OK;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    s->pos_in_central_dir = pos;
+    s->num_file = s->gi.number_entry; /* hack */
+
+    err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal, NULL, 0, NULL, 0, NULL, 0);
+
+    s->current_file_ok = (err == UNZ_OK);
+    return err;
+}
+
+extern int32_t ZEXPORT unzTell(unzFile file)
+{
+    unz64_internal *s = NULL;
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    if (s->pfile_in_zip_read == NULL)
+        return UNZ_PARAMERROR;
+    return (int32_t)s->pfile_in_zip_read->stream.total_out;
+}
+
+extern int64_t ZEXPORT unzTell64(unzFile file)
+{
+    unz64_internal *s = NULL;
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    if (s->pfile_in_zip_read == NULL)
+        return UNZ_PARAMERROR;
+    return s->pfile_in_zip_read->total_out_64;
+}
+
+extern int ZEXPORT unzSeek(unzFile file, uint32_t offset, int origin)
+{
+    return unzSeek64(file, offset, origin);
+}
+
+extern int ZEXPORT unzSeek64(unzFile file, uint64_t offset, int origin)
+{
+    unz64_internal *s = NULL;
+    uint64_t stream_pos_begin = 0;
+    uint64_t stream_pos_end = 0;
+    uint64_t position = 0;
+    int is_within_buffer = 0;
+
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+
+    if (s->pfile_in_zip_read == NULL)
+        return UNZ_ERRNO;
+    if (s->pfile_in_zip_read->compression_method != 0)
+        return UNZ_ERRNO;
+
+    if (origin == SEEK_SET)
+        position = offset;
+    else if (origin == SEEK_CUR)
+        position = s->pfile_in_zip_read->total_out_64 + offset;
+    else if (origin == SEEK_END)
+        position = s->cur_file_info.compressed_size + offset;
+    else
+        return UNZ_PARAMERROR;
+
+    if (position > s->cur_file_info.compressed_size)
+        return UNZ_PARAMERROR;
+
+    stream_pos_end = s->pfile_in_zip_read->pos_in_zipfile;
+    stream_pos_begin = stream_pos_end;
+
+    if (stream_pos_begin > UNZ_BUFSIZE)
+        stream_pos_begin -= UNZ_BUFSIZE;
+    else
+        stream_pos_begin = 0;
+
+    is_within_buffer = 
+        (s->pfile_in_zip_read->stream.avail_in != 0) &&
+        (s->pfile_in_zip_read->rest_read_compressed != 0 || s->cur_file_info.compressed_size < UNZ_BUFSIZE) &&
+        (position >= stream_pos_begin && position < stream_pos_end);
+
+    if (is_within_buffer)
+    {
+        s->pfile_in_zip_read->stream.next_in += position - s->pfile_in_zip_read->total_out_64;
+        s->pfile_in_zip_read->stream.avail_in = (uInt)(stream_pos_end - position);
+    }
+    else
+    {
+        s->pfile_in_zip_read->stream.avail_in = 0;
+        s->pfile_in_zip_read->stream.next_in = 0;
+
+        s->pfile_in_zip_read->pos_in_zipfile = s->pfile_in_zip_read->offset_local_extrafield + position;
+        s->pfile_in_zip_read->rest_read_compressed = s->cur_file_info.compressed_size - position;
+    }
+
+    s->pfile_in_zip_read->rest_read_uncompressed -= (position - s->pfile_in_zip_read->total_out_64);
+    s->pfile_in_zip_read->stream.total_out = (uint32_t)position;
+    s->pfile_in_zip_read->total_out_64 = position;
+
+    return UNZ_OK;
+}
+
+extern int ZEXPORT unzEndOfFile(unzFile file)
+{
+    unz64_internal *s = NULL;
+    if (file == NULL)
+        return UNZ_PARAMERROR;
+    s = (unz64_internal*)file;
+    if (s->pfile_in_zip_read == NULL)
+        return UNZ_PARAMERROR;
+    if (s->pfile_in_zip_read->rest_read_uncompressed == 0)
+        return 1;
+    return 0;
+}
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/unzip.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/unzip.h
new file mode 100644 (file)
index 0000000..62f668b
--- /dev/null
@@ -0,0 +1,254 @@
+/* unzip.h -- IO for uncompress .zip files using zlib
+   Version 1.2.0, September 16th, 2017
+   part of the MiniZip project
+
+   Copyright (C) 2012-2017 Nathan Moinvaziri
+     https://github.com/nmoinvaz/minizip
+   Copyright (C) 2009-2010 Mathias Svensson
+     Modifications for Zip64 support on both zip and unzip
+     http://result42.com
+   Copyright (C) 2007-2008 Even Rouault
+     Modifications of Unzip for Zip64
+   Copyright (C) 1998-2010 Gilles Vollant
+     http://www.winimage.com/zLibDll/minizip.html
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+*/
+
+#ifndef _UNZ_H
+#define _UNZ_H
+
+#include "SSZipCommon.h"
+
+#define HAVE_AES
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#include "zlib.h"
+#endif
+
+#ifndef _ZLIBIOAPI_H
+#include "ioapi.h"
+#endif
+
+#ifdef HAVE_BZIP2
+#include "bzlib.h"
+#endif
+
+#define Z_BZIP2ED 12
+
+#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+    from (void*) without cast */
+typedef struct TagunzFile__ { int unused; } unz_file__;
+typedef unz_file__ *unzFile;
+#else
+typedef voidp unzFile;
+#endif
+
+#define UNZ_OK                          (0)
+#define UNZ_END_OF_LIST_OF_FILE         (-100)
+#define UNZ_ERRNO                       (Z_ERRNO)
+#define UNZ_EOF                         (0)
+#define UNZ_PARAMERROR                  (-102)
+#define UNZ_BADZIPFILE                  (-103)
+#define UNZ_INTERNALERROR               (-104)
+#define UNZ_CRCERROR                    (-105)
+#define UNZ_BADPASSWORD                 (-106)
+
+
+/***************************************************************************/
+/* Opening and close a zip file */
+
+extern unzFile ZEXPORT unzOpen(const char *path);
+extern unzFile ZEXPORT unzOpen64(const void *path);
+/* Open a Zip file.
+
+   path should contain the full path (by example, on a Windows XP computer 
+      "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". 
+   return NULL if zipfile cannot be opened or doesn't exist
+   return unzFile handle if no error
+
+   NOTE: The "64" function take a const void *pointer, because  the path is just the value passed to the
+   open64_file_func callback. Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path 
+   is a pointer to a wide unicode string  (LPCTSTR is LPCWSTR), so const char *does not describe the reality */
+
+extern unzFile ZEXPORT unzOpen2(const char *path, zlib_filefunc_def *pzlib_filefunc_def);
+/* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write operations */
+extern unzFile ZEXPORT unzOpen2_64(const void *path, zlib_filefunc64_def *pzlib_filefunc_def);
+/* Open a Zip file, like unz64Open, but provide a set of file low level API for read/write 64-bit operations */
+
+extern int ZEXPORT unzClose(unzFile file);
+/* Close a ZipFile opened with unzOpen. If there is files inside the .Zip opened with unzOpenCurrentFile,
+   these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+
+   return UNZ_OK if there is no error */
+
+extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info);
+extern int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64 *pglobal_info);
+/* Write info about the ZipFile in the *pglobal_info structure.
+
+   return UNZ_OK if no error */
+
+extern int ZEXPORT unzGetGlobalComment(unzFile file, char *comment, uint16_t comment_size);
+/* Get the global comment string of the ZipFile, in the comment buffer.
+
+   uSizeBuf is the size of the szComment buffer.
+   return the number of byte copied or an error code <0 */
+
+extern uint64_t ZEXPORT unzCountEntries(const unzFile file);
+
+/***************************************************************************/
+/* Reading the content of the current zipfile, you can open it, read data from it, and close it
+   (you can close it before reading all the file) */
+
+extern int ZEXPORT unzOpenCurrentFile(unzFile file);
+/* Open for reading data the current file in the zipfile.
+
+   return UNZ_OK if no error */
+
+extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char *password);
+/* Open for reading data the current file in the zipfile.
+   password is a crypting password
+
+   return UNZ_OK if no error */
+
+extern int ZEXPORT unzOpenCurrentFile2(unzFile file, int *method, int *level, int raw);
+/* Same as unzOpenCurrentFile, but open for read raw the file (not uncompress)
+   if raw==1 *method will receive method of compression, *level will receive level of compression
+
+   NOTE: you can set level parameter as NULL (if you did not want known level,
+         but you CANNOT set method parameter as NULL */
+
+extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int *method, int *level, int raw, const char *password);
+/* Same as unzOpenCurrentFile, but takes extra parameter password for encrypted files */
+
+extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, uint32_t len);
+/* Read bytes from the current file (opened by unzOpenCurrentFile)
+   buf contain buffer where data must be copied
+   len the size of buf.
+
+   return the number of byte copied if somes bytes are copied
+   return 0 if the end of file was reached
+   return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */
+
+extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *filename, 
+    uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size);
+extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 *pfile_info, char *filename,
+    uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size);
+/* Get Info about the current file
+
+   pfile_info if != NULL, the *pfile_info structure will contain somes info about the current file
+   filename if != NULL, the file name string will be copied in filename 
+   filename_size is the size of the filename buffer
+   extrafield if != NULL, the extra field information from the central header will be copied in to
+   extrafield_size is the size of the extraField buffer 
+   comment if != NULL, the comment string of the file will be copied in to
+   comment_size is the size of the comment buffer */
+
+extern int ZEXPORT unzGetLocalExtrafield(unzFile file, voidp buf, uint32_t len);
+/* Read extra field from the current file (opened by unzOpenCurrentFile)
+   This is the local-header version of the extra field (sometimes, there is
+   more info in the local-header version than in the central-header)
+
+   if buf == NULL, it return the size of the local extra field
+   if buf != NULL, len is the size of the buffer, the extra header is copied in buf.
+
+   return number of bytes copied in buf, or (if <0) the error code */
+
+extern int ZEXPORT unzCloseCurrentFile(unzFile file);
+/* Close the file in zip opened with unzOpenCurrentFile
+
+   return UNZ_CRCERROR if all the file was read but the CRC is not good */
+
+/***************************************************************************/
+/* Browse the directory of the zipfile */
+
+typedef int (*unzFileNameComparer)(unzFile file, const char *filename1, const char *filename2);
+typedef int (*unzIteratorFunction)(unzFile file);
+typedef int (*unzIteratorFunction2)(unzFile file, unz_file_info64 *pfile_info, char *filename,
+    uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size);
+
+extern int ZEXPORT unzGoToFirstFile(unzFile file);
+/* Set the current file of the zipfile to the first file.
+
+   return UNZ_OK if no error */
+
+extern int ZEXPORT unzGoToFirstFile2(unzFile file, unz_file_info64 *pfile_info, char *filename,
+    uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size);
+/* Set the current file of the zipfile to the first file and retrieves the current info on success. 
+   Not as seek intensive as unzGoToFirstFile + unzGetCurrentFileInfo.
+
+   return UNZ_OK if no error */
+
+extern int ZEXPORT unzGoToNextFile(unzFile file);
+/* Set the current file of the zipfile to the next file.
+
+   return UNZ_OK if no error
+   return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest */
+
+extern int ZEXPORT unzGoToNextFile2(unzFile file, unz_file_info64 *pfile_info, char *filename,
+    uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size);
+/* Set the current file of the zipfile to the next file and retrieves the current 
+   info on success. Does less seeking around than unzGotoNextFile + unzGetCurrentFileInfo.
+
+   return UNZ_OK if no error
+   return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest */
+
+extern int ZEXPORT unzLocateFile(unzFile file, const char *filename, unzFileNameComparer filename_compare_func);
+/* Try locate the file szFileName in the zipfile. For custom filename comparison pass in comparison function.
+
+   return UNZ_OK if the file is found (it becomes the current file)
+   return UNZ_END_OF_LIST_OF_FILE if the file is not found */
+
+/***************************************************************************/
+/* Raw access to zip file */
+
+typedef struct unz_file_pos_s
+{
+    uint32_t pos_in_zip_directory;  /* offset in zip file directory */
+    uint32_t num_of_file;           /* # of file */
+} unz_file_pos;
+
+extern int ZEXPORT unzGetFilePos(unzFile file, unz_file_pos *file_pos);
+extern int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos *file_pos);
+
+typedef struct unz64_file_pos_s
+{
+    uint64_t pos_in_zip_directory;   /* offset in zip file directory */
+    uint64_t num_of_file;            /* # of file */
+} unz64_file_pos;
+
+extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos *file_pos);
+extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos *file_pos);
+
+extern int32_t ZEXPORT unzGetOffset(unzFile file);
+extern int64_t ZEXPORT unzGetOffset64(unzFile file);
+/* Get the current file offset */
+
+extern int ZEXPORT unzSetOffset(unzFile file, uint32_t pos);
+extern int ZEXPORT unzSetOffset64(unzFile file, uint64_t pos);
+/* Set the current file offset */
+
+extern int32_t ZEXPORT unzTell(unzFile file);
+extern int64_t ZEXPORT unzTell64(unzFile file);
+/* return current position in uncompressed data */
+
+extern int ZEXPORT unzSeek(unzFile file, uint32_t offset, int origin);
+extern int ZEXPORT unzSeek64(unzFile file, uint64_t offset, int origin);
+/* Seek within the uncompressed data if compression method is storage */
+
+extern int ZEXPORT unzEndOfFile(unzFile file);
+/* return 1 if the end of file was reached, 0 elsewhere */
+
+/***************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UNZ_H */
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/zip.c b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/zip.c
new file mode 100755 (executable)
index 0000000..b24416d
--- /dev/null
@@ -0,0 +1,2022 @@
+/* zip.c -- IO on .zip files using zlib
+   Version 1.2.0, September 16th, 2017
+   part of the MiniZip project
+
+   Copyright (C) 2010-2017 Nathan Moinvaziri
+     Modifications for AES, PKWARE disk spanning
+     https://github.com/nmoinvaz/minizip
+   Copyright (C) 2009-2010 Mathias Svensson
+     Modifications for Zip64 support
+     http://result42.com
+   Copyright (C) 1998-2010 Gilles Vollant
+     http://www.winimage.com/zLibDll/minizip.html
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include "zlib.h"
+#include "zip.h"
+
+#ifdef HAVE_AES
+#  define AES_METHOD          (99)
+#  define AES_PWVERIFYSIZE    (2)
+#  define AES_AUTHCODESIZE    (10)
+#  define AES_MAXSALTLENGTH   (16)
+#  define AES_VERSION         (0x0001)
+#  define AES_ENCRYPTIONMODE  (0x03)
+
+#  include "aes/aes.h"
+#  include "aes/fileenc.h"
+#  include "aes/prng.h"
+#endif
+#ifdef HAVE_APPLE_COMPRESSION
+#  include <compression.h>
+#endif
+
+#ifndef NOCRYPT
+#  include "crypt.h"
+#endif
+
+#define SIZEDATA_INDATABLOCK        (4096-(4*4))
+
+#define DISKHEADERMAGIC             (0x08074b50)
+#define LOCALHEADERMAGIC            (0x04034b50)
+#define CENTRALHEADERMAGIC          (0x02014b50)
+#define ENDHEADERMAGIC              (0x06054b50)
+#define ZIP64ENDHEADERMAGIC         (0x06064b50)
+#define ZIP64ENDLOCHEADERMAGIC      (0x07064b50)
+#define DATADESCRIPTORMAGIC         (0x08074b50)
+
+#define FLAG_LOCALHEADER_OFFSET     (0x06)
+#define CRC_LOCALHEADER_OFFSET      (0x0e)
+
+#define SIZECENTRALHEADER           (0x2e) /* 46 */
+#define SIZECENTRALHEADERLOCATOR    (0x14) /* 20 */
+#define SIZECENTRALDIRITEM          (0x2e)
+#define SIZEZIPLOCALHEADER          (0x1e)
+
+#ifndef BUFREADCOMMENT
+#  define BUFREADCOMMENT            (0x400)
+#endif
+#ifndef VERSIONMADEBY
+#  define VERSIONMADEBY             (0x0) /* platform dependent */
+#endif
+
+#ifndef Z_BUFSIZE
+#  define Z_BUFSIZE                 (UINT16_MAX)
+#endif
+
+#ifndef ALLOC
+#  define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+#  define TRYFREE(p) {if (p) free(p);}
+#endif
+
+/* NOT sure that this work on ALL platform */
+#define MAKEULONG64(a, b) ((uint64_t)(((unsigned long)(a)) | ((uint64_t)((unsigned long)(b))) << 32))
+
+#ifndef DEF_MEM_LEVEL
+#  if MAX_MEM_LEVEL >= 8
+#    define DEF_MEM_LEVEL 8
+#  else
+#    define DEF_MEM_LEVEL  MAX_MEM_LEVEL
+#  endif
+#endif
+
+const char zip_copyright[] = " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
+
+typedef struct linkedlist_datablock_internal_s
+{
+    struct linkedlist_datablock_internal_s *next_datablock;
+    uint32_t    avail_in_this_block;
+    uint32_t    filled_in_this_block;
+    uint32_t    unused; /* for future use and alignment */
+    uint8_t     data[SIZEDATA_INDATABLOCK];
+} linkedlist_datablock_internal;
+
+typedef struct linkedlist_data_s
+{
+    linkedlist_datablock_internal *first_block;
+    linkedlist_datablock_internal *last_block;
+} linkedlist_data;
+
+typedef struct
+{
+    z_stream stream;                /* zLib stream structure for inflate */
+#ifdef HAVE_BZIP2
+    bz_stream bstream;              /* bzLib stream structure for bziped */
+#endif
+#ifdef HAVE_APPLE_COMPRESSION
+    compression_stream astream;     /* libcompression stream structure */
+#endif
+#ifdef HAVE_AES
+    fcrypt_ctx aes_ctx;
+    prng_ctx aes_rng[1];
+#endif
+    int      stream_initialised;    /* 1 is stream is initialized */
+    uint32_t pos_in_buffered_data;  /* last written byte in buffered_data */
+
+    uint64_t pos_local_header;      /* offset of the local header of the file currently writing */
+    char    *central_header;        /* central header data for the current file */
+    uint16_t size_centralextra;
+    uint16_t size_centralheader;    /* size of the central header for cur file */
+    uint16_t size_centralextrafree; /* Extra bytes allocated to the central header but that are not used */
+    uint16_t size_comment;
+    uint16_t flag;                  /* flag of the file currently writing */
+
+    uint16_t method;                /* compression method written to file.*/
+    uint16_t compression_method;    /* compression method to use */
+    int      raw;                   /* 1 for directly writing raw data */
+    uint8_t  buffered_data[Z_BUFSIZE];  /* buffer contain compressed data to be writ*/
+    uint32_t dos_date;
+    uint32_t crc32;
+    int      zip64;                 /* add ZIP64 extended information in the extra field */
+    uint32_t number_disk;           /* number of current disk used for spanning ZIP */
+    uint64_t total_compressed;
+    uint64_t total_uncompressed;
+#ifndef NOCRYPT
+    uint32_t keys[3];          /* keys defining the pseudo-random sequence */
+    const z_crc_t *pcrc_32_tab;
+#endif
+} curfile64_info;
+
+typedef struct
+{
+    zlib_filefunc64_32_def z_filefunc;
+    voidpf filestream;              /* io structure of the zipfile */
+    voidpf filestream_with_CD;      /* io structure of the zipfile with the central dir */
+    linkedlist_data central_dir;    /* datablock with central dir in construction*/
+    int in_opened_file_inzip;       /* 1 if a file in the zip is currently writ.*/
+    int append;                     /* append mode */
+    curfile64_info ci;              /* info on the file currently writing */
+
+    uint64_t add_position_when_writting_offset;
+    uint64_t number_entry;
+    uint64_t disk_size;             /* size of each disk */
+    uint32_t number_disk;           /* number of the current disk, used for spanning ZIP */
+    uint32_t number_disk_with_CD;   /* number the the disk with central dir, used for spanning ZIP */
+#ifndef NO_ADDFILEINEXISTINGZIP
+    char *globalcomment;
+#endif
+} zip64_internal;
+
+/* Allocate a new data block */
+static linkedlist_datablock_internal *allocate_new_datablock(void)
+{
+    linkedlist_datablock_internal *ldi = NULL;
+
+    ldi = (linkedlist_datablock_internal*)ALLOC(sizeof(linkedlist_datablock_internal));
+
+    if (ldi != NULL)
+    {
+        ldi->next_datablock = NULL;
+        ldi->filled_in_this_block = 0;
+        ldi->avail_in_this_block = SIZEDATA_INDATABLOCK;
+    }
+    return ldi;
+}
+
+/* Free data block in linked list */
+static void free_datablock(linkedlist_datablock_internal *ldi)
+{
+    while (ldi != NULL)
+    {
+        linkedlist_datablock_internal *ldinext = ldi->next_datablock;
+        TRYFREE(ldi);
+        ldi = ldinext;
+    }
+}
+
+/* Initialize linked list */
+static void init_linkedlist(linkedlist_data *ll)
+{
+    ll->first_block = ll->last_block = NULL;
+}
+
+/* Free entire linked list and all data blocks */
+static void free_linkedlist(linkedlist_data *ll)
+{
+    free_datablock(ll->first_block);
+    ll->first_block = ll->last_block = NULL;
+}
+
+/* Add data to linked list data block */
+static int add_data_in_datablock(linkedlist_data *ll, const void *buf, uint32_t len)
+{
+    linkedlist_datablock_internal *ldi = NULL;
+    const unsigned char *from_copy = NULL;
+
+    if (ll == NULL)
+        return ZIP_INTERNALERROR;
+
+    if (ll->last_block == NULL)
+    {
+        ll->first_block = ll->last_block = allocate_new_datablock();
+        if (ll->first_block == NULL)
+            return ZIP_INTERNALERROR;
+    }
+
+    ldi = ll->last_block;
+    from_copy = (unsigned char*)buf;
+
+    while (len > 0)
+    {
+        uint32_t copy_this = 0;
+        uint32_t i = 0;
+        unsigned char *to_copy = NULL;
+
+        if (ldi->avail_in_this_block == 0)
+        {
+            ldi->next_datablock = allocate_new_datablock();
+            if (ldi->next_datablock == NULL)
+                return ZIP_INTERNALERROR;
+            ldi = ldi->next_datablock ;
+            ll->last_block = ldi;
+        }
+
+        if (ldi->avail_in_this_block < len)
+            copy_this = ldi->avail_in_this_block;
+        else
+            copy_this = len;
+
+        to_copy = &(ldi->data[ldi->filled_in_this_block]);
+
+        for (i = 0; i < copy_this; i++)
+            *(to_copy+i) = *(from_copy+i);
+
+        ldi->filled_in_this_block += copy_this;
+        ldi->avail_in_this_block -= copy_this;
+        from_copy += copy_this;
+        len -= copy_this;
+    }
+    return ZIP_OK;
+}
+
+/* Inputs a long in LSB order to the given file: nbByte == 1, 2 ,4 or 8 (byte, short or long, uint64_t) */
+static int zipWriteValue(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream,
+    uint64_t x, uint32_t len)
+{
+    unsigned char buf[8];
+    uint32_t n = 0;
+
+    for (n = 0; n < len; n++)
+    {
+        buf[n] = (unsigned char)(x & 0xff);
+        x >>= 8;
+    }
+
+    if (x != 0)
+    {
+        /* Data overflow - hack for ZIP64 (X Roche) */
+        for (n = 0; n < len; n++)
+        {
+            buf[n] = 0xff;
+        }
+    }
+
+    if (ZWRITE64(*pzlib_filefunc_def, filestream, buf, len) != len)
+        return ZIP_ERRNO;
+
+    return ZIP_OK;
+}
+
+static void zipWriteValueToMemory(void* dest, uint64_t x, uint32_t len)
+{
+    unsigned char *buf = (unsigned char*)dest;
+    uint32_t n = 0;
+
+    for (n = 0; n < len; n++)
+    {
+        buf[n] = (unsigned char)(x & 0xff);
+        x >>= 8;
+    }
+
+    if (x != 0)
+    {
+       /* data overflow - hack for ZIP64 */
+       for (n = 0; n < len; n++)
+       {
+          buf[n] = 0xff;
+       }
+    }
+}
+
+static void zipWriteValueToMemoryAndMove(unsigned char **dest_ptr, uint64_t x, uint32_t len)
+{
+    zipWriteValueToMemory(*dest_ptr, x, len);
+    *dest_ptr += len;
+}
+
+static int zipReadUInt8(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint8_t *value)
+{
+    uint8_t c = 0;
+    if (ZREAD64(*pzlib_filefunc_def, filestream, &c, 1) == 1)
+    {
+        *value = (uint8_t)c;
+        return ZIP_OK;
+    }
+    if (ZERROR64(*pzlib_filefunc_def, filestream))
+        return ZIP_ERRNO;
+    return ZIP_EOF;
+}
+
+static int zipReadUInt16(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint16_t *value)
+{
+    uint16_t x = 0;
+    uint8_t c = 0;
+    int err = ZIP_OK;
+
+    err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x = (uint16_t)c;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint16_t)c) << 8;
+
+    if (err == ZIP_OK)
+        *value = x;
+    else
+        *value = 0;
+    return err;
+}
+
+static int zipReadUInt32(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint32_t *value)
+{
+    uint32_t x = 0;
+    uint8_t c = 0;
+    int err = ZIP_OK;
+
+    err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x = (uint32_t)c;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint32_t)c) << 8;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint32_t)c) << 16;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint32_t)c) << 24;
+
+    if (err == ZIP_OK)
+        *value = x;
+    else
+        *value = 0;
+    return err;
+}
+
+static int zipReadUInt64(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint64_t *value)
+{
+    uint64_t x = 0;
+    uint8_t c = 0;
+    int err = ZIP_OK;
+
+    err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x = (uint64_t)c;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint64_t)c) << 8;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint64_t)c) << 16;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint64_t)c) << 24;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint64_t)c) << 32;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint64_t)c) << 40;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint64_t)c) << 48;
+    if (err == ZIP_OK)
+        err = zipReadUInt8(pzlib_filefunc_def, filestream, &c);
+    x += ((uint64_t)c) << 56;
+
+    if (err == ZIP_OK)
+        *value = x;
+    else
+        *value = 0;
+
+    return err;
+}
+
+/* Gets the amount of bytes left to write to the current disk for spanning archives */
+static void zipGetDiskSizeAvailable(zipFile file, uint64_t *size_available)
+{
+    zip64_internal *zi = NULL;
+    uint64_t current_disk_size = 0;
+
+    zi = (zip64_internal*)file;
+    ZSEEK64(zi->z_filefunc, zi->filestream, 0, ZLIB_FILEFUNC_SEEK_END);
+    current_disk_size = ZTELL64(zi->z_filefunc, zi->filestream);
+    *size_available = zi->disk_size - current_disk_size;
+}
+
+/* Goes to a specific disk number for spanning archives */
+static int zipGoToSpecificDisk(zipFile file, uint32_t number_disk, int open_existing)
+{
+    zip64_internal *zi = NULL;
+    int err = ZIP_OK;
+
+    zi = (zip64_internal*)file;
+    if (zi->disk_size == 0)
+        return err;
+
+    if ((zi->filestream != NULL) && (zi->filestream != zi->filestream_with_CD))
+        ZCLOSE64(zi->z_filefunc, zi->filestream);
+
+    zi->filestream = ZOPENDISK64(zi->z_filefunc, zi->filestream_with_CD, number_disk, (open_existing == 1) ?
+            (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING) :
+            (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE));
+
+    if (zi->filestream == NULL)
+        err = ZIP_ERRNO;
+
+    return err;
+}
+
+/* Goes to the first disk in a spanned archive */
+static int zipGoToFirstDisk(zipFile file)
+{
+    zip64_internal *zi = NULL;
+    uint32_t number_disk_next = 0;
+    int err = ZIP_OK;
+
+    zi = (zip64_internal*)file;
+
+    if (zi->disk_size == 0)
+        return err;
+    number_disk_next = 0;
+    if (zi->number_disk_with_CD > 0)
+        number_disk_next = zi->number_disk_with_CD - 1;
+    err = zipGoToSpecificDisk(file, number_disk_next, (zi->append == APPEND_STATUS_ADDINZIP));
+    if ((err == ZIP_ERRNO) && (zi->append == APPEND_STATUS_ADDINZIP))
+        err = zipGoToSpecificDisk(file, number_disk_next, 0);
+    if (err == ZIP_OK)
+        zi->number_disk = number_disk_next;
+    ZSEEK64(zi->z_filefunc, zi->filestream, 0, ZLIB_FILEFUNC_SEEK_END);
+    return err;
+}
+
+/* Goes to the next disk in a spanned archive */
+static int zipGoToNextDisk(zipFile file)
+{
+    zip64_internal *zi = NULL;
+    uint64_t size_available_in_disk = 0;
+    uint32_t number_disk_next = 0;
+    int err = ZIP_OK;
+
+    zi = (zip64_internal*)file;
+    if (zi->disk_size == 0)
+        return err;
+
+    number_disk_next = zi->number_disk + 1;
+
+    do
+    {
+        err = zipGoToSpecificDisk(file, number_disk_next, (zi->append == APPEND_STATUS_ADDINZIP));
+        if ((err == ZIP_ERRNO) && (zi->append == APPEND_STATUS_ADDINZIP))
+            err = zipGoToSpecificDisk(file, number_disk_next, 0);
+        if (err != ZIP_OK)
+            break;
+        zipGetDiskSizeAvailable(file, &size_available_in_disk);
+        zi->number_disk = number_disk_next;
+        zi->number_disk_with_CD = zi->number_disk + 1;
+
+        number_disk_next += 1;
+    }
+    while (size_available_in_disk <= 0);
+
+    return err;
+}
+
+/* Locate the Central directory of a zipfile (at the end, just before the global comment) */
+static uint64_t zipSearchCentralDir(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream)
+{
+    unsigned char *buf = NULL;
+    uint64_t file_size = 0;
+    uint64_t back_read = 4;
+    uint64_t max_back = UINT16_MAX; /* maximum size of global comment */
+    uint64_t pos_found = 0;
+    uint32_t read_size = 0;
+    uint64_t read_pos = 0;
+    uint32_t i = 0;
+
+    buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+    if (buf == NULL)
+        return 0;
+
+    if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) != 0)
+    {
+        TRYFREE(buf);
+        return 0;
+    }
+
+    file_size = ZTELL64(*pzlib_filefunc_def, filestream);
+
+    if (max_back > file_size)
+        max_back = file_size;
+
+    while (back_read < max_back)
+    {
+        if (back_read + BUFREADCOMMENT > max_back)
+            back_read = max_back;
+        else
+            back_read += BUFREADCOMMENT;
+
+        read_pos = file_size-back_read;
+        read_size = ((BUFREADCOMMENT+4) < (file_size - read_pos)) ?
+                     (BUFREADCOMMENT+4) : (uint32_t)(file_size - read_pos);
+
+        if (ZSEEK64(*pzlib_filefunc_def, filestream, read_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
+            break;
+        if (ZREAD64(*pzlib_filefunc_def, filestream, buf, read_size) != read_size)
+            break;
+
+        for (i = read_size-3; (i--) > 0;)
+            if ((*(buf+i)) == (ENDHEADERMAGIC & 0xff) &&
+                (*(buf+i+1)) == (ENDHEADERMAGIC >> 8 & 0xff) &&
+                (*(buf+i+2)) == (ENDHEADERMAGIC >> 16 & 0xff) &&
+                (*(buf+i+3)) == (ENDHEADERMAGIC >> 24 & 0xff))
+            {
+                pos_found = read_pos+i;
+                break;
+            }
+
+        if (pos_found != 0)
+            break;
+    }
+    TRYFREE(buf);
+    return pos_found;
+}
+
+/* Locate the Central directory 64 of a zipfile (at the end, just before the global comment) */
+static uint64_t zipSearchCentralDir64(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream,
+    const uint64_t endcentraloffset)
+{
+    uint64_t offset = 0;
+    uint32_t value32 = 0;
+
+    /* Zip64 end of central directory locator */
+    if (ZSEEK64(*pzlib_filefunc_def, filestream, endcentraloffset - SIZECENTRALHEADERLOCATOR, ZLIB_FILEFUNC_SEEK_SET) != 0)
+        return 0;
+
+    /* Read locator signature */
+    if (zipReadUInt32(pzlib_filefunc_def, filestream, &value32) != ZIP_OK)
+        return 0;
+    if (value32 != ZIP64ENDLOCHEADERMAGIC)
+        return 0;
+    /* Number of the disk with the start of the zip64 end of  central directory */
+    if (zipReadUInt32(pzlib_filefunc_def, filestream, &value32) != ZIP_OK)
+        return 0;
+    /* Relative offset of the zip64 end of central directory record */
+    if (zipReadUInt64(pzlib_filefunc_def, filestream, &offset) != ZIP_OK)
+        return 0;
+    /* Total number of disks */
+    if (zipReadUInt32(pzlib_filefunc_def, filestream, &value32) != ZIP_OK)
+        return 0;
+    /* Goto end of central directory record */
+    if (ZSEEK64(*pzlib_filefunc_def,filestream, offset, ZLIB_FILEFUNC_SEEK_SET) != 0)
+        return 0;
+    /* The signature */
+    if (zipReadUInt32(pzlib_filefunc_def, filestream, &value32) != ZIP_OK)
+        return 0;
+    if (value32 != ZIP64ENDHEADERMAGIC)
+        return 0;
+
+    return offset;
+}
+
+extern zipFile ZEXPORT zipOpen4(const void *path, int append, uint64_t disk_size, const char **globalcomment,
+    zlib_filefunc64_32_def *pzlib_filefunc64_32_def)
+{
+    zip64_internal ziinit;
+    zip64_internal *zi = NULL;
+#ifndef NO_ADDFILEINEXISTINGZIP
+    uint64_t byte_before_the_zipfile = 0;   /* byte before the zipfile, (>0 for sfx)*/
+    uint64_t size_central_dir = 0;          /* size of the central directory  */
+    uint64_t offset_central_dir = 0;        /* offset of start of central directory */
+    uint64_t number_entry_CD = 0;           /* total number of entries in the central dir */
+    uint64_t number_entry = 0;
+    uint64_t central_pos = 0;
+    uint64_t size_central_dir_to_read = 0;
+    uint16_t value16 = 0;
+    uint32_t value32 = 0;
+    uint16_t size_comment = 0;
+    size_t buf_size = SIZEDATA_INDATABLOCK;
+    void *buf_read = NULL;
+#endif
+    int err = ZIP_OK;
+    int mode = 0;
+
+    ziinit.z_filefunc.zseek32_file = NULL;
+    ziinit.z_filefunc.ztell32_file = NULL;
+
+    if (pzlib_filefunc64_32_def == NULL)
+        fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64);
+    else
+        ziinit.z_filefunc = *pzlib_filefunc64_32_def;
+
+    if (append == APPEND_STATUS_CREATE)
+        mode = (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE);
+    else
+        mode = (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING);
+
+    ziinit.filestream = ZOPEN64(ziinit.z_filefunc, path, mode);
+    if (ziinit.filestream == NULL)
+        return NULL;
+
+    if (append == APPEND_STATUS_CREATEAFTER)
+    {
+        /* Don't support spanning ZIP with APPEND_STATUS_CREATEAFTER */
+        if (disk_size > 0)
+            return NULL;
+
+        ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END);
+    }
+
+    ziinit.filestream_with_CD = ziinit.filestream;
+    ziinit.append = append;
+    ziinit.number_disk = 0;
+    ziinit.number_disk_with_CD = 0;
+    ziinit.disk_size = disk_size;
+    ziinit.in_opened_file_inzip = 0;
+    ziinit.ci.stream_initialised = 0;
+    ziinit.number_entry = 0;
+    ziinit.add_position_when_writting_offset = 0;
+    init_linkedlist(&(ziinit.central_dir));
+
+    zi = (zip64_internal*)ALLOC(sizeof(zip64_internal));
+    if (zi == NULL)
+    {
+        ZCLOSE64(ziinit.z_filefunc,ziinit.filestream);
+        return NULL;
+    }
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+    /* Add file in a zipfile */
+    ziinit.globalcomment = NULL;
+    if (append == APPEND_STATUS_ADDINZIP)
+    {
+        /* Read and Cache Central Directory Records */
+        central_pos = zipSearchCentralDir(&ziinit.z_filefunc,ziinit.filestream);
+        /* Disable to allow appending to empty ZIP archive (must be standard zip, not zip64)
+            if (central_pos == 0)
+                err = ZIP_ERRNO;
+        */
+
+        if (err == ZIP_OK)
+        {
+            /* Read end of central directory info */
+            if (ZSEEK64(ziinit.z_filefunc, ziinit.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET) != 0)
+                err = ZIP_ERRNO;
+
+            /* The signature, already checked */
+            if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &value32) != ZIP_OK)
+                err = ZIP_ERRNO;
+            /* Number of this disk */
+            if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK)
+                err = ZIP_ERRNO;
+            ziinit.number_disk = value16;
+            /* Number of the disk with the start of the central directory */
+            if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK)
+                err = ZIP_ERRNO;
+            ziinit.number_disk_with_CD = value16;
+            /* Total number of entries in the central dir on this disk */
+            number_entry = 0;
+            if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK)
+                err = ZIP_ERRNO;
+            else
+                number_entry = value16;
+            /* Total number of entries in the central dir */
+            number_entry_CD = 0;
+            if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK)
+                err = ZIP_ERRNO;
+            else
+                number_entry_CD = value16;
+            if (number_entry_CD!=number_entry)
+                err = ZIP_BADZIPFILE;
+            /* Size of the central directory */
+            size_central_dir = 0;
+            if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &value32) != ZIP_OK)
+                err = ZIP_ERRNO;
+            else
+                size_central_dir = value32;
+            /* Offset of start of central directory with respect to the starting disk number */
+            offset_central_dir = 0;
+            if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &value32) != ZIP_OK)
+                err = ZIP_ERRNO;
+            else
+                offset_central_dir = value32;
+            /* Zipfile global comment length */
+            if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &size_comment) != ZIP_OK)
+                err = ZIP_ERRNO;
+
+            if ((err == ZIP_OK) && ((number_entry_CD == UINT16_MAX) || (offset_central_dir == UINT32_MAX)))
+            {
+                /* Format should be Zip64, as the central directory or file size is too large */
+                central_pos = zipSearchCentralDir64(&ziinit.z_filefunc, ziinit.filestream, central_pos);
+
+                if (central_pos)
+                {
+                    uint64_t sizeEndOfCentralDirectory;
+
+                    if (ZSEEK64(ziinit.z_filefunc, ziinit.filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
+                        err = ZIP_ERRNO;
+
+                    /* The signature, already checked */
+                    if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &value32) != ZIP_OK)
+                        err = ZIP_ERRNO;
+                    /* Size of zip64 end of central directory record */
+                    if (zipReadUInt64(&ziinit.z_filefunc, ziinit.filestream, &sizeEndOfCentralDirectory) != ZIP_OK)
+                        err = ZIP_ERRNO;
+                    /* Version made by */
+                    if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK)
+                        err = ZIP_ERRNO;
+                    /* Version needed to extract */
+                    if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK)
+                        err = ZIP_ERRNO;
+                    /* Number of this disk */
+                    if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &ziinit.number_disk) != ZIP_OK)
+                        err = ZIP_ERRNO;
+                    /* Number of the disk with the start of the central directory */
+                    if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &ziinit.number_disk_with_CD) != ZIP_OK)
+                        err = ZIP_ERRNO;
+                    /* Total number of entries in the central directory on this disk */
+                    if (zipReadUInt64(&ziinit.z_filefunc, ziinit.filestream, &number_entry) != ZIP_OK)
+                        err = ZIP_ERRNO;
+                    /* Total number of entries in the central directory */
+                    if (zipReadUInt64(&ziinit.z_filefunc, ziinit.filestream, &number_entry_CD) != ZIP_OK)
+                        err = ZIP_ERRNO;
+                    if (number_entry_CD!=number_entry)
+                        err = ZIP_BADZIPFILE;
+                    /* Size of the central directory */
+                    if (zipReadUInt64(&ziinit.z_filefunc, ziinit.filestream, &size_central_dir) != ZIP_OK)
+                        err = ZIP_ERRNO;
+                    /* Offset of start of central directory with respect to the starting disk number */
+                    if (zipReadUInt64(&ziinit.z_filefunc, ziinit.filestream, &offset_central_dir) != ZIP_OK)
+                        err = ZIP_ERRNO;
+                }
+                else
+                    err = ZIP_BADZIPFILE;
+             }
+        }
+
+        if ((err == ZIP_OK) && (central_pos < offset_central_dir + size_central_dir))
+            err = ZIP_BADZIPFILE;
+
+        if ((err == ZIP_OK) && (size_comment > 0))
+        {
+            ziinit.globalcomment = (char*)ALLOC(size_comment+1);
+            if (ziinit.globalcomment)
+            {
+                if (ZREAD64(ziinit.z_filefunc, ziinit.filestream, ziinit.globalcomment, size_comment) != size_comment)
+                    err = ZIP_ERRNO;
+                else
+                    ziinit.globalcomment[size_comment] = 0;
+            }
+        }
+
+        if (err != ZIP_OK)
+        {
+            ZCLOSE64(ziinit.z_filefunc, ziinit.filestream);
+            TRYFREE(ziinit.globalcomment);
+            TRYFREE(zi);
+            return NULL;
+        }
+
+        byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir);
+        ziinit.add_position_when_writting_offset = byte_before_the_zipfile;
+
+        /* Store central directory in memory */
+        size_central_dir_to_read = size_central_dir;
+        buf_size = SIZEDATA_INDATABLOCK;
+        buf_read = (void*)ALLOC(buf_size);
+
+        if (ZSEEK64(ziinit.z_filefunc, ziinit.filestream,
+                offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0)
+            err = ZIP_ERRNO;
+
+        while ((size_central_dir_to_read > 0) && (err == ZIP_OK))
+        {
+            uint64_t read_this = SIZEDATA_INDATABLOCK;
+            if (read_this > size_central_dir_to_read)
+                read_this = size_central_dir_to_read;
+
+            if (ZREAD64(ziinit.z_filefunc, ziinit.filestream, buf_read, (uint32_t)read_this) != read_this)
+                err = ZIP_ERRNO;
+
+            if (err == ZIP_OK)
+                err = add_data_in_datablock(&ziinit.central_dir, buf_read, (uint32_t)read_this);
+
+            size_central_dir_to_read -= read_this;
+        }
+        TRYFREE(buf_read);
+
+        ziinit.number_entry = number_entry_CD;
+
+        if (ZSEEK64(ziinit.z_filefunc, ziinit.filestream,
+                offset_central_dir+byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0)
+            err = ZIP_ERRNO;
+    }
+
+    if (globalcomment)
+        *globalcomment = ziinit.globalcomment;
+#endif
+
+    if (err != ZIP_OK)
+    {
+#ifndef NO_ADDFILEINEXISTINGZIP
+        TRYFREE(ziinit.globalcomment);
+#endif
+        TRYFREE(zi);
+        return NULL;
+    }
+
+    *zi = ziinit;
+    zipGoToFirstDisk((zipFile)zi);
+    return(zipFile)zi;
+}
+
+extern zipFile ZEXPORT zipOpen2(const char *path, int append, const char **globalcomment,
+    zlib_filefunc_def *pzlib_filefunc32_def)
+{
+    if (pzlib_filefunc32_def != NULL)
+    {
+        zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
+        fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def);
+        return zipOpen4(path, append, 0, globalcomment, &zlib_filefunc64_32_def_fill);
+    }
+    return zipOpen4(path, append, 0, globalcomment, NULL);
+}
+
+extern zipFile ZEXPORT zipOpen2_64(const void *path, int append, const char **globalcomment,
+    zlib_filefunc64_def *pzlib_filefunc_def)
+{
+    if (pzlib_filefunc_def != NULL)
+    {
+        zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
+        zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def;
+        zlib_filefunc64_32_def_fill.ztell32_file = NULL;
+        zlib_filefunc64_32_def_fill.zseek32_file = NULL;
+        return zipOpen4(path, append, 0, globalcomment, &zlib_filefunc64_32_def_fill);
+    }
+    return zipOpen4(path, append, 0, globalcomment, NULL);
+}
+
+extern zipFile ZEXPORT zipOpen3(const char *path, int append, uint64_t disk_size, const char **globalcomment,
+    zlib_filefunc_def *pzlib_filefunc32_def)
+{
+    if (pzlib_filefunc32_def != NULL)
+    {
+        zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
+        fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def);
+        return zipOpen4(path, append, disk_size, globalcomment, &zlib_filefunc64_32_def_fill);
+    }
+    return zipOpen4(path, append, disk_size, globalcomment, NULL);
+}
+
+extern zipFile ZEXPORT zipOpen3_64(const void *path, int append, uint64_t disk_size, const char **globalcomment,
+    zlib_filefunc64_def *pzlib_filefunc_def)
+{
+    if (pzlib_filefunc_def != NULL)
+    {
+        zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
+        zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def;
+        zlib_filefunc64_32_def_fill.ztell32_file = NULL;
+        zlib_filefunc64_32_def_fill.zseek32_file = NULL;
+        return zipOpen4(path, append, disk_size, globalcomment, &zlib_filefunc64_32_def_fill);
+    }
+    return zipOpen4(path, append, disk_size, globalcomment, NULL);
+}
+
+extern zipFile ZEXPORT zipOpen(const char *path, int append)
+{
+    return zipOpen3((const void*)path, append, 0, NULL, NULL);
+}
+
+extern zipFile ZEXPORT zipOpen64(const void *path, int append)
+{
+    return zipOpen3(path, append, 0, NULL, NULL);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip_internal(zipFile file,
+                                                const char *filename,
+                                                const zip_fileinfo *zipfi,
+                                                const void *extrafield_local,
+                                                uint16_t size_extrafield_local,
+                                                const void *extrafield_global,
+                                                uint16_t size_extrafield_global,
+                                                const char *comment,
+                                                uint16_t flag_base,
+                                                int zip64,
+                                                uint16_t method,
+                                                int level,
+                                                int raw,
+                                                int windowBits,
+                                                int memLevel,
+                                                int strategy,
+                                                const char *password,
+                                                int aes,
+                                                uint16_t version_madeby)
+{
+    zip64_internal *zi = NULL;
+    uint64_t size_available = 0;
+    uint64_t size_needed = 0;
+    uint16_t size_filename = 0;
+    uint16_t size_comment = 0;
+    uint16_t i = 0;
+    unsigned char *central_dir = NULL;
+    int err = ZIP_OK;
+
+#ifdef NOCRYPT
+    if (password != NULL)
+        return ZIP_PARAMERROR;
+#endif
+
+    if (file == NULL)
+        return ZIP_PARAMERROR;
+
+    if ((method != 0) &&
+#ifdef HAVE_BZIP2
+        (method != Z_BZIP2ED) &&
+#endif
+        (method != Z_DEFLATED))
+        return ZIP_PARAMERROR;
+
+    zi = (zip64_internal*)file;
+
+    if (zi->in_opened_file_inzip == 1)
+    {
+        err = zipCloseFileInZip (file);
+        if (err != ZIP_OK)
+            return err;
+    }
+
+    if (filename == NULL)
+        filename = "-";
+    if (comment != NULL)
+        size_comment = (uint16_t)strlen(comment);
+
+    size_filename = (uint16_t)strlen(filename);
+
+    if (zipfi == NULL)
+        zi->ci.dos_date = 0;
+    else
+    {
+        if (zipfi->dos_date != 0)
+            zi->ci.dos_date = zipfi->dos_date;
+    }
+
+    zi->ci.method = method;
+    zi->ci.compression_method = method;
+    zi->ci.raw = raw;
+    zi->ci.flag = flag_base | 8;
+    if ((level == 8) || (level == 9))
+        zi->ci.flag |= 2;
+    if (level == 2)
+        zi->ci.flag |= 4;
+    if (level == 1)
+        zi->ci.flag |= 6;
+
+    if (password != NULL)
+    {
+        zi->ci.flag |= 1;
+#ifdef HAVE_AES
+        if (aes)
+            zi->ci.method = AES_METHOD;
+#endif
+    }
+    else
+    {
+        zi->ci.flag &= ~1;
+    }
+
+    if (zi->disk_size > 0)
+    {
+        if ((zi->number_disk == 0) && (zi->number_entry == 0))
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)DISKHEADERMAGIC, 4);
+
+        /* Make sure enough space available on current disk for local header */
+        zipGetDiskSizeAvailable((zipFile)zi, &size_available);
+        size_needed = 30 + size_filename + size_extrafield_local;
+#ifdef HAVE_AES
+        if (zi->ci.method == AES_METHOD)
+            size_needed += 11;
+#endif
+        if (size_available < size_needed)
+            zipGoToNextDisk((zipFile)zi);
+    }
+
+    zi->ci.zip64 = zip64;
+
+    zi->ci.pos_local_header = ZTELL64(zi->z_filefunc, zi->filestream);
+    if (zi->ci.pos_local_header >= UINT32_MAX)
+        zi->ci.zip64 = 1;
+
+    zi->ci.size_comment = size_comment;
+    zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global;
+    zi->ci.size_centralextra = size_extrafield_global;
+    zi->ci.size_centralextrafree = 32; /* Extra space reserved for ZIP64 extra info */
+#ifdef HAVE_AES
+    if (zi->ci.method == AES_METHOD)
+        zi->ci.size_centralextrafree += 11; /* Extra space reserved for AES extra info */
+#endif
+    zi->ci.central_header = (char*)ALLOC((uint32_t)zi->ci.size_centralheader + zi->ci.size_centralextrafree + size_comment);
+    zi->ci.number_disk = zi->number_disk;
+
+    /* Write central directory header */
+    central_dir = (unsigned char*)zi->ci.central_header;
+    zipWriteValueToMemoryAndMove(&central_dir, (uint32_t)CENTRALHEADERMAGIC, 4);
+    zipWriteValueToMemoryAndMove(&central_dir, version_madeby, 2);
+    if (zi->ci.zip64)
+        zipWriteValueToMemoryAndMove(&central_dir, (uint16_t)45, 2);
+    else
+        zipWriteValueToMemoryAndMove(&central_dir, (uint16_t)20, 2);
+    zipWriteValueToMemoryAndMove(&central_dir, zi->ci.flag, 2);
+    zipWriteValueToMemoryAndMove(&central_dir, zi->ci.method, 2);
+    zipWriteValueToMemoryAndMove(&central_dir, zi->ci.dos_date, 4);
+    zipWriteValueToMemoryAndMove(&central_dir, (uint32_t)0, 4); /*crc*/
+    zipWriteValueToMemoryAndMove(&central_dir, (uint32_t)0, 4); /*compr size*/
+    zipWriteValueToMemoryAndMove(&central_dir, (uint32_t)0, 4); /*uncompr size*/
+    zipWriteValueToMemoryAndMove(&central_dir, size_filename, 2);
+    zipWriteValueToMemoryAndMove(&central_dir, size_extrafield_global, 2);
+    zipWriteValueToMemoryAndMove(&central_dir, size_comment, 2);
+    zipWriteValueToMemoryAndMove(&central_dir, (uint16_t)zi->ci.number_disk, 2); /*disk nm start*/
+
+    if (zipfi == NULL)
+        zipWriteValueToMemoryAndMove(&central_dir, (uint16_t)0, 2);
+    else
+        zipWriteValueToMemoryAndMove(&central_dir, zipfi->internal_fa, 2);
+    if (zipfi == NULL)
+        zipWriteValueToMemoryAndMove(&central_dir, (uint32_t)0, 4);
+    else
+        zipWriteValueToMemoryAndMove(&central_dir, zipfi->external_fa, 4);
+    if (zi->ci.pos_local_header >= UINT32_MAX)
+        zipWriteValueToMemoryAndMove(&central_dir, UINT32_MAX, 4);
+    else
+        zipWriteValueToMemoryAndMove(&central_dir,
+            (uint32_t)(zi->ci.pos_local_header - zi->add_position_when_writting_offset), 4);
+
+    for (i = 0; i < size_filename; i++)
+        zi->ci.central_header[SIZECENTRALHEADER+i] = filename[i];
+    for (i = 0; i < size_extrafield_global; i++)
+        zi->ci.central_header[SIZECENTRALHEADER+size_filename+i] =
+            ((const char*)extrafield_global)[i];
+
+    /* Store comment at the end for later repositioning */
+    for (i = 0; i < size_comment; i++)
+        zi->ci.central_header[zi->ci.size_centralheader+
+            zi->ci.size_centralextrafree+i] = comment[i];
+
+    if (zi->ci.central_header == NULL)
+        return ZIP_INTERNALERROR;
+
+    /* Write the local header */
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)LOCALHEADERMAGIC, 4);
+
+    if (err == ZIP_OK)
+    {
+        if (zi->ci.zip64)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)45, 2); /* version needed to extract */
+        else
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)20, 2); /* version needed to extract */
+    }
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->ci.flag, 2);
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->ci.method, 2);
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->ci.dos_date, 4);
+
+    /* CRC & compressed size & uncompressed size is in data descriptor */
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)0, 4); /* crc 32, unknown */
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)0, 4); /* compressed size, unknown */
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)0, 4); /* uncompressed size, unknown */
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, size_filename, 2);
+    if (err == ZIP_OK)
+    {
+        uint64_t size_extrafield = size_extrafield_local;
+#ifdef HAVE_AES
+        if (zi->ci.method == AES_METHOD)
+            size_extrafield += 11;
+#endif
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)size_extrafield, 2);
+    }
+    if ((err == ZIP_OK) && (size_filename > 0))
+    {
+        if (ZWRITE64(zi->z_filefunc, zi->filestream, filename, size_filename) != size_filename)
+            err = ZIP_ERRNO;
+    }
+    if ((err == ZIP_OK) && (size_extrafield_local > 0))
+    {
+        if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local)
+            err = ZIP_ERRNO;
+    }
+
+#ifdef HAVE_AES
+    /* Write the AES extended info */
+    if ((err == ZIP_OK) && (zi->ci.method == AES_METHOD))
+    {
+        int headerid = 0x9901;
+        short datasize = 7;
+
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, headerid, 2);
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, datasize, 2);
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, AES_VERSION, 2);
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, 'A', 1);
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, 'E', 1);
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, AES_ENCRYPTIONMODE, 1);
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->ci.compression_method, 2);
+    }
+#endif
+
+    zi->ci.crc32 = 0;
+    zi->ci.stream_initialised = 0;
+    zi->ci.pos_in_buffered_data = 0;
+    zi->ci.total_compressed = 0;
+    zi->ci.total_uncompressed = 0;
+
+#ifdef HAVE_BZIP2
+    zi->ci.bstream.avail_in = (uint16_t)0;
+    zi->ci.bstream.avail_out = (uint16_t)Z_BUFSIZE;
+    zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
+    zi->ci.bstream.total_in_hi32 = 0;
+    zi->ci.bstream.total_in_lo32 = 0;
+    zi->ci.bstream.total_out_hi32 = 0;
+    zi->ci.bstream.total_out_lo32 = 0;
+#endif
+
+    zi->ci.stream.avail_in = (uint16_t)0;
+    zi->ci.stream.avail_out = Z_BUFSIZE;
+    zi->ci.stream.next_out = zi->ci.buffered_data;
+    zi->ci.stream.total_in = 0;
+    zi->ci.stream.total_out = 0;
+    zi->ci.stream.data_type = Z_BINARY;
+
+    if ((err == ZIP_OK) && (!zi->ci.raw))
+    {
+        if (method == Z_DEFLATED)
+        {
+            zi->ci.stream.zalloc = (alloc_func)0;
+            zi->ci.stream.zfree = (free_func)0;
+            zi->ci.stream.opaque = (voidpf)zi;
+
+            if (windowBits > 0)
+                windowBits = -windowBits;
+
+#ifdef HAVE_APPLE_COMPRESSION
+            err = compression_stream_init(&zi->ci.astream, COMPRESSION_STREAM_ENCODE, COMPRESSION_ZLIB);
+            if (err == COMPRESSION_STATUS_ERROR)
+                err = Z_ERRNO;
+            else
+                err = Z_OK;
+#else
+            err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy);
+#endif
+            if (err == Z_OK)
+                zi->ci.stream_initialised = Z_DEFLATED;
+        }
+        else if (method == Z_BZIP2ED)
+        {
+#ifdef HAVE_BZIP2
+            zi->ci.bstream.bzalloc = 0;
+            zi->ci.bstream.bzfree = 0;
+            zi->ci.bstream.opaque = (voidpf)0;
+
+            err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0, 35);
+            if (err == BZ_OK)
+                zi->ci.stream_initialised = Z_BZIP2ED;
+#endif
+        }
+    }
+
+#ifndef NOCRYPT
+    if ((err == Z_OK) && (password != NULL))
+    {
+#ifdef HAVE_AES
+        if (zi->ci.method == AES_METHOD)
+        {
+            unsigned char passverify[AES_PWVERIFYSIZE];
+            unsigned char saltvalue[AES_MAXSALTLENGTH];
+            uint16_t saltlength = 0;
+
+            if ((AES_ENCRYPTIONMODE < 1) || (AES_ENCRYPTIONMODE > 3))
+                return Z_ERRNO;
+
+            saltlength = SALT_LENGTH(AES_ENCRYPTIONMODE);
+
+            prng_init(cryptrand, zi->ci.aes_rng);
+            prng_rand(saltvalue, saltlength, zi->ci.aes_rng);
+            prng_end(zi->ci.aes_rng);
+
+            fcrypt_init(AES_ENCRYPTIONMODE, (uint8_t *)password, (uint32_t)strlen(password), saltvalue, passverify, &zi->ci.aes_ctx);
+
+            if (ZWRITE64(zi->z_filefunc, zi->filestream, saltvalue, saltlength) != saltlength)
+                err = ZIP_ERRNO;
+            if (ZWRITE64(zi->z_filefunc, zi->filestream, passverify, AES_PWVERIFYSIZE) != AES_PWVERIFYSIZE)
+                err = ZIP_ERRNO;
+
+            zi->ci.total_compressed += saltlength + AES_PWVERIFYSIZE + AES_AUTHCODESIZE;
+        }
+        else
+#endif
+        {
+            unsigned char buf_head[RAND_HEAD_LEN];
+            uint32_t size_head = 0;
+            uint8_t verify1 = 0;
+            uint8_t verify2 = 0;
+
+            zi->ci.pcrc_32_tab = get_crc_table();
+
+            /*
+            Info-ZIP modification to ZipCrypto format:
+            If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time. 
+            */
+            verify1 = (uint8_t)((zi->ci.dos_date >> 16) & 0xff);
+            verify2 = (uint8_t)((zi->ci.dos_date >> 8) & 0xff);
+
+            size_head = crypthead(password, buf_head, RAND_HEAD_LEN, zi->ci.keys, zi->ci.pcrc_32_tab, verify1, verify2);
+            zi->ci.total_compressed += size_head;
+
+            if (ZWRITE64(zi->z_filefunc, zi->filestream, buf_head, size_head) != size_head)
+                err = ZIP_ERRNO;
+        }
+    }
+#endif
+
+    if (err == Z_OK)
+        zi->in_opened_file_inzip = 1;
+    return err;
+}
+
+extern int ZEXPORT zipOpenNewFileInZip5(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t flag_base, int zip64, uint16_t method, int level, int raw,
+    int windowBits, int memLevel, int strategy, const char *password, int aes)
+{
+    return zipOpenNewFileInZip_internal(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global,
+        size_extrafield_global, comment, flag_base, zip64, method, level, raw, windowBits, memLevel, strategy, password, aes,
+        VERSIONMADEBY);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, int memLevel,
+    int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, uint16_t version_madeby, uint16_t flag_base, int zip64)
+{
+    uint8_t aes = 0;
+#ifdef HAVE_AES
+    aes = 1;
+#endif
+    return zipOpenNewFileInZip_internal(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global,
+        size_extrafield_global, comment, flag_base, zip64, method, level, raw, windowBits, memLevel, strategy, password, aes,
+        version_madeby);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits,
+    int memLevel, int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, uint16_t version_madeby, uint16_t flag_base)
+{
+    return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local,
+        extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel,
+        strategy, password, crc_for_crypting, version_madeby, flag_base, 0);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits,
+    int memLevel, int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting)
+{
+    return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local,
+        extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel,
+        strategy, password, crc_for_crypting, VERSIONMADEBY, 0, 0);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits,
+    int memLevel, int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, int zip64)
+{
+    return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local,
+        extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy,
+        password, crc_for_crypting, VERSIONMADEBY, 0, zip64);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw)
+{
+    return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local,
+        extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL,
+        Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, 0);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int zip64)
+{
+    return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local,
+        extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL,
+        Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, zip64);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int zip64)
+{
+    return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local,
+        extrafield_global, size_extrafield_global, comment, method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL,
+        Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, zip64);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level)
+{
+    return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local,
+        extrafield_global, size_extrafield_global, comment, method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL,
+        Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, 0);
+}
+
+/* Flushes the write buffer to disk */
+static int zipFlushWriteBuffer(zip64_internal *zi)
+{
+    uint64_t size_available = 0;
+    uint32_t written = 0;
+    uint32_t total_written = 0;
+    uint32_t write = 0;
+    uint32_t max_write = 0;
+    int err = ZIP_OK;
+
+    if ((zi->ci.flag & 1) != 0)
+    {
+#ifndef NOCRYPT
+#ifdef HAVE_AES
+        if (zi->ci.method == AES_METHOD)
+        {
+            fcrypt_encrypt(zi->ci.buffered_data, zi->ci.pos_in_buffered_data, &zi->ci.aes_ctx);
+        }
+        else
+#endif
+        {
+            uint32_t i = 0;
+            uint8_t t = 0;
+
+            for (i = 0; i < zi->ci.pos_in_buffered_data; i++)
+                zi->ci.buffered_data[i] = (uint8_t)zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i], t);
+        }
+#endif
+    }
+
+    write = zi->ci.pos_in_buffered_data;
+
+    do
+    {
+        max_write = write;
+
+        if (zi->disk_size > 0)
+        {
+            zipGetDiskSizeAvailable((zipFile)zi, &size_available);
+
+            if (size_available == 0)
+            {
+                err = zipGoToNextDisk((zipFile)zi);
+                if (err != ZIP_OK)
+                    return err;
+            }
+
+            if (size_available < (uint64_t)max_write)
+                max_write = (uint32_t)size_available;
+        }
+
+        written = ZWRITE64(zi->z_filefunc, zi->filestream, zi->ci.buffered_data + total_written, max_write);
+        if (written != max_write)
+        {
+            err = ZIP_ERRNO;
+            break;
+        }
+
+        total_written += written;
+        write -= written;
+    }
+    while (write > 0);
+
+    zi->ci.total_compressed += zi->ci.pos_in_buffered_data;
+
+#ifdef HAVE_BZIP2
+    if (zi->ci.compression_method == Z_BZIP2ED)
+    {
+        zi->ci.total_uncompressed += zi->ci.bstream.total_in_lo32;
+        zi->ci.bstream.total_in_lo32 = 0;
+        zi->ci.bstream.total_in_hi32 = 0;
+    }
+    else
+#endif
+    {
+        zi->ci.total_uncompressed += zi->ci.stream.total_in;
+        zi->ci.stream.total_in = 0;
+    }
+
+    zi->ci.pos_in_buffered_data = 0;
+
+    return err;
+}
+
+extern int ZEXPORT zipWriteInFileInZip(zipFile file, const void *buf, uint32_t len)
+{
+    zip64_internal *zi = NULL;
+    int err = ZIP_OK;
+
+    if (file == NULL)
+        return ZIP_PARAMERROR;
+    zi = (zip64_internal*)file;
+
+    if (zi->in_opened_file_inzip == 0)
+        return ZIP_PARAMERROR;
+
+    zi->ci.crc32 = (uint32_t)crc32(zi->ci.crc32, buf, len);
+
+#ifdef HAVE_BZIP2
+    if ((zi->ci.compression_method == Z_BZIP2ED) && (!zi->ci.raw))
+    {
+        zi->ci.bstream.next_in = (void*)buf;
+        zi->ci.bstream.avail_in = len;
+        err = BZ_RUN_OK;
+
+        while ((err == BZ_RUN_OK) && (zi->ci.bstream.avail_in > 0))
+        {
+            if (zi->ci.bstream.avail_out == 0)
+            {
+                err = zipFlushWriteBuffer(zi);
+                
+                zi->ci.bstream.avail_out = (uint16_t)Z_BUFSIZE;
+                zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
+            }
+            else
+            {
+                uint32_t total_out_before_lo = zi->ci.bstream.total_out_lo32;
+                uint32_t total_out_before_hi = zi->ci.bstream.total_out_hi32;
+
+                err = BZ2_bzCompress(&zi->ci.bstream, BZ_RUN);
+
+                zi->ci.pos_in_buffered_data += (uint16_t)(zi->ci.bstream.total_out_lo32 - total_out_before_lo);
+            }
+        }
+
+        if (err == BZ_RUN_OK)
+            err = ZIP_OK;
+    }
+    else
+#endif
+    {
+        zi->ci.stream.next_in = (uint8_t*)buf;
+        zi->ci.stream.avail_in = len;
+
+        while ((err == ZIP_OK) && (zi->ci.stream.avail_in > 0))
+        {
+            if (zi->ci.stream.avail_out == 0)
+            {
+                err = zipFlushWriteBuffer(zi);
+                
+                zi->ci.stream.avail_out = Z_BUFSIZE;
+                zi->ci.stream.next_out = zi->ci.buffered_data;
+            }
+
+            if (err != ZIP_OK)
+                break;
+
+            if ((zi->ci.compression_method == Z_DEFLATED) && (!zi->ci.raw))
+            {
+#ifdef HAVE_APPLE_COMPRESSION
+                uLong total_out_before = zi->ci.stream.total_out;
+
+                zi->ci.astream.src_ptr = zi->ci.stream.next_in;
+                zi->ci.astream.src_size = zi->ci.stream.avail_in;
+                zi->ci.astream.dst_ptr = zi->ci.stream.next_out;
+                zi->ci.astream.dst_size = zi->ci.stream.avail_out;
+
+                compression_status status = 0;
+                compression_stream_flags flags = 0;
+
+                status = compression_stream_process(&zi->ci.astream, flags);
+
+                uLong total_out_after = len - zi->ci.astream.src_size;
+
+                zi->ci.stream.next_in = zi->ci.astream.src_ptr;
+                zi->ci.stream.avail_in = zi->ci.astream.src_size;
+                zi->ci.stream.next_out = zi->ci.astream.dst_ptr;
+                zi->ci.stream.avail_out = zi->ci.astream.dst_size;
+                zi->ci.stream.total_in += total_out_after;
+                //zi->ci.stream.total_out += copy_this;
+                zi->ci.pos_in_buffered_data += total_out_after;
+
+                if (status == COMPRESSION_STATUS_ERROR)
+                    err = ZIP_INTERNALERROR;
+#else
+                uint32_t total_out_before = (uint32_t)zi->ci.stream.total_out;
+                err = deflate(&zi->ci.stream, Z_NO_FLUSH);
+                zi->ci.pos_in_buffered_data += (uint32_t)(zi->ci.stream.total_out - total_out_before);
+#endif
+            }
+            else
+            {
+                uint32_t copy_this = 0;
+                uint32_t i = 0;
+                if (zi->ci.stream.avail_in < zi->ci.stream.avail_out)
+                    copy_this = zi->ci.stream.avail_in;
+                else
+                    copy_this = zi->ci.stream.avail_out;
+
+                for (i = 0; i < copy_this; i++)
+                    *(((char*)zi->ci.stream.next_out)+i) =
+                        *(((const char*)zi->ci.stream.next_in)+i);
+
+                zi->ci.stream.avail_in -= copy_this;
+                zi->ci.stream.avail_out -= copy_this;
+                zi->ci.stream.next_in += copy_this;
+                zi->ci.stream.next_out += copy_this;
+                zi->ci.stream.total_in += copy_this;
+                zi->ci.stream.total_out += copy_this;
+                zi->ci.pos_in_buffered_data += copy_this;
+            }
+        }
+    }
+
+    return err;
+}
+
+extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, uint64_t uncompressed_size, uint32_t crc32)
+{
+    zip64_internal *zi = NULL;
+    uint16_t extra_data_size = 0;
+    uint32_t i = 0;
+    unsigned char *extra_info = NULL;
+    int err = ZIP_OK;
+
+    if (file == NULL)
+        return ZIP_PARAMERROR;
+    zi = (zip64_internal*)file;
+
+    if (zi->in_opened_file_inzip == 0)
+        return ZIP_PARAMERROR;
+    zi->ci.stream.avail_in = 0;
+
+    if (!zi->ci.raw)
+    {
+        if (zi->ci.compression_method == Z_DEFLATED)
+        {
+            while (err == ZIP_OK)
+            {
+                uint32_t total_out_before = 0;
+                
+                if (zi->ci.stream.avail_out == 0)
+                {
+                    err = zipFlushWriteBuffer(zi);
+
+                    zi->ci.stream.avail_out = Z_BUFSIZE;
+                    zi->ci.stream.next_out = zi->ci.buffered_data;
+                }
+                
+                if (err != ZIP_OK)
+                    break;
+                
+#ifdef HAVE_APPLE_COMPRESSION
+                total_out_before = zi->ci.stream.total_out;
+
+                zi->ci.astream.src_ptr = zi->ci.stream.next_in;
+                zi->ci.astream.src_size = zi->ci.stream.avail_in;
+                zi->ci.astream.dst_ptr = zi->ci.stream.next_out;
+                zi->ci.astream.dst_size = zi->ci.stream.avail_out;
+
+                compression_status status = 0;
+                status = compression_stream_process(&zi->ci.astream, COMPRESSION_STREAM_FINALIZE);
+
+                uint32_t total_out_after = Z_BUFSIZE - zi->ci.astream.dst_size;
+
+                zi->ci.stream.next_in = zi->ci.astream.src_ptr;
+                zi->ci.stream.avail_in = zi->ci.astream.src_size;
+                zi->ci.stream.next_out = zi->ci.astream.dst_ptr;
+                zi->ci.stream.avail_out = zi->ci.astream.dst_size;
+                //zi->ci.stream.total_in += total_out_after;
+                //zi->ci.stream.total_out += copy_this;
+                zi->ci.pos_in_buffered_data += total_out_after;
+
+                if (status == COMPRESSION_STATUS_ERROR)
+                {
+                    err = ZIP_INTERNALERROR;
+                }
+                else if (status == COMPRESSION_STATUS_END)
+                {
+                    err = Z_STREAM_END;
+                }
+#else
+                total_out_before = (uint32_t)zi->ci.stream.total_out;
+                err = deflate(&zi->ci.stream, Z_FINISH);
+                zi->ci.pos_in_buffered_data += (uint16_t)(zi->ci.stream.total_out - total_out_before);
+#endif
+            }
+        }
+        else if (zi->ci.compression_method == Z_BZIP2ED)
+        {
+#ifdef HAVE_BZIP2
+            err = BZ_FINISH_OK;
+            while (err == BZ_FINISH_OK)
+            {
+                uint32_t total_out_before = 0;
+                
+                if (zi->ci.bstream.avail_out == 0)
+                {
+                    err = zipFlushWriteBuffer(zi);
+                    
+                    zi->ci.bstream.avail_out = (uint16_t)Z_BUFSIZE;
+                    zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
+                }
+                
+                total_out_before = zi->ci.bstream.total_out_lo32;
+                err = BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH);
+                if (err == BZ_STREAM_END)
+                    err = Z_STREAM_END;
+                zi->ci.pos_in_buffered_data += (uint16_t)(zi->ci.bstream.total_out_lo32 - total_out_before);
+            }
+
+            if (err == BZ_FINISH_OK)
+                err = ZIP_OK;
+#endif
+        }
+    }
+
+    if (err == Z_STREAM_END)
+        err = ZIP_OK; /* this is normal */
+
+    if ((zi->ci.pos_in_buffered_data > 0) && (err == ZIP_OK))
+    {
+        err = zipFlushWriteBuffer(zi);
+    }
+
+#ifdef HAVE_AES
+    if (zi->ci.method == AES_METHOD)
+    {
+        unsigned char authcode[AES_AUTHCODESIZE];
+
+        fcrypt_end(authcode, &zi->ci.aes_ctx);
+
+        if (ZWRITE64(zi->z_filefunc, zi->filestream, authcode, AES_AUTHCODESIZE) != AES_AUTHCODESIZE)
+            err = ZIP_ERRNO;
+    }
+#endif
+
+    if (!zi->ci.raw)
+    {
+        if (zi->ci.compression_method == Z_DEFLATED)
+        {
+            int tmp_err = 0;
+#ifdef HAVE_APPLE_COMPRESSION
+            tmp_err = compression_stream_destroy(&zi->ci.astream);
+#else
+            tmp_err = deflateEnd(&zi->ci.stream);
+#endif
+            if (err == ZIP_OK)
+                err = tmp_err;
+            zi->ci.stream_initialised = 0;
+        }
+#ifdef HAVE_BZIP2
+        else if (zi->ci.compression_method == Z_BZIP2ED)
+        {
+            int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream);
+            if (err == ZIP_OK)
+                err = tmperr;
+            zi->ci.stream_initialised = 0;
+        }
+#endif
+
+        crc32 = zi->ci.crc32;
+        uncompressed_size = zi->ci.total_uncompressed;
+    }
+
+    /* Write data descriptor */
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)DATADESCRIPTORMAGIC, 4);
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, crc32, 4);
+    if (err == ZIP_OK)
+    {
+        if (zi->ci.zip64)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->ci.total_compressed, 8);
+        else
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)zi->ci.total_compressed, 4);
+    }
+    if (err == ZIP_OK)
+    {
+        if (zi->ci.zip64)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8);
+        else
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)uncompressed_size, 4);
+    }
+
+    /* Update crc and sizes to central directory */
+    zipWriteValueToMemory(zi->ci.central_header + 16, crc32, 4); /* crc */
+    if (zi->ci.total_compressed >= UINT32_MAX)
+        zipWriteValueToMemory(zi->ci.central_header + 20, UINT32_MAX, 4); /* compr size */
+    else
+        zipWriteValueToMemory(zi->ci.central_header + 20, zi->ci.total_compressed, 4); /* compr size */
+    if (uncompressed_size >= UINT32_MAX)
+        zipWriteValueToMemory(zi->ci.central_header + 24, UINT32_MAX, 4); /* uncompr size */
+    else
+        zipWriteValueToMemory(zi->ci.central_header + 24, uncompressed_size, 4); /* uncompr size */
+    if (zi->ci.stream.data_type == Z_ASCII)
+        zipWriteValueToMemory(zi->ci.central_header + 36, (uint16_t)Z_ASCII, 2); /* internal file attrib */
+
+    /* Add ZIP64 extra info field for uncompressed size */
+    if (uncompressed_size >= UINT32_MAX)
+        extra_data_size += 8;
+    /* Add ZIP64 extra info field for compressed size */
+    if (zi->ci.total_compressed >= UINT32_MAX)
+        extra_data_size += 8;
+    /* Add ZIP64 extra info field for relative offset to local file header of current file */
+    if (zi->ci.pos_local_header >= UINT32_MAX)
+        extra_data_size += 8;
+
+    /* Add ZIP64 extra info header to central directory */
+    if (extra_data_size > 0)
+    {
+        if ((uint32_t)(extra_data_size + 4) > zi->ci.size_centralextrafree)
+            return ZIP_BADZIPFILE;
+
+        extra_info = (unsigned char*)zi->ci.central_header + zi->ci.size_centralheader;
+
+        zipWriteValueToMemoryAndMove(&extra_info, 0x0001, 2);
+        zipWriteValueToMemoryAndMove(&extra_info, extra_data_size, 2);
+
+        if (uncompressed_size >= UINT32_MAX)
+            zipWriteValueToMemoryAndMove(&extra_info, uncompressed_size, 8);
+        if (zi->ci.total_compressed >= UINT32_MAX)
+            zipWriteValueToMemoryAndMove(&extra_info, zi->ci.total_compressed, 8);
+        if (zi->ci.pos_local_header >= UINT32_MAX)
+            zipWriteValueToMemoryAndMove(&extra_info, zi->ci.pos_local_header, 8);
+
+        zi->ci.size_centralextrafree -= extra_data_size + 4;
+        zi->ci.size_centralheader += extra_data_size + 4;
+        zi->ci.size_centralextra += extra_data_size + 4;
+
+        zipWriteValueToMemory(zi->ci.central_header + 30, zi->ci.size_centralextra, 2);
+    }
+
+#ifdef HAVE_AES
+    /* Write AES extra info header to central directory */
+    if (zi->ci.method == AES_METHOD)
+    {
+        extra_info = (unsigned char*)zi->ci.central_header + zi->ci.size_centralheader;
+        extra_data_size = 7;
+
+        if ((uint32_t)(extra_data_size + 4) > zi->ci.size_centralextrafree)
+            return ZIP_BADZIPFILE;
+
+        zipWriteValueToMemoryAndMove(&extra_info, 0x9901, 2);
+        zipWriteValueToMemoryAndMove(&extra_info, extra_data_size, 2);
+        zipWriteValueToMemoryAndMove(&extra_info, AES_VERSION, 2);
+        zipWriteValueToMemoryAndMove(&extra_info, 'A', 1);
+        zipWriteValueToMemoryAndMove(&extra_info, 'E', 1);
+        zipWriteValueToMemoryAndMove(&extra_info, AES_ENCRYPTIONMODE, 1);
+        zipWriteValueToMemoryAndMove(&extra_info, zi->ci.compression_method, 2);
+
+        zi->ci.size_centralextrafree -= extra_data_size + 4;
+        zi->ci.size_centralheader += extra_data_size + 4;
+        zi->ci.size_centralextra += extra_data_size + 4;
+
+        zipWriteValueToMemory(zi->ci.central_header + 30, zi->ci.size_centralextra, 2);
+    }
+#endif
+    /* Restore comment to correct position */
+    for (i = 0; i < zi->ci.size_comment; i++)
+        zi->ci.central_header[zi->ci.size_centralheader+i] =
+            zi->ci.central_header[zi->ci.size_centralheader+zi->ci.size_centralextrafree+i];
+    zi->ci.size_centralheader += zi->ci.size_comment;
+
+    if (err == ZIP_OK)
+        err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, zi->ci.size_centralheader);
+
+    free(zi->ci.central_header);
+
+    zi->number_entry++;
+    zi->in_opened_file_inzip = 0;
+
+    return err;
+}
+
+extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uint32_t uncompressed_size, uint32_t crc32)
+{
+    return zipCloseFileInZipRaw64(file, uncompressed_size, crc32);
+}
+
+extern int ZEXPORT zipCloseFileInZip(zipFile file)
+{
+    return zipCloseFileInZipRaw(file, 0, 0);
+}
+
+extern int ZEXPORT zipClose(zipFile file, const char *global_comment)
+{
+    return zipClose_64(file, global_comment);
+}
+
+extern int ZEXPORT zipClose_64(zipFile file, const char *global_comment)
+{
+    return zipClose2_64(file, global_comment, VERSIONMADEBY);
+}
+
+extern int ZEXPORT zipClose2_64(zipFile file, const char *global_comment, uint16_t version_madeby)
+{
+    zip64_internal *zi = NULL;
+    uint32_t size_centraldir = 0;
+    uint16_t size_global_comment = 0;
+    uint64_t centraldir_pos_inzip = 0;
+    uint64_t pos = 0;
+    uint64_t cd_pos = 0;
+    uint32_t write = 0;
+    int err = ZIP_OK;
+
+    if (file == NULL)
+        return ZIP_PARAMERROR;
+    zi = (zip64_internal*)file;
+
+    if (zi->in_opened_file_inzip == 1)
+        err = zipCloseFileInZip(file);
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+    if (global_comment == NULL)
+        global_comment = zi->globalcomment;
+#endif
+
+    if (zi->filestream != zi->filestream_with_CD)
+    {
+        if (ZCLOSE64(zi->z_filefunc, zi->filestream) != 0)
+            if (err == ZIP_OK)
+                err = ZIP_ERRNO;
+        if (zi->disk_size > 0)
+            zi->number_disk_with_CD = zi->number_disk + 1;
+        zi->filestream = zi->filestream_with_CD;
+    }
+
+    centraldir_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream);
+
+    if (err == ZIP_OK)
+    {
+        linkedlist_datablock_internal *ldi = zi->central_dir.first_block;
+        while (ldi != NULL)
+        {
+            if ((err == ZIP_OK) && (ldi->filled_in_this_block > 0))
+            {
+                write = ZWRITE64(zi->z_filefunc, zi->filestream, ldi->data, ldi->filled_in_this_block);
+                if (write != ldi->filled_in_this_block)
+                    err = ZIP_ERRNO;
+            }
+
+            size_centraldir += ldi->filled_in_this_block;
+            ldi = ldi->next_datablock;
+        }
+    }
+
+    free_linkedlist(&(zi->central_dir));
+
+    pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
+
+    /* Write the ZIP64 central directory header */
+    if (pos >= UINT32_MAX || zi->number_entry > UINT32_MAX)
+    {
+        uint64_t zip64_eocd_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream);
+        uint32_t zip64_datasize = 44;
+
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)ZIP64ENDHEADERMAGIC, 4);
+
+        /* Size of this 'zip64 end of central directory' */
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint64_t)zip64_datasize, 8);
+        /* Version made by */
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, version_madeby, 2);
+        /* version needed */
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)45, 2);
+        /* Number of this disk */
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_disk_with_CD, 4);
+        /* Number of the disk with the start of the central directory */
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_disk_with_CD, 4);
+        /* Total number of entries in the central dir on this disk */
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8);
+        /* Total number of entries in the central dir */
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8);
+        /* Size of the central directory */
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint64_t)size_centraldir, 8);
+
+        if (err == ZIP_OK)
+        {
+            /* Offset of start of central directory with respect to the starting disk number */
+            cd_pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, cd_pos, 8);
+        }
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)ZIP64ENDLOCHEADERMAGIC, 4);
+
+        /* Number of the disk with the start of the central directory */
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_disk_with_CD, 4);
+        /* Relative offset to the Zip64EndOfCentralDirectory */
+        if (err == ZIP_OK)
+        {
+            cd_pos = zip64_eocd_pos_inzip - zi->add_position_when_writting_offset;
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, cd_pos, 8);
+        }
+        /* Number of the disk with the start of the central directory */
+        if (err == ZIP_OK)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_disk_with_CD + 1, 4);
+    }
+
+    /* Write the central directory header */
+
+    /* Signature */
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)ENDHEADERMAGIC, 4);
+    /* Number of this disk */
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)zi->number_disk_with_CD, 2);
+    /* Number of the disk with the start of the central directory */
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)zi->number_disk_with_CD, 2);
+    /* Total number of entries in the central dir on this disk */
+    if (err == ZIP_OK)
+    {
+        if (zi->number_entry >= UINT16_MAX)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, UINT16_MAX, 2); /* use value in ZIP64 record */
+        else
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)zi->number_entry, 2);
+    }
+    /* Total number of entries in the central dir */
+    if (err == ZIP_OK)
+    {
+        if (zi->number_entry >= UINT16_MAX)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, UINT16_MAX, 2); /* use value in ZIP64 record */
+        else
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)zi->number_entry, 2);
+    }
+    /* Size of the central directory */
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, size_centraldir, 4);
+    /* Offset of start of central directory with respect to the starting disk number */
+    if (err == ZIP_OK)
+    {
+        cd_pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
+        if (pos >= UINT32_MAX)
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, UINT32_MAX, 4);
+        else
+            err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)cd_pos, 4);
+    }
+
+    /* Write global comment */
+
+    if (global_comment != NULL)
+        size_global_comment = (uint16_t)strlen(global_comment);
+    if (err == ZIP_OK)
+        err = zipWriteValue(&zi->z_filefunc, zi->filestream, size_global_comment, 2);
+    if (err == ZIP_OK && size_global_comment > 0)
+    {
+        if (ZWRITE64(zi->z_filefunc, zi->filestream, global_comment, size_global_comment) != size_global_comment)
+            err = ZIP_ERRNO;
+    }
+
+    if ((ZCLOSE64(zi->z_filefunc, zi->filestream) != 0) && (err == ZIP_OK))
+        err = ZIP_ERRNO;
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+    TRYFREE(zi->globalcomment);
+#endif
+    TRYFREE(zi);
+
+    return err;
+}
diff --git a/iOS/Pods/SSZipArchive/SSZipArchive/minizip/zip.h b/iOS/Pods/SSZipArchive/SSZipArchive/minizip/zip.h
new file mode 100644 (file)
index 0000000..8e61b19
--- /dev/null
@@ -0,0 +1,214 @@
+/* zip.h -- IO on .zip files using zlib
+   Version 1.2.0, September 16th, 2017
+   part of the MiniZip project
+
+   Copyright (C) 2012-2017 Nathan Moinvaziri
+     https://github.com/nmoinvaz/minizip
+   Copyright (C) 2009-2010 Mathias Svensson
+     Modifications for Zip64 support
+     http://result42.com
+   Copyright (C) 1998-2010 Gilles Vollant
+     http://www.winimage.com/zLibDll/minizip.html
+
+   This program is distributed under the terms of the same license as zlib.
+   See the accompanying LICENSE file for the full text of the license.
+*/
+
+#ifndef _ZIP_H
+#define _ZIP_H
+
+#define HAVE_AES
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#  include "zlib.h"
+#endif
+
+#ifndef _ZLIBIOAPI_H
+#  include "ioapi.h"
+#endif
+
+#ifdef HAVE_BZIP2
+#  include "bzlib.h"
+#endif
+
+#define Z_BZIP2ED 12
+
+#if defined(STRICTZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+    from (void*) without cast */
+typedef struct TagzipFile__ { int unused; } zip_file__;
+typedef zip_file__ *zipFile;
+#else
+typedef voidp zipFile;
+#endif
+
+#define ZIP_OK                          (0)
+#define ZIP_EOF                         (0)
+#define ZIP_ERRNO                       (Z_ERRNO)
+#define ZIP_PARAMERROR                  (-102)
+#define ZIP_BADZIPFILE                  (-103)
+#define ZIP_INTERNALERROR               (-104)
+
+#ifndef DEF_MEM_LEVEL
+#  if MAX_MEM_LEVEL >= 8
+#    define DEF_MEM_LEVEL 8
+#  else
+#    define DEF_MEM_LEVEL  MAX_MEM_LEVEL
+#  endif
+#endif
+
+typedef struct
+{
+    uint32_t    dos_date;
+    uint16_t    internal_fa;        /* internal file attributes        2 bytes */
+    uint32_t    external_fa;        /* external file attributes        4 bytes */
+} zip_fileinfo;
+
+#define APPEND_STATUS_CREATE        (0)
+#define APPEND_STATUS_CREATEAFTER   (1)
+#define APPEND_STATUS_ADDINZIP      (2)
+
+/***************************************************************************/
+/* Writing a zip file */
+
+extern zipFile ZEXPORT zipOpen(const char *path, int append);
+extern zipFile ZEXPORT zipOpen64(const void *path, int append);
+/* Create a zipfile.
+
+   path should contain the full path (by example, on a Windows XP computer 
+      "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". 
+
+   return NULL if zipfile cannot be opened
+   return zipFile handle if no error
+
+   If the file path exist and append == APPEND_STATUS_CREATEAFTER, the zip
+   will be created at the end of the file. (useful if the file contain a self extractor code)
+   If the file path exist and append == APPEND_STATUS_ADDINZIP, we will add files in existing 
+   zip (be sure you don't add file that doesn't exist)
+
+   NOTE: There is no delete function into a zipfile. If you want delete file into a zipfile, 
+   you must open a zipfile, and create another. Of course, you can use RAW reading and writing to copy
+   the file you did not want delete. */
+
+extern zipFile ZEXPORT zipOpen2(const char *path, int append, const char **globalcomment, 
+    zlib_filefunc_def *pzlib_filefunc_def);
+
+extern zipFile ZEXPORT zipOpen2_64(const void *path, int append, const char **globalcomment, 
+    zlib_filefunc64_def *pzlib_filefunc_def);
+
+extern zipFile ZEXPORT zipOpen3(const char *path, int append, uint64_t disk_size, 
+    const char **globalcomment, zlib_filefunc_def *pzlib_filefunc_def);
+/* Same as zipOpen2 but allows specification of spanned zip size */
+
+extern zipFile ZEXPORT zipOpen3_64(const void *path, int append, uint64_t disk_size, 
+    const char **globalcomment, zlib_filefunc64_def *pzlib_filefunc_def);
+
+extern int ZEXPORT zipOpenNewFileInZip(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, 
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level);
+/* Open a file in the ZIP for writing.
+
+   filename : the filename in zip (if NULL, '-' without quote will be used
+   *zipfi contain supplemental information
+   extrafield_local buffer to store the local header extra field data, can be NULL
+   size_extrafield_local size of extrafield_local buffer
+   extrafield_global buffer to store the global header extra field data, can be NULL
+   size_extrafield_global size of extrafield_local buffer
+   comment buffer for comment string
+   method contain the compression method (0 for store, Z_DEFLATED for deflate)
+   level contain the level of compression (can be Z_DEFAULT_COMPRESSION)
+   zip64 is set to 1 if a zip64 extended information block should be added to the local file header.
+   this MUST be '1' if the uncompressed size is >= 0xffffffff. */
+
+extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int zip64);
+/* Same as zipOpenNewFileInZip with zip64 support */
+
+extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw);
+/* Same as zipOpenNewFileInZip, except if raw=1, we write raw file */
+
+extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int zip64);
+/* Same as zipOpenNewFileInZip3 with zip64 support */
+
+extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, int memLevel,
+    int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting);
+/* Same as zipOpenNewFileInZip2, except
+    windowBits, memLevel, strategy : see parameter strategy in deflateInit2
+    password : crypting password (NULL for no crypting)
+    crc_for_crypting : crc of file to compress (needed for crypting) */
+
+extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, int memLevel,
+    int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, int zip64);
+/* Same as zipOpenNewFileInZip3 with zip64 support */
+
+extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, int memLevel,
+    int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, uint16_t version_madeby, uint16_t flag_base);
+/* Same as zipOpenNewFileInZip3 except versionMadeBy & flag fields */
+
+extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char *filename, const zip_fileinfo *zipfi,
+    const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global,
+    uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, int memLevel,
+    int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, uint16_t version_madeby, uint16_t flag_base, int zip64);
+/* Same as zipOpenNewFileInZip4 with zip64 support */
+
+extern int ZEXPORT zipOpenNewFileInZip5(zipFile file,
+                                        const char *filename,
+                                        const zip_fileinfo *zipfi,
+                                        const void *extrafield_local,
+                                        uint16_t size_extrafield_local,
+                                        const void *extrafield_global,
+                                        uint16_t size_extrafield_global,
+                                        const char *comment,
+                                        uint16_t flag_base,
+                                        int zip64,
+                                        uint16_t method,
+                                        int level,
+                                        int raw,
+                                        int windowBits,
+                                        int memLevel,
+                                        int strategy,
+                                        const char *password,
+                                        int aes);
+/* Allowing optional aes */
+
+extern int ZEXPORT zipWriteInFileInZip(zipFile file, const void *buf, uint32_t len);
+/* Write data in the zipfile */
+
+extern int ZEXPORT zipCloseFileInZip(zipFile file);
+/* Close the current file in the zipfile */
+
+extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uint32_t uncompressed_size, uint32_t crc32);
+extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, uint64_t uncompressed_size, uint32_t crc32);
+/* Close the current file in the zipfile, for file opened with parameter raw=1 in zipOpenNewFileInZip2
+   where raw is compressed data. Parameters uncompressed_size and crc32 are value for the uncompressed data. */
+
+extern int ZEXPORT zipClose(zipFile file, const char *global_comment);
+/* Close the zipfile */
+
+extern int ZEXPORT zipClose_64(zipFile file, const char *global_comment);
+
+extern int ZEXPORT zipClose2_64(zipFile file, const char *global_comment, uint16_t version_madeby);
+/* Same as zipClose_64 except version_madeby field */
+
+/***************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZIP_H */
diff --git a/iOS/Pods/SideMenu/LICENSE b/iOS/Pods/SideMenu/LICENSE
new file mode 100644 (file)
index 0000000..eccb52c
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Jonathan Kent <contact@jonkent.me>
+
+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/iOS/Pods/SideMenu/Pod/Classes/SideMenuManager.swift b/iOS/Pods/SideMenu/Pod/Classes/SideMenuManager.swift
new file mode 100644 (file)
index 0000000..5cde939
--- /dev/null
@@ -0,0 +1,683 @@
+//
+//  SideMenuManager.swift
+//
+//  Created by Jon Kent on 12/6/15.
+//  Copyright © 2015 Jon Kent. All rights reserved.
+//
+
+/* Example usage:
+     // Define the menus
+     SideMenuManager.menuLeftNavigationController = storyboard!.instantiateViewController(withIdentifier: "LeftMenuNavigationController") as? UISideMenuNavigationController
+     SideMenuManager.menuRightNavigationController = storyboard!.instantiateViewController(withIdentifier: "RightMenuNavigationController") as? UISideMenuNavigationController
+     
+     // Enable gestures. The left and/or right menus must be set up above for these to work.
+     // Note that these continue to work on the Navigation Controller independent of the View Controller it displays!
+     SideMenuManager.menuAddPanGestureToPresent(toView: self.navigationController!.navigationBar)
+     SideMenuManager.menuAddScreenEdgePanGesturesToPresent(toView: self.navigationController!.view)
+*/
+
+@objcMembers
+open class SideMenuManager : NSObject {
+    
+    @objc public enum MenuPushStyle : Int {
+        case defaultBehavior,
+        popWhenPossible,
+        replace,
+        preserve,
+        preserveAndHideBackButton,
+        subMenu
+    }
+    
+    @objc public enum MenuPresentMode : Int {
+        case menuSlideIn,
+        viewSlideOut,
+        viewSlideInOut,
+        menuDissolveIn
+    }
+    
+    // Bounds which has been allocated for the app on the whole device screen
+    internal static var appScreenRect: CGRect {
+        let appWindowRect = UIApplication.shared.keyWindow?.bounds ?? UIWindow().bounds
+        return appWindowRect
+    }
+
+    /**
+     The push style of the menu.
+     
+     There are six modes in MenuPushStyle:
+     - defaultBehavior: The view controller is pushed onto the stack.
+     - popWhenPossible: If a view controller already in the stack is of the same class as the pushed view controller, the stack is instead popped back to the existing view controller. This behavior can help users from getting lost in a deep navigation stack.
+     - preserve: If a view controller already in the stack is of the same class as the pushed view controller, the existing view controller is pushed to the end of the stack. This behavior is similar to a UITabBarController.
+     - preserveAndHideBackButton: Same as .preserve and back buttons are automatically hidden.
+     - replace: Any existing view controllers are released from the stack and replaced with the pushed view controller. Back buttons are automatically hidden. This behavior is ideal if view controllers require a lot of memory or their state doesn't need to be preserved..
+     - subMenu: Unlike all other behaviors that push using the menu's presentingViewController, this behavior pushes view controllers within the menu.  Use this behavior if you want to display a sub menu.
+     */
+    open var menuPushStyle: MenuPushStyle = .defaultBehavior
+
+    /**
+     The presentation mode of the menu.
+     
+     There are four modes in MenuPresentMode:
+     - menuSlideIn: Menu slides in over of the existing view.
+     - viewSlideOut: The existing view slides out to reveal the menu.
+     - viewSlideInOut: The existing view slides out while the menu slides in.
+     - menuDissolveIn: The menu dissolves in over the existing view controller.
+     */
+    open var menuPresentMode: MenuPresentMode = .viewSlideOut
+    
+    /// Prevents the same view controller (or a view controller of the same class) from being pushed more than once. Defaults to true.
+    open var menuAllowPushOfSameClassTwice = true
+
+    /**
+     Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is 75% of the screen width or 240 points, whichever is smaller.
+     
+     Note that each menu's width can be overridden using the `menuWidth` property on any `UISideMenuNavigationController` instance.
+     */
+    open var menuWidth: CGFloat = min(round(min((appScreenRect.width), (appScreenRect.height)) * 0.75), 240)
+    
+    /// Duration of the animation when the menu is presented without gestures. Default is 0.35 seconds.
+    open var menuAnimationPresentDuration: Double = 0.35
+    
+    /// Duration of the animation when the menu is dismissed without gestures. Default is 0.35 seconds.
+    open var menuAnimationDismissDuration: Double = 0.35
+    
+    /// Duration of the remaining animation when the menu is partially dismissed with gestures. Default is 0.35 seconds.
+    open var menuAnimationCompleteGestureDuration: Double = 0.35
+    
+    /// Amount to fade the existing view controller when the menu is presented. Default is 0 for no fade. Set to 1 to fade completely.
+    open var menuAnimationFadeStrength: CGFloat = 0
+    
+    /// The amount to scale the existing view controller or the menu view controller depending on the `menuPresentMode`. Default is 1 for no scaling. Less than 1 will shrink, greater than 1 will grow.
+    open var menuAnimationTransformScaleFactor: CGFloat = 1
+    
+    /// The background color behind menu animations. Depending on the animation settings this may not be visible. If `menuFadeStatusBar` is true, this color is used to fade it. Default is black.
+    open var menuAnimationBackgroundColor: UIColor?
+    
+    /// The shadow opacity around the menu view controller or existing view controller depending on the `menuPresentMode`. Default is 0.5 for 50% opacity.
+    open var menuShadowOpacity: Float = 0.5
+    
+    /// The shadow color around the menu view controller or existing view controller depending on the `menuPresentMode`. Default is black.
+    open var menuShadowColor = UIColor.black
+    
+    /// The radius of the shadow around the menu view controller or existing view controller depending on the `menuPresentMode`. Default is 5.
+    open var menuShadowRadius: CGFloat = 5
+    
+    /// Enable or disable interaction with the presenting view controller while the menu is displayed. Enabling may make it difficult to dismiss the menu or cause exceptions if the user tries to present and already presented menu. Default is false.
+    open var menuPresentingViewControllerUserInteractionEnabled: Bool = false
+    
+    /// The strength of the parallax effect on the existing view controller. Does not apply to `menuPresentMode` when set to `ViewSlideOut`. Default is 0.
+    open var menuParallaxStrength: Int = 0
+    
+    /// Draws the `menuAnimationBackgroundColor` behind the status bar. Default is true.
+    open var menuFadeStatusBar = true
+    
+    /// The animation options when a menu is displayed. Ignored when displayed with a gesture.
+    open var menuAnimationOptions: UIViewAnimationOptions = .curveEaseInOut
+    
+    /// The animation spring damping when a menu is displayed. Ignored when displayed with a gesture.
+    open var menuAnimationUsingSpringWithDamping: CGFloat = 1
+    
+    /// The animation initial spring velocity when a menu is displayed. Ignored when displayed with a gesture.
+    open var menuAnimationInitialSpringVelocity: CGFloat = 1
+    
+    /**
+     Automatically dismisses the menu when another view is pushed from it.
+     
+     Note: to prevent the menu from dismissing when presenting, set modalPresentationStyle = .overFullScreen
+     of the view controller being presented in storyboard or during its initalization.
+     */
+    open var menuDismissOnPush = true
+    
+    /// Forces menus to always animate when appearing or disappearing, regardless of a pushed view controller's animation.
+    open var menuAlwaysAnimate = false
+    
+    /// Default instance of SideMenuManager.
+    open static let `default` = SideMenuManager()
+    
+    /// Default instance of SideMenuManager (objective-C).
+    open class var defaultManager: SideMenuManager {
+        get {
+            return SideMenuManager.default
+        }
+    }
+    
+    internal var transition: SideMenuTransition!
+    
+    public override init() {
+        super.init()
+        transition = SideMenuTransition(sideMenuManager: self)
+    }
+    
+    /**
+     The blur effect style of the menu if the menu's root view controller is a UITableViewController or UICollectionViewController.
+     
+     - Note: If you want cells in a UITableViewController menu to show vibrancy, make them a subclass of UITableViewVibrantCell.
+     */
+    open var menuBlurEffectStyle: UIBlurEffectStyle? {
+        didSet {
+            if oldValue != menuBlurEffectStyle {
+                updateMenuBlurIfNecessary()
+            }
+        }
+    }
+    
+    /// The left menu.
+    open var menuLeftNavigationController: UISideMenuNavigationController? {
+        willSet {
+            guard menuLeftNavigationController != newValue, menuLeftNavigationController?.presentingViewController == nil else {
+                return
+            }
+            menuLeftNavigationController?.locked = false
+            removeMenuBlurForMenu(menuLeftNavigationController)
+        }
+        didSet {
+            guard menuLeftNavigationController != oldValue else {
+                return
+            }
+            guard oldValue?.presentingViewController == nil else {
+                print("SideMenu Warning: menuLeftNavigationController cannot be modified while it's presented.")
+                menuLeftNavigationController = oldValue
+                return
+            }
+            
+            setupNavigationController(menuLeftNavigationController, leftSide: true)
+        }
+    }
+    
+    /// The right menu.
+    open var menuRightNavigationController: UISideMenuNavigationController? {
+        willSet {
+            guard menuRightNavigationController != newValue, menuRightNavigationController?.presentingViewController == nil else {
+                return
+            }
+            removeMenuBlurForMenu(menuRightNavigationController)
+        }
+        didSet {
+            guard menuRightNavigationController != oldValue else {
+                return
+            }
+            guard oldValue?.presentingViewController == nil else {
+                print("SideMenu Warning: menuRightNavigationController cannot be modified while it's presented.")
+                menuRightNavigationController = oldValue
+                return
+            }
+            setupNavigationController(menuRightNavigationController, leftSide: false)
+        }
+    }
+    
+    /// The left menu swipe to dismiss gesture.
+    open weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer? {
+        didSet {
+            oldValue?.view?.removeGestureRecognizer(oldValue!)
+            setupGesture(gesture: menuLeftSwipeToDismissGesture)
+        }
+    }
+    
+    /// The right menu swipe to dismiss gesture.
+    open weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer? {
+        didSet {
+            oldValue?.view?.removeGestureRecognizer(oldValue!)
+            setupGesture(gesture: menuRightSwipeToDismissGesture)
+        }
+    }
+    
+    fileprivate func setupGesture(gesture: UIPanGestureRecognizer?) {
+        guard let gesture = gesture else {
+            return
+        }
+        
+        gesture.addTarget(transition, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
+    }
+    
+    fileprivate func setupNavigationController(_ forMenu: UISideMenuNavigationController?, leftSide: Bool) {
+        guard let forMenu = forMenu else {
+            return
+        }
+        
+        forMenu.transitioningDelegate = transition
+        forMenu.modalPresentationStyle = .overFullScreen
+        forMenu.leftSide = leftSide
+        
+        if forMenu.sideMenuManager != self {
+            #if !STFU_SIDEMENU
+            if forMenu.sideMenuManager?.menuLeftNavigationController == forMenu {
+                print("SideMenu Warning: \(String(describing: forMenu.self)) was already assigned to the menuLeftNavigationController of \(String(describing: forMenu.sideMenuManager!.self)). When using multiple SideMenuManagers you may want to use new instances of UISideMenuNavigationController instead of existing instances to avoid crashes if the menu is presented more than once.")
+            } else if forMenu.sideMenuManager?.menuRightNavigationController == forMenu {
+                print("SideMenu Warning: \(String(describing: forMenu.self)) was already assigned to the menuRightNavigationController of \(String(describing: forMenu.sideMenuManager!.self)). When using multiple SideMenuManagers you may want to use new instances of UISideMenuNavigationController instead of existing instances to avoid crashes if the menu is presented more than once.")
+            }
+            #endif
+            forMenu.sideMenuManager = self
+        }
+        
+        forMenu.locked = true
+        
+        if menuEnableSwipeGestures {
+            let exitPanGesture = UIPanGestureRecognizer()
+            forMenu.view.addGestureRecognizer(exitPanGesture)
+            if leftSide {
+                menuLeftSwipeToDismissGesture = exitPanGesture
+            } else {
+                menuRightSwipeToDismissGesture = exitPanGesture
+            }
+        }
+        
+        // Ensures minimal lag when revealing the menu for the first time using gestures by loading the view:
+        let _ = forMenu.topViewController?.view
+        
+        updateMenuBlurIfNecessary()
+    }
+    
+    /// Enable or disable gestures that would swipe to dismiss the menu. Default is true.
+    open var menuEnableSwipeGestures: Bool = true {
+        didSet {
+            menuLeftSwipeToDismissGesture?.view?.removeGestureRecognizer(menuLeftSwipeToDismissGesture!)
+            menuRightSwipeToDismissGesture?.view?.removeGestureRecognizer(menuRightSwipeToDismissGesture!)
+            setupNavigationController(menuLeftNavigationController, leftSide: true)
+            setupNavigationController(menuRightNavigationController, leftSide: false)
+        }
+    }
+    
+    fileprivate func updateMenuBlurIfNecessary() {
+        if let menuLeftNavigationController = self.menuLeftNavigationController {
+            setupMenuBlurForMenu(menuLeftNavigationController)
+        }
+        if let menuRightNavigationController = self.menuRightNavigationController {
+            setupMenuBlurForMenu(menuRightNavigationController)
+        }
+    }
+    
+    fileprivate func setupMenuBlurForMenu(_ forMenu: UISideMenuNavigationController?) {
+        removeMenuBlurForMenu(forMenu)
+        
+        guard let forMenu = forMenu,
+            let menuBlurEffectStyle = menuBlurEffectStyle,
+            let view = forMenu.topViewController?.view,
+            !UIAccessibilityIsReduceTransparencyEnabled() else {
+                return
+        }
+        
+        if forMenu.originalMenuBackgroundColor == nil {
+            forMenu.originalMenuBackgroundColor = view.backgroundColor
+        }
+        
+        let blurEffect = UIBlurEffect(style: menuBlurEffectStyle)
+        let blurView = UIVisualEffectView(effect: blurEffect)
+        view.backgroundColor = UIColor.clear
+        if let tableViewController = forMenu.topViewController as? UITableViewController {
+            tableViewController.tableView.backgroundView = blurView
+            tableViewController.tableView.separatorEffect = UIVibrancyEffect(blurEffect: blurEffect)
+            tableViewController.tableView.reloadData()
+        } else {
+            blurView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
+            blurView.frame = view.bounds
+            view.insertSubview(blurView, at: 0)
+        }
+    }
+    
+    fileprivate func removeMenuBlurForMenu(_ forMenu: UISideMenuNavigationController?) {
+        guard let forMenu = forMenu,
+            let originalMenuBackgroundColor = forMenu.originalMenuBackgroundColor,
+            let view = forMenu.topViewController?.view else {
+                return
+        }
+        
+        view.backgroundColor = originalMenuBackgroundColor
+        forMenu.originalMenuBackgroundColor = nil
+        
+        if let tableViewController = forMenu.topViewController as? UITableViewController {
+            tableViewController.tableView.backgroundView = nil
+            tableViewController.tableView.separatorEffect = nil
+            tableViewController.tableView.reloadData()
+        } else if let blurView = view.subviews[0] as? UIVisualEffectView {
+            blurView.removeFromSuperview()
+        }
+    }
+    
+    /**
+     Adds screen edge gestures to a view to present a menu.
+     
+     - Parameter toView: The view to add gestures to.
+     - Parameter forMenu: The menu (left or right) you want to add a gesture for. If unspecified, gestures will be added for both sides.
+     - Returns: The array of screen edge gestures added to `toView`.
+     */
+    @discardableResult open func menuAddScreenEdgePanGesturesToPresent(toView: UIView, forMenu:UIRectEdge? = nil) -> [UIScreenEdgePanGestureRecognizer] {
+        var array = [UIScreenEdgePanGestureRecognizer]()
+        
+        let newScreenEdgeGesture = { () -> UIScreenEdgePanGestureRecognizer in
+            let screenEdgeGestureRecognizer = UIScreenEdgePanGestureRecognizer()
+            screenEdgeGestureRecognizer.cancelsTouchesInView = true
+            toView.addGestureRecognizer(screenEdgeGestureRecognizer)
+            array.append(screenEdgeGestureRecognizer)
+            return screenEdgeGestureRecognizer
+        }
+        
+        if forMenu != .right {
+            let leftScreenEdgeGestureRecognizer = newScreenEdgeGesture()
+            leftScreenEdgeGestureRecognizer.addTarget(transition, action:#selector(SideMenuTransition.handlePresentMenuLeftScreenEdge(_:)))
+            leftScreenEdgeGestureRecognizer.edges = .left
+            
+            #if !STFU_SIDEMENU
+            if menuLeftNavigationController == nil {
+                print("SideMenu Warning: menuAddScreenEdgePanGesturesToPresent was called before menuLeftNavigationController was set. The gesture will not work without a menu. Use menuAddScreenEdgePanGesturesToPresent(toView:forMenu:) to add gestures for only one menu.")
+            }
+            #endif
+        }
+        
+        if forMenu != .left {
+            let rightScreenEdgeGestureRecognizer = newScreenEdgeGesture()
+            rightScreenEdgeGestureRecognizer.addTarget(transition, action:#selector(SideMenuTransition.handlePresentMenuRightScreenEdge(_:)))
+            rightScreenEdgeGestureRecognizer.edges = .right
+            
+            #if !STFU_SIDEMENU
+            if menuRightNavigationController == nil {
+                print("SideMenu Warning: menuAddScreenEdgePanGesturesToPresent was called before menuRightNavigationController was set. The gesture will not work without a menu. Use menuAddScreenEdgePanGesturesToPresent(toView:forMenu:) to add gestures for only one menu.")
+            }
+            #endif
+        }
+        
+        return array
+    }
+    
+    /**
+     Adds a pan edge gesture to a view to present menus.
+     
+     - Parameter toView: The view to add a pan gesture to.
+     
+     - Returns: The pan gesture added to `toView`.
+     */
+    @discardableResult open func menuAddPanGestureToPresent(toView: UIView) -> UIPanGestureRecognizer {
+        let panGestureRecognizer = UIPanGestureRecognizer()
+        panGestureRecognizer.addTarget(transition, action:#selector(SideMenuTransition.handlePresentMenuPan(_:)))
+        toView.addGestureRecognizer(panGestureRecognizer)
+        
+        if menuLeftNavigationController ?? menuRightNavigationController == nil {
+            print("SideMenu Warning: menuAddPanGestureToPresent called before menuLeftNavigationController or menuRightNavigationController have been defined. Gestures will not work without a menu.")
+        }
+        
+        return panGestureRecognizer
+    }
+}
+
+// Deprecations, to be removed at a future date.
+extension SideMenuManager {
+    
+    @available(*, deprecated, renamed: "default.menuPushStyle", message: "SideMenuManager class methods deprecated.")
+    open static var menuPushStyle: MenuPushStyle {
+        get {
+            return `default`.menuPushStyle
+        }
+        set {
+            `default`.menuPushStyle = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuPresentMode", message: "SideMenuManager class methods deprecated.")
+    open static var menuPresentMode: MenuPresentMode {
+        get {
+            return `default`.menuPresentMode
+        }
+        set {
+            `default`.menuPresentMode = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAllowPushOfSameClassTwice", message: "SideMenuManager class methods deprecated.")
+    open static var menuAllowPushOfSameClassTwice: Bool {
+        get {
+            return `default`.menuAllowPushOfSameClassTwice
+        }
+        set {
+            `default`.menuAllowPushOfSameClassTwice = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuWidth", message: "SideMenuManager class methods deprecated.")
+    open static var menuWidth: CGFloat {
+        get {
+            return `default`.menuWidth
+        }
+        set {
+            `default`.menuWidth = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAnimationPresentDuration", message: "SideMenuManager class methods deprecated.")
+    open static var menuAnimationPresentDuration: Double {
+        get {
+            return `default`.menuAnimationPresentDuration
+        }
+        set {
+            `default`.menuAnimationPresentDuration = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAnimationDismissDuration", message: "SideMenuManager class methods deprecated.")
+    open static var menuAnimationDismissDuration: Double {
+        get {
+            return `default`.menuAnimationDismissDuration
+        }
+        set {
+            `default`.menuAnimationDismissDuration = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAnimationCompleteGestureDuration", message: "SideMenuManager class methods deprecated.")
+    open static var menuAnimationCompleteGestureDuration: Double {
+        get {
+            return `default`.menuAnimationCompleteGestureDuration
+        }
+        set {
+            `default`.menuAnimationCompleteGestureDuration = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAnimationFadeStrength", message: "SideMenuManager class methods deprecated.")
+    open static var menuAnimationFadeStrength: CGFloat {
+        get {
+            return `default`.menuAnimationFadeStrength
+        }
+        set {
+            `default`.menuAnimationFadeStrength = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAnimationTransformScaleFactor", message: "SideMenuManager class methods deprecated.")
+    open static var menuAnimationTransformScaleFactor: CGFloat {
+        get {
+            return `default`.menuAnimationTransformScaleFactor
+        }
+        set {
+            `default`.menuAnimationTransformScaleFactor = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAnimationBackgroundColor", message: "SideMenuManager class methods deprecated.")
+    open static var menuAnimationBackgroundColor: UIColor? {
+        get {
+            return `default`.menuAnimationBackgroundColor
+        }
+        set {
+            `default`.menuAnimationBackgroundColor = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuShadowOpacity", message: "SideMenuManager class methods deprecated.")
+    open static var menuShadowOpacity: Float {
+        get {
+            return `default`.menuShadowOpacity
+        }
+        set {
+            `default`.menuShadowOpacity = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuShadowColor", message: "SideMenuManager class methods deprecated.")
+    open static var menuShadowColor: UIColor {
+        get {
+            return `default`.menuShadowColor
+        }
+        set {
+            `default`.menuShadowColor = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuShadowRadius", message: "SideMenuManager class methods deprecated.")
+    open static var menuShadowRadius: CGFloat {
+        get {
+            return `default`.menuShadowRadius
+        }
+        set {
+            `default`.menuShadowRadius = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuPresentingViewControllerUserInteractionEnabled", message: "SideMenuManager class methods deprecated.")
+    open static var menuPresentingViewControllerUserInteractionEnabled: Bool {
+        get {
+            return `default`.menuPresentingViewControllerUserInteractionEnabled
+        }
+        set {
+            `default`.menuPresentingViewControllerUserInteractionEnabled = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuParallaxStrength", message: "SideMenuManager class methods deprecated.")
+    open static var menuParallaxStrength: Int {
+        get {
+            return `default`.menuParallaxStrength
+        }
+        set {
+            `default`.menuParallaxStrength = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuFadeStatusBar", message: "SideMenuManager class methods deprecated.")
+    open static var menuFadeStatusBar: Bool {
+        get {
+            return `default`.menuFadeStatusBar
+        }
+        set {
+            `default`.menuFadeStatusBar = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAnimationOptions", message: "SideMenuManager class methods deprecated.")
+    open static var menuAnimationOptions: UIViewAnimationOptions {
+        get {
+            return `default`.menuAnimationOptions
+        }
+        set {
+            `default`.menuAnimationOptions = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAnimationUsingSpringWithDamping", message: "SideMenuManager class methods deprecated.")
+    open static var menuAnimationUsingSpringWithDamping: CGFloat {
+        get {
+            return `default`.menuAnimationUsingSpringWithDamping
+        }
+        set {
+            `default`.menuAnimationUsingSpringWithDamping = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAnimationInitialSpringVelocity", message: "SideMenuManager class methods deprecated.")
+    open static var menuAnimationInitialSpringVelocity: CGFloat {
+        get {
+            return `default`.menuAnimationInitialSpringVelocity
+        }
+        set {
+            `default`.menuAnimationInitialSpringVelocity = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuDismissOnPush", message: "SideMenuManager class methods deprecated.")
+    open static var menuDismissOnPush: Bool {
+        get {
+            return `default`.menuDismissOnPush
+        }
+        set {
+            `default`.menuDismissOnPush = newValue
+        }
+    }
+    /// -Warning: Deprecated. Use `menuPushStyle = .subMenu` instead.
+    @available(*, deprecated, renamed: "menuPushStyle", message: "Use `menuPushStyle = .subMenu` instead.")
+    open static var menuAllowSubmenus: Bool {
+        get {
+            return menuPushStyle == .subMenu
+        }
+        set {
+            if newValue {
+                menuPushStyle = .subMenu
+            }
+        }
+    }
+    /// -Warning: Deprecated. Use `menuPushStyle = .popWhenPossible` instead.
+    @available(*, deprecated, renamed: "menuPushStyle", message: "Use `menuPushStyle = .popWhenPossible` instead.")
+    open static var menuAllowPopIfPossible: Bool {
+        get {
+            return menuPushStyle == .popWhenPossible
+        }
+        set {
+            if newValue {
+                menuPushStyle = .popWhenPossible
+            }
+        }
+    }
+    /// -Warning: Deprecated. Use `menuPushStyle = .replace` instead.
+    @available(*, deprecated, renamed: "menuPushStyle", message: "Use `menuPushStyle = .replace` instead.")
+    open static var menuReplaceOnPush: Bool {
+        get {
+            return menuPushStyle == .replace
+        }
+        set {
+            if newValue {
+                menuPushStyle = .replace
+            }
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuBlurEffectStyle", message: "SideMenuManager class methods deprecated.")
+    open static var menuBlurEffectStyle: UIBlurEffectStyle? {
+        get {
+            return `default`.menuBlurEffectStyle
+        }
+        set {
+            `default`.menuBlurEffectStyle = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuLeftNavigationController", message: "SideMenuManager class methods deprecated.")
+    open static var menuLeftNavigationController: UISideMenuNavigationController? {
+        get {
+            return `default`.menuLeftNavigationController
+        }
+        set {
+            `default`.menuLeftNavigationController = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuRightNavigationController", message: "SideMenuManager class methods deprecated.")
+    open static var menuRightNavigationController: UISideMenuNavigationController? {
+        get {
+            return `default`.menuRightNavigationController
+        }
+        set {
+            `default`.menuRightNavigationController = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuLeftSwipeToDismissGesture", message: "SideMenuManager class methods deprecated.")
+    open static weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer? {
+        get {
+            return `default`.menuLeftSwipeToDismissGesture
+        }
+        set {
+            `default`.menuLeftSwipeToDismissGesture = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuRightSwipeToDismissGesture", message: "SideMenuManager class methods deprecated.")
+    open static weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer? {
+        get {
+            return `default`.menuRightSwipeToDismissGesture
+        }
+        set {
+            `default`.menuRightSwipeToDismissGesture = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuEnableSwipeGestures", message: "SideMenuManager class methods deprecated.")
+    open static var menuEnableSwipeGestures: Bool {
+        get {
+            return `default`.menuEnableSwipeGestures
+        }
+        set {
+            `default`.menuEnableSwipeGestures = newValue
+        }
+    }
+    @available(*, deprecated, renamed: "default.menuAddScreenEdgePanGesturesToPresent", message: "SideMenuManager class methods deprecated.")
+    @discardableResult open class func menuAddScreenEdgePanGesturesToPresent(toView: UIView, forMenu:UIRectEdge? = nil) -> [UIScreenEdgePanGestureRecognizer] {
+        return `default`.menuAddScreenEdgePanGesturesToPresent(toView: toView, forMenu: forMenu)
+    }
+    @available(*, deprecated, renamed: "default.menuAddPanGestureToPresent", message: "SideMenuManager class methods deprecated.")
+    @discardableResult open class func menuAddPanGestureToPresent(toView: UIView) -> UIPanGestureRecognizer {
+        return `default`.menuAddPanGestureToPresent(toView: toView)
+    }
+}
diff --git a/iOS/Pods/SideMenu/Pod/Classes/SideMenuTransition.swift b/iOS/Pods/SideMenu/Pod/Classes/SideMenuTransition.swift
new file mode 100644 (file)
index 0000000..97f35c8
--- /dev/null
@@ -0,0 +1,583 @@
+//
+//  SideMenuTransition.swift
+//  Pods
+//
+//  Created by Jon Kent on 1/14/16.
+//
+//
+
+import UIKit
+
+open class SideMenuTransition: UIPercentDrivenInteractiveTransition {
+    
+    fileprivate var presenting = false
+    fileprivate var interactive = false
+    fileprivate weak var originalSuperview: UIView?
+    fileprivate weak var activeGesture: UIGestureRecognizer?
+    fileprivate var switchMenus = false {
+        didSet {
+            if switchMenus {
+                cancel()
+            }
+        }
+    }
+    fileprivate var menuWidth: CGFloat {
+        get {
+            let overriddenWidth = menuViewController?.menuWidth ?? 0
+            if overriddenWidth > CGFloat.ulpOfOne {
+                return overriddenWidth
+            }
+            return sideMenuManager.menuWidth
+        }
+    }
+    internal weak var sideMenuManager: SideMenuManager!
+    internal weak var mainViewController: UIViewController?
+    internal weak var menuViewController: UISideMenuNavigationController? {
+        get {
+            return presentDirection == .left ? sideMenuManager.menuLeftNavigationController : sideMenuManager.menuRightNavigationController
+        }
+    }
+    internal var presentDirection: UIRectEdge = .left
+    internal weak var tapView: UIView? {
+        didSet {
+            guard let tapView = tapView else {
+                return
+            }
+            
+            tapView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
+            let exitPanGesture = UIPanGestureRecognizer()
+            exitPanGesture.addTarget(self, action:#selector(SideMenuTransition.handleHideMenuPan(_:)))
+            let exitTapGesture = UITapGestureRecognizer()
+            exitTapGesture.addTarget(self, action: #selector(SideMenuTransition.handleHideMenuTap(_:)))
+            tapView.addGestureRecognizer(exitPanGesture)
+            tapView.addGestureRecognizer(exitTapGesture)
+        }
+    }
+    internal weak var statusBarView: UIView? {
+        didSet {
+            guard let statusBarView = statusBarView else {
+                return
+            }
+            
+            statusBarView.backgroundColor = sideMenuManager.menuAnimationBackgroundColor ?? UIColor.black
+            statusBarView.isUserInteractionEnabled = false
+        }
+    }
+    
+    required public init(sideMenuManager: SideMenuManager) {
+        super.init()
+        
+        NotificationCenter.default.addObserver(self, selector:#selector(handleNotification), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
+        NotificationCenter.default.addObserver(self, selector:#selector(handleNotification), name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
+        self.sideMenuManager = sideMenuManager
+    }
+    
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
+    
+    fileprivate static var visibleViewController: UIViewController? {
+        get {
+            return getVisibleViewController(forViewController: UIApplication.shared.keyWindow?.rootViewController)
+        }
+    }
+    
+    fileprivate class func getVisibleViewController(forViewController: UIViewController?) -> UIViewController? {
+        if let navigationController = forViewController as? UINavigationController {
+            return getVisibleViewController(forViewController: navigationController.visibleViewController)
+        }
+        if let tabBarController = forViewController as? UITabBarController {
+            return getVisibleViewController(forViewController: tabBarController.selectedViewController)
+        }
+        if let splitViewController = forViewController as? UISplitViewController {
+            return getVisibleViewController(forViewController: splitViewController.viewControllers.last)
+        }
+        if let presentedViewController = forViewController?.presentedViewController {
+            return getVisibleViewController(forViewController: presentedViewController)
+        }
+        
+        return forViewController
+    }
+    
+    @objc internal func handlePresentMenuLeftScreenEdge(_ edge: UIScreenEdgePanGestureRecognizer) {
+        presentDirection = .left
+        handlePresentMenuPan(edge)
+    }
+    
+    @objc internal func handlePresentMenuRightScreenEdge(_ edge: UIScreenEdgePanGestureRecognizer) {
+        presentDirection = .right
+        handlePresentMenuPan(edge)
+    }
+    
+    @objc internal func handlePresentMenuPan(_ pan: UIPanGestureRecognizer) {
+        if activeGesture == nil {
+            activeGesture = pan
+        } else if pan != activeGesture {
+            pan.isEnabled = false
+            pan.isEnabled = true
+            return
+        } else if pan.state != .began && pan.state != .changed {
+            activeGesture = nil
+        }
+        
+        // how much distance have we panned in reference to the parent view?
+        guard let view = mainViewController?.view ?? pan.view else {
+            return
+        }
+        
+        let transform = view.transform
+        view.transform = .identity
+        let translation = pan.translation(in: pan.view!)
+        view.transform = transform
+        
+        // do some math to translate this to a percentage based value
+        if !interactive {
+            if translation.x == 0 {
+                return // not sure which way the user is swiping yet, so do nothing
+            }
+            
+            if !(pan is UIScreenEdgePanGestureRecognizer) {
+                presentDirection = translation.x > 0 ? .left : .right
+            }
+            
+            if let menuViewController = menuViewController, let visibleViewController = SideMenuTransition.visibleViewController {
+                interactive = true
+                visibleViewController.present(menuViewController, animated: true, completion: nil)
+            } else {
+                return
+            }
+        }
+        
+        let direction: CGFloat = presentDirection == .left ? 1 : -1
+        let distance = translation.x / menuWidth
+        // now lets deal with different states that the gesture recognizer sends
+        switch (pan.state) {
+        case .began, .changed:
+            if pan is UIScreenEdgePanGestureRecognizer {
+                update(min(distance * direction, 1))
+            } else if distance > 0 && presentDirection == .right && sideMenuManager.menuLeftNavigationController != nil {
+                presentDirection = .left
+                switchMenus = true
+            } else if distance < 0 && presentDirection == .left && sideMenuManager.menuRightNavigationController != nil {
+                presentDirection = .right
+                switchMenus = true
+            } else {
+                update(min(distance * direction, 1))
+            }
+        default:
+            interactive = false
+            view.transform = .identity
+            let velocity = pan.velocity(in: pan.view!).x * direction
+            view.transform = transform
+            if velocity >= 100 || velocity >= -50 && abs(distance) >= 0.5 {
+                // bug workaround: animation briefly resets after call to finishInteractiveTransition() but before animateTransition completion is called.
+                if ProcessInfo().operatingSystemVersion.majorVersion == 8 && percentComplete > 1 - CGFloat.ulpOfOne {
+                    update(0.9999)
+                }
+                finish()
+            } else {
+                cancel()
+            }
+        }
+    }
+    
+    @objc internal func handleHideMenuPan(_ pan: UIPanGestureRecognizer) {
+        if activeGesture == nil {
+            activeGesture = pan
+        } else if pan != activeGesture {
+            pan.isEnabled = false
+            pan.isEnabled = true
+            return
+        }
+        
+        let translation = pan.translation(in: pan.view!)
+        let direction:CGFloat = presentDirection == .left ? -1 : 1
+        let distance = translation.x / menuWidth * direction
+        
+        switch (pan.state) {
+            
+        case .began:
+            interactive = true
+            mainViewController?.dismiss(animated: true, completion: nil)
+        case .changed:
+            update(max(min(distance, 1), 0))
+        default:
+            interactive = false
+            let velocity = pan.velocity(in: pan.view!).x * direction
+            if velocity >= 100 || velocity >= -50 && distance >= 0.5 {
+                // bug workaround: animation briefly resets after call to finishInteractiveTransition() but before animateTransition completion is called.
+                if ProcessInfo().operatingSystemVersion.majorVersion == 8 && percentComplete > 1 - CGFloat.ulpOfOne {
+                    update(0.9999)
+                }
+                finish()
+                activeGesture = nil
+            } else {
+                cancel()
+                activeGesture = nil
+            }
+        }
+    }
+    
+    @objc internal func handleHideMenuTap(_ tap: UITapGestureRecognizer) {
+        menuViewController?.dismiss(animated: true, completion: nil)
+    }
+    
+    @discardableResult internal func hideMenuStart() -> SideMenuTransition {
+        let menuView = menuViewController?.view
+        let mainView = mainViewController?.view
+      
+        mainView?.transform = .identity
+        mainView?.alpha = 1
+        mainView?.frame.origin = .zero
+        menuView?.transform = .identity
+        menuView?.frame.origin.y = 0
+        menuView?.frame.size.width = menuWidth
+        menuView?.frame.size.height = mainView?.frame.height ?? 0 // in case status bar height changed
+        var statusBarFrame = UIApplication.shared.statusBarFrame
+        let statusBarOffset = SideMenuManager.appScreenRect.size.height - (mainView?.frame.maxY ?? 0)
+        // For in-call status bar, height is normally 40, which overlaps view. Instead, calculate height difference
+        // of view and set height to fill in remaining space.
+        if statusBarOffset >= CGFloat.ulpOfOne {
+            statusBarFrame.size.height = statusBarOffset
+        }
+        statusBarView?.frame = statusBarFrame
+        statusBarView?.alpha = 0
+        
+        switch sideMenuManager.menuPresentMode {
+            
+        case .viewSlideOut:
+            menuView?.alpha = 1 - sideMenuManager.menuAnimationFadeStrength
+            menuView?.frame.origin.x = presentDirection == .left ? 0 : (mainView?.frame.width ?? 0) - menuWidth
+            menuView?.transform = CGAffineTransform(scaleX: sideMenuManager.menuAnimationTransformScaleFactor, y: sideMenuManager.menuAnimationTransformScaleFactor)
+            
+        case .viewSlideInOut:
+            menuView?.alpha = 1
+            menuView?.frame.origin.x = presentDirection == .left ? -menuView!.frame.width : mainView!.frame.width
+            
+        case .menuSlideIn:
+            menuView?.alpha = 1
+            menuView?.frame.origin.x = presentDirection == .left ? -menuView!.frame.width : mainView!.frame.width
+            
+        case .menuDissolveIn:
+            menuView?.alpha = 0
+            menuView?.frame.origin.x = presentDirection == .left ? 0 : mainView!.frame.width - menuWidth
+        }
+        
+        return self
+    }
+    
+    @discardableResult internal func hideMenuComplete() -> SideMenuTransition {
+        let menuView = menuViewController?.view
+        let mainView = mainViewController?.view
+
+        tapView?.removeFromSuperview()
+        statusBarView?.removeFromSuperview()
+        mainView?.motionEffects.removeAll()
+        mainView?.layer.shadowOpacity = 0
+        menuView?.layer.shadowOpacity = 0
+        if let topNavigationController = mainViewController as? UINavigationController {
+            topNavigationController.interactivePopGestureRecognizer!.isEnabled = true
+        }
+        if let originalSuperview = originalSuperview, let mainView = mainViewController?.view {
+            originalSuperview.addSubview(mainView)
+            let y = originalSuperview.bounds.height - mainView.frame.size.height
+            mainView.frame.origin.y = max(y, 0)
+        }
+        
+        originalSuperview = nil
+        mainViewController = nil
+        
+        return self
+    }
+    
+    @discardableResult internal func presentMenuStart() -> SideMenuTransition {
+        let menuView = menuViewController?.view
+        let mainView = mainViewController?.view
+        
+        menuView?.alpha = 1
+        menuView?.transform = .identity
+        menuView?.frame.size.width = menuWidth
+        let size = SideMenuManager.appScreenRect.size
+        menuView?.frame.origin.x = presentDirection == .left ? 0 : size.width - menuWidth
+        mainView?.transform = .identity
+        mainView?.frame.size.width = size.width
+        let statusBarOffset = size.height - (menuView?.bounds.height ?? 0)
+        mainView?.bounds.size.height = size.height - max(statusBarOffset, 0)
+        mainView?.frame.origin.y = 0
+        var statusBarFrame = UIApplication.shared.statusBarFrame
+        // For in-call status bar, height is normally 40, which overlaps view. Instead, calculate height difference
+        // of view and set height to fill in remaining space.
+        if statusBarOffset >= CGFloat.ulpOfOne {
+            statusBarFrame.size.height = statusBarOffset
+        }
+        tapView?.transform = .identity
+        tapView?.bounds = mainView!.bounds
+        statusBarView?.frame = statusBarFrame
+        statusBarView?.alpha = 1
+        
+        switch sideMenuManager.menuPresentMode {
+            
+        case .viewSlideOut, .viewSlideInOut:
+            mainView?.layer.shadowColor = sideMenuManager.menuShadowColor.cgColor
+            mainView?.layer.shadowRadius = sideMenuManager.menuShadowRadius
+            mainView?.layer.shadowOpacity = sideMenuManager.menuShadowOpacity
+            mainView?.layer.shadowOffset = CGSize(width: 0, height: 0)
+            let direction:CGFloat = presentDirection == .left ? 1 : -1
+            mainView?.frame.origin.x = direction * (menuView!.frame.width)
+            
+        case .menuSlideIn, .menuDissolveIn:
+            if sideMenuManager.menuBlurEffectStyle == nil {
+                menuView?.layer.shadowColor = sideMenuManager.menuShadowColor.cgColor
+                menuView?.layer.shadowRadius = sideMenuManager.menuShadowRadius
+                menuView?.layer.shadowOpacity = sideMenuManager.menuShadowOpacity
+                menuView?.layer.shadowOffset = CGSize(width: 0, height: 0)
+            }
+            mainView?.frame.origin.x = 0
+        }
+        
+        if sideMenuManager.menuPresentMode != .viewSlideOut {
+            mainView?.transform = CGAffineTransform(scaleX: sideMenuManager.menuAnimationTransformScaleFactor, y: sideMenuManager.menuAnimationTransformScaleFactor)
+            if sideMenuManager.menuAnimationTransformScaleFactor > 1 {
+                tapView?.transform = mainView!.transform
+            }
+            mainView?.alpha = 1 - sideMenuManager.menuAnimationFadeStrength
+        }
+        
+        return self
+    }
+    
+    @discardableResult internal func presentMenuComplete() -> SideMenuTransition {
+        switch sideMenuManager.menuPresentMode {
+        case .menuSlideIn, .menuDissolveIn, .viewSlideInOut:
+            if let mainView = mainViewController?.view, sideMenuManager.menuParallaxStrength != 0 {
+                let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
+                horizontal.minimumRelativeValue = -sideMenuManager.menuParallaxStrength
+                horizontal.maximumRelativeValue = sideMenuManager.menuParallaxStrength
+                
+                let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis)
+                vertical.minimumRelativeValue = -sideMenuManager.menuParallaxStrength
+                vertical.maximumRelativeValue = sideMenuManager.menuParallaxStrength
+                
+                let group = UIMotionEffectGroup()
+                group.motionEffects = [horizontal, vertical]
+                mainView.addMotionEffect(group)
+            }
+        case .viewSlideOut: break;
+        }
+        if let topNavigationController = mainViewController as? UINavigationController {
+            topNavigationController.interactivePopGestureRecognizer!.isEnabled = false
+        }
+        
+        return self
+    }
+    
+    @objc internal func handleNotification(notification: NSNotification) {
+        guard menuViewController?.presentedViewController == nil &&
+            menuViewController?.presentingViewController != nil else {
+                return
+        }
+        
+        if let originalSuperview = originalSuperview, let mainViewController = mainViewController {
+            originalSuperview.addSubview(mainViewController.view)
+        }
+        
+        if notification.name == NSNotification.Name.UIApplicationDidEnterBackground {
+            hideMenuStart().hideMenuComplete()
+            menuViewController?.dismiss(animated: false, completion: nil)
+            return
+        }
+        
+        UIView.animate(withDuration: sideMenuManager.menuAnimationDismissDuration,
+                       delay: 0,
+                       usingSpringWithDamping: sideMenuManager.menuAnimationUsingSpringWithDamping,
+                       initialSpringVelocity: sideMenuManager.menuAnimationInitialSpringVelocity,
+                       options: sideMenuManager.menuAnimationOptions,
+                       animations: {
+                        self.hideMenuStart()
+        }) { (finished) -> Void in
+            self.hideMenuComplete()
+            self.menuViewController?.dismiss(animated: false, completion: nil)
+        }
+    }
+    
+}
+
+extension SideMenuTransition: UIViewControllerAnimatedTransitioning {
+    
+    // animate a change from one viewcontroller to another
+    open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
+        
+        // get reference to our fromView, toView and the container view that we should perform the transition in
+        let container = transitionContext.containerView
+        // prevent any other menu gestures from firing
+        container.isUserInteractionEnabled = false
+        
+        if let menuBackgroundColor = sideMenuManager.menuAnimationBackgroundColor {
+            container.backgroundColor = menuBackgroundColor
+        }
+        
+        let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
+        let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
+        
+        // assign references to our menu view controller and the 'bottom' view controller from the tuple
+        // remember that our menuViewController will alternate between the from and to view controller depending if we're presenting or dismissing
+        mainViewController = presenting ? fromViewController : toViewController
+        
+        let menuView = menuViewController!.view!
+        let topView = mainViewController!.view!
+        
+        // prepare menu items to slide in
+        if presenting {
+            originalSuperview = topView.superview
+            
+            // add the both views to our view controller
+            switch sideMenuManager.menuPresentMode {
+            case .viewSlideOut, .viewSlideInOut:
+                container.addSubview(menuView)
+                container.addSubview(topView)
+            case .menuSlideIn, .menuDissolveIn:
+                container.addSubview(topView)
+                container.addSubview(menuView)
+            }
+
+            if sideMenuManager.menuFadeStatusBar {
+                let statusBarView = UIView()
+                self.statusBarView = statusBarView
+                container.addSubview(statusBarView)
+            }
+            
+            hideMenuStart()
+        }
+        
+        let animate = {
+            if self.presenting {
+                self.presentMenuStart()
+            } else {
+                self.hideMenuStart()
+            }
+        }
+        
+        let complete = {
+            container.isUserInteractionEnabled = true
+            
+            // tell our transitionContext object that we've finished animating
+            if transitionContext.transitionWasCancelled {
+                let viewControllerForPresentedMenu = self.mainViewController
+                
+                if self.presenting {
+                    self.hideMenuComplete()
+                } else {
+                    self.presentMenuComplete()
+                }
+                
+                transitionContext.completeTransition(false)
+                
+                if self.switchMenus {
+                    self.switchMenus = false
+                    viewControllerForPresentedMenu?.present(self.menuViewController!, animated: true, completion: nil)
+                }
+                
+                return
+            }
+            
+            if self.presenting {
+                self.presentMenuComplete()
+                transitionContext.completeTransition(true)
+                switch self.sideMenuManager.menuPresentMode {
+                case .viewSlideOut, .viewSlideInOut:
+                    container.addSubview(topView)
+                case .menuSlideIn, .menuDissolveIn:
+                    container.insertSubview(topView, at: 0)
+                }
+                if !self.sideMenuManager.menuPresentingViewControllerUserInteractionEnabled {
+                    let tapView = UIView()
+                    container.insertSubview(tapView, aboveSubview: topView)
+                    tapView.bounds = container.bounds
+                    tapView.center = topView.center
+                    if self.sideMenuManager.menuAnimationTransformScaleFactor > 1 {
+                        tapView.transform = topView.transform
+                    }
+                    self.tapView = tapView
+                }
+                if let statusBarView = self.statusBarView {
+                    container.bringSubview(toFront: statusBarView)
+                }
+                
+                return
+            }
+            
+            self.hideMenuComplete()
+            transitionContext.completeTransition(true)
+            menuView.removeFromSuperview()
+        }
+        
+        // perform the animation!
+        let duration = transitionDuration(using: transitionContext)
+        if interactive {
+            UIView.animate(withDuration: duration,
+                           delay: duration, // HACK: If zero, the animation briefly flashes in iOS 11. UIViewPropertyAnimators (iOS 10+) may resolve this.
+                           options: .curveLinear,
+                           animations: {
+                            animate()
+            }, completion: { (finished) in
+                complete()
+            })
+        } else {
+            UIView.animate(withDuration: duration,
+                           delay: 0,
+                           usingSpringWithDamping: sideMenuManager.menuAnimationUsingSpringWithDamping,
+                           initialSpringVelocity: sideMenuManager.menuAnimationInitialSpringVelocity,
+                           options: sideMenuManager.menuAnimationOptions,
+                           animations: {
+                            animate()
+            }) { (finished) -> Void in
+                complete()
+            }
+        }
+    }
+    
+    // return how many seconds the transiton animation will take
+    open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
+        if interactive {
+            return sideMenuManager.menuAnimationCompleteGestureDuration
+        }
+        return presenting ? sideMenuManager.menuAnimationPresentDuration : sideMenuManager.menuAnimationDismissDuration
+    }
+    
+    open override func update(_ percentComplete: CGFloat) {
+        guard !switchMenus else {
+            return
+        }
+        
+        super.update(percentComplete)
+    }
+    
+}
+
+extension SideMenuTransition: UIViewControllerTransitioningDelegate {
+    
+    // return the animator when presenting a viewcontroller
+    // rememeber that an animator (or animation controller) is any object that aheres to the UIViewControllerAnimatedTransitioning protocol
+    open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
+        self.presenting = true
+        presentDirection = presented == sideMenuManager.menuLeftNavigationController ? .left : .right
+        return self
+    }
+    
+    // return the animator used when dismissing from a viewcontroller
+    open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
+        presenting = false
+        return self
+    }
+    
+    open func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
+        // if our interactive flag is true, return the transition manager object
+        // otherwise return nil
+        return interactive ? self : nil
+    }
+    
+    open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
+        return interactive ? self : nil
+    }
+    
+}
diff --git a/iOS/Pods/SideMenu/Pod/Classes/UISideMenuNavigationController.swift b/iOS/Pods/SideMenu/Pod/Classes/UISideMenuNavigationController.swift
new file mode 100644 (file)
index 0000000..0c6963c
--- /dev/null
@@ -0,0 +1,343 @@
+//
+//  UISideMenuNavigationController.swift
+//
+//  Created by Jon Kent on 1/14/16.
+//  Copyright © 2016 Jon Kent. All rights reserved.
+//
+
+import UIKit
+
+@objc public protocol UISideMenuNavigationControllerDelegate {
+    @objc optional func sideMenuWillAppear(menu: UISideMenuNavigationController, animated: Bool)
+    @objc optional func sideMenuDidAppear(menu: UISideMenuNavigationController, animated: Bool)
+    @objc optional func sideMenuWillDisappear(menu: UISideMenuNavigationController, animated: Bool)
+    @objc optional func sideMenuDidDisappear(menu: UISideMenuNavigationController, animated: Bool)
+}
+
+@objcMembers
+open class UISideMenuNavigationController: UINavigationController {
+    
+    fileprivate weak var foundDelegate: UISideMenuNavigationControllerDelegate?
+    fileprivate weak var activeDelegate: UISideMenuNavigationControllerDelegate? {
+        get {
+            guard !view.isHidden else {
+                return nil
+            }
+            
+            return sideMenuDelegate ?? foundDelegate ?? findDelegate(forViewController: presentingViewController)
+        }
+    }
+    fileprivate func findDelegate(forViewController: UIViewController?) -> UISideMenuNavigationControllerDelegate? {
+        if let navigationController = forViewController as? UINavigationController {
+            return findDelegate(forViewController: navigationController.topViewController)
+        }
+        if let tabBarController = forViewController as? UITabBarController {
+            return findDelegate(forViewController: tabBarController.selectedViewController)
+        }
+        if let splitViewController = forViewController as? UISplitViewController {
+            return findDelegate(forViewController: splitViewController.viewControllers.last)
+        }
+        
+        foundDelegate = forViewController as? UISideMenuNavigationControllerDelegate
+        return foundDelegate
+    }
+    fileprivate var usingInterfaceBuilder = false
+    internal var locked = false
+    internal var originalMenuBackgroundColor: UIColor?
+    internal var transition: SideMenuTransition {
+        get {
+            return sideMenuManager.transition
+        }
+    }
+    
+    /// Delegate for receiving appear and disappear related events. If `nil` the visible view controller that displays a `UISideMenuNavigationController` automatically receives these events.
+    open weak var sideMenuDelegate: UISideMenuNavigationControllerDelegate?
+    
+    /// SideMenuManager instance associated with this menu. Default is `SideMenuManager.default`. This property cannot be changed after the menu has loaded.
+    open weak var sideMenuManager: SideMenuManager! = SideMenuManager.default {
+        didSet {
+            if locked && oldValue != nil {
+                print("SideMenu Warning: a menu's sideMenuManager property cannot be changed after it has loaded.")
+                sideMenuManager = oldValue
+            }
+        }
+    }
+    
+    /// Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is zero. When zero, `sideMenuManager.menuWidth` is used. This property cannot be changed while the isHidden property is false.
+    @IBInspectable open var menuWidth: CGFloat = 0 {
+        didSet {
+            if !isHidden && oldValue != menuWidth {
+                print("SideMenu Warning: a menu's width property can only be changed when it is hidden.")
+                menuWidth = oldValue
+            }
+        }
+    }
+    
+    /// Whether the menu appears on the right or left side of the screen. Right is the default. This property cannot be changed after the menu has loaded.
+    @IBInspectable open var leftSide: Bool = false {
+        didSet {
+            if locked && leftSide != oldValue {
+                print("SideMenu Warning: a menu's leftSide property cannot be changed after it has loaded.")
+                leftSide = oldValue
+            }
+        }
+    }
+    
+    /// Indicates if the menu is anywhere in the view hierarchy, even if covered by another view controller.
+    open var isHidden: Bool {
+        get {
+            return self.presentingViewController == nil
+        }
+    }
+    
+    #if !STFU_SIDEMENU
+    // This override prevents newbie developers from creating black/blank menus and opening newbie issues.
+    // If you would like to remove this override, define STFU_SIDEMENU in the Active Compilation Conditions of your .plist file.
+    // Sorry for the inconvenience experienced developers :(
+    @available(*, unavailable, renamed: "init(rootViewController:)")
+    public init() {
+        fatalError("init is not available")
+    }
+    
+    public override init(rootViewController: UIViewController) {
+        super.init(rootViewController: rootViewController)
+    }
+
+    public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
+        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
+    }
+    
+    public required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+    }
+    #endif
+    
+    open override func awakeFromNib() {
+        super.awakeFromNib()
+        
+        usingInterfaceBuilder = true
+    }
+    
+    override open func viewDidLoad() {
+        super.viewDidLoad()
+        
+        if !locked && usingInterfaceBuilder {
+            if leftSide {
+                sideMenuManager.menuLeftNavigationController = self
+            } else {
+                sideMenuManager.menuRightNavigationController = self
+            }
+        }
+    }
+    
+    open override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        
+        // Dismiss keyboard to prevent weird keyboard animations from occurring during transition
+        presentingViewController?.view.endEditing(true)
+        
+        foundDelegate = nil
+        activeDelegate?.sideMenuWillAppear?(menu: self, animated: animated)
+    }
+    
+    override open func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        
+        // We had presented a view before, so lets dismiss ourselves as already acted upon
+        if view.isHidden {
+            transition.hideMenuComplete()
+            dismiss(animated: false, completion: { () -> Void in
+                self.view.isHidden = false
+            })
+            
+            return
+        }
+        
+        activeDelegate?.sideMenuDidAppear?(menu: self, animated: animated)
+        
+        #if !STFU_SIDEMENU
+        if topViewController == nil {
+            print("SideMenu Warning: the menu doesn't have a view controller to show! UISideMenuNavigationController needs a view controller to display just like a UINavigationController.")
+        }
+        #endif
+    }
+    
+    override open func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+        
+        // When presenting a view controller from the menu, the menu view gets moved into another transition view above our transition container
+        // which can break the visual layout we had before. So, we move the menu view back to its original transition view to preserve it.
+        if !isBeingDismissed {
+            guard let sideMenuManager = sideMenuManager else {
+                return
+            }
+            
+            if let mainView = transition.mainViewController?.view {
+                switch sideMenuManager.menuPresentMode {
+                case .viewSlideOut, .viewSlideInOut:
+                    mainView.superview?.insertSubview(view, belowSubview: mainView)
+                case .menuSlideIn, .menuDissolveIn:
+                    if let tapView = transition.tapView {
+                        mainView.superview?.insertSubview(view, aboveSubview: tapView)
+                    } else {
+                        mainView.superview?.insertSubview(view, aboveSubview: mainView)
+                    }
+                }
+            }
+            
+            // We're presenting a view controller from the menu, so we need to hide the menu so it isn't showing when the presented view is dismissed.
+            UIView.animate(withDuration: animated ? sideMenuManager.menuAnimationDismissDuration : 0,
+                           delay: 0,
+                           usingSpringWithDamping: sideMenuManager.menuAnimationUsingSpringWithDamping,
+                           initialSpringVelocity: sideMenuManager.menuAnimationInitialSpringVelocity,
+                           options: sideMenuManager.menuAnimationOptions,
+                           animations: {
+                            self.transition.hideMenuStart()
+                            self.activeDelegate?.sideMenuWillDisappear?(menu: self, animated: animated)
+            }) { (finished) -> Void in
+                self.activeDelegate?.sideMenuDidDisappear?(menu: self, animated: animated)
+                self.view.isHidden = true
+            }
+            
+            return
+        }
+        
+        activeDelegate?.sideMenuWillDisappear?(menu: self, animated: animated)
+    }
+    
+    override open func viewDidDisappear(_ animated: Bool) {
+        super.viewDidDisappear(animated)
+        
+        // Work-around: if the menu is dismissed without animation the transition logic is never called to restore the
+        // the view hierarchy leaving the screen black/empty. This is because the transition moves views within a container
+        // view, but dismissing without animation removes the container view before the original hierarchy is restored.
+        // This check corrects that.
+        if let sideMenuDelegate = activeDelegate as? UIViewController, sideMenuDelegate.view.window == nil {
+            transition.hideMenuStart().hideMenuComplete()
+        }
+        
+        activeDelegate?.sideMenuDidDisappear?(menu: self, animated: animated)
+        
+        // Clear selecton on UITableViewControllers when reappearing using custom transitions
+        guard let tableViewController = topViewController as? UITableViewController,
+            let tableView = tableViewController.tableView,
+            let indexPaths = tableView.indexPathsForSelectedRows,
+            tableViewController.clearsSelectionOnViewWillAppear else {
+            return
+        }
+        
+        for indexPath in indexPaths {
+            tableView.deselectRow(at: indexPath, animated: false)
+        }
+    }
+    
+    override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
+        super.viewWillTransition(to: size, with: coordinator)
+        
+        // Don't bother resizing if the view isn't visible
+        guard !view.isHidden else {
+            return
+        }
+        
+        NotificationCenter.default.removeObserver(self.transition, name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
+        coordinator.animate(alongsideTransition: { (context) in
+            self.transition.presentMenuStart()
+        }) { (context) in
+            NotificationCenter.default.addObserver(self.transition, selector:#selector(SideMenuTransition.handleNotification), name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
+        }
+    }
+    
+    override open func pushViewController(_ viewController: UIViewController, animated: Bool) {
+        guard let sideMenuManager = sideMenuManager, viewControllers.count > 0 && sideMenuManager.menuPushStyle != .subMenu else {
+            // NOTE: pushViewController is called by init(rootViewController: UIViewController)
+            // so we must perform the normal super method in this case.
+            super.pushViewController(viewController, animated: animated)
+            return
+        }
+
+        let splitViewController = presentingViewController as? UISplitViewController
+        let tabBarController = presentingViewController as? UITabBarController
+        let potentialNavigationController = (splitViewController?.viewControllers.first ?? tabBarController?.selectedViewController) ?? presentingViewController
+        guard let navigationController = potentialNavigationController as? UINavigationController else {
+            print("SideMenu Warning: attempt to push a View Controller from \(String(describing: potentialNavigationController.self)) where its navigationController == nil. It must be embedded in a Navigation Controller for this to work.")
+            return
+        }
+        
+        let activeDelegate = self.activeDelegate
+        foundDelegate = nil
+        
+        // To avoid overlapping dismiss & pop/push calls, create a transaction block where the menu
+        // is dismissed after showing the appropriate screen
+        CATransaction.begin()
+        if sideMenuManager.menuDismissOnPush {
+            let animated = animated || sideMenuManager.menuAlwaysAnimate
+            
+            CATransaction.setCompletionBlock( { () -> Void in
+                activeDelegate?.sideMenuDidDisappear?(menu: self, animated: animated)
+                if !animated {
+                    self.transition.hideMenuStart().hideMenuComplete()
+                }
+                self.dismiss(animated: animated, completion: nil)
+            })
+        
+            if animated {
+                let areAnimationsEnabled = UIView.areAnimationsEnabled
+                UIView.setAnimationsEnabled(true)
+                UIView.animate(withDuration: sideMenuManager.menuAnimationDismissDuration,
+                               delay: 0,
+                               usingSpringWithDamping: sideMenuManager.menuAnimationUsingSpringWithDamping,
+                               initialSpringVelocity: sideMenuManager.menuAnimationInitialSpringVelocity,
+                               options: sideMenuManager.menuAnimationOptions,
+                               animations: {
+                                activeDelegate?.sideMenuWillDisappear?(menu: self, animated: animated)
+                                self.transition.hideMenuStart()
+                })
+                UIView.setAnimationsEnabled(areAnimationsEnabled)
+            }
+        }
+        
+        if let lastViewController = navigationController.viewControllers.last, !sideMenuManager.menuAllowPushOfSameClassTwice && type(of: lastViewController) == type(of: viewController) {
+            CATransaction.commit()
+            return
+        }
+        
+        switch sideMenuManager.menuPushStyle {
+        case .subMenu, .defaultBehavior: break // .subMenu handled earlier, .defaultBehavior falls through to end
+        case .popWhenPossible:
+            for subViewController in navigationController.viewControllers.reversed() {
+                if type(of: subViewController) == type(of: viewController) {
+                    navigationController.popToViewController(subViewController, animated: animated)
+                    CATransaction.commit()
+                    return
+                }
+            }
+        case .preserve, .preserveAndHideBackButton:
+            var viewControllers = navigationController.viewControllers
+            let filtered = viewControllers.filter { preservedViewController in type(of: preservedViewController) == type(of: viewController) }
+            if let preservedViewController = filtered.last {
+                viewControllers = viewControllers.filter { subViewController in subViewController !== preservedViewController }
+                if sideMenuManager.menuPushStyle == .preserveAndHideBackButton {
+                    preservedViewController.navigationItem.hidesBackButton = true
+                }
+                viewControllers.append(preservedViewController)
+                navigationController.setViewControllers(viewControllers, animated: animated)
+                CATransaction.commit()
+                return
+            }
+            if sideMenuManager.menuPushStyle == .preserveAndHideBackButton {
+                viewController.navigationItem.hidesBackButton = true
+            }
+        case .replace:
+            viewController.navigationItem.hidesBackButton = true
+            navigationController.setViewControllers([viewController], animated: animated)
+            CATransaction.commit()
+            return
+        }
+        
+        navigationController.pushViewController(viewController, animated: animated)
+        CATransaction.commit()
+    }
+
+}
+
+
diff --git a/iOS/Pods/SideMenu/Pod/Classes/UITableViewVibrantCell.swift b/iOS/Pods/SideMenu/Pod/Classes/UITableViewVibrantCell.swift
new file mode 100644 (file)
index 0000000..2ececb7
--- /dev/null
@@ -0,0 +1,61 @@
+//
+//  UITableViewVibrantCell.swift
+//  Pods
+//
+//  Created by Jon Kent on 1/14/16.
+//
+//
+
+import UIKit
+
+open class UITableViewVibrantCell: UITableViewCell {
+    
+    fileprivate var vibrancyView:UIVisualEffectView = UIVisualEffectView()
+    fileprivate var vibrancySelectedBackgroundView:UIVisualEffectView = UIVisualEffectView()
+    fileprivate var defaultSelectedBackgroundView:UIView?
+    open var blurEffectStyle: UIBlurEffectStyle? {
+        didSet {
+            updateBlur()
+        }
+    }
+    
+    // For registering with UITableView without subclassing otherwise dequeuing instance of the cell causes an exception
+    public override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+    }
+    
+    required public init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        
+        vibrancyView.frame = bounds
+        vibrancyView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
+        for view in subviews {
+            vibrancyView.contentView.addSubview(view)
+        }
+        addSubview(vibrancyView)
+        
+        let blurSelectionEffect = UIBlurEffect(style: .light)
+        vibrancySelectedBackgroundView.effect = blurSelectionEffect
+        defaultSelectedBackgroundView = selectedBackgroundView
+        
+        updateBlur()
+    }
+    
+    internal func updateBlur() {
+        // shouldn't be needed but backgroundColor is set to white on iPad:
+        backgroundColor = UIColor.clear
+        
+        if let blurEffectStyle = blurEffectStyle, !UIAccessibilityIsReduceTransparencyEnabled() {
+            let blurEffect = UIBlurEffect(style: blurEffectStyle)
+            vibrancyView.effect = UIVibrancyEffect(blurEffect: blurEffect)
+            
+            if selectedBackgroundView != nil && selectedBackgroundView != vibrancySelectedBackgroundView {
+                vibrancySelectedBackgroundView.contentView.addSubview(selectedBackgroundView!)
+                selectedBackgroundView = vibrancySelectedBackgroundView
+            }
+        } else {
+            vibrancyView.effect = nil
+            selectedBackgroundView = defaultSelectedBackgroundView
+        }
+    }
+}
diff --git a/iOS/Pods/SideMenu/README.md b/iOS/Pods/SideMenu/README.md
new file mode 100644 (file)
index 0000000..edd0ee9
--- /dev/null
@@ -0,0 +1,342 @@
+# ▤ SideMenu
+[![Version](https://img.shields.io/cocoapods/v/SideMenu.svg?style=flat)](http://cocoapods.org/pods/SideMenu)
+[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
+[![License](https://img.shields.io/cocoapods/l/SideMenu.svg?style=flat)](http://cocoapods.org/pods/SideMenu)
+[![Platform](https://img.shields.io/cocoapods/p/SideMenu.svg?style=flat)](http://cocoapods.org/pods/SideMenu)
+
+### If you like SideMenu, give it a ★ at the top right of its [GitHub](https://github.com/jonkykong/SideMenu) page.
+#### Using SideMenu in your app? [Send](mailto:yo@massappeal.co?subject=SideMenu%20in%20action!) me a link to your app in the app store!
+
+> Hi, I'm Jon Kent and I am an iOS designer, developer, and mobile strategist. I love coffee and play the drums.
+> * [**Hire me**](mailto:yo@massappeal.co?subject=Let's%20build%20something%20amazing) to help you make cool stuff. *Note: If you're having a problem with SideMenu, please open an [issue](https://github.com/jonkykong/SideMenu/issues/new) and do not email me.*
+> * Check out my [website](http://massappeal.co) to see some of my other projects.
+> * Building and maintaining this free library takes time. Help keep me awake and buy me a coffee ☕️ via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=contact%40jonkent%2eme&lc=US&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted).
+
+## Overview
+
+SideMenu is a simple and versatile side menu control written in Swift.
+- [x] **It can be implemented in storyboard without a single line of [code](#code-less-storyboard-implementation).**
+- [x] Four standard animation styles to choose from (there's even a parallax effect if you want to get weird).
+- [x] Highly customizable without needing to write tons of custom code.
+- [x] Supports continuous swiping between side menus on boths sides in a single gesture.
+- [x] Global menu configuration. Set-up once and be done for all screens.
+- [x] Menus can be presented and dismissed the same as any other view controller since this control uses [custom transitions](https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html).
+- [x] Animations use your view controllers, not snapshots.
+- [x] Properly handles screen rotation and in-call status bar height changes.
+
+Check out the example project to see it in action!
+### Preview Samples
+| Slide Out | Slide In | Dissolve | Slide In + Out |
+| --- | --- | --- | --- |
+| ![](etc/SlideOut.gif) | ![](etc/SlideIn.gif) | ![](etc/Dissolve.gif) | ![](etc/InOut.gif) |
+
+## Requirements
+- [x] Xcode 9.
+- [x] Swift 4.
+- [x] iOS 8 or higher.
+
+## Installation
+### CocoaPods
+
+[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
+
+```bash
+$ gem install cocoapods
+```
+
+To integrate SideMenu 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 'SideMenu'
+
+# For Swift 3 (no longer maintained), use:
+# pod 'SideMenu', '~> 2.3.4'
+```
+
+Then, run the following command:
+
+```bash
+$ pod install
+```
+
+### 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 SideMenu into your Xcode project using Carthage, specify it in your `Cartfile`:
+
+```ogdl
+github "jonkykong/SideMenu" "master"
+```
+
+## Usage
+### Code-less Storyboard Implementation
+1. Create a Navigation Controller for a side menu. Set the `Custom Class` of the Navigation Controller to be `UISideMenuNavigationController` in the **Identity Inspector**. Set the `Module` to `SideMenu` (ignore this step if you've manually added SideMenu to your project). Create a Root View Controller for the Navigation Controller (shown as a UITableViewController below). Set up any Triggered Segues you want in that view controller.
+![](etc/Screenshot1.png)
+
+2. Set the `Left Side` property of the `UISideMenuNavigationController` to On if you want it to appear from the left side of the screen, or Off/Default if you want it to appear from the right side.
+![](etc/Screenshot2.png)
+
+3. Add a UIButton or UIBarButton to a view controller that you want to display the menu from. Set that button's Triggered Segues action to modally present the Navigation Controller from step 1.
+![](etc/Screenshot3.png)
+
+That's it. *Note: you can only enable gestures in code.*
+### Code Implementation
+First:
+```swift
+import SideMenu
+```
+
+In your view controller's `viewDidLoad` event, do something like this (**IMPORTANT: If you're seeing a black menu when you use gestures, read this section carefully!**):
+``` swift
+// Define the menus
+let menuLeftNavigationController = UISideMenuNavigationController(rootViewController: YourViewController)
+// UISideMenuNavigationController is a subclass of UINavigationController, so do any additional configuration 
+// of it here like setting its viewControllers. If you're using storyboards, you'll want to do something like:
+// let menuLeftNavigationController = storyboard!.instantiateViewController(withIdentifier: "LeftMenuNavigationController") as! UISideMenuNavigationController
+SideMenuManager.default.menuLeftNavigationController = menuLeftNavigationController
+
+let menuRightNavigationController = UISideMenuNavigationController(rootViewController: YourViewController)
+// UISideMenuNavigationController is a subclass of UINavigationController, so do any additional configuration
+// of it here like setting its viewControllers. If you're using storyboards, you'll want to do something like:
+// let menuRightNavigationController = storyboard!.instantiateViewController(withIdentifier: "RightMenuNavigationController") as! UISideMenuNavigationController
+SideMenuManager.default.menuRightNavigationController = menuRightNavigationController
+
+// Enable gestures. The left and/or right menus must be set up above for these to work.
+// Note that these continue to work on the Navigation Controller independent of the view controller it displays!
+SideMenuManager.default.menuAddPanGestureToPresent(toView: self.navigationController!.navigationBar)
+SideMenuManager.default.menuAddScreenEdgePanGesturesToPresent(toView: self.navigationController!.view)
+```
+Then from a button, do something like this:
+``` swift
+present(SideMenuManager.default.menuLeftNavigationController!, animated: true, completion: nil)
+
+// Similarly, to dismiss a menu programmatically, you would do this:
+dismiss(animated: true, completion: nil)
+```
+That's it.
+### Customization
+#### SideMenuManager
+Just type ` SideMenuManager.default.menu...` and code completion will show you everything you can customize (for Objective-C, use `SideMenuManager.defaultManager.menu...`). Defaults values are shown below for reference:
+``` swift
+/**
+The push style of the menu.
+
+There are six modes in MenuPushStyle:
+- defaultBehavior: The view controller is pushed onto the stack.
+- popWhenPossible: If a view controller already in the stack is of the same class as the pushed view controller, the stack is instead popped back to the existing view controller. This behavior can help users from getting lost in a deep navigation stack.
+- preserve: If a view controller already in the stack is of the same class as the pushed view controller, the existing view controller is pushed to the end of the stack. This behavior is similar to a UITabBarController.
+- preserveAndHideBackButton: Same as .preserve and back buttons are automatically hidden.
+- replace: Any existing view controllers are released from the stack and replaced with the pushed view controller. Back buttons are automatically hidden. This behavior is ideal if view controllers require a lot of memory or their state doesn't need to be preserved..
+- subMenu: Unlike all other behaviors that push using the menu's presentingViewController, this behavior pushes view controllers within the menu.  Use this behavior if you want to display a sub menu.
+*/
+open var menuPushStyle: MenuPushStyle = .defaultBehavior
+
+/**
+The presentation mode of the menu.
+
+There are four modes in MenuPresentMode:
+- menuSlideIn: Menu slides in over of the existing view.
+- viewSlideOut: The existing view slides out to reveal the menu.
+- viewSlideInOut: The existing view slides out while the menu slides in.
+- menuDissolveIn: The menu dissolves in over the existing view controller.
+*/
+open var menuPresentMode: MenuPresentMode = .viewSlideOut
+
+/// Prevents the same view controller (or a view controller of the same class) from being pushed more than once. Defaults to true.
+open var menuAllowPushOfSameClassTwice = true
+
+/**
+Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is 75% of the screen width.
+
+Note that each menu's width can be overridden using the `menuWidth` property on any `UISideMenuNavigationController` instance.
+*/
+open var menuWidth: CGFloat = max(round(min((appScreenRect.width), (appScreenRect.height)) * 0.75), 240)
+
+/// Duration of the animation when the menu is presented without gestures. Default is 0.35 seconds.
+open var menuAnimationPresentDuration: Double = 0.35
+
+/// Duration of the animation when the menu is dismissed without gestures. Default is 0.35 seconds.
+open var menuAnimationDismissDuration: Double = 0.35
+
+/// Duration of the remaining animation when the menu is partially dismissed with gestures. Default is 0.35 seconds.
+open var menuAnimationCompleteGestureDuration: Double = 0.35
+
+/// Amount to fade the existing view controller when the menu is presented. Default is 0 for no fade. Set to 1 to fade completely.
+open var menuAnimationFadeStrength: CGFloat = 0
+
+/// The amount to scale the existing view controller or the menu view controller depending on the `menuPresentMode`. Default is 1 for no scaling. Less than 1 will shrink, greater than 1 will grow.
+open var menuAnimationTransformScaleFactor: CGFloat = 1
+
+/// The background color behind menu animations. Depending on the animation settings this may not be visible. If `menuFadeStatusBar` is true, this color is used to fade it. Default is black.
+open var menuAnimationBackgroundColor: UIColor?
+
+/// The shadow opacity around the menu view controller or existing view controller depending on the `menuPresentMode`. Default is 0.5 for 50% opacity.
+open var menuShadowOpacity: Float = 0.5
+
+/// The shadow color around the menu view controller or existing view controller depending on the `menuPresentMode`. Default is black.
+open var menuShadowColor = UIColor.black
+
+/// The radius of the shadow around the menu view controller or existing view controller depending on the `menuPresentMode`. Default is 5.
+open var menuShadowRadius: CGFloat = 5
+
+/// The left menu swipe to dismiss gesture.
+open weak var menuLeftSwipeToDismissGesture: UIPanGestureRecognizer?
+
+/// The right menu swipe to dismiss gesture.
+open weak var menuRightSwipeToDismissGesture: UIPanGestureRecognizer?
+
+/// Enable or disable gestures that would swipe to dismiss the menu. Default is true.
+open var menuEnableSwipeGestures: Bool = true
+
+/// Enable or disable interaction with the presenting view controller while the menu is displayed. Enabling may make it difficult to dismiss the menu or cause exceptions if the user tries to present and already presented menu. Default is false.
+open var menuPresentingViewControllerUserInteractionEnabled: Bool = false
+
+/// The strength of the parallax effect on the existing view controller. Does not apply to `menuPresentMode` when set to `ViewSlideOut`. Default is 0.
+open var menuParallaxStrength: Int = 0
+
+/// Draws the `menuAnimationBackgroundColor` behind the status bar. Default is true.
+open var menuFadeStatusBar = true
+
+/// The animation options when a menu is displayed. Ignored when displayed with a gesture.
+open var menuAnimationOptions: UIViewAnimationOptions = .curveEaseInOut
+
+/// The animation spring damping when a menu is displayed. Ignored when displayed with a gesture.
+open var menuAnimationUsingSpringWithDamping: CGFloat = 1
+
+/// The animation initial spring velocity when a menu is displayed. Ignored when displayed with a gesture.
+open var menuAnimationInitialSpringVelocity: CGFloat = 1
+
+/** 
+Automatically dismisses the menu when another view is pushed from it.
+
+Note: to prevent the menu from dismissing when presenting, set modalPresentationStyle = .overFullScreen
+of the view controller being presented in storyboard or during its initalization.
+*/
+open var menuDismissOnPush = true
+
+/// Forces menus to always animate when appearing or disappearing, regardless of a pushed view controller's animation.
+open var menuAlwaysAnimate = false
+
+/**
+The blur effect style of the menu if the menu's root view controller is a UITableViewController or UICollectionViewController.
+
+- Note: If you want cells in a UITableViewController menu to show vibrancy, make them a subclass of UITableViewVibrantCell and set the `blurEffectStyle` of each cell to SideMenuManager.default.menuBlurEffectStyle.
+*/
+open var menuBlurEffectStyle: UIBlurEffectStyle?
+
+/// The left menu.
+open var menuLeftNavigationController: UISideMenuNavigationController?
+
+/// The right menu.
+open var menuRightNavigationController: UISideMenuNavigationController?
+
+/**
+Adds screen edge gestures to a view to present a menu.
+
+- Parameter toView: The view to add gestures to.
+- Parameter forMenu: The menu (left or right) you want to add a gesture for. If unspecified, gestures will be added for both sides.
+
+- Returns: The array of screen edge gestures added to `toView`.
+*/
+@discardableResult open func menuAddScreenEdgePanGesturesToPresent(toView: UIView, forMenu:UIRectEdge? = nil) -> [UIScreenEdgePanGestureRecognizer]
+
+/**
+Adds a pan edge gesture to a view to present menus.
+
+- Parameter toView: The view to add a pan gesture to.
+
+- Returns: The pan gesture added to `toView`.
+*/
+@discardableResult open func menuAddPanGestureToPresent(toView: UIView) -> UIPanGestureRecognizer
+```
+#### UISideMenuNavigationController
+`UISideMenuNavigationController` supports the following customizations and properties:
+``` swift
+/// SideMenuManager instance associated with this menu. Default is `SideMenuManager.default`. This property cannot be changed after the menu has loaded.
+open weak var sideMenuManager: SideMenuManager! = SideMenuManager.default
+
+/// Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is zero. When zero, `sideMenuManager.menuWidth` is used. This property cannot be changed while the isHidden property is false.
+@IBInspectable open var menuWidth: CGFloat = 0
+
+/// Whether the menu appears on the right or left side of the screen. Right is the default. This property cannot be changed after the menu has loaded.
+@IBInspectable open var leftSide: Bool = false
+
+/// Indicates if the menu is anywhere in the view hierarchy, even if covered by another view controller.
+open var isHidden: Bool
+```
+#### UISideMenuNavigationControllerDelegate
+To receive notifications when a menu is displayed from a view controller, have it adhere to the  `UISideMenuNavigationControllerDelegate` protocol:
+``` swift
+extension MyViewController: UISideMenuNavigationControllerDelegate {
+
+    func sideMenuWillAppear(menu: UISideMenuNavigationController, animated: Bool) {
+        print("SideMenu Appearing! (animated: \(animated))")
+    }
+
+    func sideMenuDidAppear(menu: UISideMenuNavigationController, animated: Bool) {
+        print("SideMenu Appeared! (animated: \(animated))")
+    }
+
+    func sideMenuWillDisappear(menu: UISideMenuNavigationController, animated: Bool) {
+        print("SideMenu Disappearing! (animated: \(animated))")
+    }
+
+    func sideMenuDidDisappear(menu: UISideMenuNavigationController, animated: Bool) {
+        print("SideMenu Disappeared! (animated: \(animated))")
+    }
+
+}
+```
+*Note: setting the  `sideMenuDelegate` property on `UISideMenuNavigationController` is optional. If your view controller adheres to the protocol then the methods will be called automatically.*
+### Advanced
+For simplicity, `SideMenuManager.default` serves as the primary instance as most projects will only need one menu across all screens. If you need to show a different SideMenu, such as from a modal view controller presented from a previous SideMenu, do the following:
+1. Declare a variable containing your custom `SideMenuManager` instance. You may want it to define it globally and configure it in your app delegate if menus will be used on multiple screens.
+``` swift
+let customSideMenuManager = SideMenuManager()
+```
+2. Setup and display menus with your custom instance the same as you would with the  `SideMenuManager.default` instance.
+3. If using Storyboards, subclass your instance of `UISideMenuNavigationController` and set its `sideMenuManager` property to your custom instance. This must be done before `viewDidLoad` is called:
+``` swift
+class MySideMenuNavigationController: UISideMenuNavigationController {
+
+    let customSideMenuManager = SideMenuManager()
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+
+        sideMenuManager = customSideMenuManager
+    }
+
+}
+```
+Alternatively, you can set  `sideMenuManager` from the view controller that segues to your UISideMenuNavigationController:
+``` swift
+override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
+    if let sideMenuNavigationController = segue.destination as? UISideMenuNavigationController {
+        sideMenuNavigationController.sideMenuManager = customSideMenuManager
+    }
+}
+```
+*Important: displaying SideMenu instances directly over each other is not supported. Use `menuPushStyle = .subMenu` instead.*
+## Known Issues
+* Issue [#258](https://github.com/jonkykong/SideMenu/issues/258).
+* Don't try to change the status bar appearance when presenting a menu. When used with quick gestures/animations, it causes the presentation animation to not complete properly and locks the UI. This was fixed in iOS 9.3. See [radar 21961293](http://www.openradar.me/21961293) for more information.
+
+## Thank You
+A special thank you to everyone that has [contributed](https://github.com/jonkykong/SideMenu/graphs/contributors) to this library to make it better. Your support is appreciated!
+
+## License
+
+SideMenu is available under the MIT license. See the LICENSE file for more info.
diff --git a/iOS/Pods/SwiftKeychainWrapper/LICENSE b/iOS/Pods/SwiftKeychainWrapper/LICENSE
new file mode 100644 (file)
index 0000000..82d694d
--- /dev/null
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Jason Rendel
+
+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/iOS/Pods/SwiftKeychainWrapper/README.md b/iOS/Pods/SwiftKeychainWrapper/README.md
new file mode 100644 (file)
index 0000000..316e917
--- /dev/null
@@ -0,0 +1,151 @@
+#SwiftKeychainWrapper
+
+A simple wrapper for the iOS Keychain to allow you to use it in a similar fashion to User Defaults. Written in Swift.
+
+Provides singleton instance that is setup to work for most needs. Use KeychainWrapper.standard to access the singleton instance.
+
+If you need to customize the keychain access to use a custom identifier or access group, you can create your own instance instead of using the singleton access.
+
+By default, the Keychain Wrapper saves data as a Generic Password type in the iOS Keychain. It saves items such that they can only be accessed when the app is unlocked and open. If you are not familiar with the iOS Keychain usage, this provides a safe default for using the keycain.
+
+Users that want to deviate from this default implementation, now can do so in in version 2.0 and up. Each request to save/read a key value now allows you to specify the keychain accessibility for that key.
+
+##General Usage
+
+Add a string value to keychain:
+```
+let saveSuccessful: Bool = KeychainWrapper.standard.set("Some String", forKey: "myKey")
+```
+
+Retrieve a string value from keychain:
+```
+let retrievedString: String? = KeychainWrapper.standard.string(forKey: "myKey")
+```
+
+Remove a string value from keychain:
+```
+let removeSuccessful: Bool = KeychainWrapper.standard.remove(key: "myKey")
+```
+
+##Custom Instance
+
+When the Keychain Wrapper is used, all keys are linked to a common identifier for your app, called the service name. By default this uses your main bundle identifier. However, you may also change it, or store multiple items to the keycahin under different identifiers.
+
+To share keychain items between your applications, you may specify an access group and use that same access group in each application.
+
+To set a custom service name identifier or access group, you may now create your own instance of the keychain wrapper as follows:
+
+```
+let uniqueServiceName = "customServiceName"
+let uniqueAccessGroup = "sharedAccessGroupName"
+let customKeychainWrapperInstance = KeychainWrapper(serviceName: uniqueServiceName, accessGroup: uniqueAccessGroup)
+```
+The custom instance can then be used in place of the shared instance or static accessors:
+
+```
+let saveSuccessful: Bool = customKeychainWrapperInstance.set("Some String", forKey: "myKey")
+
+let retrievedString: String? = customKeychainWrapperInstance.string(forKey: "myKey")
+
+let removeSuccessful: Bool = customKeychainWrapperInstance.removeObject(forKey: "myKey")
+```
+
+##Accessibility Options
+
+By default, all items saved to keychain can only be accessed when the device is unlocked. To change this accessibility, an optional "withAccessibility" param can be set on all requests. The enum KeychainItemAccessibilty provides an easy way to select the accessibility level desired:
+
+```
+KeychainWrapper.standard.set("Some String", forKey: "myKey", withAccessibility: .AfterFirstUnlock)
+```
+
+##Installation
+
+#### CocoaPods
+You can use [CocoaPods](http://cocoapods.org/) to install SwiftKeychainWrapper by adding it to your `Podfile`:
+
+``` ruby
+use_frameworks!
+platform :ios, '8.0'
+
+target 'target_name' do
+   pod 'SwiftKeychainWrapper'
+end
+```
+
+To use the keychain wrapper in your app, import SwiftKeychainWrapper into the file(s) where you want to use it.
+
+```
+import SwiftKeychainWrapper
+```
+
+#### Carthage
+You can use [Carthage](https://github.com/Carthage/Carthage) to install SwiftKeychainWrapper by adding it to your `Cartfile`.
+
+Swift 3.0:
+```
+github "jrendel/SwiftKeychainWrapper" ~> 3.0
+```
+
+Swift 2.3:
+```
+github "jrendel/SwiftKeychainWrapper" == 2.1.1
+```
+
+#### Manually
+Download and drop ```KeychainWrapper.swift``` and ```KeychainItemAcessibility.swift``` into your project.
+
+
+## Release History
+
+* 3.0.1
+    * Added a host app for the unit tests to get around the issue with keychain access not working the same on iOS 10 simulators
+    * Minor update to readme instructions    
+* 3.0
+    * Swift 3.0 update. Contains breaking API changes. 2.2.0 and 2.2.1 are now rolled into 3.0
+* 2.2.1 (Removed from Cocoapods)
+    * Syntax updates to be more Swift 3 like
+* 2.2 (Removed from Cocoapods)
+    * Updated to support Swift 3.0
+    * Remove deprecated functions (static access)
+* 2.1
+    * Updated to support Swift 2.3
+* 2.0
+    * Further changes to more closely align the API with how NSUserDefaults works. Access to the default implementation is now done through a singleton instance. Static accessors have been included that wrap this shared instance to maintain backwards compatibility. These will be removed in the next update
+    * Ability to change keychain service name identifier and access group on the shared instance has been deprecated. Users now have the ability to create their own instance of the keychain if they want to customize these.
+    * Addtional options have been provided to alter the keychain accessibility for each key value saved.
+
+* 1.0.11
+    * Update for Swift 2.0
+
+* 1.0.10
+    * Update License info. Merged Pull Request with Carthage support.
+
+* 1.0.8
+    * Update for Swift 1.2
+
+* 1.0.7
+    * Determined that once provisioned correctly for access groups, using KeychainWrapper on the simulator with access groups works. So I removed the simulator related check and unit tests previously added.
+
+* 1.0.6
+    * Support for Access Groups
+    * SwiftKeychainWrapperExample has been updated to show usage with an Access Group: https://github.com/jrendel/SwiftKeychainWrapperExample
+
+    * Access Groups do not work on the simulator. Apps that are built for the simulator aren't signed, so there's no keychain access group for the simulator to check. This means that all apps can see all keychain items when run on the simulator. Attempting to set an access group will result in a failure when attempting to Add or Update keychain items. Because of this, the Keychain Wrapper detects if it is being using on a simulator and will not set an access group property if one is set. This allows the Keychain Wrapper to still be used on the simulator for development of your app. To properly test Keychain Access Groups, you will need to test on a device.
+
+* 1.0.5
+    * This version converts the project to a proper Swift Framework and adds a podspec file to be compatible with the latest CocoaPods pre-release, which now supports Swift.
+
+    * To see an example of usage with CocoaPods, I've created the repo SwiftKeychainWrapperExample:  https://github.com/jrendel/SwiftKeychainWrapperExample
+
+* 1.0.2
+    * Updated for Xcode 6.1
+
+======
+
+I've been using an Objective-C based wrapper in my own projects for the past couple years. The original library I wrote for myself was based on the following tutorial:
+
+http://www.raywenderlich.com/6475/basic-security-in-ios-5-tutorial-part-1
+
+This is a rewrite of that code in Swift.
+
+[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
diff --git a/iOS/Pods/SwiftKeychainWrapper/SwiftKeychainWrapper/KeychainItemAccessibility.swift b/iOS/Pods/SwiftKeychainWrapper/SwiftKeychainWrapper/KeychainItemAccessibility.swift
new file mode 100644 (file)
index 0000000..c0f51a6
--- /dev/null
@@ -0,0 +1,123 @@
+//
+//  KeychainOptions.swift
+//  SwiftKeychainWrapper
+//
+//  Created by James Blair on 4/24/16.
+//  Copyright © 2016 Jason Rendel. All rights reserved.
+//
+//    The MIT License (MIT)
+//
+//    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
+
+protocol KeychainAttrRepresentable {
+    var keychainAttrValue: CFString { get }
+}
+
+// MARK: - KeychainItemAccessibility
+public enum KeychainItemAccessibility {
+    /**
+     The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
+     
+     After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute migrate to a new device when using encrypted backups.
+    */
+    @available(iOS 4, *)
+    case afterFirstUnlock
+    
+    /**
+     The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
+     
+     After the first unlock, the data remains accessible until the next restart. This is recommended for items that need to be accessed by background applications. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
+     */
+    @available(iOS 4, *)
+    case afterFirstUnlockThisDeviceOnly
+    
+    /**
+     The data in the keychain item can always be accessed regardless of whether the device is locked.
+     
+     This is not recommended for application use. Items with this attribute migrate to a new device when using encrypted backups.
+     */
+    @available(iOS 4, *)
+    case always
+    
+    /**
+     The data in the keychain can only be accessed when the device is unlocked. Only available if a passcode is set on the device.
+     
+     This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute never migrate to a new device. After a backup is restored to a new device, these items are missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode causes all items in this class to be deleted.
+     */
+    @available(iOS 8, *)
+    case whenPasscodeSetThisDeviceOnly
+    
+    /**
+     The data in the keychain item can always be accessed regardless of whether the device is locked.
+     
+     This is not recommended for application use. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
+     */
+    @available(iOS 4, *)
+    case alwaysThisDeviceOnly
+    
+    /**
+     The data in the keychain item can be accessed only while the device is unlocked by the user.
+     
+     This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute migrate to a new device when using encrypted backups.
+     
+     This is the default value for keychain items added without explicitly setting an accessibility constant.
+     */
+    @available(iOS 4, *)
+    case whenUnlocked
+    
+    /**
+     The data in the keychain item can be accessed only while the device is unlocked by the user.
+     
+     This is recommended for items that need to be accessible only while the application is in the foreground. Items with this attribute do not migrate to a new device. Thus, after restoring from a backup of a different device, these items will not be present.
+     */
+    @available(iOS 4, *)
+    case whenUnlockedThisDeviceOnly
+    
+    static func accessibilityForAttributeValue(_ keychainAttrValue: CFString) -> KeychainItemAccessibility? {
+        for (key, value) in keychainItemAccessibilityLookup {
+            if value == keychainAttrValue {
+                return key
+            }
+        }
+        
+        return nil
+    }
+}
+
+private let keychainItemAccessibilityLookup: [KeychainItemAccessibility:CFString] = {
+    var lookup: [KeychainItemAccessibility:CFString] = [
+        .afterFirstUnlock: kSecAttrAccessibleAfterFirstUnlock,
+        .afterFirstUnlockThisDeviceOnly: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
+        .always: kSecAttrAccessibleAlways,
+        .whenPasscodeSetThisDeviceOnly: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
+        .alwaysThisDeviceOnly : kSecAttrAccessibleAlwaysThisDeviceOnly,
+        .whenUnlocked: kSecAttrAccessibleWhenUnlocked,
+        .whenUnlockedThisDeviceOnly: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
+    ]
+
+    return lookup
+}()
+
+extension KeychainItemAccessibility : KeychainAttrRepresentable {
+    internal var keychainAttrValue: CFString {
+        return keychainItemAccessibilityLookup[self]!
+    }
+}
diff --git a/iOS/Pods/SwiftKeychainWrapper/SwiftKeychainWrapper/KeychainWrapper.swift b/iOS/Pods/SwiftKeychainWrapper/SwiftKeychainWrapper/KeychainWrapper.swift
new file mode 100644 (file)
index 0000000..b8a4efa
--- /dev/null
@@ -0,0 +1,424 @@
+//
+//  KeychainWrapper.swift
+//  KeychainWrapper
+//
+//  Created by Jason Rendel on 9/23/14.
+//  Copyright (c) 2014 Jason Rendel. All rights reserved.
+//
+//    The MIT License (MIT)
+//
+//    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
+
+
+private let SecMatchLimit: String! = kSecMatchLimit as String
+private let SecReturnData: String! = kSecReturnData as String
+private let SecReturnPersistentRef: String! = kSecReturnPersistentRef as String
+private let SecValueData: String! = kSecValueData as String
+private let SecAttrAccessible: String! = kSecAttrAccessible as String
+private let SecClass: String! = kSecClass as String
+private let SecAttrService: String! = kSecAttrService as String
+private let SecAttrGeneric: String! = kSecAttrGeneric as String
+private let SecAttrAccount: String! = kSecAttrAccount as String
+private let SecAttrAccessGroup: String! = kSecAttrAccessGroup as String
+private let SecReturnAttributes: String = kSecReturnAttributes as String
+
+/// KeychainWrapper is a class to help make Keychain access in Swift more straightforward. It is designed to make accessing the Keychain services more like using NSUserDefaults, which is much more familiar to people.
+open class KeychainWrapper {
+    
+    @available(*, deprecated: 2.2.1, message: "KeychainWrapper.defaultKeychainWrapper is deprecated, use KeychainWrapper.standard instead")
+    public static let defaultKeychainWrapper = KeychainWrapper.standard
+    
+    /// Default keychain wrapper access
+    public static let standard = KeychainWrapper()
+    
+    /// ServiceName is used for the kSecAttrService property to uniquely identify this keychain accessor. If no service name is specified, KeychainWrapper will default to using the bundleIdentifier.
+    private (set) public var serviceName: String
+    
+    /// AccessGroup is used for the kSecAttrAccessGroup property to identify which Keychain Access Group this entry belongs to. This allows you to use the KeychainWrapper with shared keychain access between different applications.
+    private (set) public var accessGroup: String?
+    
+    private static let defaultServiceName: String = {
+        return Bundle.main.bundleIdentifier ?? "SwiftKeychainWrapper"
+    }()
+
+    private convenience init() {
+        self.init(serviceName: KeychainWrapper.defaultServiceName)
+    }
+    
+    /// Create a custom instance of KeychainWrapper with a custom Service Name and optional custom access group.
+    ///
+    /// - parameter serviceName: The ServiceName for this instance. Used to uniquely identify all keys stored using this keychain wrapper instance.
+    /// - parameter accessGroup: Optional unique AccessGroup for this instance. Use a matching AccessGroup between applications to allow shared keychain access.
+    public init(serviceName: String, accessGroup: String? = nil) {
+        self.serviceName = serviceName
+        self.accessGroup = accessGroup
+    }
+
+    // MARK:- Public Methods
+    
+    /// Checks if keychain data exists for a specified key.
+    ///
+    /// - parameter forKey: The key to check for.
+    /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
+    /// - returns: True if a value exists for the key. False otherwise.
+    open func hasValue(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        if let _ = data(forKey: key, withAccessibility: accessibility) {
+            return true
+        } else {
+            return false
+        }
+    }
+    
+    open func accessibilityOfKey(_ key: String) -> KeychainItemAccessibility? {
+        var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key)
+        var result: AnyObject?
+
+        // Remove accessibility attribute
+        keychainQueryDictionary.removeValue(forKey: SecAttrAccessible)
+        
+        // Limit search results to one
+        keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
+
+        // Specify we want SecAttrAccessible returned
+        keychainQueryDictionary[SecReturnAttributes] = kCFBooleanTrue
+
+            // Search
+        let status = withUnsafeMutablePointer(to: &result) {
+            SecItemCopyMatching(keychainQueryDictionary as CFDictionary, UnsafeMutablePointer($0))
+        }
+
+        if status == noErr {
+            if let resultsDictionary = result as? [String:AnyObject], let accessibilityAttrValue = resultsDictionary[SecAttrAccessible] as? String {
+                return KeychainItemAccessibility.accessibilityForAttributeValue(accessibilityAttrValue as CFString)
+            }
+        }
+        
+        return nil
+    }
+    
+    // MARK: Public Getters
+    
+    open func integer(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Int? {
+        guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
+            return nil
+        }
+        
+        return numberValue.intValue
+    }
+    
+    open func float(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Float? {
+        guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
+            return nil
+        }
+        
+        return numberValue.floatValue
+    }
+    
+    open func double(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Double? {
+        guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
+            return nil
+        }
+        
+        return numberValue.doubleValue
+    }
+    
+    open func bool(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool? {
+        guard let numberValue = object(forKey: key, withAccessibility: accessibility) as? NSNumber else {
+            return nil
+        }
+        
+        return numberValue.boolValue
+    }
+    
+    /// Returns a string value for a specified key.
+    ///
+    /// - parameter forKey: The key to lookup data for.
+    /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
+    /// - returns: The String associated with the key if it exists. If no data exists, or the data found cannot be encoded as a string, returns nil.
+    open func string(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> String? {
+        guard let keychainData = data(forKey: key, withAccessibility: accessibility) else {
+            return nil
+        }
+        
+        return String(data: keychainData, encoding: String.Encoding.utf8) as String?
+    }
+    
+    /// Returns an object that conforms to NSCoding for a specified key.
+    ///
+    /// - parameter forKey: The key to lookup data for.
+    /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
+    /// - returns: The decoded object associated with the key if it exists. If no data exists, or the data found cannot be decoded, returns nil.
+    open func object(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> NSCoding? {
+        guard let keychainData = data(forKey: key, withAccessibility: accessibility) else {
+            return nil
+        }
+        
+        return NSKeyedUnarchiver.unarchiveObject(with: keychainData) as? NSCoding
+    }
+
+    
+    /// Returns a Data object for a specified key.
+    ///
+    /// - parameter forKey: The key to lookup data for.
+    /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
+    /// - returns: The Data object associated with the key if it exists. If no data exists, returns nil.
+    open func data(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Data? {
+        var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
+        var result: AnyObject?
+        
+        // Limit search results to one
+        keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
+        
+        // Specify we want Data/CFData returned
+        keychainQueryDictionary[SecReturnData] = kCFBooleanTrue
+        
+        // Search
+        let status = withUnsafeMutablePointer(to: &result) {
+            SecItemCopyMatching(keychainQueryDictionary as CFDictionary, UnsafeMutablePointer($0))
+        }
+        
+        return status == noErr ? result as? Data : nil
+    }
+    
+    
+    /// Returns a persistent data reference object for a specified key.
+    ///
+    /// - parameter forKey: The key to lookup data for.
+    /// - parameter withAccessibility: Optional accessibility to use when retrieving the keychain item.
+    /// - returns: The persistent data reference object associated with the key if it exists. If no data exists, returns nil.
+    open func dataRef(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Data? {
+        var keychainQueryDictionary = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
+        var result: AnyObject?
+        
+        // Limit search results to one
+        keychainQueryDictionary[SecMatchLimit] = kSecMatchLimitOne
+        
+        // Specify we want persistent Data/CFData reference returned
+        keychainQueryDictionary[SecReturnPersistentRef] = kCFBooleanTrue
+        
+        // Search
+        let status = withUnsafeMutablePointer(to: &result) {
+            SecItemCopyMatching(keychainQueryDictionary as CFDictionary, UnsafeMutablePointer($0))
+        }
+        
+        return status == noErr ? result as? Data : nil
+    }
+    
+    // MARK: Public Setters
+    
+    @discardableResult open func set(_ value: Int, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
+    }
+    
+    @discardableResult open func set(_ value: Float, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
+    }
+    
+    @discardableResult open func set(_ value: Double, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
+    }
+    
+    @discardableResult open func set(_ value: Bool, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        return set(NSNumber(value: value), forKey: key, withAccessibility: accessibility)
+    }
+
+    /// Save a String value to the keychain associated with a specified key. If a String value already exists for the given key, the string will be overwritten with the new value.
+    ///
+    /// - parameter value: The String value to save.
+    /// - parameter forKey: The key to save the String under.
+    /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
+    /// - returns: True if the save was successful, false otherwise.
+    @discardableResult open func set(_ value: String, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        if let data = value.data(using: .utf8) {
+            return set(data, forKey: key, withAccessibility: accessibility)
+        } else {
+            return false
+        }
+    }
+    
+    
+
+    /// Save an NSCoding compliant object to the keychain associated with a specified key. If an object already exists for the given key, the object will be overwritten with the new value.
+    ///
+    /// - parameter value: The NSCoding compliant object to save.
+    /// - parameter forKey: The key to save the object under.
+    /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
+    /// - returns: True if the save was successful, false otherwise.
+    @discardableResult open func set(_ value: NSCoding, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        let data = NSKeyedArchiver.archivedData(withRootObject: value)
+        
+        return set(data, forKey: key, withAccessibility: accessibility)
+    }
+
+    /// Save a Data object to the keychain associated with a specified key. If data already exists for the given key, the data will be overwritten with the new value.
+    ///
+    /// - parameter value: The Data object to save.
+    /// - parameter forKey: The key to save the object under.
+    /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item.
+    /// - returns: True if the save was successful, false otherwise.
+    @discardableResult open func set(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
+        
+        keychainQueryDictionary[SecValueData] = value
+        
+        if let accessibility = accessibility {
+            keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
+        } else {
+            // Assign default protection - Protect the keychain entry so it's only valid when the device is unlocked
+            keychainQueryDictionary[SecAttrAccessible] = KeychainItemAccessibility.whenUnlocked.keychainAttrValue
+        }
+        
+        let status: OSStatus = SecItemAdd(keychainQueryDictionary as CFDictionary, nil)
+        
+        if status == errSecSuccess {
+            return true
+        } else if status == errSecDuplicateItem {
+            return update(value, forKey: key, withAccessibility: accessibility)
+        } else {
+            return false
+        }
+    }
+
+    @available(*, deprecated: 2.2.1, message: "remove is deprecated, use removeObject instead")
+    @discardableResult open func remove(key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        return removeObject(forKey: key, withAccessibility: accessibility)
+    }
+    
+    /// Remove an object associated with a specified key. If re-using a key but with a different accessibility, first remove the previous key value using removeObjectForKey(:withAccessibility) using the same accessibilty it was saved with.
+    ///
+    /// - parameter forKey: The key value to remove data for.
+    /// - parameter withAccessibility: Optional accessibility level to use when looking up the keychain item.
+    /// - returns: True if successful, false otherwise.
+    @discardableResult open func removeObject(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        let keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
+
+        // Delete
+        let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)
+
+        if status == errSecSuccess {
+            return true
+        } else {
+            return false
+        }
+    }
+
+    /// Remove all keychain data added through KeychainWrapper. This will only delete items matching the currnt ServiceName and AccessGroup if one is set.
+    open func removeAllKeys() -> Bool {
+        // Setup dictionary to access keychain and specify we are using a generic password (rather than a certificate, internet password, etc)
+        var keychainQueryDictionary: [String:Any] = [SecClass:kSecClassGenericPassword]
+        
+        // Uniquely identify this keychain accessor
+        keychainQueryDictionary[SecAttrService] = serviceName
+        
+        // Set the keychain access group if defined
+        if let accessGroup = self.accessGroup {
+            keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
+        }
+        
+        let status: OSStatus = SecItemDelete(keychainQueryDictionary as CFDictionary)
+        
+        if status == errSecSuccess {
+            return true
+        } else {
+            return false
+        }
+    }
+    
+    /// Remove all keychain data, including data not added through keychain wrapper.
+    ///
+    /// - Warning: This may remove custom keychain entries you did not add via SwiftKeychainWrapper.
+    ///
+    open class func wipeKeychain() {
+        deleteKeychainSecClass(kSecClassGenericPassword) // Generic password items
+        deleteKeychainSecClass(kSecClassInternetPassword) // Internet password items
+        deleteKeychainSecClass(kSecClassCertificate) // Certificate items
+        deleteKeychainSecClass(kSecClassKey) // Cryptographic key items
+        deleteKeychainSecClass(kSecClassIdentity) // Identity items
+    }
+
+    // MARK:- Private Methods
+    
+    /// Remove all items for a given Keychain Item Class
+    ///
+    ///
+    @discardableResult private class func deleteKeychainSecClass(_ secClass: AnyObject) -> Bool {
+        let query = [SecClass: secClass]
+        let status: OSStatus = SecItemDelete(query as CFDictionary)
+        
+        if status == errSecSuccess {
+            return true
+        } else {
+            return false
+        }
+    }
+    
+    /// Update existing data associated with a specified key name. The existing data will be overwritten by the new data
+    private func update(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
+        var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
+        let updateDictionary = [SecValueData:value]
+        
+        // on update, only set accessibility if passed in
+        if let accessibility = accessibility {
+            keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
+        }
+        
+        // Update
+        let status: OSStatus = SecItemUpdate(keychainQueryDictionary as CFDictionary, updateDictionary as CFDictionary)
+
+        if status == errSecSuccess {
+            return true
+        } else {
+            return false
+        }
+    }
+
+    /// Setup the keychain query dictionary used to access the keychain on iOS for a specified key name. Takes into account the Service Name and Access Group if one is set.
+    ///
+    /// - parameter forKey: The key this query is for
+    /// - parameter withAccessibility: Optional accessibility to use when setting the keychain item. If none is provided, will default to .WhenUnlocked
+    /// - returns: A dictionary with all the needed properties setup to access the keychain on iOS
+    private func setupKeychainQueryDictionary(forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> [String:Any] {
+        // Setup default access as generic password (rather than a certificate, internet password, etc)
+        var keychainQueryDictionary: [String:Any] = [SecClass:kSecClassGenericPassword]
+        
+        // Uniquely identify this keychain accessor
+        keychainQueryDictionary[SecAttrService] = serviceName
+        
+        // Only set accessibiilty if its passed in, we don't want to default it here in case the user didn't want it set
+        if let accessibility = accessibility {
+            keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
+        }
+        
+        // Set the keychain access group if defined
+        if let accessGroup = self.accessGroup {
+            keychainQueryDictionary[SecAttrAccessGroup] = accessGroup
+        }
+        
+        // Uniquely identify the account who will be accessing the keychain
+        let encodedIdentifier: Data? = key.data(using: String.Encoding.utf8)
+        
+        keychainQueryDictionary[SecAttrGeneric] = encodedIdentifier
+        
+        keychainQueryDictionary[SecAttrAccount] = encodedIdentifier
+        
+        return keychainQueryDictionary
+    }
+}
diff --git a/iOS/Pods/SwiftKeychainWrapper/SwiftKeychainWrapper/SwiftKeychainWrapper.h b/iOS/Pods/SwiftKeychainWrapper/SwiftKeychainWrapper/SwiftKeychainWrapper.h
new file mode 100644 (file)
index 0000000..128c4d9
--- /dev/null
@@ -0,0 +1,37 @@
+//
+//  SwiftKeychainWrapper.h
+//  SwiftKeychainWrapper
+//
+//  Created by Jason Rendel on 1/13/15.
+//  Copyright (c) 2014 Jason Rendel. All rights reserved.
+//
+//    The MIT License (MIT)
+//
+//    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 <UIKit/UIKit.h>
+
+//! Project version number for SwiftKeychainWrapper.
+FOUNDATION_EXPORT double SwiftKeychainWrapperVersionNumber;
+
+//! Project version string for SwiftKeychainWrapper.
+FOUNDATION_EXPORT const unsigned char SwiftKeychainWrapperVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <SwiftKeychainWrapper/PublicHeader.h>
+
+
diff --git a/iOS/Pods/Target Support Files/AEXML/AEXML-dummy.m b/iOS/Pods/Target Support Files/AEXML/AEXML-dummy.m
new file mode 100644 (file)
index 0000000..f96908c
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_AEXML : NSObject
+@end
+@implementation PodsDummy_AEXML
+@end
diff --git a/iOS/Pods/Target Support Files/AEXML/AEXML-prefix.pch b/iOS/Pods/Target Support Files/AEXML/AEXML-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/AEXML/AEXML-umbrella.h b/iOS/Pods/Target Support Files/AEXML/AEXML-umbrella.h
new file mode 100644 (file)
index 0000000..da54915
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double AEXMLVersionNumber;
+FOUNDATION_EXPORT const unsigned char AEXMLVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/AEXML/AEXML.modulemap b/iOS/Pods/Target Support Files/AEXML/AEXML.modulemap
new file mode 100644 (file)
index 0000000..e022436
--- /dev/null
@@ -0,0 +1,6 @@
+framework module AEXML {
+  umbrella header "AEXML-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/AEXML/AEXML.xcconfig b/iOS/Pods/Target Support Files/AEXML/AEXML.xcconfig
new file mode 100644 (file)
index 0000000..467cbf5
--- /dev/null
@@ -0,0 +1,10 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/AEXML
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/AEXML
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+SWIFT_VERSION = 4.0
diff --git a/iOS/Pods/Target Support Files/AEXML/Info.plist b/iOS/Pods/Target Support Files/AEXML/Info.plist
new file mode 100644 (file)
index 0000000..b04e694
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>4.2.2</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/Alamofire/Alamofire-dummy.m b/iOS/Pods/Target Support Files/Alamofire/Alamofire-dummy.m
new file mode 100644 (file)
index 0000000..a6c4594
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Alamofire : NSObject
+@end
+@implementation PodsDummy_Alamofire
+@end
diff --git a/iOS/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch b/iOS/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h b/iOS/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h
new file mode 100644 (file)
index 0000000..00014e3
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double AlamofireVersionNumber;
+FOUNDATION_EXPORT const unsigned char AlamofireVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/Alamofire/Alamofire.modulemap b/iOS/Pods/Target Support Files/Alamofire/Alamofire.modulemap
new file mode 100644 (file)
index 0000000..d1f125f
--- /dev/null
@@ -0,0 +1,6 @@
+framework module Alamofire {
+  umbrella header "Alamofire-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/Alamofire/Alamofire.xcconfig b/iOS/Pods/Target Support Files/Alamofire/Alamofire.xcconfig
new file mode 100644 (file)
index 0000000..6b8baab
--- /dev/null
@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/Alamofire/Info.plist b/iOS/Pods/Target Support Files/Alamofire/Info.plist
new file mode 100644 (file)
index 0000000..3d018f8
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>4.6.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-dummy.m b/iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-dummy.m
new file mode 100644 (file)
index 0000000..bb48c12
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_AlamofireActivityLogger : NSObject
+@end
+@implementation PodsDummy_AlamofireActivityLogger
+@end
diff --git a/iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-prefix.pch b/iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-umbrella.h b/iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger-umbrella.h
new file mode 100644 (file)
index 0000000..7d1afa9
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double AlamofireActivityLoggerVersionNumber;
+FOUNDATION_EXPORT const unsigned char AlamofireActivityLoggerVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger.modulemap b/iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger.modulemap
new file mode 100644 (file)
index 0000000..3ae9ad1
--- /dev/null
@@ -0,0 +1,6 @@
+framework module AlamofireActivityLogger {
+  umbrella header "AlamofireActivityLogger-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger.xcconfig b/iOS/Pods/Target Support Files/AlamofireActivityLogger/AlamofireActivityLogger.xcconfig
new file mode 100644 (file)
index 0000000..471f2b9
--- /dev/null
@@ -0,0 +1,12 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/AlamofireActivityLogger
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire"
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -framework "Foundation"
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/AlamofireActivityLogger
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+SWIFT_VERSION = 4.0
diff --git a/iOS/Pods/Target Support Files/AlamofireActivityLogger/Info.plist b/iOS/Pods/Target Support Files/AlamofireActivityLogger/Info.plist
new file mode 100644 (file)
index 0000000..e526849
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>2.4.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore-dummy.m b/iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore-dummy.m
new file mode 100644 (file)
index 0000000..4f1eb27
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_FirebaseCore : NSObject
+@end
+@implementation PodsDummy_FirebaseCore
+@end
diff --git a/iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore-umbrella.h b/iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore-umbrella.h
new file mode 100644 (file)
index 0000000..791a7c0
--- /dev/null
@@ -0,0 +1,22 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "FIRAnalyticsConfiguration.h"
+#import "FIRApp.h"
+#import "FIRConfiguration.h"
+#import "FirebaseCore.h"
+#import "FIRLoggerLevel.h"
+#import "FIROptions.h"
+
+FOUNDATION_EXPORT double FirebaseCoreVersionNumber;
+FOUNDATION_EXPORT const unsigned char FirebaseCoreVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore.modulemap b/iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore.modulemap
new file mode 100644 (file)
index 0000000..4c38b87
--- /dev/null
@@ -0,0 +1,6 @@
+framework module FirebaseCore {
+  umbrella header "FirebaseCore-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore.xcconfig b/iOS/Pods/Target Support Files/FirebaseCore/FirebaseCore.xcconfig
new file mode 100644 (file)
index 0000000..2c42f60
--- /dev/null
@@ -0,0 +1,11 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities"
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 FIRCore_VERSION=5.1.4 Firebase_VERSION=5.9.0
+OTHER_CFLAGS = $(inherited) -fno-autolink
+OTHER_LDFLAGS = -framework "Foundation"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/FirebaseCore
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging-dummy.m b/iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging-dummy.m
new file mode 100644 (file)
index 0000000..ec94549
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_FirebaseMessaging : NSObject
+@end
+@implementation PodsDummy_FirebaseMessaging
+@end
diff --git a/iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging-umbrella.h b/iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging-umbrella.h
new file mode 100644 (file)
index 0000000..c2f51ed
--- /dev/null
@@ -0,0 +1,18 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "FirebaseMessaging.h"
+#import "FIRMessaging.h"
+
+FOUNDATION_EXPORT double FirebaseMessagingVersionNumber;
+FOUNDATION_EXPORT const unsigned char FirebaseMessagingVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging.modulemap b/iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging.modulemap
new file mode 100644 (file)
index 0000000..4ab5827
--- /dev/null
@@ -0,0 +1,6 @@
+framework module FirebaseMessaging {
+  umbrella header "FirebaseMessaging-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging.xcconfig b/iOS/Pods/Target Support Files/FirebaseMessaging/FirebaseMessaging.xcconfig
new file mode 100644 (file)
index 0000000..9c9b1e7
--- /dev/null
@@ -0,0 +1,10 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FirebaseMessaging
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf" "${PODS_ROOT}/FirebaseInstanceID/Frameworks"
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 FIRMessaging_LIB_VERSION=3.1.2
+OTHER_LDFLAGS = -l"sqlite3" -framework "SystemConfiguration"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/FirebaseMessaging
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit-dummy.m b/iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit-dummy.m
new file mode 100644 (file)
index 0000000..8241387
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_FolioReaderKit : NSObject
+@end
+@implementation PodsDummy_FolioReaderKit
+@end
diff --git a/iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit-prefix.pch b/iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit-umbrella.h b/iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit-umbrella.h
new file mode 100644 (file)
index 0000000..24606d6
--- /dev/null
@@ -0,0 +1,17 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "FolioReaderKit.h"
+
+FOUNDATION_EXPORT double FolioReaderKitVersionNumber;
+FOUNDATION_EXPORT const unsigned char FolioReaderKitVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit.modulemap b/iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit.modulemap
new file mode 100644 (file)
index 0000000..7b095a1
--- /dev/null
@@ -0,0 +1,6 @@
+framework module FolioReaderKit {
+  umbrella header "FolioReaderKit-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit.xcconfig b/iOS/Pods/Target Support Files/FolioReaderKit/FolioReaderKit.xcconfig
new file mode 100644 (file)
index 0000000..760e249
--- /dev/null
@@ -0,0 +1,11 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FolioReaderKit
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AEXML" "${PODS_CONFIGURATION_BUILD_DIR}/FontBlaster" "${PODS_CONFIGURATION_BUILD_DIR}/JSQWebViewController" "${PODS_CONFIGURATION_BUILD_DIR}/MenuItemKit" "${PODS_CONFIGURATION_BUILD_DIR}/Realm" "${PODS_CONFIGURATION_BUILD_DIR}/RealmSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/ZFDragableModalTransition"
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -l"z"
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/FolioReaderKit
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/FolioReaderKit/Info.plist b/iOS/Pods/Target Support Files/FolioReaderKit/Info.plist
new file mode 100644 (file)
index 0000000..b6b2813
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.3.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/FontBlaster/FontBlaster-dummy.m b/iOS/Pods/Target Support Files/FontBlaster/FontBlaster-dummy.m
new file mode 100644 (file)
index 0000000..fa92c08
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_FontBlaster : NSObject
+@end
+@implementation PodsDummy_FontBlaster
+@end
diff --git a/iOS/Pods/Target Support Files/FontBlaster/FontBlaster-prefix.pch b/iOS/Pods/Target Support Files/FontBlaster/FontBlaster-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/FontBlaster/FontBlaster-umbrella.h b/iOS/Pods/Target Support Files/FontBlaster/FontBlaster-umbrella.h
new file mode 100644 (file)
index 0000000..5fd61d3
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double FontBlasterVersionNumber;
+FOUNDATION_EXPORT const unsigned char FontBlasterVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/FontBlaster/FontBlaster.modulemap b/iOS/Pods/Target Support Files/FontBlaster/FontBlaster.modulemap
new file mode 100644 (file)
index 0000000..1145ed9
--- /dev/null
@@ -0,0 +1,6 @@
+framework module FontBlaster {
+  umbrella header "FontBlaster-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/FontBlaster/FontBlaster.xcconfig b/iOS/Pods/Target Support Files/FontBlaster/FontBlaster.xcconfig
new file mode 100644 (file)
index 0000000..80fd074
--- /dev/null
@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FontBlaster
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/FontBlaster
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/FontBlaster/Info.plist b/iOS/Pods/Target Support Files/FontBlaster/Info.plist
new file mode 100644 (file)
index 0000000..b672cd7
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>4.0.1</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities-dummy.m b/iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities-dummy.m
new file mode 100644 (file)
index 0000000..98ac4e9
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_GoogleUtilities : NSObject
+@end
+@implementation PodsDummy_GoogleUtilities
+@end
diff --git a/iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities-prefix.pch b/iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities-umbrella.h b/iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities-umbrella.h
new file mode 100644 (file)
index 0000000..508e631
--- /dev/null
@@ -0,0 +1,19 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "GULLoggerLevel.h"
+#import "GULLoggerCodes.h"
+#import "GULNSData+zlib.h"
+
+FOUNDATION_EXPORT double GoogleUtilitiesVersionNumber;
+FOUNDATION_EXPORT const unsigned char GoogleUtilitiesVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities.modulemap b/iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities.modulemap
new file mode 100644 (file)
index 0000000..491dd0a
--- /dev/null
@@ -0,0 +1,6 @@
+framework module GoogleUtilities {
+  umbrella header "GoogleUtilities-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities.xcconfig b/iOS/Pods/Target Support Files/GoogleUtilities/GoogleUtilities.xcconfig
new file mode 100644 (file)
index 0000000..dd562e2
--- /dev/null
@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -l"z" -framework "Security" -framework "SystemConfiguration"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/GoogleUtilities
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/GoogleUtilities/Info.plist b/iOS/Pods/Target Support Files/GoogleUtilities/Info.plist
new file mode 100644 (file)
index 0000000..ed54e5d
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>5.3.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/JSQWebViewController/Info.plist b/iOS/Pods/Target Support Files/JSQWebViewController/Info.plist
new file mode 100644 (file)
index 0000000..e92eb78
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>6.0.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController-dummy.m b/iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController-dummy.m
new file mode 100644 (file)
index 0000000..3537f68
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_JSQWebViewController : NSObject
+@end
+@implementation PodsDummy_JSQWebViewController
+@end
diff --git a/iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController-prefix.pch b/iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController-umbrella.h b/iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController-umbrella.h
new file mode 100644 (file)
index 0000000..bb43708
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double JSQWebViewControllerVersionNumber;
+FOUNDATION_EXPORT const unsigned char JSQWebViewControllerVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController.modulemap b/iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController.modulemap
new file mode 100644 (file)
index 0000000..c9f946e
--- /dev/null
@@ -0,0 +1,6 @@
+framework module JSQWebViewController {
+  umbrella header "JSQWebViewController-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController.xcconfig b/iOS/Pods/Target Support Files/JSQWebViewController/JSQWebViewController.xcconfig
new file mode 100644 (file)
index 0000000..6628b0a
--- /dev/null
@@ -0,0 +1,10 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/JSQWebViewController
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -framework "WebKit"
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/JSQWebViewController
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/Kingfisher/Info.plist b/iOS/Pods/Target Support Files/Kingfisher/Info.plist
new file mode 100644 (file)
index 0000000..1b87d15
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>4.7.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m b/iOS/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m
new file mode 100644 (file)
index 0000000..1b89d0e
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Kingfisher : NSObject
+@end
+@implementation PodsDummy_Kingfisher
+@end
diff --git a/iOS/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch b/iOS/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h b/iOS/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h
new file mode 100644 (file)
index 0000000..89b88ac
--- /dev/null
@@ -0,0 +1,17 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "Kingfisher.h"
+
+FOUNDATION_EXPORT double KingfisherVersionNumber;
+FOUNDATION_EXPORT const unsigned char KingfisherVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap b/iOS/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap
new file mode 100644 (file)
index 0000000..2a20d91
--- /dev/null
@@ -0,0 +1,6 @@
+framework module Kingfisher {
+  umbrella header "Kingfisher-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig b/iOS/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig
new file mode 100644 (file)
index 0000000..52db99f
--- /dev/null
@@ -0,0 +1,10 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -framework "CFNetwork"
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/MBProgressHUD/Info.plist b/iOS/Pods/Target Support Files/MBProgressHUD/Info.plist
new file mode 100644 (file)
index 0000000..21a30b4
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.1.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-dummy.m b/iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-dummy.m
new file mode 100644 (file)
index 0000000..67a74df
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_MBProgressHUD : NSObject
+@end
+@implementation PodsDummy_MBProgressHUD
+@end
diff --git a/iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch b/iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-umbrella.h b/iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-umbrella.h
new file mode 100644 (file)
index 0000000..8522a01
--- /dev/null
@@ -0,0 +1,17 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "MBProgressHUD.h"
+
+FOUNDATION_EXPORT double MBProgressHUDVersionNumber;
+FOUNDATION_EXPORT const unsigned char MBProgressHUDVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.modulemap b/iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.modulemap
new file mode 100644 (file)
index 0000000..dbb3f94
--- /dev/null
@@ -0,0 +1,6 @@
+framework module MBProgressHUD {
+  umbrella header "MBProgressHUD-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.xcconfig b/iOS/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.xcconfig
new file mode 100644 (file)
index 0000000..8fab560
--- /dev/null
@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -framework "CoreGraphics" -framework "QuartzCore"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/MBProgressHUD
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/MZDownloadManager/Info.plist b/iOS/Pods/Target Support Files/MZDownloadManager/Info.plist
new file mode 100644 (file)
index 0000000..ebdce25
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>3.4.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager-dummy.m b/iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager-dummy.m
new file mode 100644 (file)
index 0000000..5c102df
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_MZDownloadManager : NSObject
+@end
+@implementation PodsDummy_MZDownloadManager
+@end
diff --git a/iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager-prefix.pch b/iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager-umbrella.h b/iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager-umbrella.h
new file mode 100644 (file)
index 0000000..8959c23
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double MZDownloadManagerVersionNumber;
+FOUNDATION_EXPORT const unsigned char MZDownloadManagerVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager.modulemap b/iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager.modulemap
new file mode 100644 (file)
index 0000000..03eeb17
--- /dev/null
@@ -0,0 +1,6 @@
+framework module MZDownloadManager {
+  umbrella header "MZDownloadManager-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager.xcconfig b/iOS/Pods/Target Support Files/MZDownloadManager/MZDownloadManager.xcconfig
new file mode 100644 (file)
index 0000000..ea659ca
--- /dev/null
@@ -0,0 +1,10 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MZDownloadManager
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -framework "Foundation" -framework "UIKit"
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/MZDownloadManager
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/MatomoTracker/Info.plist b/iOS/Pods/Target Support Files/MatomoTracker/Info.plist
new file mode 100644 (file)
index 0000000..82c355f
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>5.2.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker-dummy.m b/iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker-dummy.m
new file mode 100644 (file)
index 0000000..9d87c1c
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_MatomoTracker : NSObject
+@end
+@implementation PodsDummy_MatomoTracker
+@end
diff --git a/iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker-prefix.pch b/iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker-umbrella.h b/iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker-umbrella.h
new file mode 100644 (file)
index 0000000..2155eac
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double MatomoTrackerVersionNumber;
+FOUNDATION_EXPORT const unsigned char MatomoTrackerVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker.modulemap b/iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker.modulemap
new file mode 100644 (file)
index 0000000..d70881a
--- /dev/null
@@ -0,0 +1,6 @@
+framework module MatomoTracker {
+  umbrella header "MatomoTracker-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker.xcconfig b/iOS/Pods/Target Support Files/MatomoTracker/MatomoTracker.xcconfig
new file mode 100644 (file)
index 0000000..295d589
--- /dev/null
@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MatomoTracker
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/MatomoTracker
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/MenuItemKit/Info.plist b/iOS/Pods/Target Support Files/MenuItemKit/Info.plist
new file mode 100644 (file)
index 0000000..4522675
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>3.0.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit-dummy.m b/iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit-dummy.m
new file mode 100644 (file)
index 0000000..4f9fd75
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_MenuItemKit : NSObject
+@end
+@implementation PodsDummy_MenuItemKit
+@end
diff --git a/iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit-prefix.pch b/iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit-umbrella.h b/iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit-umbrella.h
new file mode 100644 (file)
index 0000000..6055cba
--- /dev/null
@@ -0,0 +1,17 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "MenuItemKit.h"
+
+FOUNDATION_EXPORT double MenuItemKitVersionNumber;
+FOUNDATION_EXPORT const unsigned char MenuItemKitVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit.modulemap b/iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit.modulemap
new file mode 100644 (file)
index 0000000..48bf752
--- /dev/null
@@ -0,0 +1,6 @@
+framework module MenuItemKit {
+  umbrella header "MenuItemKit-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit.xcconfig b/iOS/Pods/Target Support Files/MenuItemKit/MenuItemKit.xcconfig
new file mode 100644 (file)
index 0000000..8374ae4
--- /dev/null
@@ -0,0 +1,10 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MenuItemKit
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -framework "UIKit"
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/MenuItemKit
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/OAuthSwift/Info.plist b/iOS/Pods/Target Support Files/OAuthSwift/Info.plist
new file mode 100644 (file)
index 0000000..e433362
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.2.2</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift-dummy.m b/iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift-dummy.m
new file mode 100644 (file)
index 0000000..5237a83
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_OAuthSwift : NSObject
+@end
+@implementation PodsDummy_OAuthSwift
+@end
diff --git a/iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift-prefix.pch b/iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift-umbrella.h b/iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift-umbrella.h
new file mode 100644 (file)
index 0000000..b767642
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double OAuthSwiftVersionNumber;
+FOUNDATION_EXPORT const unsigned char OAuthSwiftVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift.modulemap b/iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift.modulemap
new file mode 100644 (file)
index 0000000..d2430c0
--- /dev/null
@@ -0,0 +1,6 @@
+framework module OAuthSwift {
+  umbrella header "OAuthSwift-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift.xcconfig b/iOS/Pods/Target Support Files/OAuthSwift/OAuthSwift.xcconfig
new file mode 100644 (file)
index 0000000..8d5cb68
--- /dev/null
@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/OAuthSwift
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/OAuthSwift
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/Pods-WolneLektury/Info.plist b/iOS/Pods/Target Support Files/Pods-WolneLektury/Info.plist
new file mode 100644 (file)
index 0000000..2243fe6
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-acknowledgements.markdown b/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-acknowledgements.markdown
new file mode 100644 (file)
index 0000000..a849b0b
--- /dev/null
@@ -0,0 +1,1615 @@
+# Acknowledgements
+This application makes use of the following third party libraries:
+
+## AEXML
+
+Copyright (c) 2014-2017 Marko Tadić <tadija@me.com> http://tadija.net
+
+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.
+
+
+## Alamofire
+
+Copyright (c) 2014-2017 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.
+
+
+## AlamofireActivityLogger
+
+The MIT License (MIT)
+
+Copyright (c) 2016 Manuel García-Estañ
+
+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.
+
+
+## Crashlytics
+
+Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. Crashlytics Kit: Copyright 2017 Crashlytics, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Crashlytics Terms of Service located at http://try.crashlytics.com/terms/terms-of-service.pdf and the Crashlytics Privacy Policy located at http://try.crashlytics.com/terms/privacy-policy.pdf. OSS: http://get.fabric.io/terms/opensource.txt
+
+## Fabric
+
+Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. OSS: http://get.fabric.io/terms/opensource.txt
+
+## Firebase
+
+Copyright 2018 Google
+
+## FirebaseAnalytics
+
+Copyright 2018 Google
+
+## FirebaseCore
+
+
+                                 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+## FirebaseInstanceID
+
+Copyright 2018 Google
+
+## FirebaseMessaging
+
+
+                                 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+## FolioReaderKit
+
+Copyright (c) 2015-2017, Heberti Almeida
+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 FolioReaderKit 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.
+
+
+## FontBlaster
+
+The MIT License (MIT)
+
+Copyright (c) 2015 Arthur Ariel Sabintsev
+
+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.
+
+
+
+## GoogleAppMeasurement
+
+Copyright 2018 Google
+
+## GoogleUtilities
+
+
+                                 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+## JSQWebViewController
+
+The MIT License (MIT)
+
+Copyright (c) 2015 Jesse Squires
+
+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.
+
+
+
+## Kingfisher
+
+The MIT License (MIT)
+
+Copyright (c) 2018 Wei Wang
+
+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.
+
+
+
+## MBProgressHUD
+
+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.
+
+## MZDownloadManager
+
+Copyright (c) 2016, Arpad Goretity https://github.com/H2CO3/HCDownload.git
+Copyright (c) 2016, Muhammad Zeeshan https://github.com/mzeeshanid/MZDownloadManager.git
+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 the <organization> 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 <COPYRIGHT HOLDER> 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.
+
+
+## MatomoTracker
+
+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.
+
+## MenuItemKit
+
+Copyright (c) 2016 — Present CHEN Xian-an <xianan.chen@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+## OAuthSwift
+
+Copyright (c) 2014 Dongri Jin <dongriat@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+## Protobuf
+
+Copyright 2008 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.
+
+Code generated by the Protocol Buffer compiler is owned by the owner
+of the input file used when generating it.  This code is not
+standalone and requires a support library to be linked with it.  This
+support library is itself covered by the above license.
+
+
+## Realm
+
+TABLE OF CONTENTS
+
+1. Apache License version 2.0
+2. Realm Components
+3. Export Compliance
+
+1. -------------------------------------------------------------------------------
+
+                                 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.
+
+2. -------------------------------------------------------------------------------
+
+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 Platform Extensions component
+
+  Realm Platform Extensions License
+
+  Copyright (c) 2011-2017 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.
+
+3. -------------------------------------------------------------------------------
+
+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.
+
+
+## RealmSwift
+
+TABLE OF CONTENTS
+
+1. Apache License version 2.0
+2. Realm Components
+3. Export Compliance
+
+1. -------------------------------------------------------------------------------
+
+                                 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.
+
+2. -------------------------------------------------------------------------------
+
+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 Platform Extensions component
+
+  Realm Platform Extensions License
+
+  Copyright (c) 2011-2017 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.
+
+3. -------------------------------------------------------------------------------
+
+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.
+
+
+## SSZipArchive
+
+Copyright (c) 2010-2015, Sam Soffes, http://soff.es
+
+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.
+
+
+## SideMenu
+
+Copyright (c) 2015 Jonathan Kent <contact@jonkent.me>
+
+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.
+
+
+## SwiftKeychainWrapper
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Jason Rendel
+
+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.
+
+
+
+## Toast-Swift
+
+Copyright (c) 2015-2017 Charles Scalesse.
+
+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.
+
+
+## ZFDragableModalTransition
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Amornchai Kanokpullwad
+
+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.
+
+## nanopb
+
+Copyright (c) 2011 Petteri Aimonen <jpa at nanopb.mail.kapsi.fi>
+
+This software is provided 'as-is', without any express or 
+implied warranty. In no event will the authors be held liable 
+for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any 
+purpose, including commercial applications, and to alter it and 
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you 
+   must not claim that you wrote the original software. If you use 
+   this software in a product, an acknowledgment in the product 
+   documentation would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and 
+   must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source 
+   distribution.
+
+Generated by CocoaPods - https://cocoapods.org
diff --git a/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-acknowledgements.plist b/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-acknowledgements.plist
new file mode 100644 (file)
index 0000000..570c148
--- /dev/null
@@ -0,0 +1,1821 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>PreferenceSpecifiers</key>
+       <array>
+               <dict>
+                       <key>FooterText</key>
+                       <string>This application makes use of the following third party libraries:</string>
+                       <key>Title</key>
+                       <string>Acknowledgements</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2014-2017 Marko Tadić &lt;tadija@me.com&gt; http://tadija.net
+
+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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>AEXML</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2014-2017 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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>Alamofire</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2016 Manuel García-Estañ
+
+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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>AlamofireActivityLogger</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. Crashlytics Kit: Copyright 2017 Crashlytics, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Crashlytics Terms of Service located at http://try.crashlytics.com/terms/terms-of-service.pdf and the Crashlytics Privacy Policy located at http://try.crashlytics.com/terms/privacy-policy.pdf. OSS: http://get.fabric.io/terms/opensource.txt</string>
+                       <key>License</key>
+                       <string>Commercial</string>
+                       <key>Title</key>
+                       <string>Crashlytics</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. OSS: http://get.fabric.io/terms/opensource.txt</string>
+                       <key>License</key>
+                       <string>Commercial</string>
+                       <key>Title</key>
+                       <string>Fabric</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright 2018 Google</string>
+                       <key>License</key>
+                       <string>Copyright</string>
+                       <key>Title</key>
+                       <string>Firebase</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright 2018 Google</string>
+                       <key>License</key>
+                       <string>Copyright</string>
+                       <key>Title</key>
+                       <string>FirebaseAnalytics</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>
+                                 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</string>
+                       <key>License</key>
+                       <string>Apache</string>
+                       <key>Title</key>
+                       <string>FirebaseCore</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright 2018 Google</string>
+                       <key>License</key>
+                       <string>Copyright</string>
+                       <key>Title</key>
+                       <string>FirebaseInstanceID</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>
+                                 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</string>
+                       <key>License</key>
+                       <string>Apache</string>
+                       <key>Title</key>
+                       <string>FirebaseMessaging</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2015-2017, Heberti Almeida
+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 FolioReaderKit 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.
+</string>
+                       <key>License</key>
+                       <string>BSD</string>
+                       <key>Title</key>
+                       <string>FolioReaderKit</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2015 Arthur Ariel Sabintsev
+
+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.
+
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>FontBlaster</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright 2018 Google</string>
+                       <key>License</key>
+                       <string>Copyright</string>
+                       <key>Title</key>
+                       <string>GoogleAppMeasurement</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>
+                                 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</string>
+                       <key>License</key>
+                       <string>Apache</string>
+                       <key>Title</key>
+                       <string>GoogleUtilities</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2015 Jesse Squires
+
+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.
+
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>JSQWebViewController</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2018 Wei Wang
+
+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.
+
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>Kingfisher</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>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.</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>MBProgressHUD</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2016, Arpad Goretity https://github.com/H2CO3/HCDownload.git
+Copyright (c) 2016, Muhammad Zeeshan https://github.com/mzeeshanid/MZDownloadManager.git
+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 the &lt;organization&gt; 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 &lt;COPYRIGHT HOLDER&gt; 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.
+</string>
+                       <key>License</key>
+                       <string>BSD</string>
+                       <key>Title</key>
+                       <string>MZDownloadManager</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>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.</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>MatomoTracker</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2016 — Present CHEN Xian-an &lt;xianan.chen@gmail.com&gt;
+
+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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>MenuItemKit</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2014 Dongri Jin &lt;dongriat@gmail.com&gt;
+
+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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>OAuthSwift</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright 2008 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.
+
+Code generated by the Protocol Buffer compiler is owned by the owner
+of the input file used when generating it.  This code is not
+standalone and requires a support library to be linked with it.  This
+support library is itself covered by the above license.
+</string>
+                       <key>License</key>
+                       <string>3-Clause BSD License</string>
+                       <key>Title</key>
+                       <string>Protobuf</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>TABLE OF CONTENTS
+
+1. Apache License version 2.0
+2. Realm Components
+3. Export Compliance
+
+1. -------------------------------------------------------------------------------
+
+                                 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.
+
+2. -------------------------------------------------------------------------------
+
+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 Platform Extensions component
+
+  Realm Platform Extensions License
+
+  Copyright (c) 2011-2017 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.
+
+3. -------------------------------------------------------------------------------
+
+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.
+</string>
+                       <key>License</key>
+                       <string>Apache 2.0</string>
+                       <key>Title</key>
+                       <string>Realm</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>TABLE OF CONTENTS
+
+1. Apache License version 2.0
+2. Realm Components
+3. Export Compliance
+
+1. -------------------------------------------------------------------------------
+
+                                 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.
+
+2. -------------------------------------------------------------------------------
+
+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 Platform Extensions component
+
+  Realm Platform Extensions License
+
+  Copyright (c) 2011-2017 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.
+
+3. -------------------------------------------------------------------------------
+
+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.
+</string>
+                       <key>License</key>
+                       <string>Apache 2.0</string>
+                       <key>Title</key>
+                       <string>RealmSwift</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2010-2015, Sam Soffes, http://soff.es
+
+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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>SSZipArchive</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2015 Jonathan Kent &lt;contact@jonkent.me&gt;
+
+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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>SideMenu</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2014 Jason Rendel
+
+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.
+
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>SwiftKeychainWrapper</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2015-2017 Charles Scalesse.
+
+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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>Toast-Swift</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2014 Amornchai Kanokpullwad
+
+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.</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>ZFDragableModalTransition</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2011 Petteri Aimonen &lt;jpa at nanopb.mail.kapsi.fi&gt;
+
+This software is provided 'as-is', without any express or 
+implied warranty. In no event will the authors be held liable 
+for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any 
+purpose, including commercial applications, and to alter it and 
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you 
+   must not claim that you wrote the original software. If you use 
+   this software in a product, an acknowledgment in the product 
+   documentation would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and 
+   must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source 
+   distribution.
+</string>
+                       <key>License</key>
+                       <string>zlib</string>
+                       <key>Title</key>
+                       <string>nanopb</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Generated by CocoaPods - https://cocoapods.org</string>
+                       <key>Title</key>
+                       <string></string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+       </array>
+       <key>StringsTable</key>
+       <string>Acknowledgements</string>
+       <key>Title</key>
+       <string>Acknowledgements</string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-dummy.m b/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-dummy.m
new file mode 100644 (file)
index 0000000..0e2c71d
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Pods_WolneLektury : NSObject
+@end
+@implementation PodsDummy_Pods_WolneLektury
+@end
diff --git a/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-frameworks.sh b/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-frameworks.sh
new file mode 100755 (executable)
index 0000000..e2c381d
--- /dev/null
@@ -0,0 +1,195 @@
+#!/bin/sh
+set -e
+set -u
+set -o pipefail
+
+if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
+    # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
+    # frameworks to, so exit 0 (signalling the script phase was successful).
+    exit 0
+fi
+
+echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+
+COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
+SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
+
+# Used as a return value for each invocation of `strip_invalid_archs` function.
+STRIP_BINARY_RETVAL=0
+
+# This protects against multiple targets copying the same framework dependency at the same time. The solution
+# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
+RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
+
+# Copies and strips a vendored framework
+install_framework()
+{
+  if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
+    local source="${BUILT_PRODUCTS_DIR}/$1"
+  elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
+    local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
+  elif [ -r "$1" ]; then
+    local source="$1"
+  fi
+
+  local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+
+  if [ -L "${source}" ]; then
+      echo "Symlinked..."
+      source="$(readlink "${source}")"
+  fi
+
+  # Use filter instead of exclude so missing patterns don't throw errors.
+  echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
+  rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
+
+  local basename
+  basename="$(basename -s .framework "$1")"
+  binary="${destination}/${basename}.framework/${basename}"
+  if ! [ -r "$binary" ]; then
+    binary="${destination}/${basename}"
+  fi
+
+  # Strip invalid architectures so "fat" simulator / device frameworks work on device
+  if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
+    strip_invalid_archs "$binary"
+  fi
+
+  # Resign the code if required by the build settings to avoid unstable apps
+  code_sign_if_enabled "${destination}/$(basename "$1")"
+
+  # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
+  if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
+    local swift_runtime_libs
+    swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u  && exit ${PIPESTATUS[0]})
+    for lib in $swift_runtime_libs; do
+      echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
+      rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
+      code_sign_if_enabled "${destination}/${lib}"
+    done
+  fi
+}
+
+# Copies and strips a vendored dSYM
+install_dsym() {
+  local source="$1"
+  if [ -r "$source" ]; then
+    # Copy the dSYM into a the targets temp dir.
+    echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
+    rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
+
+    local basename
+    basename="$(basename -s .framework.dSYM "$source")"
+    binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
+
+    # Strip invalid architectures so "fat" simulator / device frameworks work on device
+    if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then
+      strip_invalid_archs "$binary"
+    fi
+
+    if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
+      # Move the stripped file into its final destination.
+      echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
+      rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
+    else
+      # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
+      touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM"
+    fi
+  fi
+}
+
+# Signs a framework with the provided identity
+code_sign_if_enabled() {
+  if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
+    # Use the current code_sign_identitiy
+    echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
+    local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
+
+    if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
+      code_sign_cmd="$code_sign_cmd &"
+    fi
+    echo "$code_sign_cmd"
+    eval "$code_sign_cmd"
+  fi
+}
+
+# Strip invalid architectures
+strip_invalid_archs() {
+  binary="$1"
+  # Get architectures for current target binary
+  binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
+  # Intersect them with the architectures we are building for
+  intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
+  # If there are no archs supported by this binary then warn the user
+  if [[ -z "$intersected_archs" ]]; then
+    echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
+    STRIP_BINARY_RETVAL=0
+    return
+  fi
+  stripped=""
+  for arch in $binary_archs; do
+    if ! [[ "${ARCHS}" == *"$arch"* ]]; then
+      # Strip non-valid architectures in-place
+      lipo -remove "$arch" -output "$binary" "$binary" || exit 1
+      stripped="$stripped $arch"
+    fi
+  done
+  if [[ "$stripped" ]]; then
+    echo "Stripped $binary of architectures:$stripped"
+  fi
+  STRIP_BINARY_RETVAL=1
+}
+
+
+if [[ "$CONFIGURATION" == "Debug" ]]; then
+  install_framework "${BUILT_PRODUCTS_DIR}/AEXML/AEXML.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/AlamofireActivityLogger/AlamofireActivityLogger.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/FolioReaderKit/FolioReaderKit.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/FontBlaster/FontBlaster.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/JSQWebViewController/JSQWebViewController.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/MZDownloadManager/MZDownloadManager.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/MatomoTracker/MatomoTracker.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/MenuItemKit/MenuItemKit.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/OAuthSwift/OAuthSwift.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/SideMenu/SideMenu.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Toast-Swift/Toast_Swift.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/ZFDragableModalTransition/ZFDragableModalTransition.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework"
+fi
+if [[ "$CONFIGURATION" == "Release" ]]; then
+  install_framework "${BUILT_PRODUCTS_DIR}/AEXML/AEXML.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/AlamofireActivityLogger/AlamofireActivityLogger.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/FolioReaderKit/FolioReaderKit.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/FontBlaster/FontBlaster.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/JSQWebViewController/JSQWebViewController.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/MZDownloadManager/MZDownloadManager.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/MatomoTracker/MatomoTracker.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/MenuItemKit/MenuItemKit.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/OAuthSwift/OAuthSwift.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/SideMenu/SideMenu.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/Toast-Swift/Toast_Swift.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/ZFDragableModalTransition/ZFDragableModalTransition.framework"
+  install_framework "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework"
+fi
+if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
+  wait
+fi
diff --git a/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-resources.sh b/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-resources.sh
new file mode 100755 (executable)
index 0000000..345301f
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/sh
+set -e
+set -u
+set -o pipefail
+
+if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then
+    # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy
+    # resources to, so exit 0 (signalling the script phase was successful).
+    exit 0
+fi
+
+mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+
+RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
+> "$RESOURCES_TO_COPY"
+
+XCASSET_FILES=()
+
+# This protects against multiple targets copying the same framework dependency at the same time. The solution
+# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
+RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
+
+case "${TARGETED_DEVICE_FAMILY:-}" in
+  1,2)
+    TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
+    ;;
+  1)
+    TARGET_DEVICE_ARGS="--target-device iphone"
+    ;;
+  2)
+    TARGET_DEVICE_ARGS="--target-device ipad"
+    ;;
+  3)
+    TARGET_DEVICE_ARGS="--target-device tv"
+    ;;
+  4)
+    TARGET_DEVICE_ARGS="--target-device watch"
+    ;;
+  *)
+    TARGET_DEVICE_ARGS="--target-device mac"
+    ;;
+esac
+
+install_resource()
+{
+  if [[ "$1" = /* ]] ; then
+    RESOURCE_PATH="$1"
+  else
+    RESOURCE_PATH="${PODS_ROOT}/$1"
+  fi
+  if [[ ! -e "$RESOURCE_PATH" ]] ; then
+    cat << EOM
+error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
+EOM
+    exit 1
+  fi
+  case $RESOURCE_PATH in
+    *.storyboard)
+      echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
+      ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
+      ;;
+    *.xib)
+      echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
+      ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
+      ;;
+    *.framework)
+      echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
+      mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+      echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
+      rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+      ;;
+    *.xcdatamodel)
+      echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
+      xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
+      ;;
+    *.xcdatamodeld)
+      echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
+      xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
+      ;;
+    *.xcmappingmodel)
+      echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
+      xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
+      ;;
+    *.xcassets)
+      ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
+      XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
+      ;;
+    *)
+      echo "$RESOURCE_PATH" || true
+      echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
+      ;;
+  esac
+}
+
+mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
+  mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+  rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+fi
+rm -f "$RESOURCES_TO_COPY"
+
+if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ]
+then
+  # Find all other xcassets (this unfortunately includes those of path pods and other targets).
+  OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
+  while read line; do
+    if [[ $line != "${PODS_ROOT}*" ]]; then
+      XCASSET_FILES+=("$line")
+    fi
+  done <<<"$OTHER_XCASSETS"
+
+  if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then
+    printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+  else
+    printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist"
+  fi
+fi
diff --git a/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-umbrella.h b/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-umbrella.h
new file mode 100644 (file)
index 0000000..dbb5be4
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double Pods_WolneLekturyVersionNumber;
+FOUNDATION_EXPORT const unsigned char Pods_WolneLekturyVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.debug.xcconfig b/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.debug.xcconfig
new file mode 100644 (file)
index 0000000..88f2d48
--- /dev/null
@@ -0,0 +1,12 @@
+ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AEXML" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/AlamofireActivityLogger" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseMessaging" "${PODS_CONFIGURATION_BUILD_DIR}/FolioReaderKit" "${PODS_CONFIGURATION_BUILD_DIR}/FontBlaster" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/JSQWebViewController" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MZDownloadManager" "${PODS_CONFIGURATION_BUILD_DIR}/MatomoTracker" "${PODS_CONFIGURATION_BUILD_DIR}/MenuItemKit" "${PODS_CONFIGURATION_BUILD_DIR}/OAuthSwift" "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf" "${PODS_CONFIGURATION_BUILD_DIR}/Realm" "${PODS_CONFIGURATION_BUILD_DIR}/RealmSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/SideMenu" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/Toast-Swift" "${PODS_CONFIGURATION_BUILD_DIR}/ZFDragableModalTransition" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/FirebaseInstanceID/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks"
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1
+HEADER_SEARCH_PATHS = $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase"
+LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
+OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/AEXML/AEXML.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/AlamofireActivityLogger/AlamofireActivityLogger.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseMessaging/FirebaseMessaging.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FolioReaderKit/FolioReaderKit.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FontBlaster/FontBlaster.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/JSQWebViewController/JSQWebViewController.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MZDownloadManager/MZDownloadManager.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MatomoTracker/MatomoTracker.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MenuItemKit/MenuItemKit.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/OAuthSwift/OAuthSwift.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Realm/Realm.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RealmSwift/RealmSwift.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive/SSZipArchive.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SideMenu/SideMenu.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Toast-Swift/Toast_Swift.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ZFDragableModalTransition/ZFDragableModalTransition.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Firebase"
+OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "AEXML" -framework "Alamofire" -framework "AlamofireActivityLogger" -framework "Crashlytics" -framework "FIRAnalyticsConnector" -framework "Fabric" -framework "FirebaseAnalytics" -framework "FirebaseCore" -framework "FirebaseCoreDiagnostics" -framework "FirebaseInstanceID" -framework "FirebaseMessaging" -framework "FolioReaderKit" -framework "FontBlaster" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "GoogleUtilities" -framework "JSQWebViewController" -framework "Kingfisher" -framework "MBProgressHUD" -framework "MZDownloadManager" -framework "MatomoTracker" -framework "MenuItemKit" -framework "OAuthSwift" -framework "Protobuf" -framework "Realm" -framework "RealmSwift" -framework "SSZipArchive" -framework "Security" -framework "SideMenu" -framework "StoreKit" -framework "SwiftKeychainWrapper" -framework "SystemConfiguration" -framework "Toast_Swift" -framework "UIKit" -framework "ZFDragableModalTransition" -framework "nanopb"
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
+PODS_ROOT = ${SRCROOT}/Pods
diff --git a/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.modulemap b/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.modulemap
new file mode 100644 (file)
index 0000000..bfcebbb
--- /dev/null
@@ -0,0 +1,6 @@
+framework module Pods_WolneLektury {
+  umbrella header "Pods-WolneLektury-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.release.xcconfig b/iOS/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.release.xcconfig
new file mode 100644 (file)
index 0000000..88f2d48
--- /dev/null
@@ -0,0 +1,12 @@
+ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AEXML" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/AlamofireActivityLogger" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseMessaging" "${PODS_CONFIGURATION_BUILD_DIR}/FolioReaderKit" "${PODS_CONFIGURATION_BUILD_DIR}/FontBlaster" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/JSQWebViewController" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MZDownloadManager" "${PODS_CONFIGURATION_BUILD_DIR}/MatomoTracker" "${PODS_CONFIGURATION_BUILD_DIR}/MenuItemKit" "${PODS_CONFIGURATION_BUILD_DIR}/OAuthSwift" "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf" "${PODS_CONFIGURATION_BUILD_DIR}/Realm" "${PODS_CONFIGURATION_BUILD_DIR}/RealmSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/SideMenu" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/Toast-Swift" "${PODS_CONFIGURATION_BUILD_DIR}/ZFDragableModalTransition" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/FirebaseInstanceID/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks"
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1
+HEADER_SEARCH_PATHS = $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase"
+LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
+OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/AEXML/AEXML.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/AlamofireActivityLogger/AlamofireActivityLogger.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseMessaging/FirebaseMessaging.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FolioReaderKit/FolioReaderKit.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FontBlaster/FontBlaster.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/JSQWebViewController/JSQWebViewController.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MZDownloadManager/MZDownloadManager.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MatomoTracker/MatomoTracker.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/MenuItemKit/MenuItemKit.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/OAuthSwift/OAuthSwift.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Realm/Realm.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RealmSwift/RealmSwift.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive/SSZipArchive.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SideMenu/SideMenu.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Toast-Swift/Toast_Swift.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ZFDragableModalTransition/ZFDragableModalTransition.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Firebase"
+OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "AEXML" -framework "Alamofire" -framework "AlamofireActivityLogger" -framework "Crashlytics" -framework "FIRAnalyticsConnector" -framework "Fabric" -framework "FirebaseAnalytics" -framework "FirebaseCore" -framework "FirebaseCoreDiagnostics" -framework "FirebaseInstanceID" -framework "FirebaseMessaging" -framework "FolioReaderKit" -framework "FontBlaster" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "GoogleUtilities" -framework "JSQWebViewController" -framework "Kingfisher" -framework "MBProgressHUD" -framework "MZDownloadManager" -framework "MatomoTracker" -framework "MenuItemKit" -framework "OAuthSwift" -framework "Protobuf" -framework "Realm" -framework "RealmSwift" -framework "SSZipArchive" -framework "Security" -framework "SideMenu" -framework "StoreKit" -framework "SwiftKeychainWrapper" -framework "SystemConfiguration" -framework "Toast_Swift" -framework "UIKit" -framework "ZFDragableModalTransition" -framework "nanopb"
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
+PODS_ROOT = ${SRCROOT}/Pods
diff --git a/iOS/Pods/Target Support Files/Protobuf/Info.plist b/iOS/Pods/Target Support Files/Protobuf/Info.plist
new file mode 100644 (file)
index 0000000..646f73c
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>3.6.1</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/Protobuf/Protobuf-dummy.m b/iOS/Pods/Target Support Files/Protobuf/Protobuf-dummy.m
new file mode 100644 (file)
index 0000000..e0f0a33
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Protobuf : NSObject
+@end
+@implementation PodsDummy_Protobuf
+@end
diff --git a/iOS/Pods/Target Support Files/Protobuf/Protobuf-prefix.pch b/iOS/Pods/Target Support Files/Protobuf/Protobuf-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/Protobuf/Protobuf-umbrella.h b/iOS/Pods/Target Support Files/Protobuf/Protobuf-umbrella.h
new file mode 100644 (file)
index 0000000..9242d72
--- /dev/null
@@ -0,0 +1,54 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "GPBArray.h"
+#import "GPBArray_PackagePrivate.h"
+#import "GPBBootstrap.h"
+#import "GPBCodedInputStream.h"
+#import "GPBCodedInputStream_PackagePrivate.h"
+#import "GPBCodedOutputStream.h"
+#import "GPBCodedOutputStream_PackagePrivate.h"
+#import "GPBDescriptor.h"
+#import "GPBDescriptor_PackagePrivate.h"
+#import "GPBDictionary.h"
+#import "GPBDictionary_PackagePrivate.h"
+#import "GPBExtensionInternals.h"
+#import "GPBExtensionRegistry.h"
+#import "GPBMessage.h"
+#import "GPBMessage_PackagePrivate.h"
+#import "GPBProtocolBuffers.h"
+#import "GPBProtocolBuffers_RuntimeSupport.h"
+#import "GPBRootObject.h"
+#import "GPBRootObject_PackagePrivate.h"
+#import "GPBRuntimeTypes.h"
+#import "GPBUnknownField.h"
+#import "GPBUnknownFieldSet.h"
+#import "GPBUnknownFieldSet_PackagePrivate.h"
+#import "GPBUnknownField_PackagePrivate.h"
+#import "GPBUtilities.h"
+#import "GPBUtilities_PackagePrivate.h"
+#import "GPBWellKnownTypes.h"
+#import "GPBWireFormat.h"
+#import "Any.pbobjc.h"
+#import "Api.pbobjc.h"
+#import "Duration.pbobjc.h"
+#import "Empty.pbobjc.h"
+#import "FieldMask.pbobjc.h"
+#import "SourceContext.pbobjc.h"
+#import "Struct.pbobjc.h"
+#import "Timestamp.pbobjc.h"
+#import "Type.pbobjc.h"
+#import "Wrappers.pbobjc.h"
+
+FOUNDATION_EXPORT double ProtobufVersionNumber;
+FOUNDATION_EXPORT const unsigned char ProtobufVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/Protobuf/Protobuf.modulemap b/iOS/Pods/Target Support Files/Protobuf/Protobuf.modulemap
new file mode 100644 (file)
index 0000000..57d3b39
--- /dev/null
@@ -0,0 +1,6 @@
+framework module Protobuf {
+  umbrella header "Protobuf-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/Protobuf/Protobuf.xcconfig b/iOS/Pods/Target Support Files/Protobuf/Protobuf.xcconfig
new file mode 100644 (file)
index 0000000..12e3c31
--- /dev/null
@@ -0,0 +1,8 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Protobuf
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/Protobuf
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/Realm/Info.plist b/iOS/Pods/Target Support Files/Realm/Info.plist
new file mode 100644 (file)
index 0000000..793d31a
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>3.1.1</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/Realm/Realm-dummy.m b/iOS/Pods/Target Support Files/Realm/Realm-dummy.m
new file mode 100644 (file)
index 0000000..dd775bf
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Realm : NSObject
+@end
+@implementation PodsDummy_Realm
+@end
diff --git a/iOS/Pods/Target Support Files/Realm/Realm-prefix.pch b/iOS/Pods/Target Support Files/Realm/Realm-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/Realm/Realm.modulemap b/iOS/Pods/Target Support Files/Realm/Realm.modulemap
new file mode 100644 (file)
index 0000000..42845f6
--- /dev/null
@@ -0,0 +1,31 @@
+framework module Realm {
+    umbrella header "Realm.h"
+
+    export *
+    module * { export * }
+
+    explicit module Private {
+        header "RLMAccessor.h"
+        header "RLMArray_Private.h"
+        header "RLMCollection_Private.h"
+        header "RLMListBase.h"
+        header "RLMObject_Private.h"
+        header "RLMObjectBase_Dynamic.h"
+        header "RLMObjectBase_Private.h"
+        header "RLMObjectSchema_Private.h"
+        header "RLMObjectStore.h"
+        header "RLMOptionalBase.h"
+        header "RLMProperty_Private.h"
+        header "RLMRealm_Private.h"
+        header "RLMRealmConfiguration_Private.h"
+        header "RLMResults_Private.h"
+        header "RLMSchema_Private.h"
+        header "RLMSyncConfiguration_Private.h"
+        header "RLMSyncUtil_Private.h"
+    }
+
+    explicit module Dynamic {
+        header "RLMRealm_Dynamic.h"
+        header "RLMObjectBase_Dynamic.h"
+    }
+}
diff --git a/iOS/Pods/Target Support Files/Realm/Realm.xcconfig b/iOS/Pods/Target Support Files/Realm/Realm.xcconfig
new file mode 100644 (file)
index 0000000..77f6c87
--- /dev/null
@@ -0,0 +1,14 @@
+APPLICATION_EXTENSION_API_ONLY = YES
+CLANG_CXX_LANGUAGE_STANDARD = c++14
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Realm
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Realm/core"
+OTHER_CPLUSPLUSFLAGS = -isystem "${PODS_ROOT}/Realm/include/core"
+OTHER_LDFLAGS = -l"c++" -l"realmcore-ios" -l"z"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/Realm
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USER_HEADER_SEARCH_PATHS = "${PODS_ROOT}/Realm/include" "${PODS_ROOT}/Realm/include/Realm"
diff --git a/iOS/Pods/Target Support Files/RealmSwift/Info.plist b/iOS/Pods/Target Support Files/RealmSwift/Info.plist
new file mode 100644 (file)
index 0000000..793d31a
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>3.1.1</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/RealmSwift/RealmSwift-dummy.m b/iOS/Pods/Target Support Files/RealmSwift/RealmSwift-dummy.m
new file mode 100644 (file)
index 0000000..cf759b1
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_RealmSwift : NSObject
+@end
+@implementation PodsDummy_RealmSwift
+@end
diff --git a/iOS/Pods/Target Support Files/RealmSwift/RealmSwift-prefix.pch b/iOS/Pods/Target Support Files/RealmSwift/RealmSwift-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/RealmSwift/RealmSwift-umbrella.h b/iOS/Pods/Target Support Files/RealmSwift/RealmSwift-umbrella.h
new file mode 100644 (file)
index 0000000..6163cfc
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double RealmSwiftVersionNumber;
+FOUNDATION_EXPORT const unsigned char RealmSwiftVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/RealmSwift/RealmSwift.modulemap b/iOS/Pods/Target Support Files/RealmSwift/RealmSwift.modulemap
new file mode 100644 (file)
index 0000000..7f9767a
--- /dev/null
@@ -0,0 +1,6 @@
+framework module RealmSwift {
+  umbrella header "RealmSwift-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/RealmSwift/RealmSwift.xcconfig b/iOS/Pods/Target Support Files/RealmSwift/RealmSwift.xcconfig
new file mode 100644 (file)
index 0000000..b3d79d8
--- /dev/null
@@ -0,0 +1,12 @@
+APPLICATION_EXTENSION_API_ONLY = YES
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RealmSwift
+FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Realm"
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/RealmSwift
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+SWIFT_WHOLE_MODULE_OPTIMIZATION = YES
diff --git a/iOS/Pods/Target Support Files/SSZipArchive/Info.plist b/iOS/Pods/Target Support Files/SSZipArchive/Info.plist
new file mode 100644 (file)
index 0000000..92aaf05
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>2.1.1</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive-dummy.m b/iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive-dummy.m
new file mode 100644 (file)
index 0000000..9fabf79
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_SSZipArchive : NSObject
+@end
+@implementation PodsDummy_SSZipArchive
+@end
diff --git a/iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive-prefix.pch b/iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive-umbrella.h b/iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive-umbrella.h
new file mode 100644 (file)
index 0000000..727fe84
--- /dev/null
@@ -0,0 +1,19 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "SSZipArchive.h"
+#import "SSZipCommon.h"
+#import "ZipArchive.h"
+
+FOUNDATION_EXPORT double SSZipArchiveVersionNumber;
+FOUNDATION_EXPORT const unsigned char SSZipArchiveVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive.modulemap b/iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive.modulemap
new file mode 100644 (file)
index 0000000..c30b519
--- /dev/null
@@ -0,0 +1,6 @@
+framework module SSZipArchive {
+  umbrella header "SSZipArchive-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive.xcconfig b/iOS/Pods/Target Support Files/SSZipArchive/SSZipArchive.xcconfig
new file mode 100644 (file)
index 0000000..62b7927
--- /dev/null
@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -l"z"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/SSZipArchive
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/SideMenu/Info.plist b/iOS/Pods/Target Support Files/SideMenu/Info.plist
new file mode 100644 (file)
index 0000000..c1aea25
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>3.1.5</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/SideMenu/SideMenu-dummy.m b/iOS/Pods/Target Support Files/SideMenu/SideMenu-dummy.m
new file mode 100644 (file)
index 0000000..bd363f7
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_SideMenu : NSObject
+@end
+@implementation PodsDummy_SideMenu
+@end
diff --git a/iOS/Pods/Target Support Files/SideMenu/SideMenu-prefix.pch b/iOS/Pods/Target Support Files/SideMenu/SideMenu-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/SideMenu/SideMenu-umbrella.h b/iOS/Pods/Target Support Files/SideMenu/SideMenu-umbrella.h
new file mode 100644 (file)
index 0000000..e005106
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double SideMenuVersionNumber;
+FOUNDATION_EXPORT const unsigned char SideMenuVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/SideMenu/SideMenu.modulemap b/iOS/Pods/Target Support Files/SideMenu/SideMenu.modulemap
new file mode 100644 (file)
index 0000000..796e914
--- /dev/null
@@ -0,0 +1,6 @@
+framework module SideMenu {
+  umbrella header "SideMenu-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/SideMenu/SideMenu.xcconfig b/iOS/Pods/Target Support Files/SideMenu/SideMenu.xcconfig
new file mode 100644 (file)
index 0000000..1888079
--- /dev/null
@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SideMenu
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/SideMenu
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/SwiftKeychainWrapper/Info.plist b/iOS/Pods/Target Support Files/SwiftKeychainWrapper/Info.plist
new file mode 100644 (file)
index 0000000..b0b461e
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>3.0.1</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-dummy.m b/iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-dummy.m
new file mode 100644 (file)
index 0000000..995098c
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_SwiftKeychainWrapper : NSObject
+@end
+@implementation PodsDummy_SwiftKeychainWrapper
+@end
diff --git a/iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-prefix.pch b/iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-umbrella.h b/iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper-umbrella.h
new file mode 100644 (file)
index 0000000..7d05eda
--- /dev/null
@@ -0,0 +1,17 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "SwiftKeychainWrapper.h"
+
+FOUNDATION_EXPORT double SwiftKeychainWrapperVersionNumber;
+FOUNDATION_EXPORT const unsigned char SwiftKeychainWrapperVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper.modulemap b/iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper.modulemap
new file mode 100644 (file)
index 0000000..313839b
--- /dev/null
@@ -0,0 +1,6 @@
+framework module SwiftKeychainWrapper {
+  umbrella header "SwiftKeychainWrapper-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper.xcconfig b/iOS/Pods/Target Support Files/SwiftKeychainWrapper/SwiftKeychainWrapper.xcconfig
new file mode 100644 (file)
index 0000000..286f980
--- /dev/null
@@ -0,0 +1,9 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftKeychainWrapper
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/Toast-Swift/Info.plist b/iOS/Pods/Target Support Files/Toast-Swift/Info.plist
new file mode 100644 (file)
index 0000000..b0b461e
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>3.0.1</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift-dummy.m b/iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift-dummy.m
new file mode 100644 (file)
index 0000000..2962aa4
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Toast_Swift : NSObject
+@end
+@implementation PodsDummy_Toast_Swift
+@end
diff --git a/iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift-prefix.pch b/iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift-umbrella.h b/iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift-umbrella.h
new file mode 100644 (file)
index 0000000..8abdfa0
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+
+FOUNDATION_EXPORT double Toast_SwiftVersionNumber;
+FOUNDATION_EXPORT const unsigned char Toast_SwiftVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift.modulemap b/iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift.modulemap
new file mode 100644 (file)
index 0000000..af3db9f
--- /dev/null
@@ -0,0 +1,6 @@
+framework module Toast_Swift {
+  umbrella header "Toast-Swift-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift.xcconfig b/iOS/Pods/Target Support Files/Toast-Swift/Toast-Swift.xcconfig
new file mode 100644 (file)
index 0000000..f1bf94c
--- /dev/null
@@ -0,0 +1,10 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Toast-Swift
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = -framework "QuartzCore"
+OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/Toast-Swift
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/ZFDragableModalTransition/Info.plist b/iOS/Pods/Target Support Files/ZFDragableModalTransition/Info.plist
new file mode 100644 (file)
index 0000000..89a577b
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>0.6.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-dummy.m b/iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-dummy.m
new file mode 100644 (file)
index 0000000..88b59a6
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_ZFDragableModalTransition : NSObject
+@end
+@implementation PodsDummy_ZFDragableModalTransition
+@end
diff --git a/iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-prefix.pch b/iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-umbrella.h b/iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition-umbrella.h
new file mode 100644 (file)
index 0000000..7fec012
--- /dev/null
@@ -0,0 +1,17 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "ZFModalTransitionAnimator.h"
+
+FOUNDATION_EXPORT double ZFDragableModalTransitionVersionNumber;
+FOUNDATION_EXPORT const unsigned char ZFDragableModalTransitionVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition.modulemap b/iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition.modulemap
new file mode 100644 (file)
index 0000000..1f2af5b
--- /dev/null
@@ -0,0 +1,6 @@
+framework module ZFDragableModalTransition {
+  umbrella header "ZFDragableModalTransition-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition.xcconfig b/iOS/Pods/Target Support Files/ZFDragableModalTransition/ZFDragableModalTransition.xcconfig
new file mode 100644 (file)
index 0000000..f23d314
--- /dev/null
@@ -0,0 +1,8 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/ZFDragableModalTransition
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/ZFDragableModalTransition
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Target Support Files/nanopb/Info.plist b/iOS/Pods/Target Support Files/nanopb/Info.plist
new file mode 100644 (file)
index 0000000..2cb2374
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>${EXECUTABLE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${PRODUCT_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>0.3.8</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>${CURRENT_PROJECT_VERSION}</string>
+  <key>NSPrincipalClass</key>
+  <string></string>
+</dict>
+</plist>
diff --git a/iOS/Pods/Target Support Files/nanopb/nanopb-dummy.m b/iOS/Pods/Target Support Files/nanopb/nanopb-dummy.m
new file mode 100644 (file)
index 0000000..b3fa595
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_nanopb : NSObject
+@end
+@implementation PodsDummy_nanopb
+@end
diff --git a/iOS/Pods/Target Support Files/nanopb/nanopb-prefix.pch b/iOS/Pods/Target Support Files/nanopb/nanopb-prefix.pch
new file mode 100644 (file)
index 0000000..beb2a24
--- /dev/null
@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
diff --git a/iOS/Pods/Target Support Files/nanopb/nanopb-umbrella.h b/iOS/Pods/Target Support Files/nanopb/nanopb-umbrella.h
new file mode 100644 (file)
index 0000000..07e77b3
--- /dev/null
@@ -0,0 +1,26 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+
+#import "pb.h"
+#import "pb_common.h"
+#import "pb_decode.h"
+#import "pb_encode.h"
+#import "pb.h"
+#import "pb_decode.h"
+#import "pb_common.h"
+#import "pb.h"
+#import "pb_encode.h"
+#import "pb_common.h"
+
+FOUNDATION_EXPORT double nanopbVersionNumber;
+FOUNDATION_EXPORT const unsigned char nanopbVersionString[];
+
diff --git a/iOS/Pods/Target Support Files/nanopb/nanopb.modulemap b/iOS/Pods/Target Support Files/nanopb/nanopb.modulemap
new file mode 100644 (file)
index 0000000..e8d4b53
--- /dev/null
@@ -0,0 +1,6 @@
+framework module nanopb {
+  umbrella header "nanopb-umbrella.h"
+
+  export *
+  module * { export * }
+}
diff --git a/iOS/Pods/Target Support Files/nanopb/nanopb.xcconfig b/iOS/Pods/Target Support Files/nanopb/nanopb.xcconfig
new file mode 100644 (file)
index 0000000..48a6a8f
--- /dev/null
@@ -0,0 +1,8 @@
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/nanopb
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/nanopb
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
diff --git a/iOS/Pods/Toast-Swift/LICENSE b/iOS/Pods/Toast-Swift/LICENSE
new file mode 100644 (file)
index 0000000..14ba7c7
--- /dev/null
@@ -0,0 +1,20 @@
+Copyright (c) 2015-2017 Charles Scalesse.
+
+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/iOS/Pods/Toast-Swift/README.md b/iOS/Pods/Toast-Swift/README.md
new file mode 100644 (file)
index 0000000..a58fb08
--- /dev/null
@@ -0,0 +1,133 @@
+Toast-Swift
+=============
+
+[![Build Status](https://travis-ci.org/scalessec/Toast-Swift.svg?branch=master)](https://travis-ci.org/scalessec/Toast-Swift)
+[![CocoaPods Version](https://img.shields.io/cocoapods/v/Toast-Swift.svg)](http://cocoadocs.org/docsets/Toast-Swift)
+[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
+
+Toast-Swift is a Swift extension that adds toast notifications to the `UIView` object class. It is intended to be simple, lightweight, and easy to use. Most toast notifications can be triggered with a single line of code.
+
+**Toast-Swift is a native Swift port of [Toast for iOS](https://github.com/scalessec/Toast "Toast for iOS").**
+
+Screenshots
+---------
+![Toast-Swift Screenshots](toast_swift_screenshot.jpg)
+
+
+Basic Examples
+---------
+```swift
+// basic usage
+self.view.makeToast("This is a piece of toast")
+
+// toast with a specific duration and position
+self.view.makeToast("This is a piece of toast", duration: 3.0, position: .top)
+
+// toast presented with multiple options and with a completion closure
+self.view.makeToast("This is a piece of toast", duration: 2.0, point: CGPoint(x: 110.0, y: 110.0), title: "Toast Title", image: UIImage(named: "toast.png")) { didTap in
+    if didTap {
+        print("completion from tap")
+    } else {
+        print("completion without tap")
+    }
+}
+
+// display toast with an activity spinner
+self.view.makeToastActivity(.center)
+
+// display any view as toast
+self.view.showToast(myView)
+
+// immediately hides all toast views in self.view
+self.view.hideAllToasts()
+```
+
+But wait, there's more!
+---------
+```swift
+// create a new style
+var style = ToastStyle()
+
+// this is just one of many style options
+style.messageColor = .blue
+
+// present the toast with the new style
+self.view.makeToast("This is a piece of toast", duration: 3.0, position: .bottom, style: style)
+
+// or perhaps you want to use this style for all toasts going forward?
+// just set the shared style and there's no need to provide the style again
+ToastManager.shared.style = style
+self.view.makeToast("This is a piece of toast") // now uses the shared style
+
+// toggle "tap to dismiss" functionality
+ToastManager.shared.tapToDismissEnabled = true
+
+// toggle queueing behavior
+ToastManager.shared.queueEnabled = true
+```
+
+See the demo project for more examples.
+
+
+Setup Instructions
+------------------
+
+[CocoaPods](http://cocoapods.org)
+------------------
+
+To integrate Toast-Swift into your Xcode project using CocoaPods, specify it in your `Podfile`:
+
+```ruby
+pod 'Toast-Swift', '~> 3.0.1'
+```
+
+and in your code add `import Toast_Swift`.
+
+[Carthage](https://github.com/Carthage/Carthage)
+------------------
+
+To integrate Toast-Swift into your Xcode project using Carthage, specify it in your `Cartfile`:
+
+```ogdl
+github "scalessec/Toast-Swift" ~> 3.0.1
+```
+
+Run `carthage update` to build the framework and drag the built `ToastSwiftFramework.framework` into your Xcode project.
+
+and in your code add `import ToastSwiftFramework`.
+
+Manually
+------------------
+
+1. Add `Toast.swift` to your project.
+2. Grab yourself a cold 🍺.
+
+Compatibility
+------------------
+* Version `3.0.0` and later requires Swift 4 and Xcode 9.
+* Version `2.0.0` requires Swift 3 and Xcode 8.
+* Version `1.4.0` requires Swift 2.2 and Xcode 7.3. 
+* Version `1.0.0` can be used with Swift 2.1 and earlier versions of Xcode.
+MIT License
+-----------
+    Copyright (c) 2015-2017 Charles Scalesse.
+
+    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/iOS/Pods/Toast-Swift/Toast/Toast.swift b/iOS/Pods/Toast-Swift/Toast/Toast.swift
new file mode 100644 (file)
index 0000000..bdda01a
--- /dev/null
@@ -0,0 +1,780 @@
+//
+//  Toast.swift
+//  Toast-Swift
+//
+//  Copyright (c) 2015-2017 Charles Scalesse.
+//
+//  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 UIKit
+import ObjectiveC
+
+/**
+ Toast is a Swift extension that adds toast notifications to the `UIView` object class.
+ It is intended to be simple, lightweight, and easy to use. Most toast notifications 
+ can be triggered with a single line of code.
+ The `makeToast` methods create a new view and then display it as toast.
+ The `showToast` methods display any view as toast.
+ */
+public extension UIView {
+    
+    /**
+     Keys used for associated objects.
+     */
+    private struct ToastKeys {
+        static var timer        = "com.toast-swift.timer"
+        static var duration     = "com.toast-swift.duration"
+        static var point        = "com.toast-swift.point"
+        static var completion   = "com.toast-swift.completion"
+        static var activeToasts = "com.toast-swift.activeToasts"
+        static var activityView = "com.toast-swift.activityView"
+        static var queue        = "com.toast-swift.queue"
+    }
+    
+    /**
+     Swift closures can't be directly associated with objects via the
+     Objective-C runtime, so the (ugly) solution is to wrap them in a
+     class that can be used with associated objects.
+     */
+    private class ToastCompletionWrapper {
+        let completion: ((Bool) -> Void)?
+        
+        init(_ completion: ((Bool) -> Void)?) {
+            self.completion = completion
+        }
+    }
+    
+    private enum ToastError: Error {
+        case missingParameters
+    }
+    
+    private var activeToasts: NSMutableArray {
+        get {
+            if let activeToasts = objc_getAssociatedObject(self, &ToastKeys.activeToasts) as? NSMutableArray {
+                return activeToasts
+            } else {
+                let activeToasts = NSMutableArray()
+                objc_setAssociatedObject(self, &ToastKeys.activeToasts, activeToasts, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+                return activeToasts
+            }
+        }
+    }
+    
+    private var queue: NSMutableArray {
+        get {
+            if let queue = objc_getAssociatedObject(self, &ToastKeys.queue) as? NSMutableArray {
+                return queue
+            } else {
+                let queue = NSMutableArray()
+                objc_setAssociatedObject(self, &ToastKeys.queue, queue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+                return queue
+            }
+        }
+    }
+    
+    // MARK: - Make Toast Methods
+    
+    /**
+     Creates and presents a new toast view.
+     
+     @param message The message to be displayed
+     @param duration The toast duration
+     @param position The toast's position
+     @param title The title
+     @param image The image
+     @param style The style. The shared style will be used when nil
+     @param completion The completion closure, executed after the toast view disappears.
+            didTap will be `true` if the toast view was dismissed from a tap.
+     */
+    public func makeToast(_ message: String?, duration: TimeInterval = ToastManager.shared.duration, position: ToastPosition = ToastManager.shared.position, title: String? = nil, image: UIImage? = nil, style: ToastStyle = ToastManager.shared.style, completion: ((_ didTap: Bool) -> Void)? = nil) {
+        do {
+            let toast = try toastViewForMessage(message, title: title, image: image, style: style)
+            showToast(toast, duration: duration, position: position, completion: completion)
+        } catch ToastError.missingParameters {
+            print("Error: message, title, and image are all nil")
+        } catch {}
+    }
+    
+    /**
+     Creates a new toast view and presents it at a given center point.
+     
+     @param message The message to be displayed
+     @param duration The toast duration
+     @param point The toast's center point
+     @param title The title
+     @param image The image
+     @param style The style. The shared style will be used when nil
+     @param completion The completion closure, executed after the toast view disappears.
+            didTap will be `true` if the toast view was dismissed from a tap.
+     */
+    public func makeToast(_ message: String?, duration: TimeInterval = ToastManager.shared.duration, point: CGPoint, title: String?, image: UIImage?, style: ToastStyle = ToastManager.shared.style, completion: ((_ didTap: Bool) -> Void)?) {
+        do {
+            let toast = try toastViewForMessage(message, title: title, image: image, style: style)
+            showToast(toast, duration: duration, point: point, completion: completion)
+        } catch ToastError.missingParameters {
+            print("Error: message, title, and image cannot all be nil")
+        } catch {}
+    }
+    
+    // MARK: - Show Toast Methods
+    
+    /**
+     Displays any view as toast at a provided position and duration. The completion closure
+     executes when the toast view completes. `didTap` will be `true` if the toast view was
+     dismissed from a tap.
+     
+     @param toast The view to be displayed as toast
+     @param duration The notification duration
+     @param position The toast's position
+     @param completion The completion block, executed after the toast view disappears.
+     didTap will be `true` if the toast view was dismissed from a tap.
+     */
+    public func showToast(_ toast: UIView, duration: TimeInterval = ToastManager.shared.duration, position: ToastPosition = ToastManager.shared.position, completion: ((_ didTap: Bool) -> Void)? = nil) {
+        let point = position.centerPoint(forToast: toast, inSuperview: self)
+        showToast(toast, duration: duration, point: point, completion: completion)
+    }
+    
+    /**
+     Displays any view as toast at a provided center point and duration. The completion closure
+     executes when the toast view completes. `didTap` will be `true` if the toast view was
+     dismissed from a tap.
+     
+     @param toast The view to be displayed as toast
+     @param duration The notification duration
+     @param point The toast's center point
+     @param completion The completion block, executed after the toast view disappears.
+     didTap will be `true` if the toast view was dismissed from a tap.
+     */
+    public func showToast(_ toast: UIView, duration: TimeInterval = ToastManager.shared.duration, point: CGPoint, completion: ((_ didTap: Bool) -> Void)? = nil) {
+        objc_setAssociatedObject(toast, &ToastKeys.completion, ToastCompletionWrapper(completion), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+        
+        if ToastManager.shared.isQueueEnabled, activeToasts.count > 0 {
+            objc_setAssociatedObject(toast, &ToastKeys.duration, NSNumber(value: duration), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+            objc_setAssociatedObject(toast, &ToastKeys.point, NSValue(cgPoint: point), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+            
+            queue.add(toast)
+        } else {
+            showToast(toast, duration: duration, point: point)
+        }
+    }
+    
+    // MARK: - Hide Toast Methods
+    
+    /**
+     Hides the active toast. If there are multiple toasts active in a view, this method
+     hides the oldest toast (the first of the toasts to have been presented).
+     
+     @see `hideAllToasts()` to remove all active toasts from a view.
+     
+     @warning This method has no effect on activity toasts. Use `hideToastActivity` to
+     hide activity toasts.
+     
+    */
+    public func hideToast() {
+        guard let activeToast = activeToasts.firstObject as? UIView else { return }
+        hideToast(activeToast)
+    }
+    
+    /**
+     Hides an active toast.
+     
+     @param toast The active toast view to dismiss. Any toast that is currently being displayed
+     on the screen is considered active.
+     
+     @warning this does not clear a toast view that is currently waiting in the queue.
+     */
+    public func hideToast(_ toast: UIView) {
+        guard activeToasts.contains(toast) else { return }
+        hideToast(toast, fromTap: false)
+    }
+    
+    /**
+     Hides all toast views.
+     
+     @param includeActivity If `true`, toast activity will also be hidden. Default is `false`.
+     @param clearQueue If `true`, removes all toast views from the queue. Default is `true`.
+    */
+    public func hideAllToasts(includeActivity: Bool = false, clearQueue: Bool = true) {
+        if clearQueue {
+            clearToastQueue()
+        }
+        
+        activeToasts.flatMap { $0 as? UIView }
+                    .forEach { hideToast($0) }
+        
+        if includeActivity {
+            hideToastActivity()
+        }
+    }
+    
+    /**
+     Removes all toast views from the queue. This has no effect on toast views that are
+     active. Use `hideAllToasts(clearQueue:)` to hide the active toasts views and clear
+     the queue.
+     */
+    public func clearToastQueue() {
+        queue.removeAllObjects()
+    }
+    
+    // MARK: - Activity Methods
+    
+    /**
+     Creates and displays a new toast activity indicator view at a specified position.
+    
+     @warning Only one toast activity indicator view can be presented per superview. Subsequent
+     calls to `makeToastActivity(position:)` will be ignored until `hideToastActivity()` is called.
+    
+     @warning `makeToastActivity(position:)` works independently of the `showToast` methods. Toast
+     activity views can be presented and dismissed while toast views are being displayed.
+     `makeToastActivity(position:)` has no effect on the queueing behavior of the `showToast` methods.
+    
+     @param position The toast's position
+     */
+    public func makeToastActivity(_ position: ToastPosition) {
+        // sanity
+        guard objc_getAssociatedObject(self, &ToastKeys.activityView) as? UIView == nil else { return }
+        
+        let toast = createToastActivityView()
+        let point = position.centerPoint(forToast: toast, inSuperview: self)
+        makeToastActivity(toast, point: point)
+    }
+    
+    /**
+     Creates and displays a new toast activity indicator view at a specified position.
+     
+     @warning Only one toast activity indicator view can be presented per superview. Subsequent
+     calls to `makeToastActivity(position:)` will be ignored until `hideToastActivity()` is called.
+     
+     @warning `makeToastActivity(position:)` works independently of the `showToast` methods. Toast
+     activity views can be presented and dismissed while toast views are being displayed.
+     `makeToastActivity(position:)` has no effect on the queueing behavior of the `showToast` methods.
+     
+     @param point The toast's center point
+     */
+    public func makeToastActivity(_ point: CGPoint) {
+        // sanity
+        guard objc_getAssociatedObject(self, &ToastKeys.activityView) as? UIView == nil else { return }
+        
+        let toast = createToastActivityView()
+        makeToastActivity(toast, point: point)
+    }
+    
+    /**
+     Dismisses the active toast activity indicator view.
+     */
+    public func hideToastActivity() {
+        if let toast = objc_getAssociatedObject(self, &ToastKeys.activityView) as? UIView {
+            UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseIn, .beginFromCurrentState], animations: {
+                toast.alpha = 0.0
+            }) { _ in
+                toast.removeFromSuperview()
+                objc_setAssociatedObject(self, &ToastKeys.activityView, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+            }
+        }
+    }
+    
+    // MARK: - Private Activity Methods
+    
+    private func makeToastActivity(_ toast: UIView, point: CGPoint) {
+        toast.alpha = 0.0
+        toast.center = point
+        
+        objc_setAssociatedObject(self, &ToastKeys.activityView, toast, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        
+        self.addSubview(toast)
+        
+        UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: .curveEaseOut, animations: {
+            toast.alpha = 1.0
+        })
+    }
+    
+    private func createToastActivityView() -> UIView {
+        let style = ToastManager.shared.style
+        
+        let activityView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: style.activitySize.width, height: style.activitySize.height))
+        activityView.backgroundColor = style.activityBackgroundColor
+        activityView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin]
+        activityView.layer.cornerRadius = style.cornerRadius
+        
+        if style.displayShadow {
+            activityView.layer.shadowColor = style.shadowColor.cgColor
+            activityView.layer.shadowOpacity = style.shadowOpacity
+            activityView.layer.shadowRadius = style.shadowRadius
+            activityView.layer.shadowOffset = style.shadowOffset
+        }
+        
+        let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
+        activityIndicatorView.center = CGPoint(x: activityView.bounds.size.width / 2.0, y: activityView.bounds.size.height / 2.0)
+        activityView.addSubview(activityIndicatorView)
+        activityIndicatorView.color = style.activityIndicatorColor
+        activityIndicatorView.startAnimating()
+        
+        return activityView
+    }
+    
+    // MARK: - Private Show/Hide Methods
+    
+    private func showToast(_ toast: UIView, duration: TimeInterval, point: CGPoint) {
+        toast.center = point
+        toast.alpha = 0.0
+        
+        if ToastManager.shared.isTapToDismissEnabled {
+            let recognizer = UITapGestureRecognizer(target: self, action: #selector(UIView.handleToastTapped(_:)))
+            toast.addGestureRecognizer(recognizer)
+            toast.isUserInteractionEnabled = true
+            toast.isExclusiveTouch = true
+        }
+        
+        activeToasts.add(toast)
+        self.addSubview(toast)
+        
+        UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseOut, .allowUserInteraction], animations: {
+            toast.alpha = 1.0
+        }) { _ in
+            let timer = Timer(timeInterval: duration, target: self, selector: #selector(UIView.toastTimerDidFinish(_:)), userInfo: toast, repeats: false)
+            RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
+            objc_setAssociatedObject(toast, &ToastKeys.timer, timer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+        }
+    }
+    
+    private func hideToast(_ toast: UIView, fromTap: Bool) {
+        if let timer = objc_getAssociatedObject(toast, &ToastKeys.timer) as? Timer {
+            timer.invalidate()
+        }
+        
+        UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseIn, .beginFromCurrentState], animations: {
+            toast.alpha = 0.0
+        }) { _ in
+            toast.removeFromSuperview()
+            self.activeToasts.remove(toast)
+            
+            if let wrapper = objc_getAssociatedObject(toast, &ToastKeys.completion) as? ToastCompletionWrapper, let completion = wrapper.completion {
+                completion(fromTap)
+            }
+            
+            if let nextToast = self.queue.firstObject as? UIView, let duration = objc_getAssociatedObject(nextToast, &ToastKeys.duration) as? NSNumber, let point = objc_getAssociatedObject(nextToast, &ToastKeys.point) as? NSValue {
+                self.queue.removeObject(at: 0)
+                self.showToast(nextToast, duration: duration.doubleValue, point: point.cgPointValue)
+            }
+        }
+    }
+    
+    // MARK: - Events
+    
+    @objc
+    private func handleToastTapped(_ recognizer: UITapGestureRecognizer) {
+        guard let toast = recognizer.view else { return }
+        hideToast(toast, fromTap: true)
+    }
+    
+    @objc
+    private func toastTimerDidFinish(_ timer: Timer) {
+        guard let toast = timer.userInfo as? UIView else { return }
+        hideToast(toast)
+    }
+    
+    // MARK: - Toast Construction
+    
+    /**
+     Creates a new toast view with any combination of message, title, and image.
+     The look and feel is configured via the style. Unlike the `makeToast` methods,
+     this method does not present the toast view automatically. One of the `showToast`
+     methods must be used to present the resulting view.
+    
+     @warning if message, title, and image are all nil, this method will throw
+     `ToastError.missingParameters`
+    
+     @param message The message to be displayed
+     @param title The title
+     @param image The image
+     @param style The style. The shared style will be used when nil
+     @throws `ToastError.missingParameters` when message, title, and image are all nil
+     @return The newly created toast view
+    */
+    public func toastViewForMessage(_ message: String?, title: String?, image: UIImage?, style: ToastStyle) throws -> UIView {
+        // sanity
+        guard message != nil || title != nil || image != nil else {
+            throw ToastError.missingParameters
+        }
+        
+        var messageLabel: UILabel?
+        var titleLabel: UILabel?
+        var imageView: UIImageView?
+        
+        let wrapperView = UIView()
+        wrapperView.backgroundColor = style.backgroundColor
+        wrapperView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin]
+        wrapperView.layer.cornerRadius = style.cornerRadius
+        
+        if style.displayShadow {
+            wrapperView.layer.shadowColor = UIColor.black.cgColor
+            wrapperView.layer.shadowOpacity = style.shadowOpacity
+            wrapperView.layer.shadowRadius = style.shadowRadius
+            wrapperView.layer.shadowOffset = style.shadowOffset
+        }
+        
+        if let image = image {
+            imageView = UIImageView(image: image)
+            imageView?.contentMode = .scaleAspectFit
+            imageView?.frame = CGRect(x: style.horizontalPadding, y: style.verticalPadding, width: style.imageSize.width, height: style.imageSize.height)
+        }
+        
+        var imageRect = CGRect.zero
+        
+        if let imageView = imageView {
+            imageRect.origin.x = style.horizontalPadding
+            imageRect.origin.y = style.verticalPadding
+            imageRect.size.width = imageView.bounds.size.width
+            imageRect.size.height = imageView.bounds.size.height
+        }
+
+        if let title = title {
+            titleLabel = UILabel()
+            titleLabel?.numberOfLines = style.titleNumberOfLines
+            titleLabel?.font = style.titleFont
+            titleLabel?.textAlignment = style.titleAlignment
+            titleLabel?.lineBreakMode = .byTruncatingTail
+            titleLabel?.textColor = style.titleColor
+            titleLabel?.backgroundColor = UIColor.clear
+            titleLabel?.text = title;
+            
+            let maxTitleSize = CGSize(width: (self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, height: self.bounds.size.height * style.maxHeightPercentage)
+            let titleSize = titleLabel?.sizeThatFits(maxTitleSize)
+            if let titleSize = titleSize {
+                titleLabel?.frame = CGRect(x: 0.0, y: 0.0, width: titleSize.width, height: titleSize.height)
+            }
+        }
+        
+        if let message = message {
+            messageLabel = UILabel()
+            messageLabel?.text = message
+            messageLabel?.numberOfLines = style.messageNumberOfLines
+            messageLabel?.font = style.messageFont
+            messageLabel?.textAlignment = style.messageAlignment
+            messageLabel?.lineBreakMode = .byTruncatingTail;
+            messageLabel?.textColor = style.messageColor
+            messageLabel?.backgroundColor = UIColor.clear
+            
+            let maxMessageSize = CGSize(width: (self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, height: self.bounds.size.height * style.maxHeightPercentage)
+            let messageSize = messageLabel?.sizeThatFits(maxMessageSize)
+            if let messageSize = messageSize {
+                let actualWidth = min(messageSize.width, maxMessageSize.width)
+                let actualHeight = min(messageSize.height, maxMessageSize.height)
+                messageLabel?.frame = CGRect(x: 0.0, y: 0.0, width: actualWidth, height: actualHeight)
+            }
+        }
+  
+        var titleRect = CGRect.zero
+        
+        if let titleLabel = titleLabel {
+            titleRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding
+            titleRect.origin.y = style.verticalPadding
+            titleRect.size.width = titleLabel.bounds.size.width
+            titleRect.size.height = titleLabel.bounds.size.height
+        }
+        
+        var messageRect = CGRect.zero
+        
+        if let messageLabel = messageLabel {
+            messageRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding
+            messageRect.origin.y = titleRect.origin.y + titleRect.size.height + style.verticalPadding
+            messageRect.size.width = messageLabel.bounds.size.width
+            messageRect.size.height = messageLabel.bounds.size.height
+        }
+        
+        let longerWidth = max(titleRect.size.width, messageRect.size.width)
+        let longerX = max(titleRect.origin.x, messageRect.origin.x)
+        let wrapperWidth = max((imageRect.size.width + (style.horizontalPadding * 2.0)), (longerX + longerWidth + style.horizontalPadding))
+        let wrapperHeight = max((messageRect.origin.y + messageRect.size.height + style.verticalPadding), (imageRect.size.height + (style.verticalPadding * 2.0)))
+        
+        wrapperView.frame = CGRect(x: 0.0, y: 0.0, width: wrapperWidth, height: wrapperHeight)
+        
+        if let titleLabel = titleLabel {
+            titleRect.size.width = longerWidth
+            titleLabel.frame = titleRect
+            wrapperView.addSubview(titleLabel)
+        }
+        
+        if let messageLabel = messageLabel {
+            messageRect.size.width = longerWidth
+            messageLabel.frame = messageRect
+            wrapperView.addSubview(messageLabel)
+        }
+        
+        if let imageView = imageView {
+            wrapperView.addSubview(imageView)
+        }
+        
+        return wrapperView
+    }
+    
+}
+
+// MARK: - Toast Style
+
+/**
+ `ToastStyle` instances define the look and feel for toast views created via the
+ `makeToast` methods as well for toast views created directly with
+ `toastViewForMessage(message:title:image:style:)`.
+
+ @warning `ToastStyle` offers relatively simple styling options for the default
+ toast view. If you require a toast view with more complex UI, it probably makes more
+ sense to create your own custom UIView subclass and present it with the `showToast`
+ methods.
+*/
+public struct ToastStyle {
+
+    public init() {}
+    
+    /**
+     The background color. Default is `.black` at 80% opacity.
+    */
+    public var backgroundColor: UIColor = UIColor.black.withAlphaComponent(0.8)
+    
+    /**
+     The title color. Default is `UIColor.whiteColor()`.
+    */
+    public var titleColor: UIColor = .white
+    
+    /**
+     The message color. Default is `.white`.
+    */
+    public var messageColor: UIColor = .white
+    
+    /**
+     A percentage value from 0.0 to 1.0, representing the maximum width of the toast
+     view relative to it's superview. Default is 0.8 (80% of the superview's width).
+    */
+    public var maxWidthPercentage: CGFloat = 0.8 {
+        didSet {
+            maxWidthPercentage = max(min(maxWidthPercentage, 1.0), 0.0)
+        }
+    }
+    
+    /**
+     A percentage value from 0.0 to 1.0, representing the maximum height of the toast
+     view relative to it's superview. Default is 0.8 (80% of the superview's height).
+    */
+    public var maxHeightPercentage: CGFloat = 0.8 {
+        didSet {
+            maxHeightPercentage = max(min(maxHeightPercentage, 1.0), 0.0)
+        }
+    }
+    
+    /**
+     The spacing from the horizontal edge of the toast view to the content. When an image
+     is present, this is also used as the padding between the image and the text.
+     Default is 10.0.
+     
+    */
+    public var horizontalPadding: CGFloat = 10.0
+    
+    /**
+     The spacing from the vertical edge of the toast view to the content. When a title
+     is present, this is also used as the padding between the title and the message.
+     Default is 10.0. On iOS11+, this value is added added to the `safeAreaInset.top`
+     and `safeAreaInsets.bottom`.
+    */
+    public var verticalPadding: CGFloat = 10.0
+    
+    /**
+     The corner radius. Default is 10.0.
+    */
+    public var cornerRadius: CGFloat = 10.0;
+    
+    /**
+     The title font. Default is `.boldSystemFont(16.0)`.
+    */
+    public var titleFont: UIFont = .boldSystemFont(ofSize: 16.0)
+    
+    /**
+     The message font. Default is `.systemFont(ofSize: 16.0)`.
+    */
+    public var messageFont: UIFont = .systemFont(ofSize: 16.0)
+    
+    /**
+     The title text alignment. Default is `NSTextAlignment.Left`.
+    */
+    public var titleAlignment: NSTextAlignment = .left
+    
+    /**
+     The message text alignment. Default is `NSTextAlignment.Left`.
+    */
+    public var messageAlignment: NSTextAlignment = .left
+    
+    /**
+     The maximum number of lines for the title. The default is 0 (no limit).
+    */
+    public var titleNumberOfLines = 0
+    
+    /**
+     The maximum number of lines for the message. The default is 0 (no limit).
+    */
+    public var messageNumberOfLines = 0
+    
+    /**
+     Enable or disable a shadow on the toast view. Default is `false`.
+    */
+    public var displayShadow = false
+    
+    /**
+     The shadow color. Default is `.black`.
+     */
+    public var shadowColor: UIColor = .black
+    
+    /**
+     A value from 0.0 to 1.0, representing the opacity of the shadow.
+     Default is 0.8 (80% opacity).
+    */
+    public var shadowOpacity: Float = 0.8 {
+        didSet {
+            shadowOpacity = max(min(shadowOpacity, 1.0), 0.0)
+        }
+    }
+
+    /**
+     The shadow radius. Default is 6.0.
+    */
+    public var shadowRadius: CGFloat = 6.0
+    
+    /**
+     The shadow offset. The default is 4 x 4.
+    */
+    public var shadowOffset = CGSize(width: 4.0, height: 4.0)
+    
+    /**
+     The image size. The default is 80 x 80.
+    */
+    public var imageSize = CGSize(width: 80.0, height: 80.0)
+    
+    /**
+     The size of the toast activity view when `makeToastActivity(position:)` is called.
+     Default is 100 x 100.
+    */
+    public var activitySize = CGSize(width: 100.0, height: 100.0)
+    
+    /**
+     The fade in/out animation duration. Default is 0.2.
+     */
+    public var fadeDuration: TimeInterval = 0.2
+    
+    /**
+     Activity indicator color. Default is `.white`.
+     */
+    public var activityIndicatorColor: UIColor = .white
+    
+    /**
+     Activity background color. Default is `.black` at 80% opacity.
+     */
+    public var activityBackgroundColor: UIColor = UIColor.black.withAlphaComponent(0.8)
+    
+}
+
+// MARK: - Toast Manager
+
+/**
+ `ToastManager` provides general configuration options for all toast
+ notifications. Backed by a singleton instance.
+*/
+public class ToastManager {
+    
+    /**
+     The `ToastManager` singleton instance.
+     
+     */
+    public static let shared = ToastManager()
+    
+    /**
+     The shared style. Used whenever toastViewForMessage(message:title:image:style:) is called
+     with with a nil style.
+     
+     */
+    public var style = ToastStyle()
+    
+    /**
+     Enables or disables tap to dismiss on toast views. Default is `true`.
+     
+     */
+    public var isTapToDismissEnabled = true
+    
+    /**
+     Enables or disables queueing behavior for toast views. When `true`,
+     toast views will appear one after the other. When `false`, multiple toast
+     views will appear at the same time (potentially overlapping depending
+     on their positions). This has no effect on the toast activity view,
+     which operates independently of normal toast views. Default is `false`.
+     
+     */
+    public var isQueueEnabled = false
+    
+    /**
+     The default duration. Used for the `makeToast` and
+     `showToast` methods that don't require an explicit duration.
+     Default is 3.0.
+     
+     */
+    public var duration: TimeInterval = 3.0
+    
+    /**
+     Sets the default position. Used for the `makeToast` and
+     `showToast` methods that don't require an explicit position.
+     Default is `ToastPosition.Bottom`.
+     
+     */
+    public var position: ToastPosition = .bottom
+    
+}
+
+// MARK: - ToastPosition
+
+public enum ToastPosition {
+    case top
+    case center
+    case bottom
+    
+    fileprivate func centerPoint(forToast toast: UIView, inSuperview superview: UIView) -> CGPoint {
+        let topPadding: CGFloat = ToastManager.shared.style.verticalPadding + superview.csSafeAreaInsets.top
+        let bottomPadding: CGFloat = ToastManager.shared.style.verticalPadding + superview.csSafeAreaInsets.bottom
+        
+        switch self {
+        case .top:
+            return CGPoint(x: superview.bounds.size.width / 2.0, y: (toast.frame.size.height / 2.0) + topPadding)
+        case .center:
+            return CGPoint(x: superview.bounds.size.width / 2.0, y: superview.bounds.size.height / 2.0)
+        case .bottom:
+            return CGPoint(x: superview.bounds.size.width / 2.0, y: (superview.bounds.size.height - (toast.frame.size.height / 2.0)) - bottomPadding)
+        }
+    }
+}
+
+fileprivate extension UIView {
+    
+    fileprivate var csSafeAreaInsets: UIEdgeInsets {
+        if #available(iOS 11.0, *) {
+            return self.safeAreaInsets
+        } else {
+            return .zero
+        }
+    }
+    
+}
diff --git a/iOS/Pods/ZFDragableModalTransition/Classes/ZFModalTransitionAnimator.h b/iOS/Pods/ZFDragableModalTransition/Classes/ZFModalTransitionAnimator.h
new file mode 100644 (file)
index 0000000..f724e90
--- /dev/null
@@ -0,0 +1,36 @@
+//
+//  ZFModalTransitionAnimator.h
+//
+//  Created by Amornchai Kanokpullwad on 5/10/14.
+//  Copyright (c) 2014 zoonref. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+#import <UIKit/UIGestureRecognizerSubclass.h>
+
+typedef NS_ENUM(NSUInteger, ZFModalTransitonDirection) {
+    ZFModalTransitonDirectionBottom,
+    ZFModalTransitonDirectionLeft,
+    ZFModalTransitonDirectionRight,
+};
+
+@interface ZFDetectScrollViewEndGestureRecognizer : UIPanGestureRecognizer
+@property (nonatomic, weak) UIScrollView *scrollview;
+@end
+
+@interface ZFModalTransitionAnimator : UIPercentDrivenInteractiveTransition <UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate, UIGestureRecognizerDelegate>
+
+@property (nonatomic, assign, getter=isDragable) BOOL dragable;
+@property (nonatomic, readonly) ZFDetectScrollViewEndGestureRecognizer *gesture;
+@property (nonatomic, assign) UIGestureRecognizer *gestureRecognizerToFailPan;
+@property BOOL bounces;
+@property (nonatomic) ZFModalTransitonDirection direction;
+@property CGFloat behindViewScale;
+@property CGFloat behindViewAlpha;
+@property CGFloat transitionDuration;
+
+- (id)initWithModalViewController:(UIViewController *)modalViewController;
+- (void)setContentScrollView:(UIScrollView *)scrollView;
+
+@end
diff --git a/iOS/Pods/ZFDragableModalTransition/Classes/ZFModalTransitionAnimator.m b/iOS/Pods/ZFDragableModalTransition/Classes/ZFModalTransitionAnimator.m
new file mode 100644 (file)
index 0000000..6d302da
--- /dev/null
@@ -0,0 +1,554 @@
+//
+//  ZFModalTransitionAnimator.m
+//
+//  Created by Amornchai Kanokpullwad on 5/10/14.
+//  Copyright (c) 2014 zoonref. All rights reserved.
+//
+
+#import "ZFModalTransitionAnimator.h"
+
+@interface ZFModalTransitionAnimator ()
+@property (nonatomic, weak) UIViewController *modalController;
+@property (nonatomic, strong) ZFDetectScrollViewEndGestureRecognizer *gesture;
+@property (nonatomic, strong) id<UIViewControllerContextTransitioning> transitionContext;
+@property CGFloat panLocationStart;
+@property BOOL isDismiss;
+@property BOOL isInteractive;
+@property CATransform3D tempTransform;
+@end
+
+@implementation ZFModalTransitionAnimator
+
+- (instancetype)initWithModalViewController:(UIViewController *)modalViewController
+{
+    self = [super init];
+    if (self) {
+        _modalController = modalViewController;
+        _direction = ZFModalTransitonDirectionBottom;
+        _dragable = NO;
+        _bounces = YES;
+        _behindViewScale = 0.9f;
+        _behindViewAlpha = 1.0f;
+        _transitionDuration = 0.8f;
+
+        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
+        [[NSNotificationCenter defaultCenter] addObserver:self
+                                                 selector:@selector(orientationChanged:)
+                                                     name:UIApplicationDidChangeStatusBarFrameNotification
+                                                   object:nil];
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
+}
+
+- (void)setDragable:(BOOL)dragable
+{
+    _dragable = dragable;
+    if (_dragable) {
+        [self removeGestureRecognizerFromModalController];
+        self.gesture = [[ZFDetectScrollViewEndGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
+        self.gesture.delegate = self;
+        [self.modalController.view addGestureRecognizer:self.gesture];
+    } else {
+        [self removeGestureRecognizerFromModalController];
+    }
+}
+
+- (void)setContentScrollView:(UIScrollView *)scrollView
+{
+    // always enable drag if scrollview is set
+    if (!self.dragable) {
+        self.dragable = YES;
+    }
+    // and scrollview will work only for bottom mode
+    self.direction = ZFModalTransitonDirectionBottom;
+    self.gesture.scrollview = scrollView;
+}
+
+- (void)setDirection:(ZFModalTransitonDirection)direction
+{
+    _direction = direction;
+    // scrollview will work only for bottom mode
+    if (_direction != ZFModalTransitonDirectionBottom) {
+        self.gesture.scrollview = nil;
+    }
+}
+
+- (void)animationEnded:(BOOL)transitionCompleted
+{
+    // Reset to our default state
+    self.isInteractive = NO;
+    self.transitionContext = nil;
+}
+
+- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
+{
+    return self.transitionDuration;
+}
+
+- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
+{
+    if (self.isInteractive) {
+        return;
+    }
+    // Grab the from and to view controllers from the context
+    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
+    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
+
+    UIView *containerView = [transitionContext containerView];
+
+    if (!self.isDismiss) {
+
+        CGRect startRect;
+
+        [containerView addSubview:toViewController.view];
+
+        toViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
+
+        if (self.direction == ZFModalTransitonDirectionBottom) {
+            startRect = CGRectMake(0,
+                                   CGRectGetHeight(containerView.frame),
+                                   CGRectGetWidth(containerView.bounds),
+                                   CGRectGetHeight(containerView.bounds));
+        } else if (self.direction == ZFModalTransitonDirectionLeft) {
+            startRect = CGRectMake(-CGRectGetWidth(containerView.frame),
+                                   0,
+                                   CGRectGetWidth(containerView.bounds),
+                                   CGRectGetHeight(containerView.bounds));
+        } else if (self.direction == ZFModalTransitonDirectionRight) {
+            startRect = CGRectMake(CGRectGetWidth(containerView.frame),
+                                   0,
+                                   CGRectGetWidth(containerView.bounds),
+                                   CGRectGetHeight(containerView.bounds));
+        }
+
+        CGPoint transformedPoint = CGPointApplyAffineTransform(startRect.origin, toViewController.view.transform);
+        toViewController.view.frame = CGRectMake(transformedPoint.x, transformedPoint.y, startRect.size.width, startRect.size.height);
+
+        if (toViewController.modalPresentationStyle == UIModalPresentationCustom) {
+            [fromViewController beginAppearanceTransition:NO animated:YES];
+        }
+
+        [UIView animateWithDuration:[self transitionDuration:transitionContext]
+                              delay:0
+             usingSpringWithDamping:0.8
+              initialSpringVelocity:0.1
+                            options:UIViewAnimationOptionCurveEaseOut
+                         animations:^{
+                             fromViewController.view.transform = CGAffineTransformScale(fromViewController.view.transform, self.behindViewScale, self.behindViewScale);
+                             fromViewController.view.alpha = self.behindViewAlpha;
+
+                             toViewController.view.frame = CGRectMake(0,0,
+                                                                      CGRectGetWidth(toViewController.view.frame),
+                                                                      CGRectGetHeight(toViewController.view.frame));
+                         } completion:^(BOOL finished) {
+                             if (toViewController.modalPresentationStyle == UIModalPresentationCustom) {
+                                 [fromViewController endAppearanceTransition];
+                             }
+                             [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
+                         }];
+    } else {
+
+        if (fromViewController.modalPresentationStyle == UIModalPresentationFullScreen) {
+            [containerView addSubview:toViewController.view];
+        }
+        
+        [containerView bringSubviewToFront:fromViewController.view];
+
+        if (![self isPriorToIOS8]) {
+            toViewController.view.layer.transform = CATransform3DScale(toViewController.view.layer.transform, self.behindViewScale, self.behindViewScale, 1);
+        }
+
+        toViewController.view.alpha = self.behindViewAlpha;
+
+        CGRect endRect;
+
+        if (self.direction == ZFModalTransitonDirectionBottom) {
+            endRect = CGRectMake(0,
+                                 CGRectGetHeight(fromViewController.view.bounds),
+                                 CGRectGetWidth(fromViewController.view.frame),
+                                 CGRectGetHeight(fromViewController.view.frame));
+        } else if (self.direction == ZFModalTransitonDirectionLeft) {
+            endRect = CGRectMake(-CGRectGetWidth(fromViewController.view.bounds),
+                                 0,
+                                 CGRectGetWidth(fromViewController.view.frame),
+                                 CGRectGetHeight(fromViewController.view.frame));
+        } else if (self.direction == ZFModalTransitonDirectionRight) {
+            endRect = CGRectMake(CGRectGetWidth(fromViewController.view.bounds),
+                                 0,
+                                 CGRectGetWidth(fromViewController.view.frame),
+                                 CGRectGetHeight(fromViewController.view.frame));
+        }
+
+        CGPoint transformedPoint = CGPointApplyAffineTransform(endRect.origin, fromViewController.view.transform);
+        endRect = CGRectMake(transformedPoint.x, transformedPoint.y, endRect.size.width, endRect.size.height);
+
+        if (fromViewController.modalPresentationStyle == UIModalPresentationCustom) {
+            [toViewController beginAppearanceTransition:YES animated:YES];
+        }
+
+        [UIView animateWithDuration:[self transitionDuration:transitionContext]
+                              delay:0
+             usingSpringWithDamping:0.8
+              initialSpringVelocity:0.1
+                            options:UIViewAnimationOptionCurveEaseOut
+                         animations:^{
+                             CGFloat scaleBack = (1 / self.behindViewScale);
+                             toViewController.view.layer.transform = CATransform3DScale(toViewController.view.layer.transform, scaleBack, scaleBack, 1);
+                             toViewController.view.alpha = 1.0f;
+                             fromViewController.view.frame = endRect;
+                         } completion:^(BOOL finished) {
+                             toViewController.view.layer.transform = CATransform3DIdentity;
+                             if (fromViewController.modalPresentationStyle == UIModalPresentationCustom) {
+                                 [toViewController endAppearanceTransition];
+                             }
+                             [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
+                         }];
+    }
+}
+
+- (void)removeGestureRecognizerFromModalController
+{
+    if (self.gesture && [self.modalController.view.gestureRecognizers containsObject:self.gesture]) {
+        [self.modalController.view removeGestureRecognizer:self.gesture];
+        self.gesture = nil;
+    }
+}
+
+# pragma mark - Gesture
+
+- (void)handlePan:(UIPanGestureRecognizer *)recognizer
+{
+    // Location reference
+    CGPoint location = [recognizer locationInView:self.modalController.view.window];
+    location = CGPointApplyAffineTransform(location, CGAffineTransformInvert(recognizer.view.transform));
+    // Velocity reference
+    CGPoint velocity = [recognizer velocityInView:[self.modalController.view window]];
+    velocity = CGPointApplyAffineTransform(velocity, CGAffineTransformInvert(recognizer.view.transform));
+
+    if (recognizer.state == UIGestureRecognizerStateBegan) {
+        self.isInteractive = YES;
+        if (self.direction == ZFModalTransitonDirectionBottom) {
+            self.panLocationStart = location.y;
+        } else {
+            self.panLocationStart = location.x;
+        }
+        [self.modalController dismissViewControllerAnimated:YES completion:nil];
+        
+    } else if (recognizer.state == UIGestureRecognizerStateChanged) {
+        CGFloat animationRatio = 0;
+
+        if (self.direction == ZFModalTransitonDirectionBottom) {
+            animationRatio = (location.y - self.panLocationStart) / (CGRectGetHeight([self.modalController view].bounds));
+        } else if (self.direction == ZFModalTransitonDirectionLeft) {
+            animationRatio = (self.panLocationStart - location.x) / (CGRectGetWidth([self.modalController view].bounds));
+        } else if (self.direction == ZFModalTransitonDirectionRight) {
+            animationRatio = (location.x - self.panLocationStart) / (CGRectGetWidth([self.modalController view].bounds));
+        }
+
+        [self updateInteractiveTransition:animationRatio];
+        
+    } else if (recognizer.state == UIGestureRecognizerStateEnded) {
+
+        CGFloat velocityForSelectedDirection;
+
+        if (self.direction == ZFModalTransitonDirectionBottom) {
+            velocityForSelectedDirection = velocity.y;
+        } else {
+            velocityForSelectedDirection = velocity.x;
+        }
+
+        if (velocityForSelectedDirection > 100
+            && (self.direction == ZFModalTransitonDirectionRight
+                || self.direction == ZFModalTransitonDirectionBottom)) {
+                [self finishInteractiveTransition];
+            } else if (velocityForSelectedDirection < -100 && self.direction == ZFModalTransitonDirectionLeft) {
+                [self finishInteractiveTransition];
+            } else {
+                [self cancelInteractiveTransition];
+            }
+        self.isInteractive = NO;
+    }
+}
+
+#pragma mark -
+
+-(void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext
+{
+    self.transitionContext = transitionContext;
+
+    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
+    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
+
+    if (![self isPriorToIOS8]) {
+        toViewController.view.layer.transform = CATransform3DScale(toViewController.view.layer.transform, self.behindViewScale, self.behindViewScale, 1);
+    }
+
+    self.tempTransform = toViewController.view.layer.transform;
+
+    toViewController.view.alpha = self.behindViewAlpha;
+    
+    if (fromViewController.modalPresentationStyle == UIModalPresentationFullScreen) {
+        [[transitionContext containerView] addSubview:toViewController.view];
+    }
+    [[transitionContext containerView] bringSubviewToFront:fromViewController.view];
+}
+
+- (void)updateInteractiveTransition:(CGFloat)percentComplete
+{
+    if (!self.bounces && percentComplete < 0) {
+        percentComplete = 0;
+    }
+
+    id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
+
+    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
+    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
+    CATransform3D transform = CATransform3DMakeScale(
+                                                     1 + (((1 / self.behindViewScale) - 1) * percentComplete),
+                                                     1 + (((1 / self.behindViewScale) - 1) * percentComplete), 1);
+    toViewController.view.layer.transform = CATransform3DConcat(self.tempTransform, transform);
+
+    toViewController.view.alpha = self.behindViewAlpha + ((1 - self.behindViewAlpha) * percentComplete);
+
+    CGRect updateRect;
+    if (self.direction == ZFModalTransitonDirectionBottom) {
+        updateRect = CGRectMake(0,
+                                (CGRectGetHeight(fromViewController.view.bounds) * percentComplete),
+                                CGRectGetWidth(fromViewController.view.frame),
+                                CGRectGetHeight(fromViewController.view.frame));
+    } else if (self.direction == ZFModalTransitonDirectionLeft) {
+        updateRect = CGRectMake(-(CGRectGetWidth(fromViewController.view.bounds) * percentComplete),
+                                0,
+                                CGRectGetWidth(fromViewController.view.frame),
+                                CGRectGetHeight(fromViewController.view.frame));
+    } else if (self.direction == ZFModalTransitonDirectionRight) {
+        updateRect = CGRectMake(CGRectGetWidth(fromViewController.view.bounds) * percentComplete,
+                                0,
+                                CGRectGetWidth(fromViewController.view.frame),
+                                CGRectGetHeight(fromViewController.view.frame));
+    }
+
+    // reset to zero if x and y has unexpected value to prevent crash
+    if (isnan(updateRect.origin.x) || isinf(updateRect.origin.x)) {
+        updateRect.origin.x = 0;
+    }
+    if (isnan(updateRect.origin.y) || isinf(updateRect.origin.y)) {
+        updateRect.origin.y = 0;
+    }
+
+    CGPoint transformedPoint = CGPointApplyAffineTransform(updateRect.origin, fromViewController.view.transform);
+    updateRect = CGRectMake(transformedPoint.x, transformedPoint.y, updateRect.size.width, updateRect.size.height);
+
+    fromViewController.view.frame = updateRect;
+}
+
+- (void)finishInteractiveTransition
+{
+    id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
+
+    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
+    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
+
+    CGRect endRect;
+
+    if (self.direction == ZFModalTransitonDirectionBottom) {
+        endRect = CGRectMake(0,
+                             CGRectGetHeight(fromViewController.view.bounds),
+                             CGRectGetWidth(fromViewController.view.frame),
+                             CGRectGetHeight(fromViewController.view.frame));
+    } else if (self.direction == ZFModalTransitonDirectionLeft) {
+        endRect = CGRectMake(-CGRectGetWidth(fromViewController.view.bounds),
+                             0,
+                             CGRectGetWidth(fromViewController.view.frame),
+                             CGRectGetHeight(fromViewController.view.frame));
+    } else if (self.direction == ZFModalTransitonDirectionRight) {
+        endRect = CGRectMake(CGRectGetWidth(fromViewController.view.bounds),
+                             0,
+                             CGRectGetWidth(fromViewController.view.frame),
+                             CGRectGetHeight(fromViewController.view.frame));
+    }
+
+    CGPoint transformedPoint = CGPointApplyAffineTransform(endRect.origin, fromViewController.view.transform);
+    endRect = CGRectMake(transformedPoint.x, transformedPoint.y, endRect.size.width, endRect.size.height);
+
+    if (fromViewController.modalPresentationStyle == UIModalPresentationCustom) {
+        [toViewController beginAppearanceTransition:YES animated:YES];
+    }
+    
+    [UIView animateWithDuration:[self transitionDuration:transitionContext]
+                          delay:0
+         usingSpringWithDamping:0.8
+          initialSpringVelocity:0.1
+                        options:UIViewAnimationOptionCurveEaseOut
+                     animations:^{
+                         CGFloat scaleBack = (1 / self.behindViewScale);
+                         toViewController.view.layer.transform = CATransform3DScale(self.tempTransform, scaleBack, scaleBack, 1);
+                         toViewController.view.alpha = 1.0f;
+                         fromViewController.view.frame = endRect;
+                     } completion:^(BOOL finished) {
+                         if (fromViewController.modalPresentationStyle == UIModalPresentationCustom) {
+                             [toViewController endAppearanceTransition];
+                         }
+                         [transitionContext completeTransition:YES];
+                     }];
+}
+
+- (void)cancelInteractiveTransition
+{
+    id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
+
+    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
+    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
+
+    [UIView animateWithDuration:0.4
+                          delay:0
+         usingSpringWithDamping:0.8
+          initialSpringVelocity:0.1
+                        options:UIViewAnimationOptionCurveEaseOut
+                     animations:^{
+                         toViewController.view.layer.transform = self.tempTransform;
+                         toViewController.view.alpha = self.behindViewAlpha;
+
+                         fromViewController.view.frame = CGRectMake(0,0,
+                                                                    CGRectGetWidth(fromViewController.view.frame),
+                                                                    CGRectGetHeight(fromViewController.view.frame));
+                     } completion:^(BOOL finished) {
+                         [transitionContext completeTransition:NO];
+                         if (fromViewController.modalPresentationStyle == UIModalPresentationFullScreen) {
+                             [toViewController.view removeFromSuperview];
+                         }
+                     }];
+}
+
+#pragma mark - UIViewControllerTransitioningDelegate Methods
+
+- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
+{
+    self.isDismiss = NO;
+    return self;
+}
+
+- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
+{
+    self.isDismiss = YES;
+    return self;
+}
+
+- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator
+{
+    return nil;
+}
+
+- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator
+{
+    // Return nil if we are not interactive
+    if (self.isInteractive && self.dragable) {
+        self.isDismiss = YES;
+        return self;
+    }
+
+    return nil;
+}
+
+#pragma mark - Gesture Delegate
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
+{
+    if (self.direction == ZFModalTransitonDirectionBottom) {
+        return YES;
+    }
+    return NO;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
+{
+    if (self.direction == ZFModalTransitonDirectionBottom) {
+        return YES;
+    }
+    return NO;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
+    if (self.gestureRecognizerToFailPan && otherGestureRecognizer && self.gestureRecognizerToFailPan == otherGestureRecognizer) {
+        return YES;
+    }
+    
+    return NO;
+}
+
+#pragma mark - Utils
+
+- (BOOL)isPriorToIOS8
+{
+    NSComparisonResult order = [[UIDevice currentDevice].systemVersion compare: @"8.0" options: NSNumericSearch];
+    if (order == NSOrderedSame || order == NSOrderedDescending) {
+        // OS version >= 8.0
+        return YES;
+    }
+    return NO;
+}
+
+#pragma mark - Orientation
+
+- (void)orientationChanged:(NSNotification *)notification
+{
+    UIViewController *backViewController = self.modalController.presentingViewController;
+    backViewController.view.transform = CGAffineTransformIdentity;
+    backViewController.view.frame = self.modalController.view.bounds;
+    backViewController.view.transform = CGAffineTransformScale(backViewController.view.transform, self.behindViewScale, self.behindViewScale);
+}
+
+@end
+
+// Gesture Class Implement
+@interface ZFDetectScrollViewEndGestureRecognizer ()
+@property (nonatomic, strong) NSNumber *isFail;
+@end
+
+@implementation ZFDetectScrollViewEndGestureRecognizer
+
+- (void)reset
+{
+    [super reset];
+    self.isFail = nil;
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
+{
+    [super touchesMoved:touches withEvent:event];
+
+    if (!self.scrollview) {
+        return;
+    }
+
+    if (self.state == UIGestureRecognizerStateFailed) return;
+    CGPoint velocity = [self velocityInView:self.view];
+    CGPoint nowPoint = [touches.anyObject locationInView:self.view];
+    CGPoint prevPoint = [touches.anyObject previousLocationInView:self.view];
+
+    if (self.isFail) {
+        if (self.isFail.boolValue) {
+            self.state = UIGestureRecognizerStateFailed;
+        }
+        return;
+    }
+
+    CGFloat topVerticalOffset = -self.scrollview.contentInset.top;
+
+    if ((fabs(velocity.x) < fabs(velocity.y)) && (nowPoint.y > prevPoint.y) && (self.scrollview.contentOffset.y <= topVerticalOffset)) {
+        self.isFail = @NO;
+    } else if (self.scrollview.contentOffset.y >= topVerticalOffset) {
+        self.state = UIGestureRecognizerStateFailed;
+        self.isFail = @YES;
+    } else {
+        self.isFail = @NO;
+    }
+}
+
+@end
diff --git a/iOS/Pods/ZFDragableModalTransition/LICENSE b/iOS/Pods/ZFDragableModalTransition/LICENSE
new file mode 100644 (file)
index 0000000..08613ad
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Amornchai Kanokpullwad
+
+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/iOS/Pods/ZFDragableModalTransition/README.md b/iOS/Pods/ZFDragableModalTransition/README.md
new file mode 100644 (file)
index 0000000..7cec9c3
--- /dev/null
@@ -0,0 +1,66 @@
+# ZFDragableModalTransition
+
+[![Version](https://img.shields.io/cocoapods/v/ZFDragableModalTransition.svg?style=flat)](http://cocoadocs.org/docsets/ZFDragableModalTransition)
+[![License](https://img.shields.io/cocoapods/l/ZFDragableModalTransition.svg?style=flat)](http://cocoadocs.org/docsets/ZFDragableModalTransition)
+[![Platform](https://img.shields.io/cocoapods/p/ZFDragableModalTransition.svg?style=flat)](http://cocoadocs.org/docsets/ZFDragableModalTransition)
+
+<p align="center"><img src="https://raw.githubusercontent.com/zoonooz/ZFDragableModalTransition/master/Screenshot/ss.gif"/></p>
+
+## Usage
+
+```objc
+- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
+{
+    TaskDetailViewController *detailViewController = segue.destinationViewController;
+    detailViewController.task = sender;
+
+    // create animator object with instance of modal view controller
+    // we need to keep it in property with strong reference so it will not get release
+    self.animator = [[ZFModalTransitionAnimator alloc] initWithModalViewController:detailViewController];
+    self.animator.dragable = YES;
+    self.animator.direction = ZFModalTransitonDirectionBottom;
+    [self.animator setContentScrollView:detailViewController.scrollview];
+
+    // set transition delegate of modal view controller to our object
+    detailViewController.transitioningDelegate = self.animator;
+
+    // if you modal cover all behind view controller, use UIModalPresentationFullScreen
+    detailViewController.modalPresentationStyle = UIModalPresentationCustom;
+}
+```
+###ScrollView
+If you have scrollview in the modal and you want to dismiss modal by drag it, you need to set scrollview to ZFModalTransitionAnimator instance.
+```objc
+[self.animator setContentScrollView:detailViewController.scrollview];
+```
+
+###Direction
+You can set that which direction will our modal present. (default is ZFModalTransitonDirectionBottom)
+```objc
+self.animator.direction = ZFModalTransitonDirectionBottom;
+```
+P.S. Now you can set content scrollview only with ZFModalTransitonDirectionBottom
+
+## Requirements
+- iOS >= 7.1
+- ARC
+
+## Installation
+
+ZFDragableModalTransition is available through [CocoaPods](http://cocoapods.org). To install
+it, simply add the following line to your Podfile:
+
+    pod "ZFDragableModalTransition"
+
+## FAQ
+
+### How can I show modal only part of view ?
+The current ViewController's view still visible behind the modal, so you just set transparent color to background view.
+
+## Author
+
+Amornchai Kanokpullwad, [@zoonref](https://twitter.com/zoonref)
+
+## License
+
+ZFDragableModalTransition is available under the MIT license. See the LICENSE file for more info.
diff --git a/iOS/Pods/nanopb/LICENSE.txt b/iOS/Pods/nanopb/LICENSE.txt
new file mode 100644 (file)
index 0000000..d11c9af
--- /dev/null
@@ -0,0 +1,20 @@
+Copyright (c) 2011 Petteri Aimonen <jpa at nanopb.mail.kapsi.fi>
+
+This software is provided 'as-is', without any express or 
+implied warranty. In no event will the authors be held liable 
+for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any 
+purpose, including commercial applications, and to alter it and 
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you 
+   must not claim that you wrote the original software. If you use 
+   this software in a product, an acknowledgment in the product 
+   documentation would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and 
+   must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source 
+   distribution.
diff --git a/iOS/Pods/nanopb/README.md b/iOS/Pods/nanopb/README.md
new file mode 100644 (file)
index 0000000..07860f0
--- /dev/null
@@ -0,0 +1,71 @@
+Nanopb - Protocol Buffers for Embedded Systems
+==============================================
+
+[![Build Status](https://travis-ci.org/nanopb/nanopb.svg?branch=master)](https://travis-ci.org/nanopb/nanopb)
+
+Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is
+especially suitable for use in microcontrollers, but fits any memory
+restricted system.
+
+* **Homepage:** https://jpa.kapsi.fi/nanopb/
+* **Documentation:** https://jpa.kapsi.fi/nanopb/docs/
+* **Downloads:** https://jpa.kapsi.fi/nanopb/download/
+* **Forum:** https://groups.google.com/forum/#!forum/nanopb
+
+
+
+Using the nanopb library
+------------------------
+To use the nanopb library, you need to do two things:
+
+1. Compile your .proto files for nanopb, using protoc.
+2. Include pb_encode.c, pb_decode.c and pb_common.c in your project.
+
+The easiest way to get started is to study the project in "examples/simple".
+It contains a Makefile, which should work directly under most Linux systems.
+However, for any other kind of build system, see the manual steps in
+README.txt in that folder.
+
+
+
+Using the Protocol Buffers compiler (protoc)
+--------------------------------------------
+The nanopb generator is implemented as a plugin for the Google's own protoc
+compiler. This has the advantage that there is no need to reimplement the
+basic parsing of .proto files. However, it does mean that you need the
+Google's protobuf library in order to run the generator.
+
+If you have downloaded a binary package for nanopb (either Windows, Linux or
+Mac OS X version), the 'protoc' binary is included in the 'generator-bin'
+folder. In this case, you are ready to go. Simply run this command:
+
+    generator-bin/protoc --nanopb_out=. myprotocol.proto
+
+However, if you are using a git checkout or a plain source distribution, you
+need to provide your own version of protoc and the Google's protobuf library.
+On Linux, the necessary packages are protobuf-compiler and python-protobuf.
+On Windows, you can either build Google's protobuf library from source or use
+one of the binary distributions of it. In either case, if you use a separate
+protoc, you need to manually give the path to nanopb generator:
+
+    protoc --plugin=protoc-gen-nanopb=nanopb/generator/protoc-gen-nanopb ...
+
+
+
+Running the tests
+-----------------
+If you want to perform further development of the nanopb core, or to verify
+its functionality using your compiler and platform, you'll want to run the
+test suite. The build rules for the test suite are implemented using Scons,
+so you need to have that installed. To run the tests:
+
+    cd tests
+    scons
+
+This will show the progress of various test cases. If the output does not
+end in an error, the test cases were successful.
+
+Note: Mac OS X by default aliases 'clang' as 'gcc', while not actually
+supporting the same command line options as gcc does. To run tests on
+Mac OS X, use: "scons CC=clang CXX=clang". Same way can be used to run
+tests with different compilers on any platform.
diff --git a/iOS/Pods/nanopb/pb.h b/iOS/Pods/nanopb/pb.h
new file mode 100644 (file)
index 0000000..bf05a63
--- /dev/null
@@ -0,0 +1,583 @@
+/* Common parts of the nanopb library. Most of these are quite low-level
+ * stuff. For the high-level interface, see pb_encode.h and pb_decode.h.
+ */
+
+#ifndef PB_H_INCLUDED
+#define PB_H_INCLUDED
+
+/*****************************************************************
+ * Nanopb compilation time options. You can change these here by *
+ * uncommenting the lines, or on the compiler command line.      *
+ *****************************************************************/
+
+/* Enable support for dynamically allocated fields */
+/* #define PB_ENABLE_MALLOC 1 */
+
+/* Define this if your CPU / compiler combination does not support
+ * unaligned memory access to packed structures. */
+/* #define PB_NO_PACKED_STRUCTS 1 */
+
+/* Increase the number of required fields that are tracked.
+ * A compiler warning will tell if you need this. */
+/* #define PB_MAX_REQUIRED_FIELDS 256 */
+
+/* Add support for tag numbers > 255 and fields larger than 255 bytes. */
+/* #define PB_FIELD_16BIT 1 */
+
+/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */
+/* #define PB_FIELD_32BIT 1 */
+
+/* Disable support for error messages in order to save some code space. */
+/* #define PB_NO_ERRMSG 1 */
+
+/* Disable support for custom streams (support only memory buffers). */
+/* #define PB_BUFFER_ONLY 1 */
+
+/* Switch back to the old-style callback function signature.
+ * This was the default until nanopb-0.2.1. */
+/* #define PB_OLD_CALLBACK_STYLE */
+
+
+/******************************************************************
+ * You usually don't need to change anything below this line.     *
+ * Feel free to look around and use the defined macros, though.   *
+ ******************************************************************/
+
+
+/* Version of the nanopb library. Just in case you want to check it in
+ * your own program. */
+#define NANOPB_VERSION nanopb-0.3.8
+
+/* Include all the system headers needed by nanopb. You will need the
+ * definitions of the following:
+ * - strlen, memcpy, memset functions
+ * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t
+ * - size_t
+ * - bool
+ *
+ * If you don't have the standard header files, you can instead provide
+ * a custom header that defines or includes all this. In that case,
+ * define PB_SYSTEM_HEADER to the path of this file.
+ */
+#ifdef PB_SYSTEM_HEADER
+#include PB_SYSTEM_HEADER
+#else
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+
+#ifdef PB_ENABLE_MALLOC
+#include <stdlib.h>
+#endif
+#endif
+
+/* Macro for defining packed structures (compiler dependent).
+ * This just reduces memory requirements, but is not required.
+ */
+#if defined(PB_NO_PACKED_STRUCTS)
+    /* Disable struct packing */
+#   define PB_PACKED_STRUCT_START
+#   define PB_PACKED_STRUCT_END
+#   define pb_packed
+#elif defined(__GNUC__) || defined(__clang__)
+    /* For GCC and clang */
+#   define PB_PACKED_STRUCT_START
+#   define PB_PACKED_STRUCT_END
+#   define pb_packed __attribute__((packed))
+#elif defined(__ICCARM__) || defined(__CC_ARM)
+    /* For IAR ARM and Keil MDK-ARM compilers */
+#   define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)")
+#   define PB_PACKED_STRUCT_END _Pragma("pack(pop)")
+#   define pb_packed
+#elif defined(_MSC_VER) && (_MSC_VER >= 1500)
+    /* For Microsoft Visual C++ */
+#   define PB_PACKED_STRUCT_START __pragma(pack(push, 1))
+#   define PB_PACKED_STRUCT_END __pragma(pack(pop))
+#   define pb_packed
+#else
+    /* Unknown compiler */
+#   define PB_PACKED_STRUCT_START
+#   define PB_PACKED_STRUCT_END
+#   define pb_packed
+#endif
+
+/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
+#ifndef PB_UNUSED
+#define PB_UNUSED(x) (void)(x)
+#endif
+
+/* Compile-time assertion, used for checking compatible compilation options.
+ * If this does not work properly on your compiler, use
+ * #define PB_NO_STATIC_ASSERT to disable it.
+ *
+ * But before doing that, check carefully the error message / place where it
+ * comes from to see if the error has a real cause. Unfortunately the error
+ * message is not always very clear to read, but you can see the reason better
+ * in the place where the PB_STATIC_ASSERT macro was called.
+ */
+#ifndef PB_NO_STATIC_ASSERT
+#ifndef PB_STATIC_ASSERT
+#define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1];
+#define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
+#define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##LINE##COUNTER
+#endif
+#else
+#define PB_STATIC_ASSERT(COND,MSG)
+#endif
+
+/* Number of required fields to keep track of. */
+#ifndef PB_MAX_REQUIRED_FIELDS
+#define PB_MAX_REQUIRED_FIELDS 64
+#endif
+
+#if PB_MAX_REQUIRED_FIELDS < 64
+#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64).
+#endif
+
+/* List of possible field types. These are used in the autogenerated code.
+ * Least-significant 4 bits tell the scalar type
+ * Most-significant 4 bits specify repeated/required/packed etc.
+ */
+
+typedef uint_least8_t pb_type_t;
+
+/**** Field data types ****/
+
+/* Numeric types */
+#define PB_LTYPE_VARINT  0x00 /* int32, int64, enum, bool */
+#define PB_LTYPE_UVARINT 0x01 /* uint32, uint64 */
+#define PB_LTYPE_SVARINT 0x02 /* sint32, sint64 */
+#define PB_LTYPE_FIXED32 0x03 /* fixed32, sfixed32, float */
+#define PB_LTYPE_FIXED64 0x04 /* fixed64, sfixed64, double */
+
+/* Marker for last packable field type. */
+#define PB_LTYPE_LAST_PACKABLE 0x04
+
+/* Byte array with pre-allocated buffer.
+ * data_size is the length of the allocated PB_BYTES_ARRAY structure. */
+#define PB_LTYPE_BYTES 0x05
+
+/* String with pre-allocated buffer.
+ * data_size is the maximum length. */
+#define PB_LTYPE_STRING 0x06
+
+/* Submessage
+ * submsg_fields is pointer to field descriptions */
+#define PB_LTYPE_SUBMESSAGE 0x07
+
+/* Extension pseudo-field
+ * The field contains a pointer to pb_extension_t */
+#define PB_LTYPE_EXTENSION 0x08
+
+/* Byte array with inline, pre-allocated byffer.
+ * data_size is the length of the inline, allocated buffer.
+ * This differs from PB_LTYPE_BYTES by defining the element as
+ * pb_byte_t[data_size] rather than pb_bytes_array_t. */
+#define PB_LTYPE_FIXED_LENGTH_BYTES 0x09
+
+/* Number of declared LTYPES */
+#define PB_LTYPES_COUNT 0x0A
+#define PB_LTYPE_MASK 0x0F
+
+/**** Field repetition rules ****/
+
+#define PB_HTYPE_REQUIRED 0x00
+#define PB_HTYPE_OPTIONAL 0x10
+#define PB_HTYPE_REPEATED 0x20
+#define PB_HTYPE_ONEOF    0x30
+#define PB_HTYPE_MASK     0x30
+
+/**** Field allocation types ****/
+#define PB_ATYPE_STATIC   0x00
+#define PB_ATYPE_POINTER  0x80
+#define PB_ATYPE_CALLBACK 0x40
+#define PB_ATYPE_MASK     0xC0
+
+#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK)
+#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
+#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
+
+/* Data type used for storing sizes of struct fields
+ * and array counts.
+ */
+#if defined(PB_FIELD_32BIT)
+    typedef uint32_t pb_size_t;
+    typedef int32_t pb_ssize_t;
+#elif defined(PB_FIELD_16BIT)
+    typedef uint_least16_t pb_size_t;
+    typedef int_least16_t pb_ssize_t;
+#else
+    typedef uint_least8_t pb_size_t;
+    typedef int_least8_t pb_ssize_t;
+#endif
+#define PB_SIZE_MAX ((pb_size_t)-1)
+
+/* Data type for storing encoded data and other byte streams.
+ * This typedef exists to support platforms where uint8_t does not exist.
+ * You can regard it as equivalent on uint8_t on other platforms.
+ */
+typedef uint_least8_t pb_byte_t;
+
+/* This structure is used in auto-generated constants
+ * to specify struct fields.
+ * You can change field sizes if you need structures
+ * larger than 256 bytes or field tags larger than 256.
+ * The compiler should complain if your .proto has such
+ * structures. Fix that by defining PB_FIELD_16BIT or
+ * PB_FIELD_32BIT.
+ */
+PB_PACKED_STRUCT_START
+typedef struct pb_field_s pb_field_t;
+struct pb_field_s {
+    pb_size_t tag;
+    pb_type_t type;
+    pb_size_t data_offset; /* Offset of field data, relative to previous field. */
+    pb_ssize_t size_offset; /* Offset of array size or has-boolean, relative to data */
+    pb_size_t data_size; /* Data size in bytes for a single item */
+    pb_size_t array_size; /* Maximum number of entries in array */
+    
+    /* Field definitions for submessage
+     * OR default value for all other non-array, non-callback types
+     * If null, then field will zeroed. */
+    const void *ptr;
+} pb_packed;
+PB_PACKED_STRUCT_END
+
+/* Make sure that the standard integer types are of the expected sizes.
+ * Otherwise fixed32/fixed64 fields can break.
+ *
+ * If you get errors here, it probably means that your stdint.h is not
+ * correct for your platform.
+ */
+PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE)
+PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE)
+
+/* This structure is used for 'bytes' arrays.
+ * It has the number of bytes in the beginning, and after that an array.
+ * Note that actual structs used will have a different length of bytes array.
+ */
+#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; }
+#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes))
+
+struct pb_bytes_array_s {
+    pb_size_t size;
+    pb_byte_t bytes[1];
+};
+typedef struct pb_bytes_array_s pb_bytes_array_t;
+
+/* This structure is used for giving the callback function.
+ * It is stored in the message structure and filled in by the method that
+ * calls pb_decode.
+ *
+ * The decoding callback will be given a limited-length stream
+ * If the wire type was string, the length is the length of the string.
+ * If the wire type was a varint/fixed32/fixed64, the length is the length
+ * of the actual value.
+ * The function may be called multiple times (especially for repeated types,
+ * but also otherwise if the message happens to contain the field multiple
+ * times.)
+ *
+ * The encoding callback will receive the actual output stream.
+ * It should write all the data in one call, including the field tag and
+ * wire type. It can write multiple fields.
+ *
+ * The callback can be null if you want to skip a field.
+ */
+typedef struct pb_istream_s pb_istream_t;
+typedef struct pb_ostream_s pb_ostream_t;
+typedef struct pb_callback_s pb_callback_t;
+struct pb_callback_s {
+#ifdef PB_OLD_CALLBACK_STYLE
+    /* Deprecated since nanopb-0.2.1 */
+    union {
+        bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg);
+        bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, const void *arg);
+    } funcs;
+#else
+    /* New function signature, which allows modifying arg contents in callback. */
+    union {
+        bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
+        bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
+    } funcs;
+#endif    
+    
+    /* Free arg for use by callback */
+    void *arg;
+};
+
+/* Wire types. Library user needs these only in encoder callbacks. */
+typedef enum {
+    PB_WT_VARINT = 0,
+    PB_WT_64BIT  = 1,
+    PB_WT_STRING = 2,
+    PB_WT_32BIT  = 5
+} pb_wire_type_t;
+
+/* Structure for defining the handling of unknown/extension fields.
+ * Usually the pb_extension_type_t structure is automatically generated,
+ * while the pb_extension_t structure is created by the user. However,
+ * if you want to catch all unknown fields, you can also create a custom
+ * pb_extension_type_t with your own callback.
+ */
+typedef struct pb_extension_type_s pb_extension_type_t;
+typedef struct pb_extension_s pb_extension_t;
+struct pb_extension_type_s {
+    /* Called for each unknown field in the message.
+     * If you handle the field, read off all of its data and return true.
+     * If you do not handle the field, do not read anything and return true.
+     * If you run into an error, return false.
+     * Set to NULL for default handler.
+     */
+    bool (*decode)(pb_istream_t *stream, pb_extension_t *extension,
+                   uint32_t tag, pb_wire_type_t wire_type);
+    
+    /* Called once after all regular fields have been encoded.
+     * If you have something to write, do so and return true.
+     * If you do not have anything to write, just return true.
+     * If you run into an error, return false.
+     * Set to NULL for default handler.
+     */
+    bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension);
+    
+    /* Free field for use by the callback. */
+    const void *arg;
+};
+
+struct pb_extension_s {
+    /* Type describing the extension field. Usually you'll initialize
+     * this to a pointer to the automatically generated structure. */
+    const pb_extension_type_t *type;
+    
+    /* Destination for the decoded data. This must match the datatype
+     * of the extension field. */
+    void *dest;
+    
+    /* Pointer to the next extension handler, or NULL.
+     * If this extension does not match a field, the next handler is
+     * automatically called. */
+    pb_extension_t *next;
+
+    /* The decoder sets this to true if the extension was found.
+     * Ignored for encoding. */
+    bool found;
+};
+
+/* Memory allocation functions to use. You can define pb_realloc and
+ * pb_free to custom functions if you want. */
+#ifdef PB_ENABLE_MALLOC
+#   ifndef pb_realloc
+#       define pb_realloc(ptr, size) realloc(ptr, size)
+#   endif
+#   ifndef pb_free
+#       define pb_free(ptr) free(ptr)
+#   endif
+#endif
+
+/* This is used to inform about need to regenerate .pb.h/.pb.c files. */
+#define PB_PROTO_HEADER_VERSION 30
+
+/* These macros are used to declare pb_field_t's in the constant array. */
+/* Size of a structure member, in bytes. */
+#define pb_membersize(st, m) (sizeof ((st*)0)->m)
+/* Number of entries in an array. */
+#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0]))
+/* Delta from start of one member to the start of another member. */
+#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2))
+/* Marks the end of the field list */
+#define PB_LAST_FIELD {0,(pb_type_t) 0,0,0,0,0,0}
+
+/* Macros for filling in the data_offset field */
+/* data_offset for first field in a message */
+#define PB_DATAOFFSET_FIRST(st, m1, m2) (offsetof(st, m1))
+/* data_offset for subsequent fields */
+#define PB_DATAOFFSET_OTHER(st, m1, m2) (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2))
+/* data offset for subsequent fields inside an union (oneof) */
+#define PB_DATAOFFSET_UNION(st, m1, m2) (PB_SIZE_MAX)
+/* Choose first/other based on m1 == m2 (deprecated, remains for backwards compatibility) */
+#define PB_DATAOFFSET_CHOOSE(st, m1, m2) (int)(offsetof(st, m1) == offsetof(st, m2) \
+                                  ? PB_DATAOFFSET_FIRST(st, m1, m2) \
+                                  : PB_DATAOFFSET_OTHER(st, m1, m2))
+
+/* Required fields are the simplest. They just have delta (padding) from
+ * previous field end, and the size of the field. Pointer is used for
+ * submessages and default values.
+ */
+#define PB_REQUIRED_STATIC(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | ltype, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+
+/* Optional fields add the delta to the has_ variable. */
+#define PB_OPTIONAL_STATIC(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \
+    fd, \
+    pb_delta(st, has_ ## m, m), \
+    pb_membersize(st, m), 0, ptr}
+
+#define PB_SINGULAR_STATIC(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+
+/* Repeated fields have a _count field and also the maximum number of entries. */
+#define PB_REPEATED_STATIC(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_REPEATED | ltype, \
+    fd, \
+    pb_delta(st, m ## _count, m), \
+    pb_membersize(st, m[0]), \
+    pb_arraysize(st, m), ptr}
+
+/* Allocated fields carry the size of the actual data, not the pointer */
+#define PB_REQUIRED_POINTER(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_REQUIRED | ltype, \
+    fd, 0, pb_membersize(st, m[0]), 0, ptr}
+
+/* Optional fields don't need a has_ variable, as information would be redundant */
+#define PB_OPTIONAL_POINTER(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_OPTIONAL | ltype, \
+    fd, 0, pb_membersize(st, m[0]), 0, ptr}
+
+/* Same as optional fields*/
+#define PB_SINGULAR_POINTER(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_OPTIONAL | ltype, \
+    fd, 0, pb_membersize(st, m[0]), 0, ptr}
+
+/* Repeated fields have a _count field and a pointer to array of pointers */
+#define PB_REPEATED_POINTER(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_REPEATED | ltype, \
+    fd, pb_delta(st, m ## _count, m), \
+    pb_membersize(st, m[0]), 0, ptr}
+
+/* Callbacks are much like required fields except with special datatype. */
+#define PB_REQUIRED_CALLBACK(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_CALLBACK | PB_HTYPE_REQUIRED | ltype, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+
+#define PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+
+#define PB_SINGULAR_CALLBACK(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+    
+#define PB_REPEATED_CALLBACK(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_CALLBACK | PB_HTYPE_REPEATED | ltype, \
+    fd, 0, pb_membersize(st, m), 0, ptr}
+
+/* Optional extensions don't have the has_ field, as that would be redundant.
+ * Furthermore, the combination of OPTIONAL without has_ field is used
+ * for indicating proto3 style fields. Extensions exist in proto2 mode only,
+ * so they should be encoded according to proto2 rules. To avoid the conflict,
+ * extensions are marked as REQUIRED instead.
+ */
+#define PB_OPTEXT_STATIC(tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | ltype, \
+    0, \
+    0, \
+    pb_membersize(st, m), 0, ptr}
+
+#define PB_OPTEXT_POINTER(tag, st, m, fd, ltype, ptr) \
+    PB_OPTIONAL_POINTER(tag, st, m, fd, ltype, ptr)
+
+#define PB_OPTEXT_CALLBACK(tag, st, m, fd, ltype, ptr) \
+    PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr)
+
+/* The mapping from protobuf types to LTYPEs is done using these macros. */
+#define PB_LTYPE_MAP_BOOL               PB_LTYPE_VARINT
+#define PB_LTYPE_MAP_BYTES              PB_LTYPE_BYTES
+#define PB_LTYPE_MAP_DOUBLE             PB_LTYPE_FIXED64
+#define PB_LTYPE_MAP_ENUM               PB_LTYPE_VARINT
+#define PB_LTYPE_MAP_UENUM              PB_LTYPE_UVARINT
+#define PB_LTYPE_MAP_FIXED32            PB_LTYPE_FIXED32
+#define PB_LTYPE_MAP_FIXED64            PB_LTYPE_FIXED64
+#define PB_LTYPE_MAP_FLOAT              PB_LTYPE_FIXED32
+#define PB_LTYPE_MAP_INT32              PB_LTYPE_VARINT
+#define PB_LTYPE_MAP_INT64              PB_LTYPE_VARINT
+#define PB_LTYPE_MAP_MESSAGE            PB_LTYPE_SUBMESSAGE
+#define PB_LTYPE_MAP_SFIXED32           PB_LTYPE_FIXED32
+#define PB_LTYPE_MAP_SFIXED64           PB_LTYPE_FIXED64
+#define PB_LTYPE_MAP_SINT32             PB_LTYPE_SVARINT
+#define PB_LTYPE_MAP_SINT64             PB_LTYPE_SVARINT
+#define PB_LTYPE_MAP_STRING             PB_LTYPE_STRING
+#define PB_LTYPE_MAP_UINT32             PB_LTYPE_UVARINT
+#define PB_LTYPE_MAP_UINT64             PB_LTYPE_UVARINT
+#define PB_LTYPE_MAP_EXTENSION          PB_LTYPE_EXTENSION
+#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES
+
+/* This is the actual macro used in field descriptions.
+ * It takes these arguments:
+ * - Field tag number
+ * - Field type:   BOOL, BYTES, DOUBLE, ENUM, UENUM, FIXED32, FIXED64,
+ *                 FLOAT, INT32, INT64, MESSAGE, SFIXED32, SFIXED64
+ *                 SINT32, SINT64, STRING, UINT32, UINT64 or EXTENSION
+ * - Field rules:  REQUIRED, OPTIONAL or REPEATED
+ * - Allocation:   STATIC, CALLBACK or POINTER
+ * - Placement: FIRST or OTHER, depending on if this is the first field in structure.
+ * - Message name
+ * - Field name
+ * - Previous field name (or field name again for first field)
+ * - Pointer to default value or submsg fields.
+ */
+
+#define PB_FIELD(tag, type, rules, allocation, placement, message, field, prevfield, ptr) \
+        PB_ ## rules ## _ ## allocation(tag, message, field, \
+        PB_DATAOFFSET_ ## placement(message, field, prevfield), \
+        PB_LTYPE_MAP_ ## type, ptr)
+
+/* Field description for oneof fields. This requires taking into account the
+ * union name also, that's why a separate set of macros is needed.
+ */
+#define PB_ONEOF_STATIC(u, tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_ONEOF | ltype, \
+    fd, pb_delta(st, which_ ## u, u.m), \
+    pb_membersize(st, u.m), 0, ptr}
+
+#define PB_ONEOF_POINTER(u, tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_ONEOF | ltype, \
+    fd, pb_delta(st, which_ ## u, u.m), \
+    pb_membersize(st, u.m[0]), 0, ptr}
+
+#define PB_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, message, field, prevfield, ptr) \
+        PB_ONEOF_ ## allocation(union_name, tag, message, field, \
+        PB_DATAOFFSET_ ## placement(message, union_name.field, prevfield), \
+        PB_LTYPE_MAP_ ## type, ptr)
+
+#define PB_ANONYMOUS_ONEOF_STATIC(u, tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_STATIC | PB_HTYPE_ONEOF | ltype, \
+    fd, pb_delta(st, which_ ## u, m), \
+    pb_membersize(st, m), 0, ptr}
+
+#define PB_ANONYMOUS_ONEOF_POINTER(u, tag, st, m, fd, ltype, ptr) \
+    {tag, PB_ATYPE_POINTER | PB_HTYPE_ONEOF | ltype, \
+    fd, pb_delta(st, which_ ## u, m), \
+    pb_membersize(st, m[0]), 0, ptr}
+
+#define PB_ANONYMOUS_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, message, field, prevfield, ptr) \
+        PB_ANONYMOUS_ONEOF_ ## allocation(union_name, tag, message, field, \
+        PB_DATAOFFSET_ ## placement(message, field, prevfield), \
+        PB_LTYPE_MAP_ ## type, ptr)
+
+/* These macros are used for giving out error messages.
+ * They are mostly a debugging aid; the main error information
+ * is the true/false return value from functions.
+ * Some code space can be saved by disabling the error
+ * messages if not used.
+ *
+ * PB_SET_ERROR() sets the error message if none has been set yet.
+ *                msg must be a constant string literal.
+ * PB_GET_ERROR() always returns a pointer to a string.
+ * PB_RETURN_ERROR() sets the error and returns false from current
+ *                   function.
+ */
+#ifdef PB_NO_ERRMSG
+#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream)
+#define PB_GET_ERROR(stream) "(errmsg disabled)"
+#else
+#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg))
+#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)")
+#endif
+
+#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false
+
+#endif
diff --git a/iOS/Pods/nanopb/pb_common.c b/iOS/Pods/nanopb/pb_common.c
new file mode 100644 (file)
index 0000000..4fb7186
--- /dev/null
@@ -0,0 +1,97 @@
+/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c.
+ *
+ * 2014 Petteri Aimonen <jpa@kapsi.fi>
+ */
+
+#include "pb_common.h"
+
+bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_field_t *fields, void *dest_struct)
+{
+    iter->start = fields;
+    iter->pos = fields;
+    iter->required_field_index = 0;
+    iter->dest_struct = dest_struct;
+    iter->pData = (char*)dest_struct + iter->pos->data_offset;
+    iter->pSize = (char*)iter->pData + iter->pos->size_offset;
+    
+    return (iter->pos->tag != 0);
+}
+
+bool pb_field_iter_next(pb_field_iter_t *iter)
+{
+    const pb_field_t *prev_field = iter->pos;
+
+    if (prev_field->tag == 0)
+    {
+        /* Handle empty message types, where the first field is already the terminator.
+         * In other cases, the iter->pos never points to the terminator. */
+        return false;
+    }
+    
+    iter->pos++;
+    
+    if (iter->pos->tag == 0)
+    {
+        /* Wrapped back to beginning, reinitialize */
+        (void)pb_field_iter_begin(iter, iter->start, iter->dest_struct);
+        return false;
+    }
+    else
+    {
+        /* Increment the pointers based on previous field size */
+        size_t prev_size = prev_field->data_size;
+    
+        if (PB_HTYPE(prev_field->type) == PB_HTYPE_ONEOF &&
+            PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF &&
+            iter->pos->data_offset == PB_SIZE_MAX)
+        {
+            /* Don't advance pointers inside unions */
+            return true;
+        }
+        else if (PB_ATYPE(prev_field->type) == PB_ATYPE_STATIC &&
+                 PB_HTYPE(prev_field->type) == PB_HTYPE_REPEATED)
+        {
+            /* In static arrays, the data_size tells the size of a single entry and
+             * array_size is the number of entries */
+            prev_size *= prev_field->array_size;
+        }
+        else if (PB_ATYPE(prev_field->type) == PB_ATYPE_POINTER)
+        {
+            /* Pointer fields always have a constant size in the main structure.
+             * The data_size only applies to the dynamically allocated area. */
+            prev_size = sizeof(void*);
+        }
+
+        if (PB_HTYPE(prev_field->type) == PB_HTYPE_REQUIRED)
+        {
+            /* Count the required fields, in order to check their presence in the
+             * decoder. */
+            iter->required_field_index++;
+        }
+    
+        iter->pData = (char*)iter->pData + prev_size + iter->pos->data_offset;
+        iter->pSize = (char*)iter->pData + iter->pos->size_offset;
+        return true;
+    }
+}
+
+bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag)
+{
+    const pb_field_t *start = iter->pos;
+    
+    do {
+        if (iter->pos->tag == tag &&
+            PB_LTYPE(iter->pos->type) != PB_LTYPE_EXTENSION)
+        {
+            /* Found the wanted field */
+            return true;
+        }
+        
+        (void)pb_field_iter_next(iter);
+    } while (iter->pos != start);
+    
+    /* Searched all the way back to start, and found nothing. */
+    return false;
+}
+
+
diff --git a/iOS/Pods/nanopb/pb_common.h b/iOS/Pods/nanopb/pb_common.h
new file mode 100644 (file)
index 0000000..60b3d37
--- /dev/null
@@ -0,0 +1,42 @@
+/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c.
+ * These functions are rarely needed by applications directly.
+ */
+
+#ifndef PB_COMMON_H_INCLUDED
+#define PB_COMMON_H_INCLUDED
+
+#include "pb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Iterator for pb_field_t list */
+struct pb_field_iter_s {
+    const pb_field_t *start;       /* Start of the pb_field_t array */
+    const pb_field_t *pos;         /* Current position of the iterator */
+    unsigned required_field_index; /* Zero-based index that counts only the required fields */
+    void *dest_struct;             /* Pointer to start of the structure */
+    void *pData;                   /* Pointer to current field value */
+    void *pSize;                   /* Pointer to count/has field */
+};
+typedef struct pb_field_iter_s pb_field_iter_t;
+
+/* Initialize the field iterator structure to beginning.
+ * Returns false if the message type is empty. */
+bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_field_t *fields, void *dest_struct);
+
+/* Advance the iterator to the next field.
+ * Returns false when the iterator wraps back to the first field. */
+bool pb_field_iter_next(pb_field_iter_t *iter);
+
+/* Advance the iterator until it points at a field with the given tag.
+ * Returns false if no such field exists. */
+bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
+
diff --git a/iOS/Pods/nanopb/pb_decode.c b/iOS/Pods/nanopb/pb_decode.c
new file mode 100644 (file)
index 0000000..e2e90ca
--- /dev/null
@@ -0,0 +1,1379 @@
+/* pb_decode.c -- decode a protobuf using minimal resources
+ *
+ * 2011 Petteri Aimonen <jpa@kapsi.fi>
+ */
+
+/* Use the GCC warn_unused_result attribute to check that all return values
+ * are propagated correctly. On other compilers and gcc before 3.4.0 just
+ * ignore the annotation.
+ */
+#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
+    #define checkreturn
+#else
+    #define checkreturn __attribute__((warn_unused_result))
+#endif
+
+#include "pb.h"
+#include "pb_decode.h"
+#include "pb_common.h"
+
+/**************************************
+ * Declarations internal to this file *
+ **************************************/
+
+typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest) checkreturn;
+
+static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
+static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size);
+static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension);
+static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type);
+static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static bool checkreturn find_extension_field(pb_field_iter_t *iter);
+static void pb_field_set_to_default(pb_field_iter_t *iter);
+static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct);
+static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_skip_varint(pb_istream_t *stream);
+static bool checkreturn pb_skip_string(pb_istream_t *stream);
+
+#ifdef PB_ENABLE_MALLOC
+static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size);
+static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter);
+static void pb_release_single_field(const pb_field_iter_t *iter);
+#endif
+
+/* --- Function pointers to field decoders ---
+ * Order in the array must match pb_action_t LTYPE numbering.
+ */
+static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = {
+    &pb_dec_varint,
+    &pb_dec_uvarint,
+    &pb_dec_svarint,
+    &pb_dec_fixed32,
+    &pb_dec_fixed64,
+    
+    &pb_dec_bytes,
+    &pb_dec_string,
+    &pb_dec_submessage,
+    NULL, /* extensions */
+    &pb_dec_fixed_length_bytes
+};
+
+/*******************************
+ * pb_istream_t implementation *
+ *******************************/
+
+static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count)
+{
+    size_t i;
+    const pb_byte_t *source = (const pb_byte_t*)stream->state;
+    stream->state = (pb_byte_t*)stream->state + count;
+    
+    if (buf != NULL)
+    {
+        for (i = 0; i < count; i++)
+            buf[i] = source[i];
+    }
+    
+    return true;
+}
+
+bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count)
+{
+#ifndef PB_BUFFER_ONLY
+       if (buf == NULL && stream->callback != buf_read)
+       {
+               /* Skip input bytes */
+               pb_byte_t tmp[16];
+               while (count > 16)
+               {
+                       if (!pb_read(stream, tmp, 16))
+                               return false;
+                       
+                       count -= 16;
+               }
+               
+               return pb_read(stream, tmp, count);
+       }
+#endif
+
+    if (stream->bytes_left < count)
+        PB_RETURN_ERROR(stream, "end-of-stream");
+    
+#ifndef PB_BUFFER_ONLY
+    if (!stream->callback(stream, buf, count))
+        PB_RETURN_ERROR(stream, "io error");
+#else
+    if (!buf_read(stream, buf, count))
+        return false;
+#endif
+    
+    stream->bytes_left -= count;
+    return true;
+}
+
+/* Read a single byte from input stream. buf may not be NULL.
+ * This is an optimization for the varint decoding. */
+static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf)
+{
+    if (stream->bytes_left == 0)
+        PB_RETURN_ERROR(stream, "end-of-stream");
+
+#ifndef PB_BUFFER_ONLY
+    if (!stream->callback(stream, buf, 1))
+        PB_RETURN_ERROR(stream, "io error");
+#else
+    *buf = *(const pb_byte_t*)stream->state;
+    stream->state = (pb_byte_t*)stream->state + 1;
+#endif
+
+    stream->bytes_left--;
+    
+    return true;    
+}
+
+pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize)
+{
+    pb_istream_t stream;
+    /* Cast away the const from buf without a compiler error.  We are
+     * careful to use it only in a const manner in the callbacks.
+     */
+    union {
+        void *state;
+        const void *c_state;
+    } state;
+#ifdef PB_BUFFER_ONLY
+    stream.callback = NULL;
+#else
+    stream.callback = &buf_read;
+#endif
+    state.c_state = buf;
+    stream.state = state.state;
+    stream.bytes_left = bufsize;
+#ifndef PB_NO_ERRMSG
+    stream.errmsg = NULL;
+#endif
+    return stream;
+}
+
+/********************
+ * Helper functions *
+ ********************/
+
+bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
+{
+    pb_byte_t byte;
+    uint32_t result;
+    
+    if (!pb_readbyte(stream, &byte))
+        return false;
+    
+    if ((byte & 0x80) == 0)
+    {
+        /* Quick case, 1 byte value */
+        result = byte;
+    }
+    else
+    {
+        /* Multibyte case */
+        uint_fast8_t bitpos = 7;
+        result = byte & 0x7F;
+        
+        do
+        {
+            if (bitpos >= 32)
+                PB_RETURN_ERROR(stream, "varint overflow");
+            
+            if (!pb_readbyte(stream, &byte))
+                return false;
+            
+            result |= (uint32_t)(byte & 0x7F) << bitpos;
+            bitpos = (uint_fast8_t)(bitpos + 7);
+        } while (byte & 0x80);
+   }
+   
+   *dest = result;
+   return true;
+}
+
+bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest)
+{
+    pb_byte_t byte;
+    uint_fast8_t bitpos = 0;
+    uint64_t result = 0;
+    
+    do
+    {
+        if (bitpos >= 64)
+            PB_RETURN_ERROR(stream, "varint overflow");
+        
+        if (!pb_readbyte(stream, &byte))
+            return false;
+
+        result |= (uint64_t)(byte & 0x7F) << bitpos;
+        bitpos = (uint_fast8_t)(bitpos + 7);
+    } while (byte & 0x80);
+    
+    *dest = result;
+    return true;
+}
+
+bool checkreturn pb_skip_varint(pb_istream_t *stream)
+{
+    pb_byte_t byte;
+    do
+    {
+        if (!pb_read(stream, &byte, 1))
+            return false;
+    } while (byte & 0x80);
+    return true;
+}
+
+bool checkreturn pb_skip_string(pb_istream_t *stream)
+{
+    uint32_t length;
+    if (!pb_decode_varint32(stream, &length))
+        return false;
+    
+    return pb_read(stream, NULL, length);
+}
+
+bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof)
+{
+    uint32_t temp;
+    *eof = false;
+    *wire_type = (pb_wire_type_t) 0;
+    *tag = 0;
+    
+    if (!pb_decode_varint32(stream, &temp))
+    {
+        if (stream->bytes_left == 0)
+            *eof = true;
+
+        return false;
+    }
+    
+    if (temp == 0)
+    {
+        *eof = true; /* Special feature: allow 0-terminated messages. */
+        return false;
+    }
+    
+    *tag = temp >> 3;
+    *wire_type = (pb_wire_type_t)(temp & 7);
+    return true;
+}
+
+bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type)
+{
+    switch (wire_type)
+    {
+        case PB_WT_VARINT: return pb_skip_varint(stream);
+        case PB_WT_64BIT: return pb_read(stream, NULL, 8);
+        case PB_WT_STRING: return pb_skip_string(stream);
+        case PB_WT_32BIT: return pb_read(stream, NULL, 4);
+        default: PB_RETURN_ERROR(stream, "invalid wire_type");
+    }
+}
+
+/* Read a raw value to buffer, for the purpose of passing it to callback as
+ * a substream. Size is maximum size on call, and actual size on return.
+ */
+static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size)
+{
+    size_t max_size = *size;
+    switch (wire_type)
+    {
+        case PB_WT_VARINT:
+            *size = 0;
+            do
+            {
+                (*size)++;
+                if (*size > max_size) return false;
+                if (!pb_read(stream, buf, 1)) return false;
+            } while (*buf++ & 0x80);
+            return true;
+            
+        case PB_WT_64BIT:
+            *size = 8;
+            return pb_read(stream, buf, 8);
+        
+        case PB_WT_32BIT:
+            *size = 4;
+            return pb_read(stream, buf, 4);
+        
+        default: PB_RETURN_ERROR(stream, "invalid wire_type");
+    }
+}
+
+/* Decode string length from stream and return a substream with limited length.
+ * Remember to close the substream using pb_close_string_substream().
+ */
+bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream)
+{
+    uint32_t size;
+    if (!pb_decode_varint32(stream, &size))
+        return false;
+    
+    *substream = *stream;
+    if (substream->bytes_left < size)
+        PB_RETURN_ERROR(stream, "parent stream too short");
+    
+    substream->bytes_left = size;
+    stream->bytes_left -= size;
+    return true;
+}
+
+bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream)
+{
+    if (substream->bytes_left) {
+        if (!pb_read(substream, NULL, substream->bytes_left))
+            return false;
+    }
+
+    stream->state = substream->state;
+
+#ifndef PB_NO_ERRMSG
+    stream->errmsg = substream->errmsg;
+#endif
+    return true;
+}
+
+/*************************
+ * Decode a single field *
+ *************************/
+
+static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+    pb_type_t type;
+    pb_decoder_t func;
+    
+    type = iter->pos->type;
+    func = PB_DECODERS[PB_LTYPE(type)];
+
+    switch (PB_HTYPE(type))
+    {
+        case PB_HTYPE_REQUIRED:
+            return func(stream, iter->pos, iter->pData);
+            
+        case PB_HTYPE_OPTIONAL:
+            if (iter->pSize != iter->pData)
+                *(bool*)iter->pSize = true;
+            return func(stream, iter->pos, iter->pData);
+    
+        case PB_HTYPE_REPEATED:
+            if (wire_type == PB_WT_STRING
+                && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
+            {
+                /* Packed array */
+                bool status = true;
+                pb_size_t *size = (pb_size_t*)iter->pSize;
+                pb_istream_t substream;
+                if (!pb_make_string_substream(stream, &substream))
+                    return false;
+                
+                while (substream.bytes_left > 0 && *size < iter->pos->array_size)
+                {
+                    void *pItem = (char*)iter->pData + iter->pos->data_size * (*size);
+                    if (!func(&substream, iter->pos, pItem))
+                    {
+                        status = false;
+                        break;
+                    }
+                    (*size)++;
+                }
+
+                if (substream.bytes_left != 0)
+                    PB_RETURN_ERROR(stream, "array overflow");
+                if (!pb_close_string_substream(stream, &substream))
+                    return false;
+
+                return status;
+            }
+            else
+            {
+                /* Repeated field */
+                pb_size_t *size = (pb_size_t*)iter->pSize;
+                void *pItem = (char*)iter->pData + iter->pos->data_size * (*size);
+                if (*size >= iter->pos->array_size)
+                    PB_RETURN_ERROR(stream, "array overflow");
+                
+                (*size)++;
+                return func(stream, iter->pos, pItem);
+            }
+
+        case PB_HTYPE_ONEOF:
+            *(pb_size_t*)iter->pSize = iter->pos->tag;
+            if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE)
+            {
+                /* We memset to zero so that any callbacks are set to NULL.
+                 * Then set any default values. */
+                memset(iter->pData, 0, iter->pos->data_size);
+                pb_message_set_to_defaults((const pb_field_t*)iter->pos->ptr, iter->pData);
+            }
+            return func(stream, iter->pos, iter->pData);
+
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+}
+
+#ifdef PB_ENABLE_MALLOC
+/* Allocate storage for the field and store the pointer at iter->pData.
+ * array_size is the number of entries to reserve in an array.
+ * Zero size is not allowed, use pb_free() for releasing.
+ */
+static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size)
+{    
+    void *ptr = *(void**)pData;
+    
+    if (data_size == 0 || array_size == 0)
+        PB_RETURN_ERROR(stream, "invalid size");
+    
+    /* Check for multiplication overflows.
+     * This code avoids the costly division if the sizes are small enough.
+     * Multiplication is safe as long as only half of bits are set
+     * in either multiplicand.
+     */
+    {
+        const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4);
+        if (data_size >= check_limit || array_size >= check_limit)
+        {
+            const size_t size_max = (size_t)-1;
+            if (size_max / array_size < data_size)
+            {
+                PB_RETURN_ERROR(stream, "size too large");
+            }
+        }
+    }
+    
+    /* Allocate new or expand previous allocation */
+    /* Note: on failure the old pointer will remain in the structure,
+     * the message must be freed by caller also on error return. */
+    ptr = pb_realloc(ptr, array_size * data_size);
+    if (ptr == NULL)
+        PB_RETURN_ERROR(stream, "realloc failed");
+    
+    *(void**)pData = ptr;
+    return true;
+}
+
+/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */
+static void initialize_pointer_field(void *pItem, pb_field_iter_t *iter)
+{
+    if (PB_LTYPE(iter->pos->type) == PB_LTYPE_STRING ||
+        PB_LTYPE(iter->pos->type) == PB_LTYPE_BYTES)
+    {
+        *(void**)pItem = NULL;
+    }
+    else if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE)
+    {
+        pb_message_set_to_defaults((const pb_field_t *) iter->pos->ptr, pItem);
+    }
+}
+#endif
+
+static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+#ifndef PB_ENABLE_MALLOC
+    PB_UNUSED(wire_type);
+    PB_UNUSED(iter);
+    PB_RETURN_ERROR(stream, "no malloc support");
+#else
+    pb_type_t type;
+    pb_decoder_t func;
+    
+    type = iter->pos->type;
+    func = PB_DECODERS[PB_LTYPE(type)];
+    
+    switch (PB_HTYPE(type))
+    {
+        case PB_HTYPE_REQUIRED:
+        case PB_HTYPE_OPTIONAL:
+        case PB_HTYPE_ONEOF:
+            if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE &&
+                *(void**)iter->pData != NULL)
+            {
+                /* Duplicate field, have to release the old allocation first. */
+                pb_release_single_field(iter);
+            }
+        
+            if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
+            {
+                *(pb_size_t*)iter->pSize = iter->pos->tag;
+            }
+
+            if (PB_LTYPE(type) == PB_LTYPE_STRING ||
+                PB_LTYPE(type) == PB_LTYPE_BYTES)
+            {
+                return func(stream, iter->pos, iter->pData);
+            }
+            else
+            {
+                if (!allocate_field(stream, iter->pData, iter->pos->data_size, 1))
+                    return false;
+                
+                initialize_pointer_field(*(void**)iter->pData, iter);
+                return func(stream, iter->pos, *(void**)iter->pData);
+            }
+    
+        case PB_HTYPE_REPEATED:
+            if (wire_type == PB_WT_STRING
+                && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
+            {
+                /* Packed array, multiple items come in at once. */
+                bool status = true;
+                pb_size_t *size = (pb_size_t*)iter->pSize;
+                size_t allocated_size = *size;
+                void *pItem;
+                pb_istream_t substream;
+                
+                if (!pb_make_string_substream(stream, &substream))
+                    return false;
+                
+                while (substream.bytes_left)
+                {
+                    if ((size_t)*size + 1 > allocated_size)
+                    {
+                        /* Allocate more storage. This tries to guess the
+                         * number of remaining entries. Round the division
+                         * upwards. */
+                        allocated_size += (substream.bytes_left - 1) / iter->pos->data_size + 1;
+                        
+                        if (!allocate_field(&substream, iter->pData, iter->pos->data_size, allocated_size))
+                        {
+                            status = false;
+                            break;
+                        }
+                    }
+
+                    /* Decode the array entry */
+                    pItem = *(char**)iter->pData + iter->pos->data_size * (*size);
+                    initialize_pointer_field(pItem, iter);
+                    if (!func(&substream, iter->pos, pItem))
+                    {
+                        status = false;
+                        break;
+                    }
+                    
+                    if (*size == PB_SIZE_MAX)
+                    {
+#ifndef PB_NO_ERRMSG
+                        stream->errmsg = "too many array entries";
+#endif
+                        status = false;
+                        break;
+                    }
+                    
+                    (*size)++;
+                }
+                if (!pb_close_string_substream(stream, &substream))
+                    return false;
+                
+                return status;
+            }
+            else
+            {
+                /* Normal repeated field, i.e. only one item at a time. */
+                pb_size_t *size = (pb_size_t*)iter->pSize;
+                void *pItem;
+                
+                if (*size == PB_SIZE_MAX)
+                    PB_RETURN_ERROR(stream, "too many array entries");
+                
+                (*size)++;
+                if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size))
+                    return false;
+            
+                pItem = *(char**)iter->pData + iter->pos->data_size * (*size - 1);
+                initialize_pointer_field(pItem, iter);
+                return func(stream, iter->pos, pItem);
+            }
+
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+#endif
+}
+
+static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+    pb_callback_t *pCallback = (pb_callback_t*)iter->pData;
+    
+#ifdef PB_OLD_CALLBACK_STYLE
+    void *arg = pCallback->arg;
+#else
+    void **arg = &(pCallback->arg);
+#endif
+    
+    if (pCallback->funcs.decode == NULL)
+        return pb_skip_field(stream, wire_type);
+    
+    if (wire_type == PB_WT_STRING)
+    {
+        pb_istream_t substream;
+        
+        if (!pb_make_string_substream(stream, &substream))
+            return false;
+        
+        do
+        {
+            if (!pCallback->funcs.decode(&substream, iter->pos, arg))
+                PB_RETURN_ERROR(stream, "callback failed");
+        } while (substream.bytes_left);
+        
+        if (!pb_close_string_substream(stream, &substream))
+            return false;
+
+        return true;
+    }
+    else
+    {
+        /* Copy the single scalar value to stack.
+         * This is required so that we can limit the stream length,
+         * which in turn allows to use same callback for packed and
+         * not-packed fields. */
+        pb_istream_t substream;
+        pb_byte_t buffer[10];
+        size_t size = sizeof(buffer);
+        
+        if (!read_raw_value(stream, wire_type, buffer, &size))
+            return false;
+        substream = pb_istream_from_buffer(buffer, size);
+        
+        return pCallback->funcs.decode(&substream, iter->pos, arg);
+    }
+}
+
+static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+#ifdef PB_ENABLE_MALLOC
+    /* When decoding an oneof field, check if there is old data that must be
+     * released first. */
+    if (PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF)
+    {
+        if (!pb_release_union_field(stream, iter))
+            return false;
+    }
+#endif
+
+    switch (PB_ATYPE(iter->pos->type))
+    {
+        case PB_ATYPE_STATIC:
+            return decode_static_field(stream, wire_type, iter);
+        
+        case PB_ATYPE_POINTER:
+            return decode_pointer_field(stream, wire_type, iter);
+        
+        case PB_ATYPE_CALLBACK:
+            return decode_callback_field(stream, wire_type, iter);
+        
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+}
+
+static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension)
+{
+    /* Fake a field iterator for the extension field.
+     * It is not actually safe to advance this iterator, but decode_field
+     * will not even try to. */
+    const pb_field_t *field = (const pb_field_t*)extension->type->arg;
+    (void)pb_field_iter_begin(iter, field, extension->dest);
+    iter->pData = extension->dest;
+    iter->pSize = &extension->found;
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+    {
+        /* For pointer extensions, the pointer is stored directly
+         * in the extension structure. This avoids having an extra
+         * indirection. */
+        iter->pData = &extension->dest;
+    }
+}
+
+/* Default handler for extension fields. Expects a pb_field_t structure
+ * in extension->type->arg. */
+static bool checkreturn default_extension_decoder(pb_istream_t *stream,
+    pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type)
+{
+    const pb_field_t *field = (const pb_field_t*)extension->type->arg;
+    pb_field_iter_t iter;
+    
+    if (field->tag != tag)
+        return true;
+    
+    iter_from_extension(&iter, extension);
+    extension->found = true;
+    return decode_field(stream, wire_type, &iter);
+}
+
+/* Try to decode an unknown field as an extension field. Tries each extension
+ * decoder in turn, until one of them handles the field or loop ends. */
+static bool checkreturn decode_extension(pb_istream_t *stream,
+    uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+    pb_extension_t *extension = *(pb_extension_t* const *)iter->pData;
+    size_t pos = stream->bytes_left;
+    
+    while (extension != NULL && pos == stream->bytes_left)
+    {
+        bool status;
+        if (extension->type->decode)
+            status = extension->type->decode(stream, extension, tag, wire_type);
+        else
+            status = default_extension_decoder(stream, extension, tag, wire_type);
+
+        if (!status)
+            return false;
+        
+        extension = extension->next;
+    }
+    
+    return true;
+}
+
+/* Step through the iterator until an extension field is found or until all
+ * entries have been checked. There can be only one extension field per
+ * message. Returns false if no extension field is found. */
+static bool checkreturn find_extension_field(pb_field_iter_t *iter)
+{
+    const pb_field_t *start = iter->pos;
+    
+    do {
+        if (PB_LTYPE(iter->pos->type) == PB_LTYPE_EXTENSION)
+            return true;
+        (void)pb_field_iter_next(iter);
+    } while (iter->pos != start);
+    
+    return false;
+}
+
+/* Initialize message fields to default values, recursively */
+static void pb_field_set_to_default(pb_field_iter_t *iter)
+{
+    pb_type_t type;
+    type = iter->pos->type;
+    
+    if (PB_LTYPE(type) == PB_LTYPE_EXTENSION)
+    {
+        pb_extension_t *ext = *(pb_extension_t* const *)iter->pData;
+        while (ext != NULL)
+        {
+            pb_field_iter_t ext_iter;
+            ext->found = false;
+            iter_from_extension(&ext_iter, ext);
+            pb_field_set_to_default(&ext_iter);
+            ext = ext->next;
+        }
+    }
+    else if (PB_ATYPE(type) == PB_ATYPE_STATIC)
+    {
+        bool init_data = true;
+        if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && iter->pSize != iter->pData)
+        {
+            /* Set has_field to false. Still initialize the optional field
+             * itself also. */
+            *(bool*)iter->pSize = false;
+        }
+        else if (PB_HTYPE(type) == PB_HTYPE_REPEATED ||
+                 PB_HTYPE(type) == PB_HTYPE_ONEOF)
+        {
+            /* REPEATED: Set array count to 0, no need to initialize contents.
+               ONEOF: Set which_field to 0. */
+            *(pb_size_t*)iter->pSize = 0;
+            init_data = false;
+        }
+
+        if (init_data)
+        {
+            if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE)
+            {
+                /* Initialize submessage to defaults */
+                pb_message_set_to_defaults((const pb_field_t *) iter->pos->ptr, iter->pData);
+            }
+            else if (iter->pos->ptr != NULL)
+            {
+                /* Initialize to default value */
+                memcpy(iter->pData, iter->pos->ptr, iter->pos->data_size);
+            }
+            else
+            {
+                /* Initialize to zeros */
+                memset(iter->pData, 0, iter->pos->data_size);
+            }
+        }
+    }
+    else if (PB_ATYPE(type) == PB_ATYPE_POINTER)
+    {
+        /* Initialize the pointer to NULL. */
+        *(void**)iter->pData = NULL;
+        
+        /* Initialize array count to 0. */
+        if (PB_HTYPE(type) == PB_HTYPE_REPEATED ||
+            PB_HTYPE(type) == PB_HTYPE_ONEOF)
+        {
+            *(pb_size_t*)iter->pSize = 0;
+        }
+    }
+    else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK)
+    {
+        /* Don't overwrite callback */
+    }
+}
+
+static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct)
+{
+    pb_field_iter_t iter;
+
+    if (!pb_field_iter_begin(&iter, fields, dest_struct))
+        return; /* Empty message type */
+    
+    do
+    {
+        pb_field_set_to_default(&iter);
+    } while (pb_field_iter_next(&iter));
+}
+
+/*********************
+ * Decode all fields *
+ *********************/
+
+bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+    uint32_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 31) / 32] = {0, 0};
+    const uint32_t allbits = ~(uint32_t)0;
+    uint32_t extension_range_start = 0;
+    pb_field_iter_t iter;
+    
+    /* Return value ignored, as empty message types will be correctly handled by
+     * pb_field_iter_find() anyway. */
+    (void)pb_field_iter_begin(&iter, fields, dest_struct);
+    
+    while (stream->bytes_left)
+    {
+        uint32_t tag;
+        pb_wire_type_t wire_type;
+        bool eof;
+        
+        if (!pb_decode_tag(stream, &wire_type, &tag, &eof))
+        {
+            if (eof)
+                break;
+            else
+                return false;
+        }
+        
+        if (!pb_field_iter_find(&iter, tag))
+        {
+            /* No match found, check if it matches an extension. */
+            if (tag >= extension_range_start)
+            {
+                if (!find_extension_field(&iter))
+                    extension_range_start = (uint32_t)-1;
+                else
+                    extension_range_start = iter.pos->tag;
+                
+                if (tag >= extension_range_start)
+                {
+                    size_t pos = stream->bytes_left;
+                
+                    if (!decode_extension(stream, tag, wire_type, &iter))
+                        return false;
+                    
+                    if (pos != stream->bytes_left)
+                    {
+                        /* The field was handled */
+                        continue;                    
+                    }
+                }
+            }
+        
+            /* No match found, skip data */
+            if (!pb_skip_field(stream, wire_type))
+                return false;
+            continue;
+        }
+        
+        if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REQUIRED
+            && iter.required_field_index < PB_MAX_REQUIRED_FIELDS)
+        {
+            uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31));
+            fields_seen[iter.required_field_index >> 5] |= tmp;
+        }
+            
+        if (!decode_field(stream, wire_type, &iter))
+            return false;
+    }
+    
+    /* Check that all required fields were present. */
+    {
+        /* First figure out the number of required fields by
+         * seeking to the end of the field array. Usually we
+         * are already close to end after decoding.
+         */
+        unsigned req_field_count;
+        pb_type_t last_type;
+        unsigned i;
+        do {
+            req_field_count = iter.required_field_index;
+            last_type = iter.pos->type;
+        } while (pb_field_iter_next(&iter));
+        
+        /* Fixup if last field was also required. */
+        if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.pos->tag != 0)
+            req_field_count++;
+        
+        if (req_field_count > 0)
+        {
+            /* Check the whole words */
+            for (i = 0; i < (req_field_count >> 5); i++)
+            {
+                if (fields_seen[i] != allbits)
+                    PB_RETURN_ERROR(stream, "missing required field");
+            }
+            
+            /* Check the remaining bits */
+            if (fields_seen[req_field_count >> 5] != (allbits >> (32 - (req_field_count & 31))))
+                PB_RETURN_ERROR(stream, "missing required field");
+        }
+    }
+    
+    return true;
+}
+
+bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+    bool status;
+    pb_message_set_to_defaults(fields, dest_struct);
+    status = pb_decode_noinit(stream, fields, dest_struct);
+    
+#ifdef PB_ENABLE_MALLOC
+    if (!status)
+        pb_release(fields, dest_struct);
+#endif
+    
+    return status;
+}
+
+bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+    pb_istream_t substream;
+    bool status;
+    
+    if (!pb_make_string_substream(stream, &substream))
+        return false;
+    
+    status = pb_decode(&substream, fields, dest_struct);
+
+    if (!pb_close_string_substream(stream, &substream))
+        return false;
+    return status;
+}
+
+#ifdef PB_ENABLE_MALLOC
+/* Given an oneof field, if there has already been a field inside this oneof,
+ * release it before overwriting with a different one. */
+static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter)
+{
+    pb_size_t old_tag = *(pb_size_t*)iter->pSize; /* Previous which_ value */
+    pb_size_t new_tag = iter->pos->tag; /* New which_ value */
+
+    if (old_tag == 0)
+        return true; /* Ok, no old data in union */
+
+    if (old_tag == new_tag)
+        return true; /* Ok, old data is of same type => merge */
+
+    /* Release old data. The find can fail if the message struct contains
+     * invalid data. */
+    if (!pb_field_iter_find(iter, old_tag))
+        PB_RETURN_ERROR(stream, "invalid union tag");
+
+    pb_release_single_field(iter);
+
+    /* Restore iterator to where it should be.
+     * This shouldn't fail unless the pb_field_t structure is corrupted. */
+    if (!pb_field_iter_find(iter, new_tag))
+        PB_RETURN_ERROR(stream, "iterator error");
+    
+    return true;
+}
+
+static void pb_release_single_field(const pb_field_iter_t *iter)
+{
+    pb_type_t type;
+    type = iter->pos->type;
+
+    if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
+    {
+        if (*(pb_size_t*)iter->pSize != iter->pos->tag)
+            return; /* This is not the current field in the union */
+    }
+
+    /* Release anything contained inside an extension or submsg.
+     * This has to be done even if the submsg itself is statically
+     * allocated. */
+    if (PB_LTYPE(type) == PB_LTYPE_EXTENSION)
+    {
+        /* Release fields from all extensions in the linked list */
+        pb_extension_t *ext = *(pb_extension_t**)iter->pData;
+        while (ext != NULL)
+        {
+            pb_field_iter_t ext_iter;
+            iter_from_extension(&ext_iter, ext);
+            pb_release_single_field(&ext_iter);
+            ext = ext->next;
+        }
+    }
+    else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE)
+    {
+        /* Release fields in submessage or submsg array */
+        void *pItem = iter->pData;
+        pb_size_t count = 1;
+        
+        if (PB_ATYPE(type) == PB_ATYPE_POINTER)
+        {
+            pItem = *(void**)iter->pData;
+        }
+        
+        if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
+        {
+            count = *(pb_size_t*)iter->pSize;
+
+            if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > iter->pos->array_size)
+            {
+                /* Protect against corrupted _count fields */
+                count = iter->pos->array_size;
+            }
+        }
+        
+        if (pItem)
+        {
+            while (count--)
+            {
+                pb_release((const pb_field_t*)iter->pos->ptr, pItem);
+                pItem = (char*)pItem + iter->pos->data_size;
+            }
+        }
+    }
+    
+    if (PB_ATYPE(type) == PB_ATYPE_POINTER)
+    {
+        if (PB_HTYPE(type) == PB_HTYPE_REPEATED &&
+            (PB_LTYPE(type) == PB_LTYPE_STRING ||
+             PB_LTYPE(type) == PB_LTYPE_BYTES))
+        {
+            /* Release entries in repeated string or bytes array */
+            void **pItem = *(void***)iter->pData;
+            pb_size_t count = *(pb_size_t*)iter->pSize;
+            while (count--)
+            {
+                pb_free(*pItem);
+                *pItem++ = NULL;
+            }
+        }
+        
+        if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
+        {
+            /* We are going to release the array, so set the size to 0 */
+            *(pb_size_t*)iter->pSize = 0;
+        }
+        
+        /* Release main item */
+        pb_free(*(void**)iter->pData);
+        *(void**)iter->pData = NULL;
+    }
+}
+
+void pb_release(const pb_field_t fields[], void *dest_struct)
+{
+    pb_field_iter_t iter;
+    
+    if (!dest_struct)
+        return; /* Ignore NULL pointers, similar to free() */
+
+    if (!pb_field_iter_begin(&iter, fields, dest_struct))
+        return; /* Empty message type */
+    
+    do
+    {
+        pb_release_single_field(&iter);
+    } while (pb_field_iter_next(&iter));
+}
+#endif
+
+/* Field decoders */
+
+bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest)
+{
+    uint64_t value;
+    if (!pb_decode_varint(stream, &value))
+        return false;
+    
+    if (value & 1)
+        *dest = (int64_t)(~(value >> 1));
+    else
+        *dest = (int64_t)(value >> 1);
+    
+    return true;
+}
+
+bool pb_decode_fixed32(pb_istream_t *stream, void *dest)
+{
+    pb_byte_t bytes[4];
+
+    if (!pb_read(stream, bytes, 4))
+        return false;
+    
+    *(uint32_t*)dest = ((uint32_t)bytes[0] << 0) |
+                       ((uint32_t)bytes[1] << 8) |
+                       ((uint32_t)bytes[2] << 16) |
+                       ((uint32_t)bytes[3] << 24);
+    return true;
+}
+
+bool pb_decode_fixed64(pb_istream_t *stream, void *dest)
+{
+    pb_byte_t bytes[8];
+
+    if (!pb_read(stream, bytes, 8))
+        return false;
+    
+    *(uint64_t*)dest = ((uint64_t)bytes[0] << 0) |
+                       ((uint64_t)bytes[1] << 8) |
+                       ((uint64_t)bytes[2] << 16) |
+                       ((uint64_t)bytes[3] << 24) |
+                       ((uint64_t)bytes[4] << 32) |
+                       ((uint64_t)bytes[5] << 40) |
+                       ((uint64_t)bytes[6] << 48) |
+                       ((uint64_t)bytes[7] << 56);
+    
+    return true;
+}
+
+static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    uint64_t value;
+    int64_t svalue;
+    int64_t clamped;
+    if (!pb_decode_varint(stream, &value))
+        return false;
+    
+    /* See issue 97: Google's C++ protobuf allows negative varint values to
+     * be cast as int32_t, instead of the int64_t that should be used when
+     * encoding. Previous nanopb versions had a bug in encoding. In order to
+     * not break decoding of such messages, we cast <=32 bit fields to
+     * int32_t first to get the sign correct.
+     */
+    if (field->data_size == sizeof(int64_t))
+        svalue = (int64_t)value;
+    else
+        svalue = (int32_t)value;
+
+    /* Cast to the proper field size, while checking for overflows */
+    if (field->data_size == sizeof(int64_t))
+        clamped = *(int64_t*)dest = svalue;
+    else if (field->data_size == sizeof(int32_t))
+        clamped = *(int32_t*)dest = (int32_t)svalue;
+    else if (field->data_size == sizeof(int_least16_t))
+        clamped = *(int_least16_t*)dest = (int_least16_t)svalue;
+    else if (field->data_size == sizeof(int_least8_t))
+        clamped = *(int_least8_t*)dest = (int_least8_t)svalue;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+
+    if (clamped != svalue)
+        PB_RETURN_ERROR(stream, "integer too large");
+    
+    return true;
+}
+
+static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    uint64_t value, clamped;
+    if (!pb_decode_varint(stream, &value))
+        return false;
+    
+    /* Cast to the proper field size, while checking for overflows */
+    if (field->data_size == sizeof(uint64_t))
+        clamped = *(uint64_t*)dest = value;
+    else if (field->data_size == sizeof(uint32_t))
+        clamped = *(uint32_t*)dest = (uint32_t)value;
+    else if (field->data_size == sizeof(uint_least16_t))
+        clamped = *(uint_least16_t*)dest = (uint_least16_t)value;
+    else if (field->data_size == sizeof(uint_least8_t))
+        clamped = *(uint_least8_t*)dest = (uint_least8_t)value;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+    
+    if (clamped != value)
+        PB_RETURN_ERROR(stream, "integer too large");
+
+    return true;
+}
+
+static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    int64_t value, clamped;
+    if (!pb_decode_svarint(stream, &value))
+        return false;
+    
+    /* Cast to the proper field size, while checking for overflows */
+    if (field->data_size == sizeof(int64_t))
+        clamped = *(int64_t*)dest = value;
+    else if (field->data_size == sizeof(int32_t))
+        clamped = *(int32_t*)dest = (int32_t)value;
+    else if (field->data_size == sizeof(int_least16_t))
+        clamped = *(int_least16_t*)dest = (int_least16_t)value;
+    else if (field->data_size == sizeof(int_least8_t))
+        clamped = *(int_least8_t*)dest = (int_least8_t)value;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+
+    if (clamped != value)
+        PB_RETURN_ERROR(stream, "integer too large");
+    
+    return true;
+}
+
+static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    PB_UNUSED(field);
+    return pb_decode_fixed32(stream, dest);
+}
+
+static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    PB_UNUSED(field);
+    return pb_decode_fixed64(stream, dest);
+}
+
+static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    uint32_t size;
+    size_t alloc_size;
+    pb_bytes_array_t *bdest;
+    
+    if (!pb_decode_varint32(stream, &size))
+        return false;
+    
+    if (size > PB_SIZE_MAX)
+        PB_RETURN_ERROR(stream, "bytes overflow");
+    
+    alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size);
+    if (size > alloc_size)
+        PB_RETURN_ERROR(stream, "size too large");
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+    {
+#ifndef PB_ENABLE_MALLOC
+        PB_RETURN_ERROR(stream, "no malloc support");
+#else
+        if (!allocate_field(stream, dest, alloc_size, 1))
+            return false;
+        bdest = *(pb_bytes_array_t**)dest;
+#endif
+    }
+    else
+    {
+        if (alloc_size > field->data_size)
+            PB_RETURN_ERROR(stream, "bytes overflow");
+        bdest = (pb_bytes_array_t*)dest;
+    }
+
+    bdest->size = (pb_size_t)size;
+    return pb_read(stream, bdest->bytes, size);
+}
+
+static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    uint32_t size;
+    size_t alloc_size;
+    bool status;
+    if (!pb_decode_varint32(stream, &size))
+        return false;
+    
+    /* Space for null terminator */
+    alloc_size = size + 1;
+    
+    if (alloc_size < size)
+        PB_RETURN_ERROR(stream, "size too large");
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+    {
+#ifndef PB_ENABLE_MALLOC
+        PB_RETURN_ERROR(stream, "no malloc support");
+#else
+        if (!allocate_field(stream, dest, alloc_size, 1))
+            return false;
+        dest = *(void**)dest;
+#endif
+    }
+    else
+    {
+        if (alloc_size > field->data_size)
+            PB_RETURN_ERROR(stream, "string overflow");
+    }
+    
+    status = pb_read(stream, (pb_byte_t*)dest, size);
+    *((pb_byte_t*)dest + size) = 0;
+    return status;
+}
+
+static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    bool status;
+    pb_istream_t substream;
+    const pb_field_t* submsg_fields = (const pb_field_t*)field->ptr;
+    
+    if (!pb_make_string_substream(stream, &substream))
+        return false;
+    
+    if (field->ptr == NULL)
+        PB_RETURN_ERROR(stream, "invalid field descriptor");
+    
+    /* New array entries need to be initialized, while required and optional
+     * submessages have already been initialized in the top-level pb_decode. */
+    if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
+        status = pb_decode(&substream, submsg_fields, dest);
+    else
+        status = pb_decode_noinit(&substream, submsg_fields, dest);
+    
+    if (!pb_close_string_substream(stream, &substream))
+        return false;
+    return status;
+}
+
+static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+    uint32_t size;
+
+    if (!pb_decode_varint32(stream, &size))
+        return false;
+
+    if (size > PB_SIZE_MAX)
+        PB_RETURN_ERROR(stream, "bytes overflow");
+
+    if (size == 0)
+    {
+        /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */
+        memset(dest, 0, field->data_size);
+        return true;
+    }
+
+    if (size != field->data_size)
+        PB_RETURN_ERROR(stream, "incorrect fixed length bytes size");
+
+    return pb_read(stream, (pb_byte_t*)dest, field->data_size);
+}
diff --git a/iOS/Pods/nanopb/pb_decode.h b/iOS/Pods/nanopb/pb_decode.h
new file mode 100644 (file)
index 0000000..a426bdd
--- /dev/null
@@ -0,0 +1,153 @@
+/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c.
+ * The main function is pb_decode. You also need an input stream, and the
+ * field descriptions created by nanopb_generator.py.
+ */
+
+#ifndef PB_DECODE_H_INCLUDED
+#define PB_DECODE_H_INCLUDED
+
+#include "pb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structure for defining custom input streams. You will need to provide
+ * a callback function to read the bytes from your storage, which can be
+ * for example a file or a network socket.
+ * 
+ * The callback must conform to these rules:
+ *
+ * 1) Return false on IO errors. This will cause decoding to abort.
+ * 2) You can use state to store your own data (e.g. buffer pointer),
+ *    and rely on pb_read to verify that no-body reads past bytes_left.
+ * 3) Your callback may be used with substreams, in which case bytes_left
+ *    is different than from the main stream. Don't use bytes_left to compute
+ *    any pointers.
+ */
+struct pb_istream_s
+{
+#ifdef PB_BUFFER_ONLY
+    /* Callback pointer is not used in buffer-only configuration.
+     * Having an int pointer here allows binary compatibility but
+     * gives an error if someone tries to assign callback function.
+     */
+    int *callback;
+#else
+    bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count);
+#endif
+
+    void *state; /* Free field for use by callback implementation */
+    size_t bytes_left;
+    
+#ifndef PB_NO_ERRMSG
+    const char *errmsg;
+#endif
+};
+
+/***************************
+ * Main decoding functions *
+ ***************************/
+/* Decode a single protocol buffers message from input stream into a C structure.
+ * Returns true on success, false on any failure.
+ * The actual struct pointed to by dest must match the description in fields.
+ * Callback fields of the destination structure must be initialized by caller.
+ * All other fields will be initialized by this function.
+ *
+ * Example usage:
+ *    MyMessage msg = {};
+ *    uint8_t buffer[64];
+ *    pb_istream_t stream;
+ *    
+ *    // ... read some data into buffer ...
+ *
+ *    stream = pb_istream_from_buffer(buffer, count);
+ *    pb_decode(&stream, MyMessage_fields, &msg);
+ */
+bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
+
+/* Same as pb_decode, except does not initialize the destination structure
+ * to default values. This is slightly faster if you need no default values
+ * and just do memset(struct, 0, sizeof(struct)) yourself.
+ *
+ * This can also be used for 'merging' two messages, i.e. update only the
+ * fields that exist in the new message.
+ *
+ * Note: If this function returns with an error, it will not release any
+ * dynamically allocated fields. You will need to call pb_release() yourself.
+ */
+bool pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
+
+/* Same as pb_decode, except expects the stream to start with the message size
+ * encoded as varint. Corresponds to parseDelimitedFrom() in Google's
+ * protobuf API.
+ */
+bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
+
+#ifdef PB_ENABLE_MALLOC
+/* Release any allocated pointer fields. If you use dynamic allocation, you should
+ * call this for any successfully decoded message when you are done with it. If
+ * pb_decode() returns with an error, the message is already released.
+ */
+void pb_release(const pb_field_t fields[], void *dest_struct);
+#endif
+
+
+/**************************************
+ * Functions for manipulating streams *
+ **************************************/
+
+/* Create an input stream for reading from a memory buffer.
+ *
+ * Alternatively, you can use a custom stream that reads directly from e.g.
+ * a file or a network socket.
+ */
+pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize);
+
+/* Function to read from a pb_istream_t. You can use this if you need to
+ * read some custom header data, or to read data in field callbacks.
+ */
+bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
+
+
+/************************************************
+ * Helper functions for writing field callbacks *
+ ************************************************/
+
+/* Decode the tag for the next field in the stream. Gives the wire type and
+ * field tag. At end of the message, returns false and sets eof to true. */
+bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof);
+
+/* Skip the field payload data, given the wire type. */
+bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
+
+/* Decode an integer in the varint format. This works for bool, enum, int32,
+ * int64, uint32 and uint64 field types. */
+bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
+
+/* Decode an integer in the varint format. This works for bool, enum, int32,
+ * and uint32 field types. */
+bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest);
+
+/* Decode an integer in the zig-zagged svarint format. This works for sint32
+ * and sint64. */
+bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
+
+/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to
+ * a 4-byte wide C variable. */
+bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
+
+/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to
+ * a 8-byte wide C variable. */
+bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
+
+/* Make a limited-length substream for reading a PB_WT_STRING field. */
+bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
+bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/iOS/Pods/nanopb/pb_encode.c b/iOS/Pods/nanopb/pb_encode.c
new file mode 100644 (file)
index 0000000..30f60d8
--- /dev/null
@@ -0,0 +1,777 @@
+/* pb_encode.c -- encode a protobuf using minimal resources
+ *
+ * 2011 Petteri Aimonen <jpa@kapsi.fi>
+ */
+
+#include "pb.h"
+#include "pb_encode.h"
+#include "pb_common.h"
+
+/* Use the GCC warn_unused_result attribute to check that all return values
+ * are propagated correctly. On other compilers and gcc before 3.4.0 just
+ * ignore the annotation.
+ */
+#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
+    #define checkreturn
+#else
+    #define checkreturn __attribute__((warn_unused_result))
+#endif
+
+/**************************************
+ * Declarations internal to this file *
+ **************************************/
+typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn;
+
+static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
+static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, const void *pData, size_t count, pb_encoder_t func);
+static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
+static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension);
+static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
+static void *pb_const_cast(const void *p);
+static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+
+/* --- Function pointers to field encoders ---
+ * Order in the array must match pb_action_t LTYPE numbering.
+ */
+static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
+    &pb_enc_varint,
+    &pb_enc_uvarint,
+    &pb_enc_svarint,
+    &pb_enc_fixed32,
+    &pb_enc_fixed64,
+    
+    &pb_enc_bytes,
+    &pb_enc_string,
+    &pb_enc_submessage,
+    NULL, /* extensions */
+    &pb_enc_fixed_length_bytes
+};
+
+/*******************************
+ * pb_ostream_t implementation *
+ *******************************/
+
+static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
+{
+    size_t i;
+    pb_byte_t *dest = (pb_byte_t*)stream->state;
+    stream->state = dest + count;
+    
+    for (i = 0; i < count; i++)
+        dest[i] = buf[i];
+    
+    return true;
+}
+
+pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize)
+{
+    pb_ostream_t stream;
+#ifdef PB_BUFFER_ONLY
+    stream.callback = (void*)1; /* Just a marker value */
+#else
+    stream.callback = &buf_write;
+#endif
+    stream.state = buf;
+    stream.max_size = bufsize;
+    stream.bytes_written = 0;
+#ifndef PB_NO_ERRMSG
+    stream.errmsg = NULL;
+#endif
+    return stream;
+}
+
+bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
+{
+    if (stream->callback != NULL)
+    {
+        if (stream->bytes_written + count > stream->max_size)
+            PB_RETURN_ERROR(stream, "stream full");
+
+#ifdef PB_BUFFER_ONLY
+        if (!buf_write(stream, buf, count))
+            PB_RETURN_ERROR(stream, "io error");
+#else        
+        if (!stream->callback(stream, buf, count))
+            PB_RETURN_ERROR(stream, "io error");
+#endif
+    }
+    
+    stream->bytes_written += count;
+    return true;
+}
+
+/*************************
+ * Encode a single field *
+ *************************/
+
+/* Encode a static array. Handles the size calculations and possible packing. */
+static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
+                         const void *pData, size_t count, pb_encoder_t func)
+{
+    size_t i;
+    const void *p;
+    size_t size;
+    
+    if (count == 0)
+        return true;
+
+    if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size)
+        PB_RETURN_ERROR(stream, "array max size exceeded");
+    
+    /* We always pack arrays if the datatype allows it. */
+    if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
+    {
+        if (!pb_encode_tag(stream, PB_WT_STRING, field->tag))
+            return false;
+        
+        /* Determine the total size of packed array. */
+        if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32)
+        {
+            size = 4 * count;
+        }
+        else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
+        {
+            size = 8 * count;
+        }
+        else
+        { 
+            pb_ostream_t sizestream = PB_OSTREAM_SIZING;
+            p = pData;
+            for (i = 0; i < count; i++)
+            {
+                if (!func(&sizestream, field, p))
+                    return false;
+                p = (const char*)p + field->data_size;
+            }
+            size = sizestream.bytes_written;
+        }
+        
+        if (!pb_encode_varint(stream, (uint64_t)size))
+            return false;
+        
+        if (stream->callback == NULL)
+            return pb_write(stream, NULL, size); /* Just sizing.. */
+        
+        /* Write the data */
+        p = pData;
+        for (i = 0; i < count; i++)
+        {
+            if (!func(stream, field, p))
+                return false;
+            p = (const char*)p + field->data_size;
+        }
+    }
+    else
+    {
+        p = pData;
+        for (i = 0; i < count; i++)
+        {
+            if (!pb_encode_tag_for_field(stream, field))
+                return false;
+
+            /* Normally the data is stored directly in the array entries, but
+             * for pointer-type string and bytes fields, the array entries are
+             * actually pointers themselves also. So we have to dereference once
+             * more to get to the actual data. */
+            if (PB_ATYPE(field->type) == PB_ATYPE_POINTER &&
+                (PB_LTYPE(field->type) == PB_LTYPE_STRING ||
+                 PB_LTYPE(field->type) == PB_LTYPE_BYTES))
+            {
+                if (!func(stream, field, *(const void* const*)p))
+                    return false;      
+            }
+            else
+            {
+                if (!func(stream, field, p))
+                    return false;
+            }
+            p = (const char*)p + field->data_size;
+        }
+    }
+    
+    return true;
+}
+
+/* In proto3, all fields are optional and are only encoded if their value is "non-zero".
+ * This function implements the check for the zero value. */
+static bool pb_check_proto3_default_value(const pb_field_t *field, const void *pData)
+{
+    if (PB_ATYPE(field->type) == PB_ATYPE_STATIC)
+    {
+        if (PB_LTYPE(field->type) == PB_LTYPE_BYTES)
+        {
+            const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)pData;
+            return bytes->size == 0;
+        }
+        else if (PB_LTYPE(field->type) == PB_LTYPE_STRING)
+        {
+            return *(const char*)pData == '\0';
+        }
+        else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED_LENGTH_BYTES)
+        {
+            /* Fixed length bytes is only empty if its length is fixed
+             * as 0. Which would be pretty strange, but we can check
+             * it anyway. */
+            return field->data_size == 0;
+        }
+        else if (PB_LTYPE(field->type) == PB_LTYPE_SUBMESSAGE)
+        {
+            /* Check all fields in the submessage to find if any of them
+             * are non-zero. The comparison cannot be done byte-per-byte
+             * because the C struct may contain padding bytes that must
+             * be skipped.
+             */
+            pb_field_iter_t iter;
+            if (pb_field_iter_begin(&iter, (const pb_field_t*)field->ptr, pb_const_cast(pData)))
+            {
+                do
+                {
+                    if (!pb_check_proto3_default_value(iter.pos, iter.pData))
+                    {
+                        return false;
+                    }
+                } while (pb_field_iter_next(&iter));
+            }
+            return true;
+        }
+    }
+    
+       {
+           /* Catch-all branch that does byte-per-byte comparison for zero value.
+            *
+            * This is for all pointer fields, and for static PB_LTYPE_VARINT,
+            * UVARINT, SVARINT, FIXED32, FIXED64, EXTENSION fields, and also
+            * callback fields. These all have integer or pointer value which
+            * can be compared with 0.
+            */
+           pb_size_t i;
+           const char *p = (const char*)pData;
+           for (i = 0; i < field->data_size; i++)
+           {
+               if (p[i] != 0)
+               {
+                   return false;
+               }
+           }
+
+           return true;
+       }
+}
+
+/* Encode a field with static or pointer allocation, i.e. one whose data
+ * is available to the encoder directly. */
+static bool checkreturn encode_basic_field(pb_ostream_t *stream,
+    const pb_field_t *field, const void *pData)
+{
+    pb_encoder_t func;
+    bool implicit_has;
+    const void *pSize = &implicit_has;
+    
+    func = PB_ENCODERS[PB_LTYPE(field->type)];
+    
+    if (field->size_offset)
+    {
+        /* Static optional, repeated or oneof field */
+        pSize = (const char*)pData + field->size_offset;
+    }
+    else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL)
+    {
+        /* Proto3 style field, optional but without explicit has_ field. */
+        implicit_has = !pb_check_proto3_default_value(field, pData);
+    }
+    else
+    {
+        /* Required field, always present */
+        implicit_has = true;
+    }
+
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+    {
+        /* pData is a pointer to the field, which contains pointer to
+         * the data. If the 2nd pointer is NULL, it is interpreted as if
+         * the has_field was false.
+         */
+        pData = *(const void* const*)pData;
+        implicit_has = (pData != NULL);
+    }
+
+    switch (PB_HTYPE(field->type))
+    {
+        case PB_HTYPE_REQUIRED:
+            if (!pData)
+                PB_RETURN_ERROR(stream, "missing required field");
+            if (!pb_encode_tag_for_field(stream, field))
+                return false;
+            if (!func(stream, field, pData))
+                return false;
+            break;
+        
+        case PB_HTYPE_OPTIONAL:
+            if (*(const bool*)pSize)
+            {
+                if (!pb_encode_tag_for_field(stream, field))
+                    return false;
+            
+                if (!func(stream, field, pData))
+                    return false;
+            }
+            break;
+        
+        case PB_HTYPE_REPEATED:
+            if (!encode_array(stream, field, pData, *(const pb_size_t*)pSize, func))
+                return false;
+            break;
+        
+        case PB_HTYPE_ONEOF:
+            if (*(const pb_size_t*)pSize == field->tag)
+            {
+                if (!pb_encode_tag_for_field(stream, field))
+                    return false;
+
+                if (!func(stream, field, pData))
+                    return false;
+            }
+            break;
+            
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+    
+    return true;
+}
+
+/* Encode a field with callback semantics. This means that a user function is
+ * called to provide and encode the actual data. */
+static bool checkreturn encode_callback_field(pb_ostream_t *stream,
+    const pb_field_t *field, const void *pData)
+{
+    const pb_callback_t *callback = (const pb_callback_t*)pData;
+    
+#ifdef PB_OLD_CALLBACK_STYLE
+    const void *arg = callback->arg;
+#else
+    void * const *arg = &(callback->arg);
+#endif    
+    
+    if (callback->funcs.encode != NULL)
+    {
+        if (!callback->funcs.encode(stream, field, arg))
+            PB_RETURN_ERROR(stream, "callback error");
+    }
+    return true;
+}
+
+/* Encode a single field of any callback or static type. */
+static bool checkreturn encode_field(pb_ostream_t *stream,
+    const pb_field_t *field, const void *pData)
+{
+    switch (PB_ATYPE(field->type))
+    {
+        case PB_ATYPE_STATIC:
+        case PB_ATYPE_POINTER:
+            return encode_basic_field(stream, field, pData);
+        
+        case PB_ATYPE_CALLBACK:
+            return encode_callback_field(stream, field, pData);
+        
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+}
+
+/* Default handler for extension fields. Expects to have a pb_field_t
+ * pointer in the extension->type->arg field. */
+static bool checkreturn default_extension_encoder(pb_ostream_t *stream,
+    const pb_extension_t *extension)
+{
+    const pb_field_t *field = (const pb_field_t*)extension->type->arg;
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+    {
+        /* For pointer extensions, the pointer is stored directly
+         * in the extension structure. This avoids having an extra
+         * indirection. */
+        return encode_field(stream, field, &extension->dest);
+    }
+    else
+    {
+        return encode_field(stream, field, extension->dest);
+    }
+}
+
+/* Walk through all the registered extensions and give them a chance
+ * to encode themselves. */
+static bool checkreturn encode_extension_field(pb_ostream_t *stream,
+    const pb_field_t *field, const void *pData)
+{
+    const pb_extension_t *extension = *(const pb_extension_t* const *)pData;
+    PB_UNUSED(field);
+    
+    while (extension)
+    {
+        bool status;
+        if (extension->type->encode)
+            status = extension->type->encode(stream, extension);
+        else
+            status = default_extension_encoder(stream, extension);
+
+        if (!status)
+            return false;
+        
+        extension = extension->next;
+    }
+    
+    return true;
+}
+
+/*********************
+ * Encode all fields *
+ *********************/
+
+static void *pb_const_cast(const void *p)
+{
+    /* Note: this casts away const, in order to use the common field iterator
+     * logic for both encoding and decoding. */
+    union {
+        void *p1;
+        const void *p2;
+    } t;
+    t.p2 = p;
+    return t.p1;
+}
+
+bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
+{
+    pb_field_iter_t iter;
+    if (!pb_field_iter_begin(&iter, fields, pb_const_cast(src_struct)))
+        return true; /* Empty message type */
+    
+    do {
+        if (PB_LTYPE(iter.pos->type) == PB_LTYPE_EXTENSION)
+        {
+            /* Special case for the extension field placeholder */
+            if (!encode_extension_field(stream, iter.pos, iter.pData))
+                return false;
+        }
+        else
+        {
+            /* Regular field */
+            if (!encode_field(stream, iter.pos, iter.pData))
+                return false;
+        }
+    } while (pb_field_iter_next(&iter));
+    
+    return true;
+}
+
+bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
+{
+    return pb_encode_submessage(stream, fields, src_struct);
+}
+
+bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct)
+{
+    pb_ostream_t stream = PB_OSTREAM_SIZING;
+    
+    if (!pb_encode(&stream, fields, src_struct))
+        return false;
+    
+    *size = stream.bytes_written;
+    return true;
+}
+
+/********************
+ * Helper functions *
+ ********************/
+bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
+{
+    pb_byte_t buffer[10];
+    size_t i = 0;
+    
+    if (value <= 0x7F)
+    {
+        pb_byte_t v = (pb_byte_t)value;
+        return pb_write(stream, &v, 1);
+    }
+    
+    while (value)
+    {
+        buffer[i] = (pb_byte_t)((value & 0x7F) | 0x80);
+        value >>= 7;
+        i++;
+    }
+    buffer[i-1] &= 0x7F; /* Unset top bit on last byte */
+    
+    return pb_write(stream, buffer, i);
+}
+
+bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value)
+{
+    uint64_t zigzagged;
+    if (value < 0)
+        zigzagged = ~((uint64_t)value << 1);
+    else
+        zigzagged = (uint64_t)value << 1;
+    
+    return pb_encode_varint(stream, zigzagged);
+}
+
+bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
+{
+    uint32_t val = *(const uint32_t*)value;
+    pb_byte_t bytes[4];
+    bytes[0] = (pb_byte_t)(val & 0xFF);
+    bytes[1] = (pb_byte_t)((val >> 8) & 0xFF);
+    bytes[2] = (pb_byte_t)((val >> 16) & 0xFF);
+    bytes[3] = (pb_byte_t)((val >> 24) & 0xFF);
+    return pb_write(stream, bytes, 4);
+}
+
+bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
+{
+    uint64_t val = *(const uint64_t*)value;
+    pb_byte_t bytes[8];
+    bytes[0] = (pb_byte_t)(val & 0xFF);
+    bytes[1] = (pb_byte_t)((val >> 8) & 0xFF);
+    bytes[2] = (pb_byte_t)((val >> 16) & 0xFF);
+    bytes[3] = (pb_byte_t)((val >> 24) & 0xFF);
+    bytes[4] = (pb_byte_t)((val >> 32) & 0xFF);
+    bytes[5] = (pb_byte_t)((val >> 40) & 0xFF);
+    bytes[6] = (pb_byte_t)((val >> 48) & 0xFF);
+    bytes[7] = (pb_byte_t)((val >> 56) & 0xFF);
+    return pb_write(stream, bytes, 8);
+}
+
+bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number)
+{
+    uint64_t tag = ((uint64_t)field_number << 3) | wiretype;
+    return pb_encode_varint(stream, tag);
+}
+
+bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field)
+{
+    pb_wire_type_t wiretype;
+    switch (PB_LTYPE(field->type))
+    {
+        case PB_LTYPE_VARINT:
+        case PB_LTYPE_UVARINT:
+        case PB_LTYPE_SVARINT:
+            wiretype = PB_WT_VARINT;
+            break;
+        
+        case PB_LTYPE_FIXED32:
+            wiretype = PB_WT_32BIT;
+            break;
+        
+        case PB_LTYPE_FIXED64:
+            wiretype = PB_WT_64BIT;
+            break;
+        
+        case PB_LTYPE_BYTES:
+        case PB_LTYPE_STRING:
+        case PB_LTYPE_SUBMESSAGE:
+        case PB_LTYPE_FIXED_LENGTH_BYTES:
+            wiretype = PB_WT_STRING;
+            break;
+        
+        default:
+            PB_RETURN_ERROR(stream, "invalid field type");
+    }
+    
+    return pb_encode_tag(stream, wiretype, field->tag);
+}
+
+bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size)
+{
+    if (!pb_encode_varint(stream, (uint64_t)size))
+        return false;
+    
+    return pb_write(stream, buffer, size);
+}
+
+bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
+{
+    /* First calculate the message size using a non-writing substream. */
+    pb_ostream_t substream = PB_OSTREAM_SIZING;
+    size_t size;
+    bool status;
+    
+    if (!pb_encode(&substream, fields, src_struct))
+    {
+#ifndef PB_NO_ERRMSG
+        stream->errmsg = substream.errmsg;
+#endif
+        return false;
+    }
+    
+    size = substream.bytes_written;
+    
+    if (!pb_encode_varint(stream, (uint64_t)size))
+        return false;
+    
+    if (stream->callback == NULL)
+        return pb_write(stream, NULL, size); /* Just sizing */
+    
+    if (stream->bytes_written + size > stream->max_size)
+        PB_RETURN_ERROR(stream, "stream full");
+        
+    /* Use a substream to verify that a callback doesn't write more than
+     * what it did the first time. */
+    substream.callback = stream->callback;
+    substream.state = stream->state;
+    substream.max_size = size;
+    substream.bytes_written = 0;
+#ifndef PB_NO_ERRMSG
+    substream.errmsg = NULL;
+#endif
+    
+    status = pb_encode(&substream, fields, src_struct);
+    
+    stream->bytes_written += substream.bytes_written;
+    stream->state = substream.state;
+#ifndef PB_NO_ERRMSG
+    stream->errmsg = substream.errmsg;
+#endif
+    
+    if (substream.bytes_written != size)
+        PB_RETURN_ERROR(stream, "submsg size changed");
+    
+    return status;
+}
+
+/* Field encoders */
+
+static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    int64_t value = 0;
+    
+    if (field->data_size == sizeof(int_least8_t))
+        value = *(const int_least8_t*)src;
+    else if (field->data_size == sizeof(int_least16_t))
+        value = *(const int_least16_t*)src;
+    else if (field->data_size == sizeof(int32_t))
+        value = *(const int32_t*)src;
+    else if (field->data_size == sizeof(int64_t))
+        value = *(const int64_t*)src;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+    
+    return pb_encode_varint(stream, (uint64_t)value);
+}
+
+static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    uint64_t value = 0;
+    
+    if (field->data_size == sizeof(uint_least8_t))
+        value = *(const uint_least8_t*)src;
+    else if (field->data_size == sizeof(uint_least16_t))
+        value = *(const uint_least16_t*)src;
+    else if (field->data_size == sizeof(uint32_t))
+        value = *(const uint32_t*)src;
+    else if (field->data_size == sizeof(uint64_t))
+        value = *(const uint64_t*)src;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+    
+    return pb_encode_varint(stream, value);
+}
+
+static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    int64_t value = 0;
+    
+    if (field->data_size == sizeof(int_least8_t))
+        value = *(const int_least8_t*)src;
+    else if (field->data_size == sizeof(int_least16_t))
+        value = *(const int_least16_t*)src;
+    else if (field->data_size == sizeof(int32_t))
+        value = *(const int32_t*)src;
+    else if (field->data_size == sizeof(int64_t))
+        value = *(const int64_t*)src;
+    else
+        PB_RETURN_ERROR(stream, "invalid data_size");
+    
+    return pb_encode_svarint(stream, value);
+}
+
+static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    PB_UNUSED(field);
+    return pb_encode_fixed64(stream, src);
+}
+
+static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    PB_UNUSED(field);
+    return pb_encode_fixed32(stream, src);
+}
+
+static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    const pb_bytes_array_t *bytes = NULL;
+
+    bytes = (const pb_bytes_array_t*)src;
+    
+    if (src == NULL)
+    {
+        /* Treat null pointer as an empty bytes field */
+        return pb_encode_string(stream, NULL, 0);
+    }
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
+        PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size)
+    {
+        PB_RETURN_ERROR(stream, "bytes size exceeded");
+    }
+    
+    return pb_encode_string(stream, bytes->bytes, bytes->size);
+}
+
+static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    size_t size = 0;
+    size_t max_size = field->data_size;
+    const char *p = (const char*)src;
+    
+    if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+        max_size = (size_t)-1;
+
+    if (src == NULL)
+    {
+        size = 0; /* Treat null pointer as an empty string */
+    }
+    else
+    {
+        /* strnlen() is not always available, so just use a loop */
+        while (size < max_size && *p != '\0')
+        {
+            size++;
+            p++;
+        }
+    }
+
+    return pb_encode_string(stream, (const pb_byte_t*)src, size);
+}
+
+static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    if (field->ptr == NULL)
+        PB_RETURN_ERROR(stream, "invalid field descriptor");
+    
+    return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src);
+}
+
+static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+    return pb_encode_string(stream, (const pb_byte_t*)src, field->data_size);
+}
+
diff --git a/iOS/Pods/nanopb/pb_encode.h b/iOS/Pods/nanopb/pb_encode.h
new file mode 100644 (file)
index 0000000..d9909fb
--- /dev/null
@@ -0,0 +1,154 @@
+/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c.
+ * The main function is pb_encode. You also need an output stream, and the
+ * field descriptions created by nanopb_generator.py.
+ */
+
+#ifndef PB_ENCODE_H_INCLUDED
+#define PB_ENCODE_H_INCLUDED
+
+#include "pb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structure for defining custom output streams. You will need to provide
+ * a callback function to write the bytes to your storage, which can be
+ * for example a file or a network socket.
+ *
+ * The callback must conform to these rules:
+ *
+ * 1) Return false on IO errors. This will cause encoding to abort.
+ * 2) You can use state to store your own data (e.g. buffer pointer).
+ * 3) pb_write will update bytes_written after your callback runs.
+ * 4) Substreams will modify max_size and bytes_written. Don't use them
+ *    to calculate any pointers.
+ */
+struct pb_ostream_s
+{
+#ifdef PB_BUFFER_ONLY
+    /* Callback pointer is not used in buffer-only configuration.
+     * Having an int pointer here allows binary compatibility but
+     * gives an error if someone tries to assign callback function.
+     * Also, NULL pointer marks a 'sizing stream' that does not
+     * write anything.
+     */
+    int *callback;
+#else
+    bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
+#endif
+    void *state;          /* Free field for use by callback implementation. */
+    size_t max_size;      /* Limit number of output bytes written (or use SIZE_MAX). */
+    size_t bytes_written; /* Number of bytes written so far. */
+    
+#ifndef PB_NO_ERRMSG
+    const char *errmsg;
+#endif
+};
+
+/***************************
+ * Main encoding functions *
+ ***************************/
+
+/* Encode a single protocol buffers message from C structure into a stream.
+ * Returns true on success, false on any failure.
+ * The actual struct pointed to by src_struct must match the description in fields.
+ * All required fields in the struct are assumed to have been filled in.
+ *
+ * Example usage:
+ *    MyMessage msg = {};
+ *    uint8_t buffer[64];
+ *    pb_ostream_t stream;
+ *
+ *    msg.field1 = 42;
+ *    stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+ *    pb_encode(&stream, MyMessage_fields, &msg);
+ */
+bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
+
+/* Same as pb_encode, but prepends the length of the message as a varint.
+ * Corresponds to writeDelimitedTo() in Google's protobuf API.
+ */
+bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
+
+/* Encode the message to get the size of the encoded data, but do not store
+ * the data. */
+bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct);
+
+/**************************************
+ * Functions for manipulating streams *
+ **************************************/
+
+/* Create an output stream for writing into a memory buffer.
+ * The number of bytes written can be found in stream.bytes_written after
+ * encoding the message.
+ *
+ * Alternatively, you can use a custom stream that writes directly to e.g.
+ * a file or a network socket.
+ */
+pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize);
+
+/* Pseudo-stream for measuring the size of a message without actually storing
+ * the encoded data.
+ * 
+ * Example usage:
+ *    MyMessage msg = {};
+ *    pb_ostream_t stream = PB_OSTREAM_SIZING;
+ *    pb_encode(&stream, MyMessage_fields, &msg);
+ *    printf("Message size is %d\n", stream.bytes_written);
+ */
+#ifndef PB_NO_ERRMSG
+#define PB_OSTREAM_SIZING {0,0,0,0,0}
+#else
+#define PB_OSTREAM_SIZING {0,0,0,0}
+#endif
+
+/* Function to write into a pb_ostream_t stream. You can use this if you need
+ * to append or prepend some custom headers to the message.
+ */
+bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
+
+
+/************************************************
+ * Helper functions for writing field callbacks *
+ ************************************************/
+
+/* Encode field header based on type and field number defined in the field
+ * structure. Call this from the callback before writing out field contents. */
+bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field);
+
+/* Encode field header by manually specifing wire type. You need to use this
+ * if you want to write out packed arrays from a callback field. */
+bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number);
+
+/* Encode an integer in the varint format.
+ * This works for bool, enum, int32, int64, uint32 and uint64 field types. */
+bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
+
+/* Encode an integer in the zig-zagged svarint format.
+ * This works for sint32 and sint64. */
+bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
+
+/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */
+bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size);
+
+/* Encode a fixed32, sfixed32 or float value.
+ * You need to pass a pointer to a 4-byte wide C variable. */
+bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
+
+/* Encode a fixed64, sfixed64 or double value.
+ * You need to pass a pointer to a 8-byte wide C variable. */
+bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
+
+/* Encode a submessage field.
+ * You need to pass the pb_field_t array and pointer to struct, just like
+ * with pb_encode(). This internally encodes the submessage twice, first to
+ * calculate message size and then to actually write it out.
+ */
+bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/iOS/Settings.bundle/Pods-acknowledgements.plist b/iOS/Settings.bundle/Pods-acknowledgements.plist
new file mode 100644 (file)
index 0000000..f9ebdd1
--- /dev/null
@@ -0,0 +1,1821 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>PreferenceSpecifiers</key>
+       <array>
+               <dict>
+                       <key>FooterText</key>
+                       <string>This application makes use of the following third party libraries:</string>
+                       <key>Title</key>
+                       <string>Acknowledgements</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2014-2017 Marko Tadić &lt;tadija@me.com&gt; http://tadija.net
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>AEXML</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2014-2017 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 &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>Alamofire</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2016 Manuel García-Estañ
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>AlamofireActivityLogger</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. Crashlytics Kit: Copyright 2017 Crashlytics, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Crashlytics Terms of Service located at http://try.crashlytics.com/terms/terms-of-service.pdf and the Crashlytics Privacy Policy located at http://try.crashlytics.com/terms/privacy-policy.pdf. OSS: http://get.fabric.io/terms/opensource.txt</string>
+                       <key>License</key>
+                       <string>Commercial</string>
+                       <key>Title</key>
+                       <string>Crashlytics</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. OSS: http://get.fabric.io/terms/opensource.txt</string>
+                       <key>License</key>
+                       <string>Commercial</string>
+                       <key>Title</key>
+                       <string>Fabric</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright 2018 Google</string>
+                       <key>License</key>
+                       <string>Copyright</string>
+                       <key>Title</key>
+                       <string>Firebase</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright 2018 Google</string>
+                       <key>License</key>
+                       <string>Copyright</string>
+                       <key>Title</key>
+                       <string>FirebaseAnalytics</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      &quot;License&quot; shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      &quot;Legal Entity&quot; 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,
+      &quot;control&quot; 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.
+
+      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      &quot;Source&quot; form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      &quot;Object&quot; 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.
+
+      &quot;Work&quot; 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).
+
+      &quot;Derivative Works&quot; 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.
+
+      &quot;Contribution&quot; 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, &quot;submitted&quot;
+      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 &quot;Not a Contribution.&quot;
+
+      &quot;Contributor&quot; 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 &quot;NOTICE&quot; 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 &quot;AS IS&quot; 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets &quot;[]&quot;
+      replaced with your own identifying information. (Don&apos;t include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same &quot;printed page&quot; as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</string>
+                       <key>License</key>
+                       <string>Apache</string>
+                       <key>Title</key>
+                       <string>FirebaseCore</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright 2018 Google</string>
+                       <key>License</key>
+                       <string>Copyright</string>
+                       <key>Title</key>
+                       <string>FirebaseInstanceID</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      &quot;License&quot; shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      &quot;Legal Entity&quot; 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,
+      &quot;control&quot; 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.
+
+      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      &quot;Source&quot; form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      &quot;Object&quot; 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.
+
+      &quot;Work&quot; 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).
+
+      &quot;Derivative Works&quot; 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.
+
+      &quot;Contribution&quot; 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, &quot;submitted&quot;
+      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 &quot;Not a Contribution.&quot;
+
+      &quot;Contributor&quot; 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 &quot;NOTICE&quot; 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 &quot;AS IS&quot; 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets &quot;[]&quot;
+      replaced with your own identifying information. (Don&apos;t include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same &quot;printed page&quot; as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</string>
+                       <key>License</key>
+                       <string>Apache</string>
+                       <key>Title</key>
+                       <string>FirebaseMessaging</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2015-2017, Heberti Almeida
+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 FolioReaderKit 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 &quot;AS IS&quot;
+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.
+</string>
+                       <key>License</key>
+                       <string>BSD</string>
+                       <key>Title</key>
+                       <string>FolioReaderKit</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2015 Arthur Ariel Sabintsev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>FontBlaster</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright 2018 Google</string>
+                       <key>License</key>
+                       <string>Copyright</string>
+                       <key>Title</key>
+                       <string>GoogleAppMeasurement</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      &quot;License&quot; shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      &quot;Legal Entity&quot; 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,
+      &quot;control&quot; 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.
+
+      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      &quot;Source&quot; form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      &quot;Object&quot; 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.
+
+      &quot;Work&quot; 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).
+
+      &quot;Derivative Works&quot; 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.
+
+      &quot;Contribution&quot; 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, &quot;submitted&quot;
+      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 &quot;Not a Contribution.&quot;
+
+      &quot;Contributor&quot; 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 &quot;NOTICE&quot; 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 &quot;AS IS&quot; 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.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets &quot;[]&quot;
+      replaced with your own identifying information. (Don&apos;t include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same &quot;printed page&quot; as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</string>
+                       <key>License</key>
+                       <string>Apache</string>
+                       <key>Title</key>
+                       <string>GoogleUtilities</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2015 Jesse Squires
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>JSQWebViewController</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2018 Wei Wang
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>Kingfisher</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>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 &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>MBProgressHUD</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2016, Arpad Goretity https://github.com/H2CO3/HCDownload.git
+Copyright (c) 2016, Muhammad Zeeshan https://github.com/mzeeshanid/MZDownloadManager.git
+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 the &lt;organization&gt; 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 &quot;AS IS&quot; 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 &lt;COPYRIGHT HOLDER&gt; 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.
+</string>
+                       <key>License</key>
+                       <string>BSD</string>
+                       <key>Title</key>
+                       <string>MZDownloadManager</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>MatomoTracker</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2016 — Present CHEN Xian-an &lt;xianan.chen@gmail.com&gt;
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>MenuItemKit</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2014 Dongri Jin &lt;dongriat@gmail.com&gt;
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>OAuthSwift</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright 2008 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
+&quot;AS IS&quot; 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.
+
+Code generated by the Protocol Buffer compiler is owned by the owner
+of the input file used when generating it.  This code is not
+standalone and requires a support library to be linked with it.  This
+support library is itself covered by the above license.
+</string>
+                       <key>License</key>
+                       <string>3-Clause BSD License</string>
+                       <key>Title</key>
+                       <string>Protobuf</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>TABLE OF CONTENTS
+
+1. Apache License version 2.0
+2. Realm Components
+3. Export Compliance
+
+1. -------------------------------------------------------------------------------
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      &quot;License&quot; shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      &quot;Legal Entity&quot; 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,
+      &quot;control&quot; 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.
+
+      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      &quot;Source&quot; form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      &quot;Object&quot; 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.
+
+      &quot;Work&quot; 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).
+
+      &quot;Derivative Works&quot; 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.
+
+      &quot;Contribution&quot; 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, &quot;submitted&quot;
+      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 &quot;Not a Contribution.&quot;
+
+      &quot;Contributor&quot; 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 &quot;NOTICE&quot; 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 &quot;AS IS&quot; 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.
+
+2. -------------------------------------------------------------------------------
+
+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 Platform Extensions component
+
+  Realm Platform Extensions License
+
+  Copyright (c) 2011-2017 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 &quot;AS IS&quot;
+  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.
+
+3. -------------------------------------------------------------------------------
+
+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.
+</string>
+                       <key>License</key>
+                       <string>Apache 2.0</string>
+                       <key>Title</key>
+                       <string>Realm</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>TABLE OF CONTENTS
+
+1. Apache License version 2.0
+2. Realm Components
+3. Export Compliance
+
+1. -------------------------------------------------------------------------------
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      &quot;License&quot; shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      &quot;Legal Entity&quot; 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,
+      &quot;control&quot; 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.
+
+      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      &quot;Source&quot; form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      &quot;Object&quot; 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.
+
+      &quot;Work&quot; 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).
+
+      &quot;Derivative Works&quot; 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.
+
+      &quot;Contribution&quot; 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, &quot;submitted&quot;
+      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 &quot;Not a Contribution.&quot;
+
+      &quot;Contributor&quot; 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 &quot;NOTICE&quot; 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 &quot;AS IS&quot; 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.
+
+2. -------------------------------------------------------------------------------
+
+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 Platform Extensions component
+
+  Realm Platform Extensions License
+
+  Copyright (c) 2011-2017 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 &quot;AS IS&quot;
+  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.
+
+3. -------------------------------------------------------------------------------
+
+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.
+</string>
+                       <key>License</key>
+                       <string>Apache 2.0</string>
+                       <key>Title</key>
+                       <string>RealmSwift</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2010-2015, Sam Soffes, http://soff.es
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+&quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>SSZipArchive</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2015 Jonathan Kent &lt;contact@jonkent.me&gt;
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>SideMenu</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2014 Jason Rendel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>SwiftKeychainWrapper</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2015-2017 Charles Scalesse.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+&quot;Software&quot;), 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 &quot;AS IS&quot;, 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.
+</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>Toast-Swift</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>The MIT License (MIT)
+
+Copyright (c) 2014 Amornchai Kanokpullwad
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), 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 &quot;AS IS&quot;, 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.</string>
+                       <key>License</key>
+                       <string>MIT</string>
+                       <key>Title</key>
+                       <string>ZFDragableModalTransition</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Copyright (c) 2011 Petteri Aimonen &lt;jpa at nanopb.mail.kapsi.fi&gt;
+
+This software is provided &apos;as-is&apos;, without any express or 
+implied warranty. In no event will the authors be held liable 
+for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any 
+purpose, including commercial applications, and to alter it and 
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you 
+   must not claim that you wrote the original software. If you use 
+   this software in a product, an acknowledgment in the product 
+   documentation would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and 
+   must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source 
+   distribution.
+</string>
+                       <key>License</key>
+                       <string>zlib</string>
+                       <key>Title</key>
+                       <string>nanopb</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Generated by CocoaPods - https://cocoapods.org</string>
+                       <key>Title</key>
+                       <string></string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+       </array>
+       <key>StringsTable</key>
+       <string>Acknowledgements</string>
+       <key>Title</key>
+       <string>Licencje</string>
+</dict>
+</plist>
diff --git a/iOS/Settings.bundle/Root.plist b/iOS/Settings.bundle/Root.plist
new file mode 100644 (file)
index 0000000..6beec10
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>StringsTable</key>
+       <string>Root</string>
+       <key>PreferenceSpecifiers</key>
+       <array>
+               <dict>
+                       <key>Type</key>
+                       <string>PSChildPaneSpecifier</string>
+                       <key>Title</key>
+                       <string>Licencje</string>
+                       <key>File</key>
+                       <string>Pods-acknowledgements</string>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/iOS/Settings.bundle/en.lproj/Root.strings b/iOS/Settings.bundle/en.lproj/Root.strings
new file mode 100644 (file)
index 0000000..e1ce513
Binary files /dev/null and b/iOS/Settings.bundle/en.lproj/Root.strings differ
diff --git a/iOS/WolneLektury.xcodeproj/project.pbxproj b/iOS/WolneLektury.xcodeproj/project.pbxproj
new file mode 100644 (file)
index 0000000..b03c732
--- /dev/null
@@ -0,0 +1,1526 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 48;
+       objects = {
+
+/* Begin PBXBuildFile section */
+               B91EB6A8C4277586651638EF /* Pods_WolneLektury.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE822B2181477BBA84CE63A2 /* Pods_WolneLektury.framework */; };
+               E506BA34214D6E0700009699 /* NewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E506BA33214D6E0700009699 /* NewsViewController.swift */; };
+               E506BA38214D6E5100009699 /* NewsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E506BA36214D6E5100009699 /* NewsTableViewCell.swift */; };
+               E506BA39214D6E5100009699 /* NewsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E506BA37214D6E5100009699 /* NewsTableViewCell.xib */; };
+               E506BA3B214D737800009699 /* NewsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E506BA3A214D737800009699 /* NewsModel.swift */; };
+               E506BA3D214D7FA000009699 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E506BA3C214D7FA000009699 /* ListViewController.swift */; };
+               E506BA40214DA31600009699 /* NewsDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E506BA3F214DA31600009699 /* NewsDetailsViewController.swift */; };
+               E50D100920F393D5000FA5CA /* OAuthTokenModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E50D100820F393D5000FA5CA /* OAuthTokenModel.swift */; };
+               E51273B420DCE02300BCEA45 /* DownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51273B320DCE02300BCEA45 /* DownloadManager.swift */; };
+               E514EF6A20D15C23002D2031 /* LibraryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E514EF6920D15C23002D2031 /* LibraryViewController.swift */; };
+               E514EF6C20D1679C002D2031 /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E514EF6B20D1679C002D2031 /* SearchViewController.swift */; };
+               E52A474120BE6AE500998554 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E52A474320BE6AE500998554 /* Localizable.strings */; };
+               E52A474620BE6C2200998554 /* UIViewController+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A474520BE6C2200998554 /* UIViewController+Ext.swift */; };
+               E52A474820BE6C3800998554 /* NSObject+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A474720BE6C3800998554 /* NSObject+Ext.swift */; };
+               E52A474A20BE6C4F00998554 /* UIButton+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A474920BE6C4F00998554 /* UIButton+Ext.swift */; };
+               E52A474C20BE6C6200998554 /* UIImage+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A474B20BE6C6200998554 /* UIImage+Ext.swift */; };
+               E52A474E20BE6C7400998554 /* UIView+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A474D20BE6C7400998554 /* UIView+Ext.swift */; };
+               E52A475020BE6C8000998554 /* String+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A474F20BE6C8000998554 /* String+Ext.swift */; };
+               E52A475220BE6C9400998554 /* NSAttributedString+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A475120BE6C9400998554 /* NSAttributedString+Ext.swift */; };
+               E52A475420BE6CBC00998554 /* UITableView+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A475320BE6CBC00998554 /* UITableView+Ext.swift */; };
+               E52A475620BE6CD700998554 /* DateFormatter+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A475520BE6CD700998554 /* DateFormatter+Ext.swift */; };
+               E52A475820BE6DB800998554 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A475720BE6DB700998554 /* Constants.swift */; };
+               E52A475B20BE858B00998554 /* BookDetailsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A475A20BE858B00998554 /* BookDetailsModel.swift */; };
+               E52A475D20BE879400998554 /* BookModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A475C20BE879400998554 /* BookModel.swift */; };
+               E52A475F20BE87A200998554 /* CategoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A475E20BE87A200998554 /* CategoryModel.swift */; };
+               E52A476120BE87BC00998554 /* FragmentModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A476020BE87BC00998554 /* FragmentModel.swift */; };
+               E52A476320BE87C900998554 /* MediaModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A476220BE87C900998554 /* MediaModel.swift */; };
+               E52A476620BEAE7A00998554 /* Routes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A476520BEAE7A00998554 /* Routes.swift */; };
+               E52A476820BEAFC200998554 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A476720BEAFC200998554 /* NetworkService.swift */; };
+               E52A476A20BEBB6700998554 /* SyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A476920BEBB6700998554 /* SyncManager.swift */; };
+               E52A476C20BEBDD700998554 /* RestAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A476B20BEBDD700998554 /* RestAction.swift */; };
+               E52A476E20BEC0BD00998554 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A476D20BEC0BD00998554 /* Config.swift */; };
+               E52A477120BED97200998554 /* FilterBooksParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A477020BED97200998554 /* FilterBooksParameters.swift */; };
+               E52A477320BEDCC400998554 /* MenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A477220BEDCC400998554 /* MenuItem.swift */; };
+               E52A477620BEE8F300998554 /* MenuLineTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A477420BEE8F300998554 /* MenuLineTableViewCell.swift */; };
+               E52A477720BEE8F300998554 /* MenuLineTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E52A477520BEE8F300998554 /* MenuLineTableViewCell.xib */; };
+               E52A477A20BEE90A00998554 /* MenuBottomTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A477820BEE90A00998554 /* MenuBottomTableViewCell.swift */; };
+               E52A477B20BEE90A00998554 /* MenuBottomTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E52A477920BEE90A00998554 /* MenuBottomTableViewCell.xib */; };
+               E52A477F20BEEB2800998554 /* WLTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A477D20BEEB2800998554 /* WLTableViewCell.swift */; };
+               E52A478020BEEB2800998554 /* WLTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E52A477E20BEEB2800998554 /* WLTableViewCell.xib */; };
+               E52A478320BEF86800998554 /* MenuSupportUsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52A478120BEF86800998554 /* MenuSupportUsTableViewCell.swift */; };
+               E52A478420BEF86800998554 /* MenuSupportUsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E52A478220BEF86800998554 /* MenuSupportUsTableViewCell.xib */; };
+               E52C13C620FA132A00480BF3 /* AuthorizationWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52C13C520FA132A00480BF3 /* AuthorizationWebViewController.swift */; };
+               E52C13C820FA945D00480BF3 /* Credentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = E52C13C720FA945D00480BF3 /* Credentials.swift */; };
+               E531C82B2137282B00655C12 /* ReadingStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E531C82A2137282B00655C12 /* ReadingStateModel.swift */; };
+               E531C82D2137B1DE00655C12 /* UIColor+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E531C82C2137B1DE00655C12 /* UIColor+Ext.swift */; };
+               E5365A24215A82E100DEF847 /* MatomoTracker+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5365A23215A82E100DEF847 /* MatomoTracker+Ext.swift */; };
+               E5365A26215A86BA00DEF847 /* WLViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5365A25215A86BA00DEF847 /* WLViewController.swift */; };
+               E5374D6B2142A83D00482DFA /* PlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5374D6A2142A83D00482DFA /* PlayerViewController.swift */; };
+               E53B7E9E20DA816E007F88E0 /* BookDetailsFragmentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E53B7E9C20DA816E007F88E0 /* BookDetailsFragmentTableViewCell.swift */; };
+               E53B7E9F20DA816E007F88E0 /* BookDetailsFragmentTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E53B7E9D20DA816E007F88E0 /* BookDetailsFragmentTableViewCell.xib */; };
+               E53B7EA220DA8456007F88E0 /* BookDetailsButtonTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E53B7EA020DA8456007F88E0 /* BookDetailsButtonTableViewCell.swift */; };
+               E53B7EA320DA8456007F88E0 /* BookDetailsButtonTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E53B7EA120DA8456007F88E0 /* BookDetailsButtonTableViewCell.xib */; };
+               E54CDA43214FD2E9009D0127 /* GalleryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54CDA42214FD2E9009D0127 /* GalleryCell.swift */; };
+               E54CDA46214FD87E009D0127 /* SimpleGalleryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54CDA44214FD87E009D0127 /* SimpleGalleryCollectionViewCell.swift */; };
+               E54CDA47214FD87E009D0127 /* SimpleGalleryCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E54CDA45214FD87E009D0127 /* SimpleGalleryCollectionViewCell.xib */; };
+               E54CDA4A214FF3C8009D0127 /* GalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54CDA49214FF3C8009D0127 /* GalleryViewController.swift */; };
+               E54CDA4E214FFA3D009D0127 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54CDA4D214FFA3D009D0127 /* SettingsViewController.swift */; };
+               E54D850D2153F73F003B087E /* ProgressButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54D850C2153F73F003B087E /* ProgressButton.swift */; };
+               E54D850F2154020C003B087E /* ProgressLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54D850E2154020C003B087E /* ProgressLabel.swift */; };
+               E553129B2164EF5900B2D216 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = E553129A2164EF5900B2D216 /* GoogleService-Info.plist */; };
+               E55BFD7020E0F9370090F8BE /* WLReaderConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55BFD6F20E0F9370090F8BE /* WLReaderConfig.swift */; };
+               E55DA33720D9151E00F1D5F8 /* BookDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55DA33620D9151E00F1D5F8 /* BookDetailsViewController.swift */; };
+               E55DA33B20D91DD000F1D5F8 /* BookDetailsHeaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55DA33920D91DD000F1D5F8 /* BookDetailsHeaderTableViewCell.swift */; };
+               E55DA33C20D91DD000F1D5F8 /* BookDetailsHeaderTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E55DA33A20D91DD000F1D5F8 /* BookDetailsHeaderTableViewCell.xib */; };
+               E55DA34020D9338D00F1D5F8 /* BookDetailsInfoTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55DA33E20D9338D00F1D5F8 /* BookDetailsInfoTableViewCell.swift */; };
+               E55DA34120D9338D00F1D5F8 /* BookDetailsInfoTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E55DA33F20D9338D00F1D5F8 /* BookDetailsInfoTableViewCell.xib */; };
+               E55DA34420D933F700F1D5F8 /* BookDetailsSeparatorTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55DA34220D933F700F1D5F8 /* BookDetailsSeparatorTableViewCell.swift */; };
+               E55DA34520D933F700F1D5F8 /* BookDetailsSeparatorTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E55DA34320D933F700F1D5F8 /* BookDetailsSeparatorTableViewCell.xib */; };
+               E565D28420BF506800786E1F /* FilterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E565D28320BF506700786E1F /* FilterViewController.swift */; };
+               E5699A0C214081B900F0A94C /* LikeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5699A0B214081B900F0A94C /* LikeModel.swift */; };
+               E56B2F1920D7CC9600671B24 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E56B2F1820D7CC9600671B24 /* MainViewController.swift */; };
+               E56B2F1C20D7D74800671B24 /* LibraryEarlyAccessTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E56B2F1A20D7D74800671B24 /* LibraryEarlyAccessTableViewCell.swift */; };
+               E56B2F1D20D7D74800671B24 /* LibraryEarlyAccessTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E56B2F1B20D7D74800671B24 /* LibraryEarlyAccessTableViewCell.xib */; };
+               E56B2F2020D7D75F00671B24 /* LibraryCollectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E56B2F1E20D7D75F00671B24 /* LibraryCollectionTableViewCell.swift */; };
+               E56B2F2120D7D75F00671B24 /* LibraryCollectionTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E56B2F1F20D7D75F00671B24 /* LibraryCollectionTableViewCell.xib */; };
+               E56B2F2420D7DAE500671B24 /* BookDescriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E56B2F2320D7DAE500671B24 /* BookDescriptionView.swift */; };
+               E56B2F2620D7DB1F00671B24 /* BookDescriptionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E56B2F2520D7DB1F00671B24 /* BookDescriptionView.xib */; };
+               E56B2F2820D7ED3B00671B24 /* DesignableXibView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E56B2F2720D7ED3B00671B24 /* DesignableXibView.swift */; };
+               E56B2F2A20D7FE8700671B24 /* BookImageOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E56B2F2920D7FE8700671B24 /* BookImageOverlayView.swift */; };
+               E56B2F2C20D7FEA000671B24 /* BookImageOverlayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E56B2F2B20D7FEA000671B24 /* BookImageOverlayView.xib */; };
+               E56B2F2F20D80FA800671B24 /* BookCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E56B2F2D20D80FA800671B24 /* BookCollectionViewCell.swift */; };
+               E56B2F3020D80FA800671B24 /* BookCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E56B2F2E20D80FA800671B24 /* BookCollectionViewCell.xib */; };
+               E56EBB9C2139369600DA3600 /* BecomeFriendTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E56EBB9A2139369600DA3600 /* BecomeFriendTableViewCell.swift */; };
+               E56EBB9D2139369600DA3600 /* BecomeFriendTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E56EBB9B2139369600DA3600 /* BecomeFriendTableViewCell.xib */; };
+               E5732B742155411900616C91 /* PlayerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5732B732155411900616C91 /* PlayerController.swift */; };
+               E5732B7621560EF200616C91 /* PlayerItemTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5732B7521560EF200616C91 /* PlayerItemTableViewCell.swift */; };
+               E575E2CA21381E060072BB45 /* BookListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E575E2C921381E060072BB45 /* BookListViewController.swift */; };
+               E57B247420D1826A00430DDC /* SearchFilterCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E57B247220D1826A00430DDC /* SearchFilterCollectionViewCell.swift */; };
+               E57B247520D1826A00430DDC /* SearchFilterCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E57B247320D1826A00430DDC /* SearchFilterCollectionViewCell.xib */; };
+               E57B247820D2649E00430DDC /* BookTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E57B247620D2649E00430DDC /* BookTableViewCell.swift */; };
+               E57B247920D2649E00430DDC /* BookTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E57B247720D2649E00430DDC /* BookTableViewCell.xib */; };
+               E58F928820D4E63E00400375 /* SearchFiltersManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58F928720D4E63E00400375 /* SearchFiltersManager.swift */; };
+               E58FADC421518CD000CA6DD9 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58FADC321518CD000CA6DD9 /* AboutViewController.swift */; };
+               E58FADC72151A68E00CA6DD9 /* SupportUsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58FADC62151A68E00CA6DD9 /* SupportUsViewController.swift */; };
+               E58FADCA2151A6E600CA6DD9 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58FADC92151A6E600CA6DD9 /* LoginViewController.swift */; };
+               E5914DBF2148FF33004993C6 /* DownloadedListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5914DBE2148FF33004993C6 /* DownloadedListViewController.swift */; };
+               E5920EB6212FF73B00118EE6 /* UsernameModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5920EB5212FF73B00118EE6 /* UsernameModel.swift */; };
+               E5920EB9212FFA9300118EE6 /* RLMApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5920EB8212FFA9300118EE6 /* RLMApplication.swift */; };
+               E5920EBB2130088200118EE6 /* RLMUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5920EBA2130088200118EE6 /* RLMUser.swift */; };
+               E5A17756212EF2A2004F026E /* SharedGlobals.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A17755212EF2A2004F026E /* SharedGlobals.swift */; };
+               E5A281CF215E6FFE000DCD83 /* WLSideMenuNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A281CE215E6FFE000DCD83 /* WLSideMenuNavigationController.swift */; };
+               E5A281D1215E708C000DCD83 /* WLNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A281D0215E708C000DCD83 /* WLNavigationController.swift */; };
+               E5AE0EAC206D31F600C61957 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AE0EAB206D31F600C61957 /* AppDelegate.swift */; };
+               E5AE0EB1206D31F600C61957 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5AE0EAF206D31F600C61957 /* Main.storyboard */; };
+               E5AE0EB3206D31F600C61957 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E5AE0EB2206D31F600C61957 /* Assets.xcassets */; };
+               E5AE0EB6206D31F600C61957 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5AE0EB4206D31F600C61957 /* LaunchScreen.storyboard */; };
+               E5AE0EC1206D31F700C61957 /* WolneLekturyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AE0EC0206D31F700C61957 /* WolneLekturyTests.swift */; };
+               E5AE0ECC206D31F700C61957 /* WolneLekturyUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AE0ECB206D31F700C61957 /* WolneLekturyUITests.swift */; };
+               E5B3EF7F20BDEFB7007B0139 /* MainNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B3EF7E20BDEFB7007B0139 /* MainNavigator.swift */; };
+               E5B3EF8120BDF0C5007B0139 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B3EF8020BDF0C5007B0139 /* MenuViewController.swift */; };
+               E5B3EF8420BDF24D007B0139 /* MainNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B3EF8320BDF24D007B0139 /* MainNavigationViewController.swift */; };
+               E5B3EF8820BDF8AD007B0139 /* MenuTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5B3EF8620BDF8AD007B0139 /* MenuTableViewCell.swift */; };
+               E5B3EF8920BDF8AD007B0139 /* MenuTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E5B3EF8720BDF8AD007B0139 /* MenuTableViewCell.xib */; };
+               E5D09FA2214803AA007DCB43 /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5D09FA1214803AA007DCB43 /* DatabaseManager.swift */; };
+               E5E5BAF520CFD46F00BE3795 /* Roboto-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E5E5BAF020CFD46F00BE3795 /* Roboto-Medium.ttf */; };
+               E5E5BAF620CFD46F00BE3795 /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E5E5BAF120CFD46F00BE3795 /* Roboto-Light.ttf */; };
+               E5E5BAF720CFD46F00BE3795 /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E5E5BAF220CFD46F00BE3795 /* Roboto-Regular.ttf */; };
+               E5E5BAF820CFD46F00BE3795 /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E5E5BAF320CFD46F00BE3795 /* Roboto-Bold.ttf */; };
+               E5E5BAF920CFD46F00BE3795 /* Roboto-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E5E5BAF420CFD46F00BE3795 /* Roboto-Thin.ttf */; };
+               E5E5BAFE20CFE2A500BE3795 /* FilterHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E5BAFD20CFE2A500BE3795 /* FilterHeaderView.swift */; };
+               E5E5BB0120CFE44B00BE3795 /* FilterOnlyLecturesReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E5BAFF20CFE44B00BE3795 /* FilterOnlyLecturesReusableView.swift */; };
+               E5E5BB0220CFE44B00BE3795 /* FilterOnlyLecturesReusableView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E5E5BB0020CFE44B00BE3795 /* FilterOnlyLecturesReusableView.xib */; };
+               E5E5BB0520CFE46900BE3795 /* FilterSectionHeaderCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E5BB0320CFE46900BE3795 /* FilterSectionHeaderCollectionReusableView.swift */; };
+               E5E5BB0620CFE46900BE3795 /* FilterSectionHeaderCollectionReusableView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E5E5BB0420CFE46900BE3795 /* FilterSectionHeaderCollectionReusableView.xib */; };
+               E5E5BB0920CFE49500BE3795 /* FilterCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E5BB0720CFE49500BE3795 /* FilterCollectionViewCell.swift */; };
+               E5E5BB0A20CFE49500BE3795 /* FilterCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E5E5BB0820CFE49500BE3795 /* FilterCollectionViewCell.xib */; };
+               E5E5BB0D20CFF5FD00BE3795 /* ActivityIndicatorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E5BB0C20CFF5FD00BE3795 /* ActivityIndicatorButton.swift */; };
+               E5E89BC320F4BEFC00A0A6C2 /* Dictionary+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E89BC220F4BEFC00A0A6C2 /* Dictionary+Ext.swift */; };
+               E5F55E562189B55600D95421 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = E5F55E552189B55600D95421 /* Settings.bundle */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+               E5AE0EBD206D31F700C61957 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = E5AE0EA0206D31F600C61957 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = E5AE0EA7206D31F600C61957;
+                       remoteInfo = WolneLektury;
+               };
+               E5AE0EC8206D31F700C61957 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = E5AE0EA0206D31F600C61957 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = E5AE0EA7206D31F600C61957;
+                       remoteInfo = WolneLektury;
+               };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+               279BDA32FF0C4D2B7D20B0F8 /* Pods-WolneLektury.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WolneLektury.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.debug.xcconfig"; sourceTree = "<group>"; };
+               BE822B2181477BBA84CE63A2 /* Pods_WolneLektury.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WolneLektury.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               C6023269DAF53C83665BCEED /* Pods-WolneLektury.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WolneLektury.release.xcconfig"; path = "Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury.release.xcconfig"; sourceTree = "<group>"; };
+               E506BA33214D6E0700009699 /* NewsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsViewController.swift; sourceTree = "<group>"; };
+               E506BA36214D6E5100009699 /* NewsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsTableViewCell.swift; sourceTree = "<group>"; };
+               E506BA37214D6E5100009699 /* NewsTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NewsTableViewCell.xib; sourceTree = "<group>"; };
+               E506BA3A214D737800009699 /* NewsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsModel.swift; sourceTree = "<group>"; };
+               E506BA3C214D7FA000009699 /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = "<group>"; };
+               E506BA3F214DA31600009699 /* NewsDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsDetailsViewController.swift; sourceTree = "<group>"; };
+               E50D100820F393D5000FA5CA /* OAuthTokenModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthTokenModel.swift; sourceTree = "<group>"; };
+               E51273B320DCE02300BCEA45 /* DownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadManager.swift; sourceTree = "<group>"; };
+               E514EF6920D15C23002D2031 /* LibraryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryViewController.swift; sourceTree = "<group>"; };
+               E514EF6B20D1679C002D2031 /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = "<group>"; };
+               E52A474220BE6AE500998554 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
+               E52A474520BE6C2200998554 /* UIViewController+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Ext.swift"; sourceTree = "<group>"; };
+               E52A474720BE6C3800998554 /* NSObject+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSObject+Ext.swift"; sourceTree = "<group>"; };
+               E52A474920BE6C4F00998554 /* UIButton+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Ext.swift"; sourceTree = "<group>"; };
+               E52A474B20BE6C6200998554 /* UIImage+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Ext.swift"; sourceTree = "<group>"; };
+               E52A474D20BE6C7400998554 /* UIView+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Ext.swift"; sourceTree = "<group>"; };
+               E52A474F20BE6C8000998554 /* String+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Ext.swift"; sourceTree = "<group>"; };
+               E52A475120BE6C9400998554 /* NSAttributedString+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Ext.swift"; sourceTree = "<group>"; };
+               E52A475320BE6CBC00998554 /* UITableView+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Ext.swift"; sourceTree = "<group>"; };
+               E52A475520BE6CD700998554 /* DateFormatter+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Ext.swift"; sourceTree = "<group>"; };
+               E52A475720BE6DB700998554 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
+               E52A475A20BE858B00998554 /* BookDetailsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookDetailsModel.swift; sourceTree = "<group>"; };
+               E52A475C20BE879400998554 /* BookModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookModel.swift; sourceTree = "<group>"; };
+               E52A475E20BE87A200998554 /* CategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryModel.swift; sourceTree = "<group>"; };
+               E52A476020BE87BC00998554 /* FragmentModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FragmentModel.swift; sourceTree = "<group>"; };
+               E52A476220BE87C900998554 /* MediaModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaModel.swift; sourceTree = "<group>"; };
+               E52A476520BEAE7A00998554 /* Routes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Routes.swift; sourceTree = "<group>"; };
+               E52A476720BEAFC200998554 /* NetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = "<group>"; };
+               E52A476920BEBB6700998554 /* SyncManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncManager.swift; sourceTree = "<group>"; };
+               E52A476B20BEBDD700998554 /* RestAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestAction.swift; sourceTree = "<group>"; };
+               E52A476D20BEC0BD00998554 /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = "<group>"; };
+               E52A477020BED97200998554 /* FilterBooksParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterBooksParameters.swift; sourceTree = "<group>"; };
+               E52A477220BEDCC400998554 /* MenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItem.swift; sourceTree = "<group>"; };
+               E52A477420BEE8F300998554 /* MenuLineTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuLineTableViewCell.swift; sourceTree = "<group>"; };
+               E52A477520BEE8F300998554 /* MenuLineTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MenuLineTableViewCell.xib; sourceTree = "<group>"; };
+               E52A477820BEE90A00998554 /* MenuBottomTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBottomTableViewCell.swift; sourceTree = "<group>"; };
+               E52A477920BEE90A00998554 /* MenuBottomTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MenuBottomTableViewCell.xib; sourceTree = "<group>"; };
+               E52A477D20BEEB2800998554 /* WLTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WLTableViewCell.swift; sourceTree = "<group>"; };
+               E52A477E20BEEB2800998554 /* WLTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WLTableViewCell.xib; sourceTree = "<group>"; };
+               E52A478120BEF86800998554 /* MenuSupportUsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuSupportUsTableViewCell.swift; sourceTree = "<group>"; };
+               E52A478220BEF86800998554 /* MenuSupportUsTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MenuSupportUsTableViewCell.xib; sourceTree = "<group>"; };
+               E52C13C520FA132A00480BF3 /* AuthorizationWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorizationWebViewController.swift; sourceTree = "<group>"; };
+               E52C13C720FA945D00480BF3 /* Credentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = "<group>"; };
+               E531C82A2137282B00655C12 /* ReadingStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingStateModel.swift; sourceTree = "<group>"; };
+               E531C82C2137B1DE00655C12 /* UIColor+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Ext.swift"; sourceTree = "<group>"; };
+               E5365A23215A82E100DEF847 /* MatomoTracker+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MatomoTracker+Ext.swift"; sourceTree = "<group>"; };
+               E5365A25215A86BA00DEF847 /* WLViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WLViewController.swift; sourceTree = "<group>"; };
+               E5374D6A2142A83D00482DFA /* PlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerViewController.swift; sourceTree = "<group>"; };
+               E53B7E9C20DA816E007F88E0 /* BookDetailsFragmentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookDetailsFragmentTableViewCell.swift; sourceTree = "<group>"; };
+               E53B7E9D20DA816E007F88E0 /* BookDetailsFragmentTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookDetailsFragmentTableViewCell.xib; sourceTree = "<group>"; };
+               E53B7EA020DA8456007F88E0 /* BookDetailsButtonTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookDetailsButtonTableViewCell.swift; sourceTree = "<group>"; };
+               E53B7EA120DA8456007F88E0 /* BookDetailsButtonTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookDetailsButtonTableViewCell.xib; sourceTree = "<group>"; };
+               E54CDA42214FD2E9009D0127 /* GalleryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryCell.swift; sourceTree = "<group>"; };
+               E54CDA44214FD87E009D0127 /* SimpleGalleryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleGalleryCollectionViewCell.swift; sourceTree = "<group>"; };
+               E54CDA45214FD87E009D0127 /* SimpleGalleryCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SimpleGalleryCollectionViewCell.xib; sourceTree = "<group>"; };
+               E54CDA49214FF3C8009D0127 /* GalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryViewController.swift; sourceTree = "<group>"; };
+               E54CDA4D214FFA3D009D0127 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
+               E54D850C2153F73F003B087E /* ProgressButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressButton.swift; sourceTree = "<group>"; };
+               E54D850E2154020C003B087E /* ProgressLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressLabel.swift; sourceTree = "<group>"; };
+               E553129A2164EF5900B2D216 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
+               E553129C2164F32600B2D216 /* WolneLektury.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WolneLektury.entitlements; sourceTree = "<group>"; };
+               E55BFD6F20E0F9370090F8BE /* WLReaderConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WLReaderConfig.swift; sourceTree = "<group>"; };
+               E55DA33620D9151E00F1D5F8 /* BookDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookDetailsViewController.swift; sourceTree = "<group>"; };
+               E55DA33920D91DD000F1D5F8 /* BookDetailsHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookDetailsHeaderTableViewCell.swift; sourceTree = "<group>"; };
+               E55DA33A20D91DD000F1D5F8 /* BookDetailsHeaderTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookDetailsHeaderTableViewCell.xib; sourceTree = "<group>"; };
+               E55DA33E20D9338D00F1D5F8 /* BookDetailsInfoTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookDetailsInfoTableViewCell.swift; sourceTree = "<group>"; };
+               E55DA33F20D9338D00F1D5F8 /* BookDetailsInfoTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookDetailsInfoTableViewCell.xib; sourceTree = "<group>"; };
+               E55DA34220D933F700F1D5F8 /* BookDetailsSeparatorTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookDetailsSeparatorTableViewCell.swift; sourceTree = "<group>"; };
+               E55DA34320D933F700F1D5F8 /* BookDetailsSeparatorTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookDetailsSeparatorTableViewCell.xib; sourceTree = "<group>"; };
+               E565D28320BF506700786E1F /* FilterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterViewController.swift; sourceTree = "<group>"; };
+               E5699A0B214081B900F0A94C /* LikeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeModel.swift; sourceTree = "<group>"; };
+               E56B2F1820D7CC9600671B24 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
+               E56B2F1A20D7D74800671B24 /* LibraryEarlyAccessTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryEarlyAccessTableViewCell.swift; sourceTree = "<group>"; };
+               E56B2F1B20D7D74800671B24 /* LibraryEarlyAccessTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LibraryEarlyAccessTableViewCell.xib; sourceTree = "<group>"; };
+               E56B2F1E20D7D75F00671B24 /* LibraryCollectionTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryCollectionTableViewCell.swift; sourceTree = "<group>"; };
+               E56B2F1F20D7D75F00671B24 /* LibraryCollectionTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LibraryCollectionTableViewCell.xib; sourceTree = "<group>"; };
+               E56B2F2320D7DAE500671B24 /* BookDescriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookDescriptionView.swift; sourceTree = "<group>"; };
+               E56B2F2520D7DB1F00671B24 /* BookDescriptionView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookDescriptionView.xib; sourceTree = "<group>"; };
+               E56B2F2720D7ED3B00671B24 /* DesignableXibView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesignableXibView.swift; sourceTree = "<group>"; };
+               E56B2F2920D7FE8700671B24 /* BookImageOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookImageOverlayView.swift; sourceTree = "<group>"; };
+               E56B2F2B20D7FEA000671B24 /* BookImageOverlayView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookImageOverlayView.xib; sourceTree = "<group>"; };
+               E56B2F2D20D80FA800671B24 /* BookCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookCollectionViewCell.swift; sourceTree = "<group>"; };
+               E56B2F2E20D80FA800671B24 /* BookCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookCollectionViewCell.xib; sourceTree = "<group>"; };
+               E56EBB9A2139369600DA3600 /* BecomeFriendTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BecomeFriendTableViewCell.swift; sourceTree = "<group>"; };
+               E56EBB9B2139369600DA3600 /* BecomeFriendTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BecomeFriendTableViewCell.xib; sourceTree = "<group>"; };
+               E5732B732155411900616C91 /* PlayerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerController.swift; sourceTree = "<group>"; };
+               E5732B7521560EF200616C91 /* PlayerItemTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerItemTableViewCell.swift; sourceTree = "<group>"; };
+               E575E2C921381E060072BB45 /* BookListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookListViewController.swift; sourceTree = "<group>"; };
+               E57B247220D1826A00430DDC /* SearchFilterCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchFilterCollectionViewCell.swift; sourceTree = "<group>"; };
+               E57B247320D1826A00430DDC /* SearchFilterCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SearchFilterCollectionViewCell.xib; sourceTree = "<group>"; };
+               E57B247620D2649E00430DDC /* BookTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookTableViewCell.swift; sourceTree = "<group>"; };
+               E57B247720D2649E00430DDC /* BookTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookTableViewCell.xib; sourceTree = "<group>"; };
+               E58F928720D4E63E00400375 /* SearchFiltersManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchFiltersManager.swift; sourceTree = "<group>"; };
+               E58FADC321518CD000CA6DD9 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
+               E58FADC62151A68E00CA6DD9 /* SupportUsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportUsViewController.swift; sourceTree = "<group>"; };
+               E58FADC92151A6E600CA6DD9 /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = "<group>"; };
+               E5914DBE2148FF33004993C6 /* DownloadedListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadedListViewController.swift; sourceTree = "<group>"; };
+               E5920EB5212FF73B00118EE6 /* UsernameModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsernameModel.swift; sourceTree = "<group>"; };
+               E5920EB8212FFA9300118EE6 /* RLMApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RLMApplication.swift; sourceTree = "<group>"; };
+               E5920EBA2130088200118EE6 /* RLMUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RLMUser.swift; sourceTree = "<group>"; };
+               E5A17755212EF2A2004F026E /* SharedGlobals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedGlobals.swift; sourceTree = "<group>"; };
+               E5A281CE215E6FFE000DCD83 /* WLSideMenuNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WLSideMenuNavigationController.swift; sourceTree = "<group>"; };
+               E5A281D0215E708C000DCD83 /* WLNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WLNavigationController.swift; sourceTree = "<group>"; };
+               E5AE0EA8206D31F600C61957 /* WolneLektury.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WolneLektury.app; sourceTree = BUILT_PRODUCTS_DIR; };
+               E5AE0EAB206D31F600C61957 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+               E5AE0EB0206D31F600C61957 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+               E5AE0EB2206D31F600C61957 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+               E5AE0EB5206D31F600C61957 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+               E5AE0EB7206D31F600C61957 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               E5AE0EBC206D31F700C61957 /* WolneLekturyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WolneLekturyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+               E5AE0EC0206D31F700C61957 /* WolneLekturyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WolneLekturyTests.swift; sourceTree = "<group>"; };
+               E5AE0EC2206D31F700C61957 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               E5AE0EC7206D31F700C61957 /* WolneLekturyUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WolneLekturyUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+               E5AE0ECB206D31F700C61957 /* WolneLekturyUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WolneLekturyUITests.swift; sourceTree = "<group>"; };
+               E5AE0ECD206D31F700C61957 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               E5B3EF7E20BDEFB7007B0139 /* MainNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainNavigator.swift; sourceTree = "<group>"; };
+               E5B3EF8020BDF0C5007B0139 /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = "<group>"; };
+               E5B3EF8320BDF24D007B0139 /* MainNavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainNavigationViewController.swift; sourceTree = "<group>"; };
+               E5B3EF8620BDF8AD007B0139 /* MenuTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuTableViewCell.swift; sourceTree = "<group>"; };
+               E5B3EF8720BDF8AD007B0139 /* MenuTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MenuTableViewCell.xib; sourceTree = "<group>"; };
+               E5D09FA1214803AA007DCB43 /* DatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
+               E5E5BAF020CFD46F00BE3795 /* Roboto-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Medium.ttf"; sourceTree = "<group>"; };
+               E5E5BAF120CFD46F00BE3795 /* Roboto-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Light.ttf"; sourceTree = "<group>"; };
+               E5E5BAF220CFD46F00BE3795 /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = "<group>"; };
+               E5E5BAF320CFD46F00BE3795 /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = "<group>"; };
+               E5E5BAF420CFD46F00BE3795 /* Roboto-Thin.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Thin.ttf"; sourceTree = "<group>"; };
+               E5E5BAFD20CFE2A500BE3795 /* FilterHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterHeaderView.swift; sourceTree = "<group>"; };
+               E5E5BAFF20CFE44B00BE3795 /* FilterOnlyLecturesReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterOnlyLecturesReusableView.swift; sourceTree = "<group>"; };
+               E5E5BB0020CFE44B00BE3795 /* FilterOnlyLecturesReusableView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FilterOnlyLecturesReusableView.xib; sourceTree = "<group>"; };
+               E5E5BB0320CFE46900BE3795 /* FilterSectionHeaderCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterSectionHeaderCollectionReusableView.swift; sourceTree = "<group>"; };
+               E5E5BB0420CFE46900BE3795 /* FilterSectionHeaderCollectionReusableView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FilterSectionHeaderCollectionReusableView.xib; sourceTree = "<group>"; };
+               E5E5BB0720CFE49500BE3795 /* FilterCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterCollectionViewCell.swift; sourceTree = "<group>"; };
+               E5E5BB0820CFE49500BE3795 /* FilterCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FilterCollectionViewCell.xib; sourceTree = "<group>"; };
+               E5E5BB0C20CFF5FD00BE3795 /* ActivityIndicatorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorButton.swift; sourceTree = "<group>"; };
+               E5E89BC220F4BEFC00A0A6C2 /* Dictionary+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Ext.swift"; sourceTree = "<group>"; };
+               E5F55E552189B55600D95421 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               E5AE0EA5206D31F600C61957 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               B91EB6A8C4277586651638EF /* Pods_WolneLektury.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               E5AE0EB9206D31F700C61957 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               E5AE0EC4206D31F700C61957 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               060167A9F380BA2A2BA6491B /* Pods */ = {
+                       isa = PBXGroup;
+                       children = (
+                               279BDA32FF0C4D2B7D20B0F8 /* Pods-WolneLektury.debug.xcconfig */,
+                               C6023269DAF53C83665BCEED /* Pods-WolneLektury.release.xcconfig */,
+                       );
+                       name = Pods;
+                       sourceTree = "<group>";
+               };
+               0F1561A5B3389A3D6F47162F /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               BE822B2181477BBA84CE63A2 /* Pods_WolneLektury.framework */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               E506BA32214D6DEC00009699 /* News */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E506BA35214D6E3500009699 /* Cells */,
+                               E506BA33214D6E0700009699 /* NewsViewController.swift */,
+                       );
+                       path = News;
+                       sourceTree = "<group>";
+               };
+               E506BA35214D6E3500009699 /* Cells */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E506BA36214D6E5100009699 /* NewsTableViewCell.swift */,
+                               E506BA37214D6E5100009699 /* NewsTableViewCell.xib */,
+                       );
+                       path = Cells;
+                       sourceTree = "<group>";
+               };
+               E506BA3E214D9F0400009699 /* NewsDetails */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E54CDA48214FF36D009D0127 /* Gallery */,
+                               E54CDA41214FD2AC009D0127 /* Cells */,
+                               E506BA3F214DA31600009699 /* NewsDetailsViewController.swift */,
+                       );
+                       path = NewsDetails;
+                       sourceTree = "<group>";
+               };
+               E514EF6720D15C0E002D2031 /* Library */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E56B2F1720D7CAB300671B24 /* Cells */,
+                               E514EF6920D15C23002D2031 /* LibraryViewController.swift */,
+                       );
+                       path = Library;
+                       sourceTree = "<group>";
+               };
+               E514EF6820D15C0E002D2031 /* Search */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E57B247120D1824100430DDC /* Cells */,
+                               E514EF6B20D1679C002D2031 /* SearchViewController.swift */,
+                               E58F928720D4E63E00400375 /* SearchFiltersManager.swift */,
+                       );
+                       path = Search;
+                       sourceTree = "<group>";
+               };
+               E52A474420BE6BD500998554 /* Extensions */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E52A474520BE6C2200998554 /* UIViewController+Ext.swift */,
+                               E52A474720BE6C3800998554 /* NSObject+Ext.swift */,
+                               E52A474920BE6C4F00998554 /* UIButton+Ext.swift */,
+                               E52A474B20BE6C6200998554 /* UIImage+Ext.swift */,
+                               E52A474D20BE6C7400998554 /* UIView+Ext.swift */,
+                               E52A474F20BE6C8000998554 /* String+Ext.swift */,
+                               E5365A23215A82E100DEF847 /* MatomoTracker+Ext.swift */,
+                               E52A475120BE6C9400998554 /* NSAttributedString+Ext.swift */,
+                               E52A475320BE6CBC00998554 /* UITableView+Ext.swift */,
+                               E52A475520BE6CD700998554 /* DateFormatter+Ext.swift */,
+                               E5E89BC220F4BEFC00A0A6C2 /* Dictionary+Ext.swift */,
+                               E531C82C2137B1DE00655C12 /* UIColor+Ext.swift */,
+                       );
+                       path = Extensions;
+                       sourceTree = "<group>";
+               };
+               E52A475920BE856800998554 /* Model */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E52A476F20BED8E000998554 /* Parameters */,
+                               E52A475A20BE858B00998554 /* BookDetailsModel.swift */,
+                               E5920EB7212FFA8100118EE6 /* Realm */,
+                               E52A475C20BE879400998554 /* BookModel.swift */,
+                               E52A475E20BE87A200998554 /* CategoryModel.swift */,
+                               E52A476020BE87BC00998554 /* FragmentModel.swift */,
+                               E5699A0B214081B900F0A94C /* LikeModel.swift */,
+                               E5920EB5212FF73B00118EE6 /* UsernameModel.swift */,
+                               E506BA3A214D737800009699 /* NewsModel.swift */,
+                               E52A476220BE87C900998554 /* MediaModel.swift */,
+                               E50D100820F393D5000FA5CA /* OAuthTokenModel.swift */,
+                               E52C13C720FA945D00480BF3 /* Credentials.swift */,
+                               E531C82A2137282B00655C12 /* ReadingStateModel.swift */,
+                       );
+                       path = Model;
+                       sourceTree = "<group>";
+               };
+               E52A476420BEAE5900998554 /* Connection */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E52A476520BEAE7A00998554 /* Routes.swift */,
+                               E52A476720BEAFC200998554 /* NetworkService.swift */,
+                               E52A476B20BEBDD700998554 /* RestAction.swift */,
+                               E52A476920BEBB6700998554 /* SyncManager.swift */,
+                               E51273B320DCE02300BCEA45 /* DownloadManager.swift */,
+                       );
+                       path = Connection;
+                       sourceTree = "<group>";
+               };
+               E52A476F20BED8E000998554 /* Parameters */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E52A477020BED97200998554 /* FilterBooksParameters.swift */,
+                       );
+                       path = Parameters;
+                       sourceTree = "<group>";
+               };
+               E52A477C20BEEB1200998554 /* Cells */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E52A477D20BEEB2800998554 /* WLTableViewCell.swift */,
+                               E52A477E20BEEB2800998554 /* WLTableViewCell.xib */,
+                               E57B247620D2649E00430DDC /* BookTableViewCell.swift */,
+                               E57B247720D2649E00430DDC /* BookTableViewCell.xib */,
+                       );
+                       path = Cells;
+                       sourceTree = "<group>";
+               };
+               E52C13C420FA130F00480BF3 /* Authorization */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E52C13C520FA132A00480BF3 /* AuthorizationWebViewController.swift */,
+                       );
+                       path = Authorization;
+                       sourceTree = "<group>";
+               };
+               E5374D692142A80F00482DFA /* Player */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5374D6A2142A83D00482DFA /* PlayerViewController.swift */,
+                               E5732B732155411900616C91 /* PlayerController.swift */,
+                               E5732B7521560EF200616C91 /* PlayerItemTableViewCell.swift */,
+                       );
+                       path = Player;
+                       sourceTree = "<group>";
+               };
+               E54CDA41214FD2AC009D0127 /* Cells */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E54CDA44214FD87E009D0127 /* SimpleGalleryCollectionViewCell.swift */,
+                               E54CDA45214FD87E009D0127 /* SimpleGalleryCollectionViewCell.xib */,
+                       );
+                       path = Cells;
+                       sourceTree = "<group>";
+               };
+               E54CDA48214FF36D009D0127 /* Gallery */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E54CDA4B214FF3E7009D0127 /* Cells */,
+                               E54CDA49214FF3C8009D0127 /* GalleryViewController.swift */,
+                       );
+                       path = Gallery;
+                       sourceTree = "<group>";
+               };
+               E54CDA4B214FF3E7009D0127 /* Cells */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E54CDA42214FD2E9009D0127 /* GalleryCell.swift */,
+                       );
+                       path = Cells;
+                       sourceTree = "<group>";
+               };
+               E54CDA4C214FFA2A009D0127 /* Settings */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E54CDA4D214FFA3D009D0127 /* SettingsViewController.swift */,
+                       );
+                       path = Settings;
+                       sourceTree = "<group>";
+               };
+               E55BFD6E20E0F9060090F8BE /* Reader */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E55BFD6F20E0F9370090F8BE /* WLReaderConfig.swift */,
+                       );
+                       path = Reader;
+                       sourceTree = "<group>";
+               };
+               E55DA33520D914FF00F1D5F8 /* BookDetails */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E55DA33820D91DA900F1D5F8 /* Cells */,
+                               E55DA33620D9151E00F1D5F8 /* BookDetailsViewController.swift */,
+                       );
+                       path = BookDetails;
+                       sourceTree = "<group>";
+               };
+               E55DA33820D91DA900F1D5F8 /* Cells */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E53B7EA020DA8456007F88E0 /* BookDetailsButtonTableViewCell.swift */,
+                               E53B7EA120DA8456007F88E0 /* BookDetailsButtonTableViewCell.xib */,
+                               E53B7E9C20DA816E007F88E0 /* BookDetailsFragmentTableViewCell.swift */,
+                               E53B7E9D20DA816E007F88E0 /* BookDetailsFragmentTableViewCell.xib */,
+                               E55DA34220D933F700F1D5F8 /* BookDetailsSeparatorTableViewCell.swift */,
+                               E55DA34320D933F700F1D5F8 /* BookDetailsSeparatorTableViewCell.xib */,
+                               E55DA33E20D9338D00F1D5F8 /* BookDetailsInfoTableViewCell.swift */,
+                               E55DA33F20D9338D00F1D5F8 /* BookDetailsInfoTableViewCell.xib */,
+                               E55DA33920D91DD000F1D5F8 /* BookDetailsHeaderTableViewCell.swift */,
+                               E55DA33A20D91DD000F1D5F8 /* BookDetailsHeaderTableViewCell.xib */,
+                       );
+                       path = Cells;
+                       sourceTree = "<group>";
+               };
+               E565D28220BF504E00786E1F /* Filter */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5E5BAFA20CFDE5D00BE3795 /* Cells */,
+                               E565D28320BF506700786E1F /* FilterViewController.swift */,
+                               E5E5BAFD20CFE2A500BE3795 /* FilterHeaderView.swift */,
+                       );
+                       path = Filter;
+                       sourceTree = "<group>";
+               };
+               E56B2F1720D7CAB300671B24 /* Cells */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E56EBB9A2139369600DA3600 /* BecomeFriendTableViewCell.swift */,
+                               E56EBB9B2139369600DA3600 /* BecomeFriendTableViewCell.xib */,
+                               E56B2F1A20D7D74800671B24 /* LibraryEarlyAccessTableViewCell.swift */,
+                               E56B2F1B20D7D74800671B24 /* LibraryEarlyAccessTableViewCell.xib */,
+                               E56B2F1E20D7D75F00671B24 /* LibraryCollectionTableViewCell.swift */,
+                               E56B2F1F20D7D75F00671B24 /* LibraryCollectionTableViewCell.xib */,
+                               E56B2F2D20D80FA800671B24 /* BookCollectionViewCell.swift */,
+                               E56B2F2E20D80FA800671B24 /* BookCollectionViewCell.xib */,
+                       );
+                       path = Cells;
+                       sourceTree = "<group>";
+               };
+               E56B2F2220D7DABB00671B24 /* Views */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E56B2F2720D7ED3B00671B24 /* DesignableXibView.swift */,
+                               E56B2F2920D7FE8700671B24 /* BookImageOverlayView.swift */,
+                               E56B2F2B20D7FEA000671B24 /* BookImageOverlayView.xib */,
+                               E56B2F2320D7DAE500671B24 /* BookDescriptionView.swift */,
+                               E56B2F2520D7DB1F00671B24 /* BookDescriptionView.xib */,
+                       );
+                       name = Views;
+                       path = WolneLektury/Screens/Common/Views;
+                       sourceTree = SOURCE_ROOT;
+               };
+               E5727A1C20BD9BC200135BE4 /* Screens */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E52C13C420FA130F00480BF3 /* Authorization */,
+                               E58FADC82151A6D500CA6DD9 /* Login */,
+                               E58FADC52151A67400CA6DD9 /* SupportUs */,
+                               E58FADC221518CBD00CA6DD9 /* About */,
+                               E54CDA4C214FFA2A009D0127 /* Settings */,
+                               E506BA3E214D9F0400009699 /* NewsDetails */,
+                               E506BA32214D6DEC00009699 /* News */,
+                               E5914DBD2148FF1E004993C6 /* DownloadedList */,
+                               E5374D692142A80F00482DFA /* Player */,
+                               E575E2C821381DE60072BB45 /* BookList */,
+                               E55DA33520D914FF00F1D5F8 /* BookDetails */,
+                               E514EF6720D15C0E002D2031 /* Library */,
+                               E514EF6820D15C0E002D2031 /* Search */,
+                               E565D28220BF504E00786E1F /* Filter */,
+                               E5B3EF8220BDF22E007B0139 /* Common */,
+                               E5727A1D20BD9BEF00135BE4 /* Menu */,
+                       );
+                       path = Screens;
+                       sourceTree = "<group>";
+               };
+               E5727A1D20BD9BEF00135BE4 /* Menu */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5B3EF8520BDF890007B0139 /* Cells */,
+                               E5B3EF8020BDF0C5007B0139 /* MenuViewController.swift */,
+                               E52A477220BEDCC400998554 /* MenuItem.swift */,
+                       );
+                       path = Menu;
+                       sourceTree = "<group>";
+               };
+               E575E2C821381DE60072BB45 /* BookList */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E575E2C921381E060072BB45 /* BookListViewController.swift */,
+                       );
+                       path = BookList;
+                       sourceTree = "<group>";
+               };
+               E57B247120D1824100430DDC /* Cells */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E57B247220D1826A00430DDC /* SearchFilterCollectionViewCell.swift */,
+                               E57B247320D1826A00430DDC /* SearchFilterCollectionViewCell.xib */,
+                       );
+                       path = Cells;
+                       sourceTree = "<group>";
+               };
+               E58FADC221518CBD00CA6DD9 /* About */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E58FADC321518CD000CA6DD9 /* AboutViewController.swift */,
+                       );
+                       path = About;
+                       sourceTree = "<group>";
+               };
+               E58FADC52151A67400CA6DD9 /* SupportUs */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E58FADC62151A68E00CA6DD9 /* SupportUsViewController.swift */,
+                       );
+                       path = SupportUs;
+                       sourceTree = "<group>";
+               };
+               E58FADC82151A6D500CA6DD9 /* Login */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E58FADC92151A6E600CA6DD9 /* LoginViewController.swift */,
+                       );
+                       path = Login;
+                       sourceTree = "<group>";
+               };
+               E5914DBD2148FF1E004993C6 /* DownloadedList */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5914DBE2148FF33004993C6 /* DownloadedListViewController.swift */,
+                       );
+                       path = DownloadedList;
+                       sourceTree = "<group>";
+               };
+               E5920EB7212FFA8100118EE6 /* Realm */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5920EB8212FFA9300118EE6 /* RLMApplication.swift */,
+                               E5920EBA2130088200118EE6 /* RLMUser.swift */,
+                       );
+                       path = Realm;
+                       sourceTree = "<group>";
+               };
+               E5A17754212EF28D004F026E /* Utils */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5A17755212EF2A2004F026E /* SharedGlobals.swift */,
+                               E5D09FA1214803AA007DCB43 /* DatabaseManager.swift */,
+                       );
+                       path = Utils;
+                       sourceTree = "<group>";
+               };
+               E5AE0E9F206D31F600C61957 = {
+                       isa = PBXGroup;
+                       children = (
+                               E553129A2164EF5900B2D216 /* GoogleService-Info.plist */,
+                               E5F55E552189B55600D95421 /* Settings.bundle */,
+                               E5AE0EAA206D31F600C61957 /* WolneLektury */,
+                               E5AE0EBF206D31F700C61957 /* WolneLekturyTests */,
+                               E5AE0ECA206D31F700C61957 /* WolneLekturyUITests */,
+                               E5AE0EA9206D31F600C61957 /* Products */,
+                               060167A9F380BA2A2BA6491B /* Pods */,
+                               0F1561A5B3389A3D6F47162F /* Frameworks */,
+                       );
+                       sourceTree = "<group>";
+               };
+               E5AE0EA9206D31F600C61957 /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5AE0EA8206D31F600C61957 /* WolneLektury.app */,
+                               E5AE0EBC206D31F700C61957 /* WolneLekturyTests.xctest */,
+                               E5AE0EC7206D31F700C61957 /* WolneLekturyUITests.xctest */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               E5AE0EAA206D31F600C61957 /* WolneLektury */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E553129C2164F32600B2D216 /* WolneLektury.entitlements */,
+                               E5E5BAED20CFD46F00BE3795 /* Resources */,
+                               E5AE0EAB206D31F600C61957 /* AppDelegate.swift */,
+                               E5E89BC420F4C24F00A0A6C2 /* External */,
+                               E5AE0EAF206D31F600C61957 /* Main.storyboard */,
+                               E5AE0EB2206D31F600C61957 /* Assets.xcassets */,
+                               E5AE0EB4206D31F600C61957 /* LaunchScreen.storyboard */,
+                               E5AE0EB7206D31F600C61957 /* Info.plist */,
+                               E52A476420BEAE5900998554 /* Connection */,
+                               E5B3EF7E20BDEFB7007B0139 /* MainNavigator.swift */,
+                               E52A475920BE856800998554 /* Model */,
+                               E52A475720BE6DB700998554 /* Constants.swift */,
+                               E55BFD6E20E0F9060090F8BE /* Reader */,
+                               E52A476D20BEC0BD00998554 /* Config.swift */,
+                               E52A474420BE6BD500998554 /* Extensions */,
+                               E52A474320BE6AE500998554 /* Localizable.strings */,
+                               E5E5BB0B20CFF5D900BE3795 /* Controls */,
+                               E5727A1C20BD9BC200135BE4 /* Screens */,
+                               E5A17754212EF28D004F026E /* Utils */,
+                       );
+                       path = WolneLektury;
+                       sourceTree = "<group>";
+               };
+               E5AE0EBF206D31F700C61957 /* WolneLekturyTests */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5AE0EC0206D31F700C61957 /* WolneLekturyTests.swift */,
+                               E5AE0EC2206D31F700C61957 /* Info.plist */,
+                       );
+                       path = WolneLekturyTests;
+                       sourceTree = "<group>";
+               };
+               E5AE0ECA206D31F700C61957 /* WolneLekturyUITests */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5AE0ECB206D31F700C61957 /* WolneLekturyUITests.swift */,
+                               E5AE0ECD206D31F700C61957 /* Info.plist */,
+                       );
+                       path = WolneLekturyUITests;
+                       sourceTree = "<group>";
+               };
+               E5B3EF8220BDF22E007B0139 /* Common */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E56B2F2220D7DABB00671B24 /* Views */,
+                               E52A477C20BEEB1200998554 /* Cells */,
+                               E5B3EF8320BDF24D007B0139 /* MainNavigationViewController.swift */,
+                               E56B2F1820D7CC9600671B24 /* MainViewController.swift */,
+                               E506BA3C214D7FA000009699 /* ListViewController.swift */,
+                               E5365A25215A86BA00DEF847 /* WLViewController.swift */,
+                               E5A281CE215E6FFE000DCD83 /* WLSideMenuNavigationController.swift */,
+                               E5A281D0215E708C000DCD83 /* WLNavigationController.swift */,
+                       );
+                       path = Common;
+                       sourceTree = "<group>";
+               };
+               E5B3EF8520BDF890007B0139 /* Cells */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E52A477420BEE8F300998554 /* MenuLineTableViewCell.swift */,
+                               E52A477520BEE8F300998554 /* MenuLineTableViewCell.xib */,
+                               E5B3EF8620BDF8AD007B0139 /* MenuTableViewCell.swift */,
+                               E5B3EF8720BDF8AD007B0139 /* MenuTableViewCell.xib */,
+                               E52A478120BEF86800998554 /* MenuSupportUsTableViewCell.swift */,
+                               E52A478220BEF86800998554 /* MenuSupportUsTableViewCell.xib */,
+                               E52A477820BEE90A00998554 /* MenuBottomTableViewCell.swift */,
+                               E52A477920BEE90A00998554 /* MenuBottomTableViewCell.xib */,
+                       );
+                       path = Cells;
+                       sourceTree = "<group>";
+               };
+               E5E5BAED20CFD46F00BE3795 /* Resources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5E5BAEE20CFD46F00BE3795 /* Font */,
+                       );
+                       path = Resources;
+                       sourceTree = "<group>";
+               };
+               E5E5BAEE20CFD46F00BE3795 /* Font */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5E5BAEF20CFD46F00BE3795 /* Roboto */,
+                       );
+                       path = Font;
+                       sourceTree = "<group>";
+               };
+               E5E5BAEF20CFD46F00BE3795 /* Roboto */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5E5BAF020CFD46F00BE3795 /* Roboto-Medium.ttf */,
+                               E5E5BAF120CFD46F00BE3795 /* Roboto-Light.ttf */,
+                               E5E5BAF220CFD46F00BE3795 /* Roboto-Regular.ttf */,
+                               E5E5BAF320CFD46F00BE3795 /* Roboto-Bold.ttf */,
+                               E5E5BAF420CFD46F00BE3795 /* Roboto-Thin.ttf */,
+                       );
+                       path = Roboto;
+                       sourceTree = "<group>";
+               };
+               E5E5BAFA20CFDE5D00BE3795 /* Cells */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5E5BB0720CFE49500BE3795 /* FilterCollectionViewCell.swift */,
+                               E5E5BB0820CFE49500BE3795 /* FilterCollectionViewCell.xib */,
+                               E5E5BAFF20CFE44B00BE3795 /* FilterOnlyLecturesReusableView.swift */,
+                               E5E5BB0020CFE44B00BE3795 /* FilterOnlyLecturesReusableView.xib */,
+                               E5E5BB0320CFE46900BE3795 /* FilterSectionHeaderCollectionReusableView.swift */,
+                               E5E5BB0420CFE46900BE3795 /* FilterSectionHeaderCollectionReusableView.xib */,
+                       );
+                       path = Cells;
+                       sourceTree = "<group>";
+               };
+               E5E5BB0B20CFF5D900BE3795 /* Controls */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E5E5BB0C20CFF5FD00BE3795 /* ActivityIndicatorButton.swift */,
+                               E54D850E2154020C003B087E /* ProgressLabel.swift */,
+                               E54D850C2153F73F003B087E /* ProgressButton.swift */,
+                       );
+                       path = Controls;
+                       sourceTree = "<group>";
+               };
+               E5E89BC420F4C24F00A0A6C2 /* External */ = {
+                       isa = PBXGroup;
+                       children = (
+                       );
+                       path = External;
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+               E5AE0EA7206D31F600C61957 /* WolneLektury */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = E5AE0ED0206D31F700C61957 /* Build configuration list for PBXNativeTarget "WolneLektury" */;
+                       buildPhases = (
+                               C2F70426272AFB4A69A7A9D2 /* [CP] Check Pods Manifest.lock */,
+                               E5AE0EA4206D31F600C61957 /* Sources */,
+                               E5AE0EA5206D31F600C61957 /* Frameworks */,
+                               E5AE0EA6206D31F600C61957 /* Resources */,
+                               B28876BCAC1F711876A77D9A /* [CP] Embed Pods Frameworks */,
+                               E560F131215EC7D3004E9A71 /* ShellScript */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = WolneLektury;
+                       productName = WolneLektury;
+                       productReference = E5AE0EA8206D31F600C61957 /* WolneLektury.app */;
+                       productType = "com.apple.product-type.application";
+               };
+               E5AE0EBB206D31F700C61957 /* WolneLekturyTests */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = E5AE0ED3206D31F700C61957 /* Build configuration list for PBXNativeTarget "WolneLekturyTests" */;
+                       buildPhases = (
+                               E5AE0EB8206D31F700C61957 /* Sources */,
+                               E5AE0EB9206D31F700C61957 /* Frameworks */,
+                               E5AE0EBA206D31F700C61957 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               E5AE0EBE206D31F700C61957 /* PBXTargetDependency */,
+                       );
+                       name = WolneLekturyTests;
+                       productName = WolneLekturyTests;
+                       productReference = E5AE0EBC206D31F700C61957 /* WolneLekturyTests.xctest */;
+                       productType = "com.apple.product-type.bundle.unit-test";
+               };
+               E5AE0EC6206D31F700C61957 /* WolneLekturyUITests */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = E5AE0ED6206D31F700C61957 /* Build configuration list for PBXNativeTarget "WolneLekturyUITests" */;
+                       buildPhases = (
+                               E5AE0EC3206D31F700C61957 /* Sources */,
+                               E5AE0EC4206D31F700C61957 /* Frameworks */,
+                               E5AE0EC5206D31F700C61957 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               E5AE0EC9206D31F700C61957 /* PBXTargetDependency */,
+                       );
+                       name = WolneLekturyUITests;
+                       productName = WolneLekturyUITests;
+                       productReference = E5AE0EC7206D31F700C61957 /* WolneLekturyUITests.xctest */;
+                       productType = "com.apple.product-type.bundle.ui-testing";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               E5AE0EA0206D31F600C61957 /* Project object */ = {
+                       isa = PBXProject;
+                       attributes = {
+                               LastSwiftUpdateCheck = 0920;
+                               LastUpgradeCheck = 0930;
+                               ORGANIZATIONNAME = Lightcoding;
+                               TargetAttributes = {
+                                       E5AE0EA7206D31F600C61957 = {
+                                               CreatedOnToolsVersion = 9.2;
+                                               ProvisioningStyle = Manual;
+                                               SystemCapabilities = {
+                                                       com.apple.BackgroundModes = {
+                                                               enabled = 1;
+                                                       };
+                                                       com.apple.Push = {
+                                                               enabled = 1;
+                                                       };
+                                               };
+                                       };
+                                       E5AE0EBB206D31F700C61957 = {
+                                               CreatedOnToolsVersion = 9.2;
+                                               ProvisioningStyle = Automatic;
+                                               TestTargetID = E5AE0EA7206D31F600C61957;
+                                       };
+                                       E5AE0EC6206D31F700C61957 = {
+                                               CreatedOnToolsVersion = 9.2;
+                                               ProvisioningStyle = Automatic;
+                                               TestTargetID = E5AE0EA7206D31F600C61957;
+                                       };
+                               };
+                       };
+                       buildConfigurationList = E5AE0EA3206D31F600C61957 /* Build configuration list for PBXProject "WolneLektury" */;
+                       compatibilityVersion = "Xcode 8.0";
+                       developmentRegion = en;
+                       hasScannedForEncodings = 0;
+                       knownRegions = (
+                               en,
+                               Base,
+                       );
+                       mainGroup = E5AE0E9F206D31F600C61957;
+                       productRefGroup = E5AE0EA9206D31F600C61957 /* Products */;
+                       projectDirPath = "";
+                       projectRoot = "";
+                       targets = (
+                               E5AE0EA7206D31F600C61957 /* WolneLektury */,
+                               E5AE0EBB206D31F700C61957 /* WolneLekturyTests */,
+                               E5AE0EC6206D31F700C61957 /* WolneLekturyUITests */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+               E5AE0EA6206D31F600C61957 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E5F55E562189B55600D95421 /* Settings.bundle in Resources */,
+                               E55DA34120D9338D00F1D5F8 /* BookDetailsInfoTableViewCell.xib in Resources */,
+                               E57B247520D1826A00430DDC /* SearchFilterCollectionViewCell.xib in Resources */,
+                               E5E5BB0A20CFE49500BE3795 /* FilterCollectionViewCell.xib in Resources */,
+                               E5E5BAF920CFD46F00BE3795 /* Roboto-Thin.ttf in Resources */,
+                               E5E5BAF520CFD46F00BE3795 /* Roboto-Medium.ttf in Resources */,
+                               E5E5BAF820CFD46F00BE3795 /* Roboto-Bold.ttf in Resources */,
+                               E52A477720BEE8F300998554 /* MenuLineTableViewCell.xib in Resources */,
+                               E52A474120BE6AE500998554 /* Localizable.strings in Resources */,
+                               E553129B2164EF5900B2D216 /* GoogleService-Info.plist in Resources */,
+                               E506BA39214D6E5100009699 /* NewsTableViewCell.xib in Resources */,
+                               E55DA33C20D91DD000F1D5F8 /* BookDetailsHeaderTableViewCell.xib in Resources */,
+                               E56B2F2C20D7FEA000671B24 /* BookImageOverlayView.xib in Resources */,
+                               E5E5BAF620CFD46F00BE3795 /* Roboto-Light.ttf in Resources */,
+                               E56B2F2620D7DB1F00671B24 /* BookDescriptionView.xib in Resources */,
+                               E56EBB9D2139369600DA3600 /* BecomeFriendTableViewCell.xib in Resources */,
+                               E52A478020BEEB2800998554 /* WLTableViewCell.xib in Resources */,
+                               E56B2F2120D7D75F00671B24 /* LibraryCollectionTableViewCell.xib in Resources */,
+                               E5AE0EB6206D31F600C61957 /* LaunchScreen.storyboard in Resources */,
+                               E56B2F3020D80FA800671B24 /* BookCollectionViewCell.xib in Resources */,
+                               E57B247920D2649E00430DDC /* BookTableViewCell.xib in Resources */,
+                               E53B7E9F20DA816E007F88E0 /* BookDetailsFragmentTableViewCell.xib in Resources */,
+                               E5E5BAF720CFD46F00BE3795 /* Roboto-Regular.ttf in Resources */,
+                               E5AE0EB3206D31F600C61957 /* Assets.xcassets in Resources */,
+                               E52A477B20BEE90A00998554 /* MenuBottomTableViewCell.xib in Resources */,
+                               E5AE0EB1206D31F600C61957 /* Main.storyboard in Resources */,
+                               E55DA34520D933F700F1D5F8 /* BookDetailsSeparatorTableViewCell.xib in Resources */,
+                               E5E5BB0620CFE46900BE3795 /* FilterSectionHeaderCollectionReusableView.xib in Resources */,
+                               E52A478420BEF86800998554 /* MenuSupportUsTableViewCell.xib in Resources */,
+                               E53B7EA320DA8456007F88E0 /* BookDetailsButtonTableViewCell.xib in Resources */,
+                               E5B3EF8920BDF8AD007B0139 /* MenuTableViewCell.xib in Resources */,
+                               E54CDA47214FD87E009D0127 /* SimpleGalleryCollectionViewCell.xib in Resources */,
+                               E5E5BB0220CFE44B00BE3795 /* FilterOnlyLecturesReusableView.xib in Resources */,
+                               E56B2F1D20D7D74800671B24 /* LibraryEarlyAccessTableViewCell.xib in Resources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               E5AE0EBA206D31F700C61957 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               E5AE0EC5206D31F700C61957 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+               B28876BCAC1F711876A77D9A /* [CP] Embed Pods Frameworks */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputPaths = (
+                               "${SRCROOT}/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-frameworks.sh",
+                               "${BUILT_PRODUCTS_DIR}/AEXML/AEXML.framework",
+                               "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
+                               "${BUILT_PRODUCTS_DIR}/AlamofireActivityLogger/AlamofireActivityLogger.framework",
+                               "${BUILT_PRODUCTS_DIR}/FolioReaderKit/FolioReaderKit.framework",
+                               "${BUILT_PRODUCTS_DIR}/FontBlaster/FontBlaster.framework",
+                               "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
+                               "${BUILT_PRODUCTS_DIR}/JSQWebViewController/JSQWebViewController.framework",
+                               "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework",
+                               "${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework",
+                               "${BUILT_PRODUCTS_DIR}/MZDownloadManager/MZDownloadManager.framework",
+                               "${BUILT_PRODUCTS_DIR}/MatomoTracker/MatomoTracker.framework",
+                               "${BUILT_PRODUCTS_DIR}/MenuItemKit/MenuItemKit.framework",
+                               "${BUILT_PRODUCTS_DIR}/OAuthSwift/OAuthSwift.framework",
+                               "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework",
+                               "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework",
+                               "${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework",
+                               "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework",
+                               "${BUILT_PRODUCTS_DIR}/SideMenu/SideMenu.framework",
+                               "${BUILT_PRODUCTS_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework",
+                               "${BUILT_PRODUCTS_DIR}/Toast-Swift/Toast_Swift.framework",
+                               "${BUILT_PRODUCTS_DIR}/ZFDragableModalTransition/ZFDragableModalTransition.framework",
+                               "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
+                       );
+                       name = "[CP] Embed Pods Frameworks";
+                       outputPaths = (
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AEXML.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AlamofireActivityLogger.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FolioReaderKit.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FontBlaster.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JSQWebViewController.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MBProgressHUD.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MZDownloadManager.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MatomoTracker.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MenuItemKit.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OAuthSwift.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RealmSwift.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SideMenu.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftKeychainWrapper.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Toast_Swift.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZFDragableModalTransition.framework",
+                               "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-WolneLektury/Pods-WolneLektury-frameworks.sh\"\n";
+                       showEnvVarsInLog = 0;
+               };
+               C2F70426272AFB4A69A7A9D2 /* [CP] Check Pods Manifest.lock */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputPaths = (
+                               "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+                               "${PODS_ROOT}/Manifest.lock",
+                       );
+                       name = "[CP] Check Pods Manifest.lock";
+                       outputPaths = (
+                               "$(DERIVED_FILE_DIR)/Pods-WolneLektury-checkManifestLockResult.txt",
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+                       showEnvVarsInLog = 0;
+               };
+               E560F131215EC7D3004E9A71 /* ShellScript */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "\"${PODS_ROOT}/Fabric/run\" 67296ed66bf2e7658d0e3c71701e4fa0db8a85b0 796679c30fbaef26a0434d1bcdcd854c0738b19604d75509b89818e53eb5f6be";
+               };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+               E5AE0EA4206D31F600C61957 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E52A477F20BEEB2800998554 /* WLTableViewCell.swift in Sources */,
+                               E5B3EF8420BDF24D007B0139 /* MainNavigationViewController.swift in Sources */,
+                               E52A474E20BE6C7400998554 /* UIView+Ext.swift in Sources */,
+                               E506BA34214D6E0700009699 /* NewsViewController.swift in Sources */,
+                               E54CDA46214FD87E009D0127 /* SimpleGalleryCollectionViewCell.swift in Sources */,
+                               E52A476820BEAFC200998554 /* NetworkService.swift in Sources */,
+                               E52A474820BE6C3800998554 /* NSObject+Ext.swift in Sources */,
+                               E50D100920F393D5000FA5CA /* OAuthTokenModel.swift in Sources */,
+                               E5732B742155411900616C91 /* PlayerController.swift in Sources */,
+                               E52A474A20BE6C4F00998554 /* UIButton+Ext.swift in Sources */,
+                               E5920EB9212FFA9300118EE6 /* RLMApplication.swift in Sources */,
+                               E5B3EF8820BDF8AD007B0139 /* MenuTableViewCell.swift in Sources */,
+                               E52A474620BE6C2200998554 /* UIViewController+Ext.swift in Sources */,
+                               E52A475020BE6C8000998554 /* String+Ext.swift in Sources */,
+                               E52A475820BE6DB800998554 /* Constants.swift in Sources */,
+                               E5365A24215A82E100DEF847 /* MatomoTracker+Ext.swift in Sources */,
+                               E55DA34020D9338D00F1D5F8 /* BookDetailsInfoTableViewCell.swift in Sources */,
+                               E54D850D2153F73F003B087E /* ProgressButton.swift in Sources */,
+                               E57B247820D2649E00430DDC /* BookTableViewCell.swift in Sources */,
+                               E5365A26215A86BA00DEF847 /* WLViewController.swift in Sources */,
+                               E5A281CF215E6FFE000DCD83 /* WLSideMenuNavigationController.swift in Sources */,
+                               E5E89BC320F4BEFC00A0A6C2 /* Dictionary+Ext.swift in Sources */,
+                               E531C82B2137282B00655C12 /* ReadingStateModel.swift in Sources */,
+                               E57B247420D1826A00430DDC /* SearchFilterCollectionViewCell.swift in Sources */,
+                               E506BA38214D6E5100009699 /* NewsTableViewCell.swift in Sources */,
+                               E52C13C620FA132A00480BF3 /* AuthorizationWebViewController.swift in Sources */,
+                               E5732B7621560EF200616C91 /* PlayerItemTableViewCell.swift in Sources */,
+                               E514EF6C20D1679C002D2031 /* SearchViewController.swift in Sources */,
+                               E56B2F2F20D80FA800671B24 /* BookCollectionViewCell.swift in Sources */,
+                               E55BFD7020E0F9370090F8BE /* WLReaderConfig.swift in Sources */,
+                               E53B7EA220DA8456007F88E0 /* BookDetailsButtonTableViewCell.swift in Sources */,
+                               E52A474C20BE6C6200998554 /* UIImage+Ext.swift in Sources */,
+                               E5E5BB0920CFE49500BE3795 /* FilterCollectionViewCell.swift in Sources */,
+                               E5B3EF7F20BDEFB7007B0139 /* MainNavigator.swift in Sources */,
+                               E506BA3B214D737800009699 /* NewsModel.swift in Sources */,
+                               E54CDA43214FD2E9009D0127 /* GalleryCell.swift in Sources */,
+                               E52C13C820FA945D00480BF3 /* Credentials.swift in Sources */,
+                               E52A477620BEE8F300998554 /* MenuLineTableViewCell.swift in Sources */,
+                               E55DA33720D9151E00F1D5F8 /* BookDetailsViewController.swift in Sources */,
+                               E58FADCA2151A6E600CA6DD9 /* LoginViewController.swift in Sources */,
+                               E55DA33B20D91DD000F1D5F8 /* BookDetailsHeaderTableViewCell.swift in Sources */,
+                               E52A476A20BEBB6700998554 /* SyncManager.swift in Sources */,
+                               E52A475B20BE858B00998554 /* BookDetailsModel.swift in Sources */,
+                               E56EBB9C2139369600DA3600 /* BecomeFriendTableViewCell.swift in Sources */,
+                               E52A478320BEF86800998554 /* MenuSupportUsTableViewCell.swift in Sources */,
+                               E52A475D20BE879400998554 /* BookModel.swift in Sources */,
+                               E58F928820D4E63E00400375 /* SearchFiltersManager.swift in Sources */,
+                               E56B2F2020D7D75F00671B24 /* LibraryCollectionTableViewCell.swift in Sources */,
+                               E56B2F1920D7CC9600671B24 /* MainViewController.swift in Sources */,
+                               E55DA34420D933F700F1D5F8 /* BookDetailsSeparatorTableViewCell.swift in Sources */,
+                               E5AE0EAC206D31F600C61957 /* AppDelegate.swift in Sources */,
+                               E5920EBB2130088200118EE6 /* RLMUser.swift in Sources */,
+                               E5374D6B2142A83D00482DFA /* PlayerViewController.swift in Sources */,
+                               E52A475620BE6CD700998554 /* DateFormatter+Ext.swift in Sources */,
+                               E5A17756212EF2A2004F026E /* SharedGlobals.swift in Sources */,
+                               E54D850F2154020C003B087E /* ProgressLabel.swift in Sources */,
+                               E506BA40214DA31600009699 /* NewsDetailsViewController.swift in Sources */,
+                               E56B2F2A20D7FE8700671B24 /* BookImageOverlayView.swift in Sources */,
+                               E52A475420BE6CBC00998554 /* UITableView+Ext.swift in Sources */,
+                               E58FADC72151A68E00CA6DD9 /* SupportUsViewController.swift in Sources */,
+                               E56B2F1C20D7D74800671B24 /* LibraryEarlyAccessTableViewCell.swift in Sources */,
+                               E5A281D1215E708C000DCD83 /* WLNavigationController.swift in Sources */,
+                               E506BA3D214D7FA000009699 /* ListViewController.swift in Sources */,
+                               E52A475F20BE87A200998554 /* CategoryModel.swift in Sources */,
+                               E5E5BB0520CFE46900BE3795 /* FilterSectionHeaderCollectionReusableView.swift in Sources */,
+                               E54CDA4E214FFA3D009D0127 /* SettingsViewController.swift in Sources */,
+                               E52A476E20BEC0BD00998554 /* Config.swift in Sources */,
+                               E54CDA4A214FF3C8009D0127 /* GalleryViewController.swift in Sources */,
+                               E5D09FA2214803AA007DCB43 /* DatabaseManager.swift in Sources */,
+                               E514EF6A20D15C23002D2031 /* LibraryViewController.swift in Sources */,
+                               E52A477A20BEE90A00998554 /* MenuBottomTableViewCell.swift in Sources */,
+                               E5699A0C214081B900F0A94C /* LikeModel.swift in Sources */,
+                               E53B7E9E20DA816E007F88E0 /* BookDetailsFragmentTableViewCell.swift in Sources */,
+                               E52A476320BE87C900998554 /* MediaModel.swift in Sources */,
+                               E52A477320BEDCC400998554 /* MenuItem.swift in Sources */,
+                               E58FADC421518CD000CA6DD9 /* AboutViewController.swift in Sources */,
+                               E52A477120BED97200998554 /* FilterBooksParameters.swift in Sources */,
+                               E565D28420BF506800786E1F /* FilterViewController.swift in Sources */,
+                               E52A475220BE6C9400998554 /* NSAttributedString+Ext.swift in Sources */,
+                               E52A476C20BEBDD700998554 /* RestAction.swift in Sources */,
+                               E531C82D2137B1DE00655C12 /* UIColor+Ext.swift in Sources */,
+                               E5E5BB0D20CFF5FD00BE3795 /* ActivityIndicatorButton.swift in Sources */,
+                               E56B2F2420D7DAE500671B24 /* BookDescriptionView.swift in Sources */,
+                               E5E5BAFE20CFE2A500BE3795 /* FilterHeaderView.swift in Sources */,
+                               E5E5BB0120CFE44B00BE3795 /* FilterOnlyLecturesReusableView.swift in Sources */,
+                               E575E2CA21381E060072BB45 /* BookListViewController.swift in Sources */,
+                               E5920EB6212FF73B00118EE6 /* UsernameModel.swift in Sources */,
+                               E51273B420DCE02300BCEA45 /* DownloadManager.swift in Sources */,
+                               E52A476620BEAE7A00998554 /* Routes.swift in Sources */,
+                               E5B3EF8120BDF0C5007B0139 /* MenuViewController.swift in Sources */,
+                               E5914DBF2148FF33004993C6 /* DownloadedListViewController.swift in Sources */,
+                               E56B2F2820D7ED3B00671B24 /* DesignableXibView.swift in Sources */,
+                               E52A476120BE87BC00998554 /* FragmentModel.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               E5AE0EB8206D31F700C61957 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E5AE0EC1206D31F700C61957 /* WolneLekturyTests.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               E5AE0EC3206D31F700C61957 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               E5AE0ECC206D31F700C61957 /* WolneLekturyUITests.swift in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+               E5AE0EBE206D31F700C61957 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = E5AE0EA7206D31F600C61957 /* WolneLektury */;
+                       targetProxy = E5AE0EBD206D31F700C61957 /* PBXContainerItemProxy */;
+               };
+               E5AE0EC9206D31F700C61957 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = E5AE0EA7206D31F600C61957 /* WolneLektury */;
+                       targetProxy = E5AE0EC8206D31F700C61957 /* PBXContainerItemProxy */;
+               };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+               E52A474320BE6AE500998554 /* Localizable.strings */ = {
+                       isa = PBXVariantGroup;
+                       children = (
+                               E52A474220BE6AE500998554 /* en */,
+                       );
+                       name = Localizable.strings;
+                       sourceTree = "<group>";
+               };
+               E5AE0EAF206D31F600C61957 /* Main.storyboard */ = {
+                       isa = PBXVariantGroup;
+                       children = (
+                               E5AE0EB0206D31F600C61957 /* Base */,
+                       );
+                       name = Main.storyboard;
+                       sourceTree = "<group>";
+               };
+               E5AE0EB4206D31F600C61957 /* LaunchScreen.storyboard */ = {
+                       isa = PBXVariantGroup;
+                       children = (
+                               E5AE0EB5206D31F600C61957 /* Base */,
+                       );
+                       name = LaunchScreen.storyboard;
+                       sourceTree = "<group>";
+               };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+               E5AE0ECE206D31F700C61957 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CODE_SIGN_IDENTITY = "iPhone Developer";
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               ENABLE_TESTABILITY = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               ONLY_ACTIVE_ARCH = YES;
+                               SDKROOT = iphoneos;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+                               SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+                       };
+                       name = Debug;
+               };
+               E5AE0ECF206D31F700C61957 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CODE_SIGN_IDENTITY = "iPhone Developer";
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               ENABLE_NS_ASSERTIONS = NO;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               SDKROOT = iphoneos;
+                               SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+                               VALIDATE_PRODUCT = YES;
+                       };
+                       name = Release;
+               };
+               E5AE0ED1206D31F700C61957 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = 279BDA32FF0C4D2B7D20B0F8 /* Pods-WolneLektury.debug.xcconfig */;
+                       buildSettings = {
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+                               CODE_SIGN_ENTITLEMENTS = WolneLektury/WolneLektury.entitlements;
+                               CODE_SIGN_STYLE = Manual;
+                               DEVELOPMENT_TEAM = WX9PLY5GYU;
+                               INFOPLIST_FILE = WolneLektury/Info.plist;
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+                               PRODUCT_BUNDLE_IDENTIFIER = pl.wolnelektury.wolnelektury;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               PROVISIONING_PROFILE = "16994683-3da8-46ed-a812-a0a320a80d28";
+                               PROVISIONING_PROFILE_SPECIFIER = "WolneLektury Development";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                       };
+                       name = Debug;
+               };
+               E5AE0ED2206D31F700C61957 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = C6023269DAF53C83665BCEED /* Pods-WolneLektury.release.xcconfig */;
+                       buildSettings = {
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+                               CODE_SIGN_ENTITLEMENTS = WolneLektury/WolneLektury.entitlements;
+                               CODE_SIGN_IDENTITY = "iPhone Distribution";
+                               CODE_SIGN_STYLE = Manual;
+                               DEVELOPMENT_TEAM = WX9PLY5GYU;
+                               INFOPLIST_FILE = WolneLektury/Info.plist;
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+                               PRODUCT_BUNDLE_IDENTIFIER = pl.wolnelektury.wolnelektury;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               PROVISIONING_PROFILE = "532189bd-d181-4e31-9bf1-8d93e29efa64";
+                               PROVISIONING_PROFILE_SPECIFIER = "WolneLektury AdHoc";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                       };
+                       name = Release;
+               };
+               E5AE0ED4206D31F700C61957 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+                               BUNDLE_LOADER = "$(TEST_HOST)";
+                               CODE_SIGN_STYLE = Automatic;
+                               DEVELOPMENT_TEAM = MLSCCXBM5Z;
+                               INFOPLIST_FILE = WolneLekturyTests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               PRODUCT_BUNDLE_IDENTIFIER = com.moiseum.WolneLekturyTests;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WolneLektury.app/WolneLektury";
+                       };
+                       name = Debug;
+               };
+               E5AE0ED5206D31F700C61957 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+                               BUNDLE_LOADER = "$(TEST_HOST)";
+                               CODE_SIGN_STYLE = Automatic;
+                               DEVELOPMENT_TEAM = MLSCCXBM5Z;
+                               INFOPLIST_FILE = WolneLekturyTests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               PRODUCT_BUNDLE_IDENTIFIER = com.moiseum.WolneLekturyTests;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WolneLektury.app/WolneLektury";
+                       };
+                       name = Release;
+               };
+               E5AE0ED7206D31F700C61957 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+                               CODE_SIGN_STYLE = Automatic;
+                               DEVELOPMENT_TEAM = MLSCCXBM5Z;
+                               INFOPLIST_FILE = WolneLekturyUITests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               PRODUCT_BUNDLE_IDENTIFIER = com.moiseum.WolneLekturyUITests;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               TEST_TARGET_NAME = WolneLektury;
+                       };
+                       name = Debug;
+               };
+               E5AE0ED8206D31F700C61957 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+                               CODE_SIGN_STYLE = Automatic;
+                               DEVELOPMENT_TEAM = MLSCCXBM5Z;
+                               INFOPLIST_FILE = WolneLekturyUITests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               PRODUCT_BUNDLE_IDENTIFIER = com.moiseum.WolneLekturyUITests;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SWIFT_VERSION = 4.0;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               TEST_TARGET_NAME = WolneLektury;
+                       };
+                       name = Release;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               E5AE0EA3206D31F600C61957 /* Build configuration list for PBXProject "WolneLektury" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               E5AE0ECE206D31F700C61957 /* Debug */,
+                               E5AE0ECF206D31F700C61957 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               E5AE0ED0206D31F700C61957 /* Build configuration list for PBXNativeTarget "WolneLektury" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               E5AE0ED1206D31F700C61957 /* Debug */,
+                               E5AE0ED2206D31F700C61957 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               E5AE0ED3206D31F700C61957 /* Build configuration list for PBXNativeTarget "WolneLekturyTests" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               E5AE0ED4206D31F700C61957 /* Debug */,
+                               E5AE0ED5206D31F700C61957 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               E5AE0ED6206D31F700C61957 /* Build configuration list for PBXNativeTarget "WolneLekturyUITests" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               E5AE0ED7206D31F700C61957 /* Debug */,
+                               E5AE0ED8206D31F700C61957 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = E5AE0EA0206D31F600C61957 /* Project object */;
+}
diff --git a/iOS/WolneLektury.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iOS/WolneLektury.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644 (file)
index 0000000..127d49f
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:WolneLektury.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/iOS/WolneLektury.xcodeproj/project.xcworkspace/xcuserdata/pawel.xcuserdatad/UserInterfaceState.xcuserstate b/iOS/WolneLektury.xcodeproj/project.xcworkspace/xcuserdata/pawel.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644 (file)
index 0000000..f9e8f47
Binary files /dev/null and b/iOS/WolneLektury.xcodeproj/project.xcworkspace/xcuserdata/pawel.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/iOS/WolneLektury.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/WolneLektury.xcscheme b/iOS/WolneLektury.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/WolneLektury.xcscheme
new file mode 100644 (file)
index 0000000..876e615
--- /dev/null
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0940"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "E5AE0EA7206D31F600C61957"
+               BuildableName = "WolneLektury.app"
+               BlueprintName = "WolneLektury"
+               ReferencedContainer = "container:WolneLektury.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "E5AE0EBB206D31F700C61957"
+               BuildableName = "WolneLekturyTests.xctest"
+               BlueprintName = "WolneLekturyTests"
+               ReferencedContainer = "container:WolneLektury.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "E5AE0EC6206D31F700C61957"
+               BuildableName = "WolneLekturyUITests.xctest"
+               BlueprintName = "WolneLekturyUITests"
+               ReferencedContainer = "container:WolneLektury.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "E5AE0EA7206D31F600C61957"
+            BuildableName = "WolneLektury.app"
+            BlueprintName = "WolneLektury"
+            ReferencedContainer = "container:WolneLektury.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "E5AE0EA7206D31F600C61957"
+            BuildableName = "WolneLektury.app"
+            BlueprintName = "WolneLektury"
+            ReferencedContainer = "container:WolneLektury.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "E5AE0EA7206D31F600C61957"
+            BuildableName = "WolneLektury.app"
+            BlueprintName = "WolneLektury"
+            ReferencedContainer = "container:WolneLektury.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/WolneLektury.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/WolneLektury_release.xcscheme b/iOS/WolneLektury.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/WolneLektury_release.xcscheme
new file mode 100644 (file)
index 0000000..430b00e
--- /dev/null
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0940"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "E5AE0EA7206D31F600C61957"
+               BuildableName = "WolneLektury.app"
+               BlueprintName = "WolneLektury"
+               ReferencedContainer = "container:WolneLektury.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "E5AE0EBB206D31F700C61957"
+               BuildableName = "WolneLekturyTests.xctest"
+               BlueprintName = "WolneLekturyTests"
+               ReferencedContainer = "container:WolneLektury.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "E5AE0EC6206D31F700C61957"
+               BuildableName = "WolneLekturyUITests.xctest"
+               BlueprintName = "WolneLekturyUITests"
+               ReferencedContainer = "container:WolneLektury.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "E5AE0EA7206D31F600C61957"
+            BuildableName = "WolneLektury.app"
+            BlueprintName = "WolneLektury"
+            ReferencedContainer = "container:WolneLektury.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Release"
+      selectedDebuggerIdentifier = ""
+      selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "E5AE0EA7206D31F600C61957"
+            BuildableName = "WolneLektury.app"
+            BlueprintName = "WolneLektury"
+            ReferencedContainer = "container:WolneLektury.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "E5AE0EA7206D31F600C61957"
+            BuildableName = "WolneLektury.app"
+            BlueprintName = "WolneLektury"
+            ReferencedContainer = "container:WolneLektury.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/iOS/WolneLektury.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/xcschememanagement.plist b/iOS/WolneLektury.xcodeproj/xcuserdata/pawel.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644 (file)
index 0000000..418e0b2
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>SchemeUserState</key>
+       <dict>
+               <key>WolneLektury.xcscheme</key>
+               <dict>
+                       <key>orderHint</key>
+                       <integer>19</integer>
+               </dict>
+               <key>WolneLektury_release.xcscheme</key>
+               <dict>
+                       <key>orderHint</key>
+                       <integer>20</integer>
+               </dict>
+       </dict>
+       <key>SuppressBuildableAutocreation</key>
+       <dict>
+               <key>E5AE0EA7206D31F600C61957</key>
+               <dict>
+                       <key>primary</key>
+                       <true/>
+               </dict>
+               <key>E5AE0EBB206D31F700C61957</key>
+               <dict>
+                       <key>primary</key>
+                       <true/>
+               </dict>
+               <key>E5AE0EC6206D31F700C61957</key>
+               <dict>
+                       <key>primary</key>
+                       <true/>
+               </dict>
+       </dict>
+</dict>
+</plist>
diff --git a/iOS/WolneLektury.xcworkspace/contents.xcworkspacedata b/iOS/WolneLektury.xcworkspace/contents.xcworkspacedata
new file mode 100644 (file)
index 0000000..f464865
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:WolneLektury.xcodeproj">
+   </FileRef>
+   <FileRef
+      location = "group:Pods/Pods.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/IDEFindNavigatorScopes.plist b/iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/IDEFindNavigatorScopes.plist
new file mode 100644 (file)
index 0000000..5dd5da8
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<array/>
+</plist>
diff --git a/iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/UserInterfaceState.xcuserstate b/iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644 (file)
index 0000000..a52ddc4
Binary files /dev/null and b/iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644 (file)
index 0000000..359511b
--- /dev/null
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Bucket
+   type = "0"
+   version = "2.0">
+   <Breakpoints>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "Yes"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Pods/FolioReaderKit/Source/Extensions.swift"
+            timestampString = "561589149.444455"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "499"
+            endingLineNumber = "499"
+            landmarkName = "preferredStatusBarStyle"
+            landmarkType = "24">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "Yes"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Pods/OAuthSwift/Sources/OAuthSwiftHTTPRequest.swift"
+            timestampString = "552830002.730597"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "250"
+            endingLineNumber = "250"
+            landmarkName = "setupRequestForOAuth(request:parameters:dataEncoding:body:paramsLocation:)"
+            landmarkType = "7">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "Yes"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Pods/OAuthSwift/Sources/OAuthWebViewController.swift"
+            timestampString = "553292994.6033829"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "88"
+            endingLineNumber = "88"
+            landmarkName = "doHandle(_:)"
+            landmarkType = "7">
+            <Locations>
+               <Location
+                  shouldBeEnabled = "Yes"
+                  ignoreCount = "0"
+                  continueAfterRunningActions = "No"
+                  symbolName = "OAuthSwift.OAuthWebViewController.doHandle(Foundation.URL) -&gt; ()"
+                  moduleName = "OAuthSwift"
+                  usesParentBreakpointCondition = "Yes"
+                  urlString = "file:///Users/pawel/Documents/DEV/WolneLekturyIOS/WolneLektury/Pods/OAuthSwift/Sources/OAuthWebViewController.swift"
+                  timestampString = "567099438.432732"
+                  startingColumnNumber = "9223372036854775807"
+                  endingColumnNumber = "9223372036854775807"
+                  startingLineNumber = "88"
+                  endingLineNumber = "88"
+                  offsetFromSymbolStart = "75">
+               </Location>
+               <Location
+                  shouldBeEnabled = "Yes"
+                  ignoreCount = "0"
+                  continueAfterRunningActions = "No"
+                  symbolName = "closure #1 () -&gt; () in OAuthSwift.OAuthWebViewController.doHandle(Foundation.URL) -&gt; ()"
+                  moduleName = "OAuthSwift"
+                  usesParentBreakpointCondition = "Yes"
+                  urlString = "file:///Users/pawel/Documents/DEV/WolneLekturyIOS/WolneLektury/Pods/OAuthSwift/Sources/OAuthWebViewController.swift"
+                  timestampString = "567099438.4356149"
+                  startingColumnNumber = "9223372036854775807"
+                  endingColumnNumber = "9223372036854775807"
+                  startingLineNumber = "88"
+                  endingLineNumber = "88"
+                  offsetFromSymbolStart = "4">
+               </Location>
+            </Locations>
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "Yes"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Pods/OAuthSwift/Sources/OAuthWebViewController.swift"
+            timestampString = "553292994.603452"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "76"
+            endingLineNumber = "76"
+            landmarkName = "handle(_:)"
+            landmarkType = "7">
+            <Locations>
+               <Location
+                  shouldBeEnabled = "Yes"
+                  ignoreCount = "0"
+                  continueAfterRunningActions = "No"
+                  symbolName = "OAuthSwift.OAuthWebViewController.handle(Foundation.URL) -&gt; ()"
+                  moduleName = "OAuthSwift"
+                  usesParentBreakpointCondition = "Yes"
+                  urlString = "file:///Users/pawel/Documents/DEV/WolneLekturyIOS/WolneLektury/Pods/OAuthSwift/Sources/OAuthWebViewController.swift"
+                  timestampString = "567099438.439404"
+                  startingColumnNumber = "9223372036854775807"
+                  endingColumnNumber = "9223372036854775807"
+                  startingLineNumber = "77"
+                  endingLineNumber = "77"
+                  offsetFromSymbolStart = "18">
+               </Location>
+               <Location
+                  shouldBeEnabled = "Yes"
+                  ignoreCount = "0"
+                  continueAfterRunningActions = "No"
+                  symbolName = "closure #1 () -&gt; () in OAuthSwift.OAuthWebViewController.handle(Foundation.URL) -&gt; ()"
+                  moduleName = "OAuthSwift"
+                  usesParentBreakpointCondition = "Yes"
+                  urlString = "file:///Users/pawel/Documents/DEV/WolneLekturyIOS/WolneLektury/Pods/OAuthSwift/Sources/OAuthWebViewController.swift"
+                  timestampString = "567099438.441412"
+                  startingColumnNumber = "9223372036854775807"
+                  endingColumnNumber = "9223372036854775807"
+                  startingLineNumber = "77"
+                  endingLineNumber = "77"
+                  offsetFromSymbolStart = "4">
+               </Location>
+            </Locations>
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "WolneLektury/Screens/BookDetails/Cells/BookDetailsButtonTableViewCell.swift"
+            timestampString = "567099429.128668"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "105"
+            endingLineNumber = "105"
+            landmarkName = "setup(bookDetailsButtonType:progress:bookDetailsModel:)"
+            landmarkType = "7">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "Yes"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Pods/FolioReaderKit/Source/Resources/Bridge.js"
+            timestampString = "561473530.657325"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "200"
+            endingLineNumber = "200"
+            landmarkName = "getAnchorOffset"
+            landmarkType = "9">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Pods/FolioReaderKit/Source/FolioReaderCenter.swift"
+            timestampString = "563802944.88905"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "148"
+            endingLineNumber = "148"
+            landmarkName = "viewDidLoad()"
+            landmarkType = "7">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "Yes"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Pods/FolioReaderKit/Source/FolioReaderWebView.swift"
+            timestampString = "567099429.128971"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "74"
+            endingLineNumber = "74"
+            landmarkName = "share(_:)"
+            landmarkType = "7">
+         </BreakpointContent>
+      </BreakpointProxy>
+   </Breakpoints>
+</Bucket>
diff --git a/iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/xcdebugger/Expressions.xcexplist b/iOS/WolneLektury.xcworkspace/xcuserdata/pawel.xcuserdatad/xcdebugger/Expressions.xcexplist
new file mode 100644 (file)
index 0000000..abdb0ed
--- /dev/null
@@ -0,0 +1,219 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VariablesViewState
+   version = "1.0">
+   <ContextStates>
+      <ContextState
+         contextName = "FolioReaderPage.webViewDidFinishLoad(_:):FolioReaderPage.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "self.folioReader.readerCenter?.pageScrollDirection">
+            </PersistentString>
+            <PersistentString
+               value = "self.folioReader.needsRTLChange">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "FolioReaderPage.refreshPageMode():FolioReaderPage.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "webView.frame.width">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "BookDetailsViewController.viewDidLoad():BookDetailsViewController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "UIApplication.shared.statusBarFrame.size.height">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "FolioReaderPage.webViewFrame():FolioReaderPage.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "bounds.origin.x">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "BookDetailsModel.getAuthor():BookDetailsModel.swift">
+      </ContextState>
+      <ContextState
+         contextName = "RLMApplication.addBookToDownloaded(bookDetailsModel:):RLMApplication.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "authors.count">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "MenuViewController.viewDidLoad():MenuViewController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "tableView.contentOffset">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "FilterViewController.getSection(filterSection:):FilterViewController.swift">
+      </ContextState>
+      <ContextState
+         contextName = "FilterViewController.collectionView(_:didSelectItemAt:):FilterViewController.swift">
+      </ContextState>
+      <ContextState
+         contextName = "closure #1 in OAuthSwiftHTTPRequest.start(success:failure:):OAuthSwiftHTTPRequest.swift">
+      </ContextState>
+      <ContextState
+         contextName = "BookDetailsInfoTableViewCell.awakeFromNib():BookDetailsInfoTableViewCell.swift">
+      </ContextState>
+      <ContextState
+         contextName = "BookDetailsViewController.tableView(_:cellForRowAt:):BookDetailsViewController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "tableView.frame">
+            </PersistentString>
+            <PersistentString
+               value = "tableView.contentInset">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "SearchViewController.setupSearchBar():SearchViewController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "navigationItem.titleView.bounds">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "closure #1 in FolioReader.nightMode.setter:FolioReaderKit.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "self.readerContainer?.readerConfig.nightModeBackground.cgColor.components">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "NetworkService.urlStringWithRequestTokenParameters(action:):NetworkService.swift">
+      </ContextState>
+      <ContextState
+         contextName = "SearchViewController.filtersChanged(reloadData:):SearchViewController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = " filterTopConstraint.constant">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "FilterSectionHeaderCollectionReusableView.refreshButtonAction(_:):FilterSectionHeaderCollectionReusableView.swift">
+      </ContextState>
+      <ContextState
+         contextName = "closure #2 in AppDelegate.application(_:didFinishLaunchingWithOptions:):AppDelegate.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "closure #1 in BookDetailsModel.getAuthor():BookDetailsModel.swift">
+      </ContextState>
+      <ContextState
+         contextName = "BookDetailsHeaderTableViewCell.setup(bookModel:):BookDetailsHeaderTableViewCell.swift">
+      </ContextState>
+      <ContextState
+         contextName = "AppDelegate.applicationHandle(url:):AppDelegate.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "url.host">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "SearchViewController.singleTap(sender:):SearchViewController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "navigationItem.titleView.bounds">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "OAuthSwiftHTTPRequest.start(success:failure:):OAuthSwiftHTTPRequest.swift">
+      </ContextState>
+      <ContextState
+         contextName = "PlayerController.saveCurrentTrackNumber():PlayerController.swift">
+      </ContextState>
+      <ContextState
+         contextName = "closure #1 in FilterViewController.getKinds():FilterViewController.swift">
+      </ContextState>
+      <ContextState
+         contextName = "PlayerController.changePlaybackPositionCommand(event:):PlayerController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "event.positionTime">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "LeftAlignedCollectionViewFlowLayout.layoutAttributesForElements(in:):FilterViewController.swift">
+      </ContextState>
+      <ContextState
+         contextName = "LibraryViewController.viewDidLoad():LibraryViewController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "navigationController">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "SearchViewController.collectionView(_:numberOfItemsInSection:):SearchViewController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "numberOfFilters()">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "BookDescriptionView.setup(bookDetailsModel:):BookDescriptionView.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "bookDetailsModel.totalChapters">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "OAuth1Swift.postOAuthRequestToken(callbackURL:success:failure:):OAuth1Swift.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "self.requestTokenUrl">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "PlayerController.startTimer():PlayerController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "timer.isValid">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "OAuthSwiftCredential.signature(method:url:parameters:):OAuthSwiftCredential.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "url.absoluteString">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+      <ContextState
+         contextName = "MenuViewController.refreshBottomView():MenuViewController.swift">
+         <PersistentStrings>
+            <PersistentString
+               value = "appDelegate.rlmApplication">
+            </PersistentString>
+         </PersistentStrings>
+      </ContextState>
+   </ContextStates>
+</VariablesViewState>
diff --git a/iOS/WolneLektury/AppDelegate.swift b/iOS/WolneLektury/AppDelegate.swift
new file mode 100644 (file)
index 0000000..8df6ce5
--- /dev/null
@@ -0,0 +1,354 @@
+//
+//  AppDelegate.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 29/03/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import OAuthSwift
+import SafariServices
+import MBProgressHUD
+import MatomoTracker
+import Fabric
+import Crashlytics
+import Firebase
+import UserNotifications
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+    
+    var window: UIWindow?
+    var windowHud: MBProgressHUD?
+    var mainNavigator: MainNavigator!
+    var syncManager = SyncManager()
+    weak var sideMenuNavigationController: UINavigationController?
+    weak var safariParentViewController: UIViewController?
+    let gcmMessageIDKey = "gcm.message_id"
+
+    func showWindowHud(){
+        
+        guard let window = window else {return}
+        
+        if let windowHud = windowHud {
+            windowHud.show(animated: true)
+        }
+        else{
+            windowHud = MBProgressHUD.showAdded(to: window, animated: true)
+        }
+    }
+    
+    func hideWindowHud(){
+        windowHud?.hide(animated: true)
+    }
+    
+    var backgroundSessionCompletionHandler : (() -> Void)?    
+
+    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
+        // Override point for customization after application launch.
+        
+        Fabric.with([Crashlytics.self])
+        FirebaseApp.configure()
+
+        mainNavigator = MainNavigator(window: window!)
+        setCustomAppearance()
+        
+        if syncManager.isLoggedIn(){
+            fetchUsername()
+        }
+        
+//        configureMessaging(application: application)
+        
+        return true
+    }
+    
+    func configureMessaging(application: UIApplication) {
+        // [START set_messaging_delegate]
+        Messaging.messaging().delegate = self
+        // [END set_messaging_delegate]
+        
+        // Register for remote notifications. This shows a permission dialog on first run, to
+        // show the dialog at a more appropriate time move this registration accordingly.
+        // [START register_for_notifications]
+        if #available(iOS 10.0, *) {
+            // For iOS 10 display notification (sent via APNS)
+            UNUserNotificationCenter.current().delegate = self
+            
+            let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
+            UNUserNotificationCenter.current().requestAuthorization(
+                options: authOptions,
+                completionHandler: {_, _ in })
+        } else {
+            let settings: UIUserNotificationSettings =
+                UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
+            application.registerUserNotificationSettings(settings)
+        }
+        
+        application.registerForRemoteNotifications()
+        
+        // [END register_for_notifications]
+    }
+    
+    func setCustomAppearance() {
+        
+        UINavigationBar.appearance().isTranslucent = false
+        UINavigationBar.appearance().tintColor = .white
+        UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]
+        UINavigationBar.appearance().barTintColor = Constants.Colors.navbarBgColor()
+        UINavigationBar.appearance().barStyle = .blackOpaque
+        UIApplication.shared.statusBarStyle = .lightContent
+    }
+    
+    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 invalidate graphics rendering callbacks. 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 active state; here you can undo many of the changes made on entering the background.
+    }
+
+    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.
+        mainNavigator.updateSettingsViewIfPresented()
+    }
+
+    func applicationWillTerminate(_ application: UIApplication) {
+        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+    }
+    
+    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
+        backgroundSessionCompletionHandler = completionHandler
+    }
+    
+    func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
+        
+        applicationHandle(url: url)
+        return true
+    }
+    
+    private func applicationHandle(url: URL) {
+        if url.host == Constants.callbackOauthHost {
+            showWindowHud()
+            syncManager.accessToken { (result) in
+                self.hideWindowHud()
+                switch result {
+                case .success(let model):
+                    self.syncManager.updateUserCredentials(oAuthTokenModel: (model as! OAuthTokenModel))
+                    self.safariParentViewController?.presentedViewController?.dismiss(animated: true, completion: nil)
+                    self.mainNavigator.presentLibrary(dismissSideMenu: true)
+                    self.fetchUsername()
+                    break
+                case .failure/*(let error)*/:
+                    break
+                }
+            }
+        }
+        else if url.host == Constants.callbackPaypalSuccessHost {
+            self.safariParentViewController?.presentedViewController?.dismiss(animated: true, completion: nil)
+            safariParentViewController?.presentToast(message: "premium_purchase_succeeded".localized)
+            DatabaseManager.shared.setUserPremium()
+            mainNavigator.reset()
+        }
+        else if url.host == Constants.callbackPaypalErrorHost {
+            self.safariParentViewController?.presentedViewController?.dismiss(animated: true, completion: nil)
+            safariParentViewController?.presentToast(message: "premium_purchase_failed".localized)
+        }
+    }
+    
+    private func fetchUsername(){
+        showWindowHud()
+        self.syncManager.getUsername(completionHandler: { (result) in
+            self.hideWindowHud()
+            switch result {
+            case .success(let model):
+                DatabaseManager.shared.updateUser(usernameModel: model as? UsernameModel)
+                self.mainNavigator.setLoggedIn()
+                break
+            case .failure/*(let error)*/:
+                //We can download username later
+                self.mainNavigator.setLoggedIn()
+                break
+            }
+        })
+    }
+    
+    func login(fromViewController: UIViewController){
+        let oauthswift = OAuth1Swift(
+            consumerKey:    Config.CONSUMER_KEY,
+            consumerSecret: Config.CONSUMER_SECRET,
+            requestTokenUrl: Config.OAUTH_REQUEST_TOKEN,
+            authorizeUrl:    Config.OAUTH_AUTHORIZE,
+            accessTokenUrl:  Config.OAUTH_ACCESS_TOKEN
+        )
+        
+        safariParentViewController = fromViewController
+        
+        let handler = SafariURLHandler(viewController: fromViewController, oauthSwift: oauthswift)
+        handler.presentCompletion = {
+            print("Safari presented")
+        }
+        handler.dismissCompletion = {
+            print("Safari dismissed")
+        }
+        handler.factory = { url in
+            let controller = SFSafariViewController(url: url)
+            // Customize it, for instance
+            if #available(iOS 10.0, *) {
+                //  controller.preferredBarTintColor = UIColor.red
+            }
+            return controller
+        }
+        
+        oauthswift.authorizeURLHandler = handler
+        showWindowHud()
+        syncManager.requestToken { (result) in
+            self.hideWindowHud()
+            switch result {
+            case .success(let model):
+                let url = URL(string: String(format: Constants.authorizationUrlFormat, (model as! OAuthTokenModel).token))
+                oauthswift.authorizeURLHandler.handle(url!)
+                break
+            case .failure/*(let error)*/:
+                break
+            }
+        }
+    }
+    
+    func presentPaypal(fromViewController: UIViewController) {
+        
+        let controller = SFSafariViewController(url: URL(string: Constants.webPaypalFormUrl)!)
+        safariParentViewController = fromViewController
+        fromViewController.present(controller, animated: true, completion: nil)
+    }
+    
+    
+    // ############################################
+    // MARK: - Messaging
+    // ############################################
+    
+    // [START receive_message]
+    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
+        // If you are receiving a notification message while your app is in the background,
+        // this callback will not be fired till the user taps on the notification launching the application.
+        // TODO: Handle data of notification
+        
+        // With swizzling disabled you must let Messaging know about the message, for Analytics
+        // Messaging.messaging().appDidReceiveMessage(userInfo)
+        
+        // Print message ID.
+        if let messageID = userInfo[gcmMessageIDKey] {
+            print("Message ID: \(messageID)")
+        }
+        
+        // Print full message.
+        print(userInfo)
+    }
+    
+    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
+                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
+        // If you are receiving a notification message while your app is in the background,
+        // this callback will not be fired till the user taps on the notification launching the application.
+        // TODO: Handle data of notification
+        
+        // With swizzling disabled you must let Messaging know about the message, for Analytics
+        // Messaging.messaging().appDidReceiveMessage(userInfo)
+        
+        // Print message ID.
+        if let messageID = userInfo[gcmMessageIDKey] {
+            print("Message ID: \(messageID)")
+        }
+        
+        // Print full message.
+        print(userInfo)
+        
+        completionHandler(UIBackgroundFetchResult.newData)
+    }
+    // [END receive_message]
+    
+    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
+        print("Unable to register for remote notifications: \(error.localizedDescription)")
+    }
+    
+    // This function is added here only for debugging purposes, and can be removed if swizzling is enabled.
+    // If swizzling is disabled then this function must be implemented so that the APNs token can be paired to
+    // the FCM registration token.
+    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
+        print("APNs token retrieved: \(deviceToken)")
+        
+        // With swizzling disabled you must set the APNs token here.
+        // Messaging.messaging().apnsToken = deviceToken
+    }
+}
+
+// [START ios_10_message_handling]
+@available(iOS 10, *)
+extension AppDelegate : UNUserNotificationCenterDelegate {
+    
+    // Receive displayed notifications for iOS 10 devices.
+    func userNotificationCenter(_ center: UNUserNotificationCenter,
+                                willPresent notification: UNNotification,
+                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
+        let userInfo = notification.request.content.userInfo
+        
+        // With swizzling disabled you must let Messaging know about the message, for Analytics
+        // Messaging.messaging().appDidReceiveMessage(userInfo)
+        
+        // Print message ID.
+        if let messageID = userInfo[gcmMessageIDKey] {
+            print("Message ID: \(messageID)")
+        }
+        
+        // Print full message.
+        print(userInfo)
+        
+        // Change this to your preferred presentation option
+        completionHandler([])
+    }
+    
+    func userNotificationCenter(_ center: UNUserNotificationCenter,
+                                didReceive response: UNNotificationResponse,
+                                withCompletionHandler completionHandler: @escaping () -> Void) {
+        let userInfo = response.notification.request.content.userInfo
+        // Print message ID.
+        if let messageID = userInfo[gcmMessageIDKey] {
+            print("Message ID: \(messageID)")
+        }
+        
+        // Print full message.
+        print(userInfo)
+        
+        completionHandler()
+    }
+}
+// [END ios_10_message_handling]
+
+
+extension AppDelegate : MessagingDelegate {
+    // [START refresh_token]
+    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
+        print("Firebase registration token: \(fcmToken)")
+        
+        let dataDict:[String: String] = ["token": fcmToken]
+        
+        Messaging.messaging().subscribe(toTopic: "wolnelektury")
+//        NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
+        // TODO: If necessary send token to application server.
+        // Note: This callback is fired at each app startup and whenever a new token is generated.
+    }
+    // [END refresh_token]
+    
+    // [START ios_10_data_message]
+    // Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
+    // To enable direct data messages, you can set Messaging.messaging().shouldEstablishDirectChannel to true.
+    func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
+        print("Received data message: \(remoteMessage.appData)")
+    }
+    // [END ios_10_data_message]
+}
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Contents.json b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644 (file)
index 0000000..0c7e6d8
--- /dev/null
@@ -0,0 +1,116 @@
+{
+  "images" : [
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "Icon-Spotlight-40.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.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-Spotlight-40 copy.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-42.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-Spotlight-40@2x-1.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"
+    },
+    {
+      "size" : "1024x1024",
+      "idiom" : "ios-marketing",
+      "filename" : "iTunesArtwork@2x.png",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60.png
new file mode 100644 (file)
index 0000000..ca3c53d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
new file mode 100644 (file)
index 0000000..4905f8e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
new file mode 100644 (file)
index 0000000..5c34634
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-76.png
new file mode 100644 (file)
index 0000000..3360e19
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-76.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
new file mode 100644 (file)
index 0000000..1d0af7e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
new file mode 100644 (file)
index 0000000..e37c7d0
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png
new file mode 100644 (file)
index 0000000..cc29f07
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
new file mode 100644 (file)
index 0000000..cc29f07
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
new file mode 100644 (file)
index 0000000..87e6b2c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40 copy.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40 copy.png
new file mode 100644 (file)
index 0000000..ae01058
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40 copy.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png
new file mode 100644 (file)
index 0000000..6c3de9e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png
new file mode 100644 (file)
index 0000000..6aa0b54
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png
new file mode 100644 (file)
index 0000000..6aa0b54
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png
new file mode 100644 (file)
index 0000000..1e2b3c6
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-41.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-41.png
new file mode 100644 (file)
index 0000000..6c3de9e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-41.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-42.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-42.png
new file mode 100644 (file)
index 0000000..6c3de9e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-42.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-iPadPro@2x.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-iPadPro@2x.png
new file mode 100644 (file)
index 0000000..a9e9c26
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/Icon-iPadPro@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png
new file mode 100644 (file)
index 0000000..625943d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/Contents.json b/iOS/WolneLektury/Assets.xcassets/Contents.json
new file mode 100644 (file)
index 0000000..da4a164
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/Contents.json
new file mode 100644 (file)
index 0000000..131d1f6
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "arrrow_right.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "arrrow_right@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "arrrow_right@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right.png b/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right.png
new file mode 100644 (file)
index 0000000..a4123eb
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right@2x.png b/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right@2x.png
new file mode 100644 (file)
index 0000000..b428604
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right@3x.png b/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right@3x.png
new file mode 100644 (file)
index 0000000..e132109
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/arrrow_right.imageset/arrrow_right@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/Contents.json
new file mode 100644 (file)
index 0000000..2a6c0ca
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "ic_accept.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "ic_accept@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "ic_accept@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept.png b/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept.png
new file mode 100644 (file)
index 0000000..d34adcc
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept@2x.png b/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept@2x.png
new file mode 100644 (file)
index 0000000..1c1b692
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept@3x.png b/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept@3x.png
new file mode 100644 (file)
index 0000000..22441da
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_accept.imageset/ic_accept@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/Contents.json
new file mode 100644 (file)
index 0000000..337cdd4
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "ic_accept2.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "ic_accept2@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "ic_accept2@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2.png b/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2.png
new file mode 100644 (file)
index 0000000..9acc161
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2@2x.png b/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2@2x.png
new file mode 100644 (file)
index 0000000..12fd20d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2@3x.png b/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2@3x.png
new file mode 100644 (file)
index 0000000..3278942
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_accept2.imageset/ic_accept2@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/Contents.json
new file mode 100644 (file)
index 0000000..58c2e20
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "ic_play_arrow.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "ic_play_arrow@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "ic_play_arrow@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow.png b/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow.png
new file mode 100644 (file)
index 0000000..5c36b4d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow@2x.png b/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow@2x.png
new file mode 100644 (file)
index 0000000..ea51a44
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow@3x.png b/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow@3x.png
new file mode 100644 (file)
index 0000000..aba85d3
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_play_arrow.imageset/ic_play_arrow@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/Contents.json
new file mode 100644 (file)
index 0000000..20b46b9
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "ic_reload.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "ic_reload@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "ic_reload@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload.png b/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload.png
new file mode 100644 (file)
index 0000000..8d8611d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload@2x.png b/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload@2x.png
new file mode 100644 (file)
index 0000000..7a01502
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload@3x.png b/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload@3x.png
new file mode 100644 (file)
index 0000000..144d4c9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_reload.imageset/ic_reload@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/Contents.json
new file mode 100644 (file)
index 0000000..5b77062
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "ic_toggle.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "ic_toggle@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "ic_toggle@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle.png b/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle.png
new file mode 100644 (file)
index 0000000..bea27f1
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle@2x.png b/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle@2x.png
new file mode 100644 (file)
index 0000000..db729b7
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle@3x.png b/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle@3x.png
new file mode 100644 (file)
index 0000000..fd54e36
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/ic_toggle.imageset/ic_toggle@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/Contents.json
new file mode 100644 (file)
index 0000000..47a6ede
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_glasses-big.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_glasses-big@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_glasses-big@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big.png b/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big.png
new file mode 100644 (file)
index 0000000..dccb5a7
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big@2x.png
new file mode 100644 (file)
index 0000000..b89d5d2
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big@3x.png
new file mode 100644 (file)
index 0000000..63b57e7
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_glasses-big.imageset/icon_glasses-big@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/Contents.json
new file mode 100644 (file)
index 0000000..5a7be74
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_glasses-mid.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_glasses-mid@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_glasses-mid@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid.png b/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid.png
new file mode 100644 (file)
index 0000000..186f262
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid@2x.png
new file mode 100644 (file)
index 0000000..41dc416
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid@3x.png
new file mode 100644 (file)
index 0000000..2d1060a
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_glasses-mid.imageset/icon_glasses-mid@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/Contents.json
new file mode 100644 (file)
index 0000000..fabbffb
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_glasses-small.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_glasses-small@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_glasses-small@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small.png b/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small.png
new file mode 100644 (file)
index 0000000..039c275
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small@2x.png
new file mode 100644 (file)
index 0000000..11e7aff
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small@3x.png
new file mode 100644 (file)
index 0000000..fb6a74e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_glasses-small.imageset/icon_glasses-small@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/Contents.json
new file mode 100644 (file)
index 0000000..ef54a01
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-fill-big.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-fill-big@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-fill-big@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big.png
new file mode 100644 (file)
index 0000000..2d73e3d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big@2x.png
new file mode 100644 (file)
index 0000000..11289ee
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big@3x.png
new file mode 100644 (file)
index 0000000..6e97d80
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-big.imageset/icon_heart-fill-big@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/Contents.json
new file mode 100644 (file)
index 0000000..21573dc
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-fill-small.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-fill-small@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-fill-small@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small.png
new file mode 100644 (file)
index 0000000..1947f5a
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small@2x.png
new file mode 100644 (file)
index 0000000..0ae4fce
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small@3x.png
new file mode 100644 (file)
index 0000000..0bcbbbd
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-fill-small.imageset/icon_heart-fill-small@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/Contents.json
new file mode 100644 (file)
index 0000000..dbc762b
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-outline-big.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-outline-big@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-outline-big@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big.png
new file mode 100644 (file)
index 0000000..d1041c1
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big@2x.png
new file mode 100644 (file)
index 0000000..f1accb9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big@3x.png
new file mode 100644 (file)
index 0000000..398b9bb
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-big.imageset/icon_heart-outline-big@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/Contents.json
new file mode 100644 (file)
index 0000000..f120484
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-outline-small.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-outline-small@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_heart-outline-small@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small.png
new file mode 100644 (file)
index 0000000..16bc7d8
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small@2x.png
new file mode 100644 (file)
index 0000000..ccc2019
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small@3x.png
new file mode 100644 (file)
index 0000000..ba5c040
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_heart-outline-small.imageset/icon_heart-outline-small@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/Contents.json
new file mode 100644 (file)
index 0000000..a9d7c48
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_refresh.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_refresh@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_refresh@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh.png b/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh.png
new file mode 100644 (file)
index 0000000..4b8bd41
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh@2x.png
new file mode 100644 (file)
index 0000000..737d9f2
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh@3x.png
new file mode 100644 (file)
index 0000000..430b802
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_refresh.imageset/icon_refresh@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/Contents.json
new file mode 100644 (file)
index 0000000..e6ffbcd
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_speaker-big.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_speaker-big@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_speaker-big@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big.png b/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big.png
new file mode 100644 (file)
index 0000000..1f1beb5
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big@2x.png
new file mode 100644 (file)
index 0000000..955eda5
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big@3x.png
new file mode 100644 (file)
index 0000000..361c6db
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_speaker-big.imageset/icon_speaker-big@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/Contents.json
new file mode 100644 (file)
index 0000000..301e78d
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_speaker-mid.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_speaker-mid@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_speaker-mid@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid.png b/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid.png
new file mode 100644 (file)
index 0000000..97b2571
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid@2x.png
new file mode 100644 (file)
index 0000000..714af8d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid@3x.png
new file mode 100644 (file)
index 0000000..fa015dd
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_speaker-mid.imageset/icon_speaker-mid@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/Contents.json
new file mode 100644 (file)
index 0000000..b2ae4e0
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_speaker-small.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_speaker-small@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_speaker-small@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small.png b/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small.png
new file mode 100644 (file)
index 0000000..08ecf59
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small@2x.png
new file mode 100644 (file)
index 0000000..1bad0d0
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small@3x.png
new file mode 100644 (file)
index 0000000..2f87dc2
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_speaker-small.imageset/icon_speaker-small@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/Contents.json
new file mode 100644 (file)
index 0000000..9be12b4
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_spinner.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_spinner@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_spinner@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner.png b/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner.png
new file mode 100644 (file)
index 0000000..9437dc8
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner@2x.png
new file mode 100644 (file)
index 0000000..d76ebe9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner@3x.png
new file mode 100644 (file)
index 0000000..02db0fd
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_spinner.imageset/icon_spinner@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/Contents.json
new file mode 100644 (file)
index 0000000..e532f2d
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_star-big.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_star-big@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_star-big@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big.png b/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big.png
new file mode 100644 (file)
index 0000000..6cdda49
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big@2x.png
new file mode 100644 (file)
index 0000000..6cd1e72
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big@3x.png
new file mode 100644 (file)
index 0000000..82634c2
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_star-big.imageset/icon_star-big@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/Contents.json
new file mode 100644 (file)
index 0000000..eeb9ceb
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_star-mid.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_star-mid@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_star-mid@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid.png b/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid.png
new file mode 100644 (file)
index 0000000..31052dc
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid@2x.png
new file mode 100644 (file)
index 0000000..ebad582
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid@3x.png
new file mode 100644 (file)
index 0000000..a0334a9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_star-mid.imageset/icon_star-mid@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/Contents.json
new file mode 100644 (file)
index 0000000..bbe4f94
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_star-small.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_star-small@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_star-small@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small.png b/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small.png
new file mode 100644 (file)
index 0000000..a028a9c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small@2x.png
new file mode 100644 (file)
index 0000000..a46528d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small@3x.png
new file mode 100644 (file)
index 0000000..c0a754b
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_star-small.imageset/icon_star-small@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/Contents.json
new file mode 100644 (file)
index 0000000..0b09cb6
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "icon_trash.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_trash@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "icon_trash@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash.png b/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash.png
new file mode 100644 (file)
index 0000000..3e9ad0a
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash@2x.png b/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash@2x.png
new file mode 100644 (file)
index 0000000..f12062d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash@3x.png b/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash@3x.png
new file mode 100644 (file)
index 0000000..0a67ede
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/icon_trash.imageset/icon_trash@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/Contents.json
new file mode 100644 (file)
index 0000000..0c1465b
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "list_nocover.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "list_nocover@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "list_nocover@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover.png b/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover.png
new file mode 100644 (file)
index 0000000..04d6183
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover@2x.png b/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover@2x.png
new file mode 100644 (file)
index 0000000..2cc7361
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover@3x.png b/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover@3x.png
new file mode 100644 (file)
index 0000000..524888f
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/list_nocover.imageset/list_nocover@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/Contents.json
new file mode 100644 (file)
index 0000000..72e3b37
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "logo_fnp.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "logo_fnp@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "logo_fnp@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp.png b/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp.png
new file mode 100644 (file)
index 0000000..76b6397
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp@2x.png b/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp@2x.png
new file mode 100644 (file)
index 0000000..0c1f357
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp@3x.png b/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp@3x.png
new file mode 100644 (file)
index 0000000..89d342e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/logo_fnp.imageset/logo_fnp@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/Contents.json
new file mode 100644 (file)
index 0000000..9efe25b
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "logo_mkidn.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "logo_mkidn@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "logo_mkidn@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn.png b/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn.png
new file mode 100644 (file)
index 0000000..bf5d962
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn@2x.png b/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn@2x.png
new file mode 100644 (file)
index 0000000..eaba8c4
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn@3x.png b/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn@3x.png
new file mode 100644 (file)
index 0000000..ab6d543
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/logo_mkidn.imageset/logo_mkidn@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/Contents.json
new file mode 100644 (file)
index 0000000..8002be0
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "logo_opp.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "logo_opp@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "logo_opp@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp.png b/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp.png
new file mode 100644 (file)
index 0000000..d20529e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp@2x.png b/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp@2x.png
new file mode 100644 (file)
index 0000000..6149794
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp@3x.png b/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp@3x.png
new file mode 100644 (file)
index 0000000..8ce421a
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/logo_opp.imageset/logo_opp@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/Contents.json
new file mode 100644 (file)
index 0000000..6215b48
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_about.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_about@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_about@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about.png b/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about.png
new file mode 100644 (file)
index 0000000..2e56e67
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about@2x.png
new file mode 100644 (file)
index 0000000..9f4a515
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about@3x.png
new file mode 100644 (file)
index 0000000..2539e84
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_about.imageset/menu_about@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/Contents.json
new file mode 100644 (file)
index 0000000..6941d28
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_audiobooks.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_audiobooks@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_audiobooks@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks.png b/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks.png
new file mode 100644 (file)
index 0000000..572d5c5
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks@2x.png
new file mode 100644 (file)
index 0000000..95137f9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks@3x.png
new file mode 100644 (file)
index 0000000..ecaac3c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_audiobooks.imageset/menu_audiobooks@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/Contents.json
new file mode 100644 (file)
index 0000000..1bca51f
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_search.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_search@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_search@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search.png b/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search.png
new file mode 100644 (file)
index 0000000..be70ead
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search@2x.png
new file mode 100644 (file)
index 0000000..4cbba79
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search@3x.png
new file mode 100644 (file)
index 0000000..b8da9d0
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_catalog.imageset/menu_search@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/Contents.json
new file mode 100644 (file)
index 0000000..8679834
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_collection.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_collection@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_collection@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection.png b/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection.png
new file mode 100644 (file)
index 0000000..2ab84c2
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection@2x.png
new file mode 100644 (file)
index 0000000..f3e1fae
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection@3x.png
new file mode 100644 (file)
index 0000000..62664f1
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_collection.imageset/menu_collection@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/Contents.json
new file mode 100644 (file)
index 0000000..81d6ec5
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_tick.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_tick@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_tick@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick.png b/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick.png
new file mode 100644 (file)
index 0000000..31841d2
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick@2x.png
new file mode 100644 (file)
index 0000000..c34f7ba
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick@3x.png
new file mode 100644 (file)
index 0000000..e8425a3
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_completed.imageset/navbar_tick@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/Contents.json
new file mode 100644 (file)
index 0000000..6603555
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_downloaded.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_downloaded@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_downloaded@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded.png b/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded.png
new file mode 100644 (file)
index 0000000..6d51826
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded@2x.png
new file mode 100644 (file)
index 0000000..796a3ce
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded@3x.png
new file mode 100644 (file)
index 0000000..6d909f0
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_downloaded.imageset/menu_downloaded@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/Contents.json
new file mode 100644 (file)
index 0000000..8679834
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_collection.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_collection@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_collection@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection.png b/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection.png
new file mode 100644 (file)
index 0000000..2ab84c2
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection@2x.png
new file mode 100644 (file)
index 0000000..f3e1fae
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection@3x.png
new file mode 100644 (file)
index 0000000..62664f1
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_favourites.imageset/menu_collection@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/Contents.json
new file mode 100644 (file)
index 0000000..0262565
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_all.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_all@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_all@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all.png b/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all.png
new file mode 100644 (file)
index 0000000..9481e58
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all@2x.png
new file mode 100644 (file)
index 0000000..afbf84d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all@3x.png
new file mode 100644 (file)
index 0000000..ba4c6a1
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_library.imageset/menu_all@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/Contents.json
new file mode 100644 (file)
index 0000000..f501812
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_new.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_new@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_new@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new.png b/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new.png
new file mode 100644 (file)
index 0000000..67775ff
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new@2x.png
new file mode 100644 (file)
index 0000000..3e6614d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new@3x.png
new file mode 100644 (file)
index 0000000..fec8fea
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_newest.imageset/menu_new@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/Contents.json
new file mode 100644 (file)
index 0000000..c8a5dd2
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_news.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_news@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_news@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news.png b/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news.png
new file mode 100644 (file)
index 0000000..b6635b1
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news@2x.png
new file mode 100644 (file)
index 0000000..7fa7f0e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news@3x.png
new file mode 100644 (file)
index 0000000..c97f497
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_news.imageset/menu_news@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/Contents.json
new file mode 100644 (file)
index 0000000..eb16241
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_early.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_early@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_early@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early.png b/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early.png
new file mode 100644 (file)
index 0000000..392c035
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early@2x.png
new file mode 100644 (file)
index 0000000..7844c12
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early@3x.png
new file mode 100644 (file)
index 0000000..5ba44fa
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_premium.imageset/menu_early@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/Contents.json
new file mode 100644 (file)
index 0000000..7772a4c
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_ebooks.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_ebooks@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_ebooks@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks.png b/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks.png
new file mode 100644 (file)
index 0000000..97aea88
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks@2x.png
new file mode 100644 (file)
index 0000000..b6421db
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks@3x.png
new file mode 100644 (file)
index 0000000..b072c3b
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_reading.imageset/menu_ebooks@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/Contents.json
new file mode 100644 (file)
index 0000000..023040f
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_settings.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_settings@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_settings@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings.png b/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings.png
new file mode 100644 (file)
index 0000000..97bdc8c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings@2x.png
new file mode 100644 (file)
index 0000000..6dab455
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings@3x.png
new file mode 100644 (file)
index 0000000..e70548c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_settings.imageset/menu_settings@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/Contents.json
new file mode 100644 (file)
index 0000000..69d9aab
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "menu_wolne.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_wolne@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "menu_wolne@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne.png b/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne.png
new file mode 100644 (file)
index 0000000..425909c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne@2x.png b/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne@2x.png
new file mode 100644 (file)
index 0000000..bb0659a
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne@3x.png b/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne@3x.png
new file mode 100644 (file)
index 0000000..9de24ee
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/menu_wolne_lektury.imageset/menu_wolne@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/Contents.json
new file mode 100644 (file)
index 0000000..05c2930
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_back-alt.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_back-alt@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_back-alt@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt.png b/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt.png
new file mode 100644 (file)
index 0000000..1f578b1
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt@2x.png
new file mode 100644 (file)
index 0000000..af86285
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt@3x.png
new file mode 100644 (file)
index 0000000..90a86e0
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_back-alt.imageset/navbar_back-alt@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/Contents.json
new file mode 100644 (file)
index 0000000..c9a318b
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_back.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_back@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_back@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back.png b/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back.png
new file mode 100644 (file)
index 0000000..dfb4d38
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back@2x.png
new file mode 100644 (file)
index 0000000..0b941e6
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back@3x.png
new file mode 100644 (file)
index 0000000..31028d2
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_back.imageset/navbar_back@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/Contents.json
new file mode 100644 (file)
index 0000000..e2a67ac
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_chapters.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_chapters@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_chapters@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters.png b/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters.png
new file mode 100644 (file)
index 0000000..7bcdba9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters@2x.png
new file mode 100644 (file)
index 0000000..75c18df
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters@3x.png
new file mode 100644 (file)
index 0000000..8f6a966
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_chapters.imageset/navbar_chapters@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/Contents.json
new file mode 100644 (file)
index 0000000..e2cf220
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_checkbox-tick.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_checkbox-tick@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_checkbox-tick@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick.png b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick.png
new file mode 100644 (file)
index 0000000..21e4add
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick@2x.png
new file mode 100644 (file)
index 0000000..32ec282
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick@3x.png
new file mode 100644 (file)
index 0000000..7d081f9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox-tick.imageset/navbar_checkbox-tick@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/Contents.json
new file mode 100644 (file)
index 0000000..58331fa
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_checkbox.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_checkbox@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_checkbox@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox.png b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox.png
new file mode 100644 (file)
index 0000000..90c82d8
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox@2x.png
new file mode 100644 (file)
index 0000000..42d25fd
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox@3x.png
new file mode 100644 (file)
index 0000000..956ea71
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_checkbox.imageset/navbar_checkbox@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/Contents.json
new file mode 100644 (file)
index 0000000..17647f4
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_close copy.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_close copy@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_close copy@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy.png b/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy.png
new file mode 100644 (file)
index 0000000..04bec46
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy@2x.png
new file mode 100644 (file)
index 0000000..de77048
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy@3x.png
new file mode 100644 (file)
index 0000000..0c067ad
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_close copy.imageset/navbar_close copy@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/Contents.json
new file mode 100644 (file)
index 0000000..09c1034
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_close-small.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_close-small@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_close-small@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small.png b/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small.png
new file mode 100644 (file)
index 0000000..8f1b3be
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small@2x.png
new file mode 100644 (file)
index 0000000..ea6459e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small@3x.png
new file mode 100644 (file)
index 0000000..90ddedd
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_close-small.imageset/navbar_close-small@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/Contents.json
new file mode 100644 (file)
index 0000000..2c6c89d
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_close.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_close@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_close@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close.png b/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close.png
new file mode 100644 (file)
index 0000000..04bec46
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close@2x.png
new file mode 100644 (file)
index 0000000..de77048
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close@3x.png
new file mode 100644 (file)
index 0000000..0c067ad
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_close.imageset/navbar_close@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/Contents.json
new file mode 100644 (file)
index 0000000..b021a57
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_filter.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_filter@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_filter@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter.png b/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter.png
new file mode 100644 (file)
index 0000000..60141a3
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter@2x.png
new file mode 100644 (file)
index 0000000..fc5b0f7
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter@3x.png
new file mode 100644 (file)
index 0000000..4b13fb7
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_filter.imageset/navbar_filter@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/Contents.json
new file mode 100644 (file)
index 0000000..5c6389f
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_menu.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_menu@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_menu@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu.png b/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu.png
new file mode 100644 (file)
index 0000000..3186c1c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu@2x.png
new file mode 100644 (file)
index 0000000..0ad1975
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu@3x.png
new file mode 100644 (file)
index 0000000..e29a2fe
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_menu.imageset/navbar_menu@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/Contents.json
new file mode 100644 (file)
index 0000000..c49e535
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_search.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_search@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_search@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search.png b/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search.png
new file mode 100644 (file)
index 0000000..2838062
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search@2x.png
new file mode 100644 (file)
index 0000000..107fe9a
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search@3x.png
new file mode 100644 (file)
index 0000000..c1b59f7
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_search.imageset/navbar_search@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/Contents.json
new file mode 100644 (file)
index 0000000..bf737f3
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_share.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_share@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_share@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share.png b/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share.png
new file mode 100644 (file)
index 0000000..cf06de9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share@2x.png
new file mode 100644 (file)
index 0000000..7294ae1
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share@3x.png
new file mode 100644 (file)
index 0000000..89f1ae4
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_share.imageset/navbar_share@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/Contents.json
new file mode 100644 (file)
index 0000000..8c7d1c7
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_sort.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_sort@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_sort@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort.png b/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort.png
new file mode 100644 (file)
index 0000000..e494afa
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort@2x.png
new file mode 100644 (file)
index 0000000..3d75955
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort@3x.png
new file mode 100644 (file)
index 0000000..4830db9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_sort.imageset/navbar_sort@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/Contents.json
new file mode 100644 (file)
index 0000000..81d6ec5
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_tick.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_tick@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "navbar_tick@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick.png b/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick.png
new file mode 100644 (file)
index 0000000..381eba4
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick@2x.png b/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick@2x.png
new file mode 100644 (file)
index 0000000..e909b79
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick@3x.png b/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick@3x.png
new file mode 100644 (file)
index 0000000..c0b0a3c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/navbar_tick.imageset/navbar_tick@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/Contents.json
new file mode 100644 (file)
index 0000000..3f79dbd
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "player_chapter_next.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_chapter_next@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_chapter_next@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next.png b/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next.png
new file mode 100644 (file)
index 0000000..4e41fa6
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next@2x.png b/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next@2x.png
new file mode 100644 (file)
index 0000000..e7c36ca
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next@3x.png b/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next@3x.png
new file mode 100644 (file)
index 0000000..8756dce
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_chapter_next.imageset/player_chapter_next@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/Contents.json
new file mode 100644 (file)
index 0000000..e46dce3
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "player_chapter_previous.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_chapter_previous@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_chapter_previous@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous.png b/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous.png
new file mode 100644 (file)
index 0000000..a571206
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous@2x.png b/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous@2x.png
new file mode 100644 (file)
index 0000000..a22963a
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous@3x.png b/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous@3x.png
new file mode 100644 (file)
index 0000000..339db3b
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_chapter_previous.imageset/player_chapter_previous@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/Contents.json
new file mode 100644 (file)
index 0000000..e29573e
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_forward.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_forward@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_forward@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward.png b/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward.png
new file mode 100644 (file)
index 0000000..7b3c70d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward@2x.png b/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward@2x.png
new file mode 100644 (file)
index 0000000..f7153f9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward@3x.png b/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward@3x.png
new file mode 100644 (file)
index 0000000..3997592
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_forward.imageset/player_controls_forward@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/Contents.json
new file mode 100644 (file)
index 0000000..b97e117
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_pause.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_pause@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_pause@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause.png b/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause.png
new file mode 100644 (file)
index 0000000..fa458b3
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause@2x.png b/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause@2x.png
new file mode 100644 (file)
index 0000000..b909651
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause@3x.png b/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause@3x.png
new file mode 100644 (file)
index 0000000..89f6749
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_pause.imageset/player_controls_pause@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/Contents.json
new file mode 100644 (file)
index 0000000..4cae424
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_play.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_play@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_play@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play.png b/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play.png
new file mode 100644 (file)
index 0000000..8a01f85
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play@2x.png b/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play@2x.png
new file mode 100644 (file)
index 0000000..990e912
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play@3x.png b/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play@3x.png
new file mode 100644 (file)
index 0000000..863d370
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_play.imageset/player_controls_play@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/Contents.json
new file mode 100644 (file)
index 0000000..ef87e74
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_rewind.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_rewind@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "player_controls_rewind@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind.png b/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind.png
new file mode 100644 (file)
index 0000000..6d8cb4c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind@2x.png b/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind@2x.png
new file mode 100644 (file)
index 0000000..f7f36df
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind@3x.png b/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind@3x.png
new file mode 100644 (file)
index 0000000..c4a2427
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/player_controls_rewind.imageset/player_controls_rewind@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/Contents.json
new file mode 100644 (file)
index 0000000..6f2754d
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "reader_bright.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_bright@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_bright@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright.png b/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright.png
new file mode 100644 (file)
index 0000000..5aa97f9
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright@2x.png b/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright@2x.png
new file mode 100644 (file)
index 0000000..cbaff07
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright@3x.png b/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright@3x.png
new file mode 100644 (file)
index 0000000..e5a6abc
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_bright.imageset/reader_bright@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/Contents.json
new file mode 100644 (file)
index 0000000..29c6b1d
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "reader_comments.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_comments@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_comments@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments.png b/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments.png
new file mode 100644 (file)
index 0000000..5c09706
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments@2x.png b/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments@2x.png
new file mode 100644 (file)
index 0000000..b3023b5
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments@3x.png b/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments@3x.png
new file mode 100644 (file)
index 0000000..dfdcf1d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_comments.imageset/reader_comments@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/Contents.json
new file mode 100644 (file)
index 0000000..3d7d74d
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "reader_dark.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_dark@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_dark@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark.png b/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark.png
new file mode 100644 (file)
index 0000000..324f7d3
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark@2x.png b/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark@2x.png
new file mode 100644 (file)
index 0000000..21456d3
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark@3x.png b/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark@3x.png
new file mode 100644 (file)
index 0000000..480cd33
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_dark.imageset/reader_dark@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/Contents.json
new file mode 100644 (file)
index 0000000..7d58561
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "reader_font-big.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_font-big@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_font-big@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big.png b/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big.png
new file mode 100644 (file)
index 0000000..0f9ca9e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big@2x.png b/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big@2x.png
new file mode 100644 (file)
index 0000000..b64bf95
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big@3x.png b/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big@3x.png
new file mode 100644 (file)
index 0000000..0c662d8
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_font-big.imageset/reader_font-big@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/Contents.json
new file mode 100644 (file)
index 0000000..797dca9
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "reader_font-small.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_font-small@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_font-small@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small.png b/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small.png
new file mode 100644 (file)
index 0000000..dc6a9c0
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small@2x.png b/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small@2x.png
new file mode 100644 (file)
index 0000000..48acc7c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small@3x.png b/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small@3x.png
new file mode 100644 (file)
index 0000000..4f7a7c4
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_font-small.imageset/reader_font-small@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/Contents.json
new file mode 100644 (file)
index 0000000..8697133
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "reader_leading-big.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_leading-big@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_leading-big@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big.png b/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big.png
new file mode 100644 (file)
index 0000000..eb64ad7
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big@2x.png b/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big@2x.png
new file mode 100644 (file)
index 0000000..bfd9115
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big@3x.png b/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big@3x.png
new file mode 100644 (file)
index 0000000..ec83435
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_leading-big.imageset/reader_leading-big@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/Contents.json
new file mode 100644 (file)
index 0000000..79e918c
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "reader_leading-small.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_leading-small@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_leading-small@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small.png b/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small.png
new file mode 100644 (file)
index 0000000..4e2be2e
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small@2x.png b/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small@2x.png
new file mode 100644 (file)
index 0000000..f319a14
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small@3x.png b/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small@3x.png
new file mode 100644 (file)
index 0000000..7f9bab7
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_leading-small.imageset/reader_leading-small@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/Contents.json
new file mode 100644 (file)
index 0000000..d18f2d5
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "reader_margin-big.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_margin-big@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_margin-big@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big.png b/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big.png
new file mode 100644 (file)
index 0000000..d188212
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big@2x.png b/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big@2x.png
new file mode 100644 (file)
index 0000000..e9b04c2
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big@3x.png b/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big@3x.png
new file mode 100644 (file)
index 0000000..8306cba
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_margin-big.imageset/reader_margin-big@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/Contents.json
new file mode 100644 (file)
index 0000000..50c1cca
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "reader_margin-small.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_margin-small@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "reader_margin-small@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small.png b/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small.png
new file mode 100644 (file)
index 0000000..89151c2
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small@2x.png b/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small@2x.png
new file mode 100644 (file)
index 0000000..20adbef
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small@3x.png b/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small@3x.png
new file mode 100644 (file)
index 0000000..1f64b57
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/reader_margin-small.imageset/reader_margin-small@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/share.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/share.imageset/Contents.json
new file mode 100644 (file)
index 0000000..32b1fe2
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "share.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "share@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "share@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/share.imageset/share.png b/iOS/WolneLektury/Assets.xcassets/share.imageset/share.png
new file mode 100644 (file)
index 0000000..a8ebc2d
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/share.imageset/share.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/share.imageset/share@2x.png b/iOS/WolneLektury/Assets.xcassets/share.imageset/share@2x.png
new file mode 100644 (file)
index 0000000..a9581bf
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/share.imageset/share@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/share.imageset/share@3x.png b/iOS/WolneLektury/Assets.xcassets/share.imageset/share@3x.png
new file mode 100644 (file)
index 0000000..b188724
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/share.imageset/share@3x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/Contents.json b/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/Contents.json
new file mode 100644 (file)
index 0000000..abf4915
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "wl_logo.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "wl_logo@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "wl_logo@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo.png b/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo.png
new file mode 100644 (file)
index 0000000..53fd4c0
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo@2x.png b/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo@2x.png
new file mode 100644 (file)
index 0000000..1848ebe
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo@2x.png differ
diff --git a/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo@3x.png b/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo@3x.png
new file mode 100644 (file)
index 0000000..930db4c
Binary files /dev/null and b/iOS/WolneLektury/Assets.xcassets/wl_logo.imageset/wl_logo@3x.png differ
diff --git a/iOS/WolneLektury/Base.lproj/LaunchScreen.storyboard b/iOS/WolneLektury/Base.lproj/LaunchScreen.storyboard
new file mode 100644 (file)
index 0000000..0bae832
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="wl_logo" translatesAutoresizingMaskIntoConstraints="NO" id="5aZ-5i-zMX">
+                                <rect key="frame" x="55" y="281.5" width="265" height="104"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="265" id="7cq-Lv-RVi"/>
+                                    <constraint firstAttribute="height" constant="104" id="Jpf-36-abf"/>
+                                </constraints>
+                            </imageView>
+                        </subviews>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="5aZ-5i-zMX" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="cSM-NU-OHp"/>
+                            <constraint firstItem="5aZ-5i-zMX" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="hYx-MR-2Kh"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="52" y="374.66266866566718"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="wl_logo" width="265" height="104"/>
+    </resources>
+</document>
diff --git a/iOS/WolneLektury/Base.lproj/Main.storyboard b/iOS/WolneLektury/Base.lproj/Main.storyboard
new file mode 100644 (file)
index 0000000..f5a4412
--- /dev/null
@@ -0,0 +1,1881 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
+        <capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+        <!--Player View Controller-->
+        <scene sceneID="Cqv-2I-z8p">
+            <objects>
+                <viewController storyboardIdentifier="PlayerViewController" id="8WU-9u-6d6" customClass="PlayerViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="PFe-m0-dMf">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="U12-gG-Igl">
+                                <rect key="frame" x="0.0" y="0.0" width="375" height="388"/>
+                            </imageView>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Wb3-0V-vfc">
+                                <rect key="frame" x="0.0" y="0.0" width="375" height="388"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            </view>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="MCu-xu-Ehc">
+                                <rect key="frame" x="0.0" y="388" width="375" height="279"/>
+                                <subviews>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="P2M-Gd-U9n">
+                                        <rect key="frame" x="179" y="0.0" width="17" height="24"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="24" id="TnY-Ug-Yd4"/>
+                                        </constraints>
+                                        <state key="normal" image="ic_toggle"/>
+                                        <connections>
+                                            <action selector="toggleListButtonActionWithSender:" destination="8WU-9u-6d6" eventType="touchUpInside" id="48z-g7-HQt"/>
+                                        </connections>
+                                    </button>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ROZDZIAŁ 01" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FPS-kh-u2Y">
+                                        <rect key="frame" x="144.5" y="26" width="87" height="17"/>
+                                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                        <color key="textColor" red="0.32156862745098036" green="0.32156862745098036" blue="0.32156862745098036" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Dziady. Poema" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="kYt-rI-R3U">
+                                        <rect key="frame" x="70" y="43" width="235" height="66"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="66" id="e5n-gu-Vog"/>
+                                        </constraints>
+                                        <fontDescription key="fontDescription" type="system" weight="light" pointSize="21"/>
+                                        <color key="textColor" red="0.32156862745098036" green="0.32156862745098036" blue="0.32156862745098036" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7ux-cf-CIC">
+                                        <rect key="frame" x="162.5" y="194" width="50" height="50"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" constant="50" id="EGa-CR-sDt"/>
+                                            <constraint firstAttribute="height" constant="50" id="ab6-gs-xRB"/>
+                                        </constraints>
+                                        <state key="normal" image="player_controls_play"/>
+                                        <connections>
+                                            <action selector="playPauseButtonActionWithSender:" destination="8WU-9u-6d6" eventType="touchUpInside" id="0p5-rF-bKn"/>
+                                        </connections>
+                                    </button>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jHO-Gw-YRT">
+                                        <rect key="frame" x="232.5" y="194" width="50" height="50"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="50" id="bmJ-pG-Wbw"/>
+                                            <constraint firstAttribute="width" constant="50" id="m5e-v3-UfN"/>
+                                        </constraints>
+                                        <state key="normal" image="player_controls_forward"/>
+                                        <connections>
+                                            <action selector="forwardButtonActionWithSender:" destination="8WU-9u-6d6" eventType="touchUpInside" id="vOj-L0-b6N"/>
+                                        </connections>
+                                    </button>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CIs-L5-1o6">
+                                        <rect key="frame" x="92.5" y="194" width="50" height="50"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" constant="50" id="krx-7k-SoR"/>
+                                            <constraint firstAttribute="height" constant="50" id="nKL-nm-bfS"/>
+                                        </constraints>
+                                        <state key="normal" image="player_controls_rewind"/>
+                                        <connections>
+                                            <action selector="rewindButtonActionWithSender:" destination="8WU-9u-6d6" eventType="touchUpInside" id="UAz-ZG-Mh8"/>
+                                        </connections>
+                                    </button>
+                                    <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="xRk-Fv-SVV">
+                                        <rect key="frame" x="50.5" y="152" width="274" height="30"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" constant="270" id="A9I-W3-beC"/>
+                                            <constraint firstAttribute="height" constant="29" id="vfj-Ao-knP"/>
+                                        </constraints>
+                                        <color key="minimumTrackTintColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/>
+                                        <color key="thumbTintColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/>
+                                        <connections>
+                                            <action selector="progressSliderActionWithSender:" destination="8WU-9u-6d6" eventType="valueChanged" id="sIl-Wc-mso"/>
+                                        </connections>
+                                    </slider>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Tq9-n5-IXl">
+                                        <rect key="frame" x="52.5" y="130" width="39" height="17"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="17" id="pT4-uK-dKf"/>
+                                        </constraints>
+                                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                        <color key="textColor" red="0.0039215686274509803" green="0.50588235294117645" blue="0.53725490196078429" alpha="1" colorSpace="calibratedRGB"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iQL-Cs-lZ9">
+                                        <rect key="frame" x="283.5" y="130" width="39" height="17"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="17" id="pMt-rc-zha"/>
+                                        </constraints>
+                                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                        <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oxD-97-8TJ">
+                                        <rect key="frame" x="20" y="51" width="50" height="50"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" constant="50" id="LOa-vb-ttc"/>
+                                            <constraint firstAttribute="height" constant="50" id="npP-KN-YS6"/>
+                                        </constraints>
+                                        <state key="normal" image="player_chapter_previous"/>
+                                        <connections>
+                                            <action selector="prevButtonActionWithSender:" destination="8WU-9u-6d6" eventType="touchUpInside" id="BJb-aw-8ow"/>
+                                        </connections>
+                                    </button>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NVT-Lw-NXQ">
+                                        <rect key="frame" x="305" y="51" width="50" height="50"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="50" id="kfY-NK-8D9"/>
+                                            <constraint firstAttribute="width" constant="50" id="lav-tR-UeC"/>
+                                        </constraints>
+                                        <state key="normal" image="player_chapter_next"/>
+                                        <connections>
+                                            <action selector="nextButtonActionWithSender:" destination="8WU-9u-6d6" eventType="touchUpInside" id="Dvp-Mt-HTU"/>
+                                        </connections>
+                                    </button>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="36h-sa-v8G">
+                                        <rect key="frame" x="30" y="114" width="315" height="1"/>
+                                        <color key="backgroundColor" red="0.63921568627450975" green="0.63921568627450975" blue="0.63921568627450975" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="1" id="N8x-jO-bRU"/>
+                                        </constraints>
+                                    </view>
+                                </subviews>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstAttribute="trailing" secondItem="NVT-Lw-NXQ" secondAttribute="trailing" constant="20" id="3nt-AZ-CKo"/>
+                                    <constraint firstItem="kYt-rI-R3U" firstAttribute="top" secondItem="MCu-xu-Ehc" secondAttribute="top" constant="43" id="57P-Dj-sgm"/>
+                                    <constraint firstItem="7ux-cf-CIC" firstAttribute="centerX" secondItem="MCu-xu-Ehc" secondAttribute="centerX" id="6lL-DE-Y2d"/>
+                                    <constraint firstItem="iQL-Cs-lZ9" firstAttribute="trailing" secondItem="xRk-Fv-SVV" secondAttribute="trailing" id="7eI-92-a8w"/>
+                                    <constraint firstItem="xRk-Fv-SVV" firstAttribute="top" secondItem="iQL-Cs-lZ9" secondAttribute="bottom" constant="5" id="94p-Gc-uJF"/>
+                                    <constraint firstItem="NVT-Lw-NXQ" firstAttribute="leading" secondItem="kYt-rI-R3U" secondAttribute="trailing" id="A03-GO-5TN"/>
+                                    <constraint firstItem="7ux-cf-CIC" firstAttribute="top" secondItem="xRk-Fv-SVV" secondAttribute="bottom" constant="13" id="BrM-Ro-Ihr"/>
+                                    <constraint firstItem="Tq9-n5-IXl" firstAttribute="top" secondItem="36h-sa-v8G" secondAttribute="bottom" constant="15" id="Cnt-Zv-iq5"/>
+                                    <constraint firstItem="jHO-Gw-YRT" firstAttribute="bottom" secondItem="7ux-cf-CIC" secondAttribute="bottom" id="DEK-Zy-ZIw"/>
+                                    <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Tq9-n5-IXl" secondAttribute="trailing" constant="20" symbolic="YES" id="EIX-fG-yEA"/>
+                                    <constraint firstAttribute="bottom" secondItem="7ux-cf-CIC" secondAttribute="bottom" constant="35" id="Fda-hU-CKZ"/>
+                                    <constraint firstItem="P2M-Gd-U9n" firstAttribute="centerX" secondItem="MCu-xu-Ehc" secondAttribute="centerX" id="Gua-qd-8hv"/>
+                                    <constraint firstItem="kYt-rI-R3U" firstAttribute="leading" secondItem="oxD-97-8TJ" secondAttribute="trailing" id="HfC-w5-GBG"/>
+                                    <constraint firstItem="7ux-cf-CIC" firstAttribute="leading" secondItem="CIs-L5-1o6" secondAttribute="trailing" constant="20" id="Krl-am-POs"/>
+                                    <constraint firstItem="xRk-Fv-SVV" firstAttribute="top" secondItem="Tq9-n5-IXl" secondAttribute="bottom" constant="5" id="LIt-sY-uPh"/>
+                                    <constraint firstItem="iQL-Cs-lZ9" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="MCu-xu-Ehc" secondAttribute="leading" constant="20" symbolic="YES" id="PSo-fC-tHb"/>
+                                    <constraint firstItem="jHO-Gw-YRT" firstAttribute="leading" secondItem="7ux-cf-CIC" secondAttribute="trailing" constant="20" id="U8z-aK-WNm"/>
+                                    <constraint firstItem="xRk-Fv-SVV" firstAttribute="centerX" secondItem="MCu-xu-Ehc" secondAttribute="centerX" id="UZ2-5x-z34"/>
+                                    <constraint firstItem="oxD-97-8TJ" firstAttribute="centerY" secondItem="kYt-rI-R3U" secondAttribute="centerY" id="V17-DH-zUI"/>
+                                    <constraint firstItem="Tq9-n5-IXl" firstAttribute="leading" secondItem="xRk-Fv-SVV" secondAttribute="leading" id="WqM-m8-J24"/>
+                                    <constraint firstItem="36h-sa-v8G" firstAttribute="leading" secondItem="MCu-xu-Ehc" secondAttribute="leading" constant="30" id="dtN-s2-9nn"/>
+                                    <constraint firstItem="36h-sa-v8G" firstAttribute="top" secondItem="kYt-rI-R3U" secondAttribute="bottom" constant="5" id="hS6-CX-jlk"/>
+                                    <constraint firstItem="oxD-97-8TJ" firstAttribute="leading" secondItem="MCu-xu-Ehc" secondAttribute="leading" constant="20" id="jD0-Oz-PcX"/>
+                                    <constraint firstItem="CIs-L5-1o6" firstAttribute="bottom" secondItem="7ux-cf-CIC" secondAttribute="bottom" id="mga-5k-9rg"/>
+                                    <constraint firstItem="FPS-kh-u2Y" firstAttribute="top" secondItem="P2M-Gd-U9n" secondAttribute="bottom" constant="2" id="onb-Lf-5A8"/>
+                                    <constraint firstItem="NVT-Lw-NXQ" firstAttribute="centerY" secondItem="kYt-rI-R3U" secondAttribute="centerY" id="r7T-55-dpo"/>
+                                    <constraint firstAttribute="trailing" secondItem="36h-sa-v8G" secondAttribute="trailing" constant="30" id="t6Y-6f-WTQ"/>
+                                    <constraint firstItem="P2M-Gd-U9n" firstAttribute="top" secondItem="MCu-xu-Ehc" secondAttribute="top" id="tRC-oh-uDE"/>
+                                    <constraint firstItem="FPS-kh-u2Y" firstAttribute="centerX" secondItem="MCu-xu-Ehc" secondAttribute="centerX" id="tsL-GP-haR"/>
+                                </constraints>
+                            </view>
+                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="F38-PA-t8E">
+                                <rect key="frame" x="106.5" y="204.5" width="162" height="183.5"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="162" id="BYo-S8-EIr"/>
+                                    <constraint firstAttribute="width" secondItem="F38-PA-t8E" secondAttribute="height" multiplier="81:92" id="Bue-YR-uXm"/>
+                                </constraints>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oIX-HK-L9B">
+                                <rect key="frame" x="19" y="60" width="337" height="20.5"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WoN-yQ-od9">
+                                <rect key="frame" x="0.0" y="20" width="50" height="40"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="50" id="3GZ-cz-taA"/>
+                                    <constraint firstAttribute="height" constant="40" id="KeP-B1-IdF"/>
+                                </constraints>
+                                <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <state key="normal" image="navbar_back"/>
+                                <connections>
+                                    <action selector="backButtonActionWithSender:" destination="8WU-9u-6d6" eventType="touchUpInside" id="eZJ-xk-p1d"/>
+                                </connections>
+                            </button>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="GcW-r3-RGx">
+                                <rect key="frame" x="0.0" y="0.0" width="375" height="388"/>
+                                <color key="backgroundColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/>
+                                <prototypes>
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="PlayerItemTableViewCell" id="cLs-CK-w38" customClass="PlayerItemTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+                                        <rect key="frame" x="0.0" y="28" width="375" height="44"/>
+                                        <autoresizingMask key="autoresizingMask"/>
+                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="cLs-CK-w38" id="H03-ZX-zgp">
+                                            <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
+                                            <autoresizingMask key="autoresizingMask"/>
+                                            <subviews>
+                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="y0D-a3-5Fc">
+                                                    <rect key="frame" x="16" y="7" width="299" height="29.5"/>
+                                                    <fontDescription key="fontDescription" type="system" weight="light" pointSize="17"/>
+                                                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                    <nil key="highlightedColor"/>
+                                                </label>
+                                                <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_play_arrow" translatesAutoresizingMaskIntoConstraints="NO" id="6CQ-JX-IIs">
+                                                    <rect key="frame" x="335" y="10" width="24" height="24"/>
+                                                    <constraints>
+                                                        <constraint firstAttribute="height" constant="24" id="Gm8-SV-Kni"/>
+                                                        <constraint firstAttribute="width" constant="24" id="cfq-9K-2TH"/>
+                                                    </constraints>
+                                                </imageView>
+                                            </subviews>
+                                            <constraints>
+                                                <constraint firstAttribute="bottom" secondItem="y0D-a3-5Fc" secondAttribute="bottom" constant="7" id="8ez-xc-aPs"/>
+                                                <constraint firstAttribute="trailing" secondItem="6CQ-JX-IIs" secondAttribute="trailing" constant="16" id="Xxc-cQ-XN1"/>
+                                                <constraint firstItem="y0D-a3-5Fc" firstAttribute="leading" secondItem="H03-ZX-zgp" secondAttribute="leading" constant="16" id="ZYM-hv-tee"/>
+                                                <constraint firstItem="6CQ-JX-IIs" firstAttribute="centerY" secondItem="H03-ZX-zgp" secondAttribute="centerY" id="bXR-yJ-6h6"/>
+                                                <constraint firstItem="6CQ-JX-IIs" firstAttribute="leading" secondItem="y0D-a3-5Fc" secondAttribute="trailing" constant="20" id="nZG-Ov-CfA"/>
+                                                <constraint firstItem="y0D-a3-5Fc" firstAttribute="top" secondItem="H03-ZX-zgp" secondAttribute="top" constant="7" id="uKC-nV-MNI"/>
+                                            </constraints>
+                                        </tableViewCellContentView>
+                                        <connections>
+                                            <outlet property="playingIcon" destination="6CQ-JX-IIs" id="sbe-oc-W7o"/>
+                                            <outlet property="titleLabel" destination="y0D-a3-5Fc" id="XdB-aF-ePw"/>
+                                        </connections>
+                                    </tableViewCell>
+                                </prototypes>
+                                <connections>
+                                    <outlet property="dataSource" destination="8WU-9u-6d6" id="1ZB-3u-mXT"/>
+                                    <outlet property="delegate" destination="8WU-9u-6d6" id="fmV-HP-Jak"/>
+                                </connections>
+                            </tableView>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="MCu-xu-Ehc" firstAttribute="top" secondItem="Wb3-0V-vfc" secondAttribute="bottom" id="2fy-3b-SjY"/>
+                            <constraint firstItem="F38-PA-t8E" firstAttribute="centerX" secondItem="PFe-m0-dMf" secondAttribute="centerX" id="51L-gT-aaN"/>
+                            <constraint firstItem="oIX-HK-L9B" firstAttribute="top" secondItem="WoN-yQ-od9" secondAttribute="bottom" id="5cS-4a-xkp"/>
+                            <constraint firstItem="MCu-xu-Ehc" firstAttribute="top" secondItem="U12-gG-Igl" secondAttribute="bottom" id="C2L-aT-XhI"/>
+                            <constraint firstItem="GcW-r3-RGx" firstAttribute="top" secondItem="PFe-m0-dMf" secondAttribute="top" id="Caq-aO-8pg"/>
+                            <constraint firstItem="MCu-xu-Ehc" firstAttribute="top" secondItem="GcW-r3-RGx" secondAttribute="bottom" id="EJk-ug-F8a"/>
+                            <constraint firstItem="U12-gG-Igl" firstAttribute="leading" secondItem="Yf9-wk-7rE" secondAttribute="leading" id="GvU-L9-n9b"/>
+                            <constraint firstItem="MCu-xu-Ehc" firstAttribute="leading" secondItem="Yf9-wk-7rE" secondAttribute="leading" id="Ida-Od-sRX"/>
+                            <constraint firstItem="WoN-yQ-od9" firstAttribute="top" secondItem="Yf9-wk-7rE" secondAttribute="top" id="LWa-9o-3YI"/>
+                            <constraint firstItem="Yf9-wk-7rE" firstAttribute="trailing" secondItem="MCu-xu-Ehc" secondAttribute="trailing" id="LiK-He-C1K"/>
+                            <constraint firstItem="Wb3-0V-vfc" firstAttribute="top" secondItem="PFe-m0-dMf" secondAttribute="top" id="MqJ-rn-965"/>
+                            <constraint firstItem="Yf9-wk-7rE" firstAttribute="trailing" secondItem="oIX-HK-L9B" secondAttribute="trailing" constant="19" id="PUE-TD-bek"/>
+                            <constraint firstItem="Wb3-0V-vfc" firstAttribute="leading" secondItem="Yf9-wk-7rE" secondAttribute="leading" id="S7s-I5-88s"/>
+                            <constraint firstItem="oIX-HK-L9B" firstAttribute="leading" secondItem="Yf9-wk-7rE" secondAttribute="leading" constant="19" id="WeU-JQ-eJg"/>
+                            <constraint firstItem="GcW-r3-RGx" firstAttribute="leading" secondItem="Yf9-wk-7rE" secondAttribute="leading" id="awi-js-JI7"/>
+                            <constraint firstItem="U12-gG-Igl" firstAttribute="trailing" secondItem="Yf9-wk-7rE" secondAttribute="trailing" id="eMP-ES-Rrp"/>
+                            <constraint firstItem="GcW-r3-RGx" firstAttribute="trailing" secondItem="Yf9-wk-7rE" secondAttribute="trailing" id="esM-3q-3fP"/>
+                            <constraint firstItem="U12-gG-Igl" firstAttribute="top" secondItem="PFe-m0-dMf" secondAttribute="top" id="fRH-Vv-SUL"/>
+                            <constraint firstItem="Yf9-wk-7rE" firstAttribute="bottom" secondItem="MCu-xu-Ehc" secondAttribute="bottom" id="gRv-po-Qvu"/>
+                            <constraint firstItem="MCu-xu-Ehc" firstAttribute="top" secondItem="F38-PA-t8E" secondAttribute="bottom" id="okc-bo-a5T"/>
+                            <constraint firstItem="F38-PA-t8E" firstAttribute="top" relation="greaterThanOrEqual" secondItem="oIX-HK-L9B" secondAttribute="bottom" id="vee-X5-Rhc"/>
+                            <constraint firstItem="Wb3-0V-vfc" firstAttribute="trailing" secondItem="Yf9-wk-7rE" secondAttribute="trailing" id="vlK-nJ-7Ye"/>
+                            <constraint firstItem="WoN-yQ-od9" firstAttribute="leading" secondItem="Yf9-wk-7rE" secondAttribute="leading" id="vnt-DF-v5q"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="Yf9-wk-7rE"/>
+                    </view>
+                    <connections>
+                        <outlet property="backButton" destination="WoN-yQ-od9" id="6IU-4Q-b3k"/>
+                        <outlet property="bgImageView" destination="U12-gG-Igl" id="de8-1h-whe"/>
+                        <outlet property="bgOverlayView" destination="Wb3-0V-vfc" id="k3q-P2-BKv"/>
+                        <outlet property="forwardButton" destination="jHO-Gw-YRT" id="65u-BC-qBU"/>
+                        <outlet property="miniatureImageView" destination="F38-PA-t8E" id="e9p-Jd-bkn"/>
+                        <outlet property="miniatureImageWidthConstraint" destination="BYo-S8-EIr" id="a1H-dI-FeU"/>
+                        <outlet property="nextButton" destination="NVT-Lw-NXQ" id="ic4-66-X9B"/>
+                        <outlet property="playPauseButton" destination="7ux-cf-CIC" id="SeQ-RA-bIT"/>
+                        <outlet property="prevButton" destination="oxD-97-8TJ" id="qvE-1c-ggo"/>
+                        <outlet property="progressSlider" destination="xRk-Fv-SVV" id="wV8-va-JBj"/>
+                        <outlet property="rewindButton" destination="CIs-L5-1o6" id="fzL-3y-Bj3"/>
+                        <outlet property="tableView" destination="GcW-r3-RGx" id="80a-G3-WXX"/>
+                        <outlet property="titleLabel" destination="oIX-HK-L9B" id="PkJ-Tr-d2a"/>
+                        <outlet property="toggleListButton" destination="P2M-Gd-U9n" id="uvL-FC-TYe"/>
+                        <outlet property="trackCurrentTimeLabel" destination="Tq9-n5-IXl" id="Q9m-yf-bST"/>
+                        <outlet property="trackNumberLabel" destination="FPS-kh-u2Y" id="QCQ-3W-qs5"/>
+                        <outlet property="trackTimeLabel" destination="iQL-Cs-lZ9" id="Ncl-V4-5d5"/>
+                        <outlet property="trackTitleLabel" destination="kYt-rI-R3U" id="MQl-b0-kJ0"/>
+                        <outlet property="trackTitleLabelTopConstraint" destination="57P-Dj-sgm" id="IFD-qn-oN3"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="BJZ-A0-5qi" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-1839.2" y="139.880059970015"/>
+        </scene>
+        <!--Navigation Controller-->
+        <scene sceneID="hBS-em-spN">
+            <objects>
+                <navigationController storyboardIdentifier="MainNavigationViewController" id="N0o-ld-YOE" sceneMemberID="viewController">
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="l6a-mK-BXh">
+                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </navigationBar>
+                    <connections>
+                        <segue destination="5Yc-8b-MQv" kind="relationship" relationship="rootViewController" id="thA-Cc-tfW"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="AAG-uA-ztp" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-1838" y="870"/>
+        </scene>
+        <!--View Controller-->
+        <scene sceneID="1gR-Qv-P6t">
+            <objects>
+                <viewController storyboardIdentifier="MainViewController" id="5Yc-8b-MQv" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="TzM-lh-lIV">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                        <viewLayoutGuide key="safeArea" id="J7W-4q-I6e"/>
+                    </view>
+                    <navigationItem key="navigationItem" id="itZ-So-TyF">
+                        <barButtonItem key="leftBarButtonItem" image="navbar_menu" id="p6S-gh-3ON">
+                            <connections>
+                                <segue destination="wgP-p6-oDr" kind="presentation" id="aBD-R9-PqL"/>
+                            </connections>
+                        </barButtonItem>
+                        <barButtonItem key="rightBarButtonItem" image="search" id="tuv-mC-d6Q"/>
+                    </navigationItem>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="fVQ-OJ-6z2" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-917.60000000000002" y="870.31484257871068"/>
+        </scene>
+        <!--Book Details View Controller-->
+        <scene sceneID="sOc-I6-WLI">
+            <objects>
+                <viewController storyboardIdentifier="BookDetailsViewController" automaticallyAdjustsScrollViewInsets="NO" id="2KM-9T-5YP" customClass="BookDetailsViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="PUc-oj-6Wx">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="GeJ-zH-BMu">
+                                <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <connections>
+                                    <outlet property="dataSource" destination="2KM-9T-5YP" id="m7H-qE-yB6"/>
+                                    <outlet property="delegate" destination="2KM-9T-5YP" id="49P-Xc-crY"/>
+                                </connections>
+                            </tableView>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IaK-R5-Evg">
+                                <rect key="frame" x="0.0" y="0.0" width="375" height="210"/>
+                                <subviews>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Axk-km-FgR" customClass="ActivityIndicatorButton" customModule="WolneLektury" customModuleProvider="target">
+                                        <rect key="frame" x="162.5" y="80" width="50" height="50"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="50" id="5p2-hA-0SM"/>
+                                            <constraint firstAttribute="width" constant="50" id="FaH-Dl-hH4"/>
+                                        </constraints>
+                                        <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <connections>
+                                            <action selector="refreshButtonAction:" destination="2KM-9T-5YP" eventType="touchUpInside" id="8VS-NZ-VV7"/>
+                                        </connections>
+                                    </button>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Zn-hg-knf">
+                                        <rect key="frame" x="50" y="175" width="275" height="21"/>
+                                        <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
+                                        <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                </subviews>
+                                <color key="backgroundColor" red="0.0" green="0.50196078431372548" blue="0.53333333333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="210" id="9uN-LL-U4c"/>
+                                    <constraint firstAttribute="trailing" secondItem="4Zn-hg-knf" secondAttribute="trailing" constant="50" id="Q2E-wJ-edj"/>
+                                    <constraint firstItem="4Zn-hg-knf" firstAttribute="leading" secondItem="IaK-R5-Evg" secondAttribute="leading" constant="50" id="aSE-ap-Enp"/>
+                                    <constraint firstAttribute="bottom" secondItem="4Zn-hg-knf" secondAttribute="bottom" constant="14" id="mOP-bO-57p"/>
+                                    <constraint firstItem="Axk-km-FgR" firstAttribute="centerY" secondItem="IaK-R5-Evg" secondAttribute="centerY" id="wlu-vh-A9m"/>
+                                    <constraint firstItem="Axk-km-FgR" firstAttribute="centerX" secondItem="IaK-R5-Evg" secondAttribute="centerX" id="xgL-2W-eRX"/>
+                                </constraints>
+                            </view>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BC1-lD-TWu">
+                                <rect key="frame" x="256.5" y="189" width="97" height="42"/>
+                                <subviews>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VzR-hn-YUV">
+                                        <rect key="frame" x="0.0" y="0.0" width="42" height="42"/>
+                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="42" id="enX-JE-3AP"/>
+                                            <constraint firstAttribute="width" constant="42" id="oaa-eg-jcq"/>
+                                        </constraints>
+                                        <state key="normal" image="icon_heart-outline-big"/>
+                                        <connections>
+                                            <action selector="favouriteButtonAction:" destination="2KM-9T-5YP" eventType="touchUpInside" id="HVz-GN-ure"/>
+                                        </connections>
+                                    </button>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8rm-XE-1IA">
+                                        <rect key="frame" x="55" y="0.0" width="42" height="42"/>
+                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="42" id="RLm-Zg-oqX"/>
+                                            <constraint firstAttribute="width" constant="42" id="fR8-iL-0q0"/>
+                                        </constraints>
+                                        <state key="normal" image="share"/>
+                                        <connections>
+                                            <action selector="sharebuttonAction:" destination="2KM-9T-5YP" eventType="touchUpInside" id="c5i-0l-KjO"/>
+                                        </connections>
+                                    </button>
+                                </subviews>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstItem="VzR-hn-YUV" firstAttribute="top" secondItem="BC1-lD-TWu" secondAttribute="top" id="JvB-Pc-MPN"/>
+                                    <constraint firstAttribute="trailing" secondItem="8rm-XE-1IA" secondAttribute="trailing" id="Qw9-jV-MZP"/>
+                                    <constraint firstItem="8rm-XE-1IA" firstAttribute="top" secondItem="BC1-lD-TWu" secondAttribute="top" id="a8M-Fa-9Wn"/>
+                                    <constraint firstAttribute="width" constant="97" id="cfw-iL-MF3"/>
+                                    <constraint firstAttribute="height" constant="42" id="jM1-ax-832"/>
+                                    <constraint firstItem="VzR-hn-YUV" firstAttribute="leading" secondItem="BC1-lD-TWu" secondAttribute="leading" id="xv4-bE-GGd"/>
+                                </constraints>
+                            </view>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iOU-QI-tHK">
+                                <rect key="frame" x="0.0" y="20" width="50" height="40"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="50" id="5Ip-fY-kt7"/>
+                                    <constraint firstAttribute="height" constant="40" id="L13-4d-H5b"/>
+                                </constraints>
+                                <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <state key="normal" image="navbar_back"/>
+                                <connections>
+                                    <action selector="backButtonAction:" destination="2KM-9T-5YP" eventType="touchUpInside" id="XZv-iJ-X3P"/>
+                                </connections>
+                            </button>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2zN-fk-dWA">
+                                <rect key="frame" x="0.0" y="540" width="375" height="127"/>
+                                <subviews>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dba-e5-UNO">
+                                        <rect key="frame" x="10" y="20" width="355" height="18"/>
+                                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                        <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="x47-Tx-RoF">
+                                        <rect key="frame" x="83.5" y="53" width="209" height="36"/>
+                                        <color key="backgroundColor" red="1" green="0.64705882349999999" blue="0.1843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="36" id="uyg-d1-2m1"/>
+                                        </constraints>
+                                        <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
+                                        <inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="30" maxY="0.0"/>
+                                        <state key="normal" title="ZOSTAŃ PRZYJACIELEM">
+                                            <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        </state>
+                                        <connections>
+                                            <action selector="becomeFriendButtonAction:" destination="2KM-9T-5YP" eventType="touchUpInside" id="0N9-gr-87U"/>
+                                        </connections>
+                                    </button>
+                                    <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="menu_premium" translatesAutoresizingMaskIntoConstraints="NO" id="cTF-Si-AQ6">
+                                        <rect key="frame" x="268.5" y="63" width="16" height="16"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" constant="16" id="2H0-1T-cQf"/>
+                                            <constraint firstAttribute="height" constant="16" id="Yfw-cb-JpL"/>
+                                        </constraints>
+                                    </imageView>
+                                </subviews>
+                                <color key="backgroundColor" red="0.0" green="0.50196078430000002" blue="0.53333333329999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <constraints>
+                                    <constraint firstItem="x47-Tx-RoF" firstAttribute="top" secondItem="dba-e5-UNO" secondAttribute="bottom" constant="15" id="J3r-Zu-cbG"/>
+                                    <constraint firstAttribute="trailing" secondItem="dba-e5-UNO" secondAttribute="trailing" constant="10" id="OjN-N1-C7h"/>
+                                    <constraint firstItem="dba-e5-UNO" firstAttribute="leading" secondItem="2zN-fk-dWA" secondAttribute="leading" constant="10" id="PZG-PQ-h3x"/>
+                                    <constraint firstItem="cTF-Si-AQ6" firstAttribute="centerY" secondItem="x47-Tx-RoF" secondAttribute="centerY" id="Vhj-fH-w7n"/>
+                                    <constraint firstItem="x47-Tx-RoF" firstAttribute="centerX" secondItem="2zN-fk-dWA" secondAttribute="centerX" id="ddM-LF-oHS"/>
+                                    <constraint firstItem="dba-e5-UNO" firstAttribute="top" secondItem="2zN-fk-dWA" secondAttribute="top" constant="20" id="f50-sY-IRy"/>
+                                    <constraint firstItem="cTF-Si-AQ6" firstAttribute="leading" secondItem="x47-Tx-RoF" secondAttribute="trailing" constant="-24" id="fHC-zP-IuZ"/>
+                                    <constraint firstAttribute="bottom" secondItem="x47-Tx-RoF" secondAttribute="bottom" constant="38" id="kXJ-PG-5Vb"/>
+                                </constraints>
+                            </view>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="iOU-QI-tHK" firstAttribute="leading" secondItem="Tx8-R4-wD7" secondAttribute="leading" id="23d-ee-UJI"/>
+                            <constraint firstItem="GeJ-zH-BMu" firstAttribute="leading" secondItem="Tx8-R4-wD7" secondAttribute="leading" id="ETL-Yr-e2b"/>
+                            <constraint firstItem="2zN-fk-dWA" firstAttribute="bottom" secondItem="PUc-oj-6Wx" secondAttribute="bottom" id="LmA-RH-gGX"/>
+                            <constraint firstItem="2zN-fk-dWA" firstAttribute="leading" secondItem="Tx8-R4-wD7" secondAttribute="leading" id="MsK-4D-17k"/>
+                            <constraint firstItem="IaK-R5-Evg" firstAttribute="leading" secondItem="Tx8-R4-wD7" secondAttribute="leading" id="SDs-Qg-LPT"/>
+                            <constraint firstItem="IaK-R5-Evg" firstAttribute="top" secondItem="PUc-oj-6Wx" secondAttribute="top" id="WIN-fz-xjQ"/>
+                            <constraint firstItem="IaK-R5-Evg" firstAttribute="trailing" secondItem="Tx8-R4-wD7" secondAttribute="trailing" id="cLi-5R-b6z"/>
+                            <constraint firstItem="GeJ-zH-BMu" firstAttribute="top" secondItem="PUc-oj-6Wx" secondAttribute="top" id="hK2-0Y-GPc"/>
+                            <constraint firstItem="2zN-fk-dWA" firstAttribute="trailing" secondItem="Tx8-R4-wD7" secondAttribute="trailing" id="jWA-xy-P0G"/>
+                            <constraint firstItem="iOU-QI-tHK" firstAttribute="top" secondItem="Tx8-R4-wD7" secondAttribute="top" id="lAc-pw-thH"/>
+                            <constraint firstItem="Tx8-R4-wD7" firstAttribute="trailing" secondItem="BC1-lD-TWu" secondAttribute="centerX" constant="70" id="qff-BS-UfS"/>
+                            <constraint firstItem="Tx8-R4-wD7" firstAttribute="trailing" secondItem="GeJ-zH-BMu" secondAttribute="trailing" id="rfY-ah-Mx0"/>
+                            <constraint firstItem="iOU-QI-tHK" firstAttribute="top" secondItem="Tx8-R4-wD7" secondAttribute="top" id="tJX-5u-i8u"/>
+                            <constraint firstAttribute="bottom" secondItem="GeJ-zH-BMu" secondAttribute="bottom" id="tZe-s2-h0T"/>
+                            <constraint firstItem="BC1-lD-TWu" firstAttribute="centerY" secondItem="IaK-R5-Evg" secondAttribute="bottom" id="v2E-Nh-qsA"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="Tx8-R4-wD7"/>
+                    </view>
+                    <extendedEdge key="edgesForExtendedLayout"/>
+                    <navigationItem key="navigationItem" id="Y8r-kH-I6o"/>
+                    <connections>
+                        <outlet property="backButton" destination="iOU-QI-tHK" id="eqV-X5-TOp"/>
+                        <outlet property="becomeFriendButton" destination="x47-Tx-RoF" id="qSx-10-X1D"/>
+                        <outlet property="becomeFriendLabel" destination="dba-e5-UNO" id="I9K-I6-ttU"/>
+                        <outlet property="becomeFriendStarImageView" destination="cTF-Si-AQ6" id="BWk-Yn-s4W"/>
+                        <outlet property="becomeFriendView" destination="2zN-fk-dWA" id="cwl-kj-fnV"/>
+                        <outlet property="buttonsContainer" destination="BC1-lD-TWu" id="jza-9o-46H"/>
+                        <outlet property="buttonsContainerWidthConstraint" destination="cfw-iL-MF3" id="i46-Ti-i0j"/>
+                        <outlet property="favouriteButton" destination="VzR-hn-YUV" id="crF-1E-Rb0"/>
+                        <outlet property="headerHeightConstraint" destination="9uN-LL-U4c" id="Aah-IA-uHh"/>
+                        <outlet property="headerLabel" destination="4Zn-hg-knf" id="gVt-jx-ha9"/>
+                        <outlet property="headerView" destination="IaK-R5-Evg" id="0LK-pJ-8l2"/>
+                        <outlet property="refreshButton" destination="Axk-km-FgR" id="ZAU-5j-ozl"/>
+                        <outlet property="shareButton" destination="8rm-XE-1IA" id="mC9-nW-jep"/>
+                        <outlet property="tableView" destination="GeJ-zH-BMu" id="1U5-dS-tcw"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="eFk-Yt-LGX" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-3562.4000000000001" y="139.880059970015"/>
+        </scene>
+        <!--Library View Controller-->
+        <scene sceneID="9kE-de-Fx8">
+            <objects>
+                <viewController storyboardIdentifier="LibraryViewController" id="jRD-Sd-cD2" customClass="LibraryViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="niH-yl-91f">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="d7f-lS-oOK">
+                                <rect key="frame" x="0.0" y="64" width="375" height="603"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <connections>
+                                    <outlet property="dataSource" destination="jRD-Sd-cD2" id="a8n-MP-EJd"/>
+                                    <outlet property="delegate" destination="jRD-Sd-cD2" id="owu-sG-aX3"/>
+                                </connections>
+                            </tableView>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="aSG-5H-bhq" firstAttribute="trailing" secondItem="d7f-lS-oOK" secondAttribute="trailing" id="T9M-yN-4g8"/>
+                            <constraint firstAttribute="bottom" secondItem="d7f-lS-oOK" secondAttribute="bottom" id="VrF-kB-jTD"/>
+                            <constraint firstItem="d7f-lS-oOK" firstAttribute="leading" secondItem="aSG-5H-bhq" secondAttribute="leading" id="evZ-nS-kN5"/>
+                            <constraint firstItem="d7f-lS-oOK" firstAttribute="top" secondItem="aSG-5H-bhq" secondAttribute="top" id="jBK-Uw-pKv"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="aSG-5H-bhq"/>
+                    </view>
+                    <navigationItem key="navigationItem" id="VRg-LU-n1h"/>
+                    <connections>
+                        <outlet property="tableView" destination="d7f-lS-oOK" id="E4G-Pn-EYp"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="pJD-dY-5dN" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-3562" y="870"/>
+        </scene>
+        <!--Side Menu Navigation Controller-->
+        <scene sceneID="wGB-di-sHs">
+            <objects>
+                <navigationController storyboardIdentifier="UISideMenuNavigationController" navigationBarHidden="YES" id="wgP-p6-oDr" customClass="WLSideMenuNavigationController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="iFA-3F-6Zj">
+                        <autoresizingMask key="autoresizingMask"/>
+                    </navigationBar>
+                    <toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="MSS-Rs-No7">
+                        <autoresizingMask key="autoresizingMask"/>
+                    </toolbar>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="boolean" keyPath="leftSide" value="YES"/>
+                    </userDefinedRuntimeAttributes>
+                    <connections>
+                        <segue destination="4Pn-Kw-bbB" kind="relationship" relationship="rootViewController" id="snB-rb-3Ji"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="ucP-gm-pQ7" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-1710" y="1781"/>
+        </scene>
+        <!--Navigation Controller-->
+        <scene sceneID="tJp-A4-2Hs">
+            <objects>
+                <navigationController id="dkP-9S-heE" sceneMemberID="viewController">
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="eKy-pA-wJq">
+                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </navigationBar>
+                    <connections>
+                        <segue destination="QsI-WC-Gqz" kind="relationship" relationship="rootViewController" id="1nx-4C-SJy"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="4VS-RE-MRn" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-2310" y="2587"/>
+        </scene>
+        <!--Filter View Controller-->
+        <scene sceneID="unL-UJ-q7T">
+            <objects>
+                <viewController storyboardIdentifier="FilterViewController" id="QsI-WC-Gqz" customClass="FilterViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="eNM-KE-uf0">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" prefetchingEnabled="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vUW-hV-3K9">
+                                <rect key="frame" x="0.0" y="64" width="375" height="603"/>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="2ds-iV-l2f">
+                                    <size key="itemSize" width="136" height="30"/>
+                                    <size key="headerReferenceSize" width="0.0" height="0.0"/>
+                                    <size key="footerReferenceSize" width="0.0" height="0.0"/>
+                                    <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
+                                </collectionViewFlowLayout>
+                                <cells/>
+                            </collectionView>
+                        </subviews>
+                        <color key="backgroundColor" red="0.0" green="0.40392156862745099" blue="0.42745098039215684" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="bottom" secondItem="vUW-hV-3K9" secondAttribute="bottom" id="Ixt-Kd-EuW"/>
+                            <constraint firstItem="dFB-Ru-h0F" firstAttribute="trailing" secondItem="vUW-hV-3K9" secondAttribute="trailing" id="LmT-F5-AmU"/>
+                            <constraint firstItem="vUW-hV-3K9" firstAttribute="leading" secondItem="dFB-Ru-h0F" secondAttribute="leading" id="mxn-ro-zZl"/>
+                            <constraint firstItem="vUW-hV-3K9" firstAttribute="top" secondItem="dFB-Ru-h0F" secondAttribute="top" id="wFT-Le-c1I"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="dFB-Ru-h0F"/>
+                    </view>
+                    <navigationItem key="navigationItem" id="UOm-fs-qPc">
+                        <barButtonItem key="leftBarButtonItem" image="navbar_close" id="iW2-fZ-hKf">
+                            <connections>
+                                <action selector="closeAction:" destination="QsI-WC-Gqz" id="2uc-Cw-hOO"/>
+                            </connections>
+                        </barButtonItem>
+                        <barButtonItem key="rightBarButtonItem" image="navbar_tick" id="I8C-xe-hKm">
+                            <connections>
+                                <action selector="confirmAction:" destination="QsI-WC-Gqz" id="J8e-k8-4a2"/>
+                            </connections>
+                        </barButtonItem>
+                    </navigationItem>
+                    <connections>
+                        <outlet property="collectionView" destination="vUW-hV-3K9" id="h2Y-9b-j3l"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="tM0-Xz-407" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-1556" y="2587"/>
+        </scene>
+        <!--Menu View Controller-->
+        <scene sceneID="0gM-rv-pOk">
+            <objects>
+                <viewController id="4Pn-Kw-bbB" customClass="MenuViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="bEN-UF-N4E">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="3vv-iP-Asu">
+                                <rect key="frame" x="0.0" y="20" width="375" height="547"/>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <connections>
+                                    <outlet property="dataSource" destination="4Pn-Kw-bbB" id="UnK-ow-8JK"/>
+                                    <outlet property="delegate" destination="4Pn-Kw-bbB" id="pAL-Kz-KSv"/>
+                                </connections>
+                            </tableView>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DoZ-Ag-ujS">
+                                <rect key="frame" x="0.0" y="567" width="375" height="100"/>
+                                <color key="backgroundColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/>
+                            </view>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7cL-i9-Czu">
+                                <rect key="frame" x="0.0" y="567" width="375" height="100"/>
+                                <subviews>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VCp-Os-nxa">
+                                        <rect key="frame" x="0.0" y="0.0" width="375" height="100"/>
+                                        <subviews>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ZALOGOWANY JAKO" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5I2-ac-Yg1">
+                                                <rect key="frame" x="137" y="16" width="102" height="12"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lxm-7E-lvO">
+                                                <rect key="frame" x="126.5" y="38" width="123" height="30"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="width" constant="123" id="GKy-n6-wW4"/>
+                                                    <constraint firstAttribute="height" constant="30" id="xJt-hL-6XS"/>
+                                                </constraints>
+                                                <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
+                                                <state key="normal" title="Button">
+                                                    <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                </state>
+                                                <connections>
+                                                    <action selector="logoutButtonAction:" destination="4Pn-Kw-bbB" eventType="touchUpInside" id="4fR-GJ-rZr"/>
+                                                </connections>
+                                            </button>
+                                        </subviews>
+                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="100" id="QRy-T0-QIQ"/>
+                                            <constraint firstItem="5I2-ac-Yg1" firstAttribute="top" secondItem="VCp-Os-nxa" secondAttribute="top" constant="16" id="YMO-g2-tsp"/>
+                                            <constraint firstItem="lxm-7E-lvO" firstAttribute="centerX" secondItem="VCp-Os-nxa" secondAttribute="centerX" id="bku-Kq-fsd"/>
+                                            <constraint firstItem="lxm-7E-lvO" firstAttribute="top" secondItem="5I2-ac-Yg1" secondAttribute="bottom" constant="10" id="rSl-7D-9uc"/>
+                                            <constraint firstItem="5I2-ac-Yg1" firstAttribute="centerX" secondItem="VCp-Os-nxa" secondAttribute="centerX" id="yyo-0u-hgM"/>
+                                        </constraints>
+                                    </view>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="t5a-lg-ZvD">
+                                        <rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
+                                        <subviews>
+                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="afR-3I-KBr">
+                                                <rect key="frame" x="126.5" y="10" width="123" height="30"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="width" constant="123" id="Vf6-e8-QRb"/>
+                                                    <constraint firstAttribute="height" constant="30" id="mdy-qC-cY2"/>
+                                                </constraints>
+                                                <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
+                                                <state key="normal" title="Button">
+                                                    <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                </state>
+                                                <connections>
+                                                    <action selector="loginButtonAction:" destination="4Pn-Kw-bbB" eventType="touchUpInside" id="mas-yi-UHl"/>
+                                                </connections>
+                                            </button>
+                                        </subviews>
+                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <constraints>
+                                            <constraint firstItem="afR-3I-KBr" firstAttribute="top" secondItem="t5a-lg-ZvD" secondAttribute="top" constant="10" id="4n9-uM-Pih"/>
+                                            <constraint firstItem="afR-3I-KBr" firstAttribute="centerX" secondItem="t5a-lg-ZvD" secondAttribute="centerX" id="81E-gp-Ogm"/>
+                                            <constraint firstAttribute="height" constant="50" id="zEJ-fB-WSd"/>
+                                        </constraints>
+                                    </view>
+                                </subviews>
+                                <color key="backgroundColor" red="0.0039215686274509803" green="0.50588235294117645" blue="0.53725490196078429" alpha="1" colorSpace="calibratedRGB"/>
+                                <constraints>
+                                    <constraint firstItem="VCp-Os-nxa" firstAttribute="top" secondItem="7cL-i9-Czu" secondAttribute="top" id="4Pr-Ko-ObX"/>
+                                    <constraint firstAttribute="trailing" secondItem="t5a-lg-ZvD" secondAttribute="trailing" id="8pG-RN-tAq"/>
+                                    <constraint firstItem="VCp-Os-nxa" firstAttribute="leading" secondItem="7cL-i9-Czu" secondAttribute="leading" id="IR7-OU-gQS"/>
+                                    <constraint firstAttribute="trailing" secondItem="VCp-Os-nxa" secondAttribute="trailing" id="RTu-Vc-8OI"/>
+                                    <constraint firstItem="t5a-lg-ZvD" firstAttribute="top" secondItem="7cL-i9-Czu" secondAttribute="top" id="Zdz-UU-TLP"/>
+                                    <constraint firstItem="t5a-lg-ZvD" firstAttribute="leading" secondItem="7cL-i9-Czu" secondAttribute="leading" id="hnS-hq-OLa"/>
+                                    <constraint firstAttribute="height" constant="100" id="odS-9u-OVN"/>
+                                </constraints>
+                            </view>
+                        </subviews>
+                        <color key="backgroundColor" red="0.0" green="0.40392156862745099" blue="0.42745098039215684" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="7cL-i9-Czu" firstAttribute="leading" secondItem="Dca-IF-5jM" secondAttribute="leading" id="1VB-DH-wwp"/>
+                            <constraint firstItem="Dca-IF-5jM" firstAttribute="trailing" secondItem="7cL-i9-Czu" secondAttribute="trailing" id="6IH-ge-2Td"/>
+                            <constraint firstItem="DoZ-Ag-ujS" firstAttribute="trailing" secondItem="Dca-IF-5jM" secondAttribute="trailing" id="7D8-xK-Ma5"/>
+                            <constraint firstItem="3vv-iP-Asu" firstAttribute="top" secondItem="Dca-IF-5jM" secondAttribute="top" id="AJh-Wo-4X0"/>
+                            <constraint firstItem="DoZ-Ag-ujS" firstAttribute="leading" secondItem="Dca-IF-5jM" secondAttribute="leading" id="C8f-L3-AZy"/>
+                            <constraint firstItem="3vv-iP-Asu" firstAttribute="trailing" secondItem="Dca-IF-5jM" secondAttribute="trailing" id="MgG-Ud-oxI"/>
+                            <constraint firstItem="Dca-IF-5jM" firstAttribute="bottom" secondItem="7cL-i9-Czu" secondAttribute="bottom" id="UUa-B4-aEY"/>
+                            <constraint firstItem="DoZ-Ag-ujS" firstAttribute="bottom" secondItem="bEN-UF-N4E" secondAttribute="bottom" id="jy3-CE-TBQ"/>
+                            <constraint firstItem="3vv-iP-Asu" firstAttribute="leading" secondItem="Dca-IF-5jM" secondAttribute="leading" id="kak-4R-YC4"/>
+                            <constraint firstItem="DoZ-Ag-ujS" firstAttribute="top" secondItem="7cL-i9-Czu" secondAttribute="top" id="lb2-Tu-hex"/>
+                            <constraint firstItem="7cL-i9-Czu" firstAttribute="top" secondItem="3vv-iP-Asu" secondAttribute="bottom" id="yLL-F7-H2r"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="Dca-IF-5jM"/>
+                    </view>
+                    <navigationItem key="navigationItem" id="yMC-xp-CEq"/>
+                    <nil key="simulatedTopBarMetrics"/>
+                    <connections>
+                        <outlet property="bottomView" destination="7cL-i9-Czu" id="tN6-go-WBS"/>
+                        <outlet property="bottomViewBgView" destination="DoZ-Ag-ujS" id="jPN-yg-Wu0"/>
+                        <outlet property="bottomViewHeightConstraint" destination="odS-9u-OVN" id="gQN-2p-9rl"/>
+                        <outlet property="guestBottomView" destination="t5a-lg-ZvD" id="jQr-Yc-8ru"/>
+                        <outlet property="loggedInLabel" destination="5I2-ac-Yg1" id="21G-Vf-V5I"/>
+                        <outlet property="loginButton" destination="afR-3I-KBr" id="abu-6F-eRI"/>
+                        <outlet property="logoutButton" destination="lxm-7E-lvO" id="2Uf-zv-poY"/>
+                        <outlet property="tableView" destination="3vv-iP-Asu" id="lPz-GF-oC4"/>
+                        <outlet property="userBottomView" destination="VCp-Os-nxa" id="f8D-G4-q0P"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="pKv-Y4-IAM" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-919.20000000000005" y="1780.6596701649178"/>
+        </scene>
+        <!--Search View Controller-->
+        <scene sceneID="MRZ-TX-hI1">
+            <objects>
+                <viewController storyboardIdentifier="SearchViewController" id="j6W-Ut-hwW" customClass="SearchViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="OTi-FC-ht8">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SxK-V5-yTp">
+                                <rect key="frame" x="0.0" y="64" width="375" height="44"/>
+                                <subviews>
+                                    <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" prefetchingEnabled="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jhy-XZ-zEQ">
+                                        <rect key="frame" x="0.0" y="0.0" width="325" height="44"/>
+                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="TQY-53-kpe">
+                                            <size key="itemSize" width="50" height="44"/>
+                                            <size key="headerReferenceSize" width="0.0" height="0.0"/>
+                                            <size key="footerReferenceSize" width="0.0" height="0.0"/>
+                                            <inset key="sectionInset" minX="4" minY="0.0" maxX="0.0" maxY="0.0"/>
+                                        </collectionViewFlowLayout>
+                                        <cells>
+                                            <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="OJ5-yF-yYi">
+                                                <rect key="frame" x="4" y="0.0" width="50" height="44"/>
+                                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                                <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
+                                                    <rect key="frame" x="0.0" y="0.0" width="50" height="44"/>
+                                                    <autoresizingMask key="autoresizingMask"/>
+                                                </view>
+                                            </collectionViewCell>
+                                        </cells>
+                                    </collectionView>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xNQ-e1-qAm">
+                                        <rect key="frame" x="340" y="8" width="28" height="28"/>
+                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" constant="28" id="B5X-J5-WFo"/>
+                                            <constraint firstAttribute="height" constant="28" id="c3B-HB-dBK"/>
+                                        </constraints>
+                                        <state key="normal" image="navbar_close-small"/>
+                                        <connections>
+                                            <action selector="filterCloseButtonAction:" destination="j6W-Ut-hwW" eventType="touchUpInside" id="znY-PB-jUE"/>
+                                        </connections>
+                                    </button>
+                                </subviews>
+                                <color key="backgroundColor" red="0.0" green="0.40392156862745099" blue="0.42745098039215684" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="44" id="LQ2-ul-EwT"/>
+                                    <constraint firstItem="xNQ-e1-qAm" firstAttribute="centerY" secondItem="SxK-V5-yTp" secondAttribute="centerY" id="PN7-QS-Xae"/>
+                                    <constraint firstItem="Jhy-XZ-zEQ" firstAttribute="top" secondItem="SxK-V5-yTp" secondAttribute="top" id="Q5x-CR-BaL"/>
+                                    <constraint firstAttribute="trailing" secondItem="Jhy-XZ-zEQ" secondAttribute="trailing" constant="50" id="R4N-Ft-iBh"/>
+                                    <constraint firstItem="Jhy-XZ-zEQ" firstAttribute="leading" secondItem="SxK-V5-yTp" secondAttribute="leading" id="m6A-pL-ZBO"/>
+                                    <constraint firstAttribute="trailing" secondItem="xNQ-e1-qAm" secondAttribute="trailing" constant="7" id="wMy-Yt-elb"/>
+                                    <constraint firstAttribute="bottom" secondItem="Jhy-XZ-zEQ" secondAttribute="bottom" id="zgP-GR-tWY"/>
+                                </constraints>
+                            </view>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="j6m-Gz-ZWq">
+                                <rect key="frame" x="0.0" y="108" width="375" height="559"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <view key="tableFooterView" contentMode="scaleToFill" id="a6v-NV-8PO">
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+                                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                    <subviews>
+                                        <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="jji-Dr-DIC">
+                                            <rect key="frame" x="177.5" y="12" width="20" height="20"/>
+                                        </activityIndicatorView>
+                                    </subviews>
+                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                    <constraints>
+                                        <constraint firstItem="jji-Dr-DIC" firstAttribute="centerY" secondItem="a6v-NV-8PO" secondAttribute="centerY" id="Lev-Q0-m4C"/>
+                                        <constraint firstItem="jji-Dr-DIC" firstAttribute="centerX" secondItem="a6v-NV-8PO" secondAttribute="centerX" id="ht6-cv-tgO"/>
+                                    </constraints>
+                                </view>
+                            </tableView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rZw-17-V6l">
+                                <rect key="frame" x="166.5" y="323" width="42" height="21"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MuP-B7-Jzs" customClass="ActivityIndicatorButton" customModule="WolneLektury" customModuleProvider="target">
+                                <rect key="frame" x="162.5" y="308.5" width="50" height="50"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="50" id="QBX-5K-k5N"/>
+                                    <constraint firstAttribute="width" constant="50" id="Ujr-Lc-Wfh"/>
+                                </constraints>
+                                <color key="tintColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/>
+                                <connections>
+                                    <action selector="refreshDataButtonAction:" destination="j6W-Ut-hwW" eventType="touchUpInside" id="nHQ-Eg-IaC"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="SxK-V5-yTp" firstAttribute="bottom" secondItem="VoV-Ht-bsL" secondAttribute="top" constant="44" id="1ji-8a-bvt"/>
+                            <constraint firstItem="VoV-Ht-bsL" firstAttribute="trailing" secondItem="SxK-V5-yTp" secondAttribute="trailing" id="7pU-Hh-P0S"/>
+                            <constraint firstItem="MuP-B7-Jzs" firstAttribute="centerX" secondItem="OTi-FC-ht8" secondAttribute="centerX" id="EvX-1h-TGM"/>
+                            <constraint firstAttribute="bottom" secondItem="j6m-Gz-ZWq" secondAttribute="bottom" id="K0M-fw-W0u"/>
+                            <constraint firstItem="j6m-Gz-ZWq" firstAttribute="top" secondItem="SxK-V5-yTp" secondAttribute="bottom" id="QqQ-Ag-tUm"/>
+                            <constraint firstItem="VoV-Ht-bsL" firstAttribute="trailing" secondItem="j6m-Gz-ZWq" secondAttribute="trailing" id="U1M-NA-T7d"/>
+                            <constraint firstItem="rZw-17-V6l" firstAttribute="centerY" secondItem="OTi-FC-ht8" secondAttribute="centerY" id="WRn-uD-p0a"/>
+                            <constraint firstItem="j6m-Gz-ZWq" firstAttribute="leading" secondItem="VoV-Ht-bsL" secondAttribute="leading" id="cRH-ZY-Qoj"/>
+                            <constraint firstItem="MuP-B7-Jzs" firstAttribute="centerY" secondItem="OTi-FC-ht8" secondAttribute="centerY" id="fTB-oh-Ns0"/>
+                            <constraint firstItem="SxK-V5-yTp" firstAttribute="leading" secondItem="VoV-Ht-bsL" secondAttribute="leading" id="mhO-Em-m1C"/>
+                            <constraint firstItem="rZw-17-V6l" firstAttribute="centerX" secondItem="OTi-FC-ht8" secondAttribute="centerX" id="r5K-AN-AN1"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="VoV-Ht-bsL"/>
+                    </view>
+                    <navigationItem key="navigationItem" id="2ES-G9-iOi">
+                        <barButtonItem key="leftBarButtonItem" image="ic_toggle" id="Gud-3J-9Mb"/>
+                        <barButtonItem key="rightBarButtonItem" image="navbar_filter" id="BLS-kz-COq">
+                            <connections>
+                                <action selector="filterButtonAction:" destination="j6W-Ut-hwW" id="omu-2I-hds"/>
+                            </connections>
+                        </barButtonItem>
+                    </navigationItem>
+                    <connections>
+                        <outlet property="filterCloseButton" destination="xNQ-e1-qAm" id="c8f-fY-hTz"/>
+                        <outlet property="filterCollectionView" destination="Jhy-XZ-zEQ" id="i7G-D0-wg1"/>
+                        <outlet property="filterContainer" destination="SxK-V5-yTp" id="jDd-Z0-Wcb"/>
+                        <outlet property="filterTopConstraint" destination="1ji-8a-bvt" id="6Is-Vi-Qdf"/>
+                        <outlet property="footerViewActivityIndicator" destination="jji-Dr-DIC" id="ANl-4s-jRt"/>
+                        <outlet property="noDataLabel" destination="rZw-17-V6l" id="MS7-ih-b7d"/>
+                        <outlet property="refreshDataButton" destination="MuP-B7-Jzs" id="fs6-mZ-rZ2"/>
+                        <outlet property="tableView" destination="j6m-Gz-ZWq" id="bpt-4M-zZ3"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="Ahd-av-RQy" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-3562.4000000000001" y="1780.6596701649178"/>
+        </scene>
+        <!--Navigation Controller-->
+        <scene sceneID="iyl-nZ-Hrj">
+            <objects>
+                <navigationController id="UIY-RQ-eSv" sceneMemberID="viewController">
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="fWe-KX-fmg">
+                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </navigationBar>
+                    <connections>
+                        <segue destination="jRD-Sd-cD2" kind="relationship" relationship="rootViewController" id="9GK-it-BMx"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="48Y-OP-PKb" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-4306" y="870"/>
+        </scene>
+        <!--Navigation Controller-->
+        <scene sceneID="m6J-bD-51l">
+            <objects>
+                <navigationController id="hHe-RL-t8r" sceneMemberID="viewController">
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="MDg-RX-T9g">
+                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </navigationBar>
+                    <connections>
+                        <segue destination="j6W-Ut-hwW" kind="relationship" relationship="rootViewController" id="YQt-kw-Zjh"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="GGI-BG-tnp" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-4306" y="1781"/>
+        </scene>
+        <!--Downloaded List View Controller-->
+        <scene sceneID="GPd-aV-QQB">
+            <objects>
+                <viewController storyboardIdentifier="DownloadedListViewController" id="2HM-wH-jXr" customClass="DownloadedListViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="6EN-Sv-IaY">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="aZM-ON-dOh">
+                                <rect key="frame" x="0.0" y="20" width="375" height="647"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            </tableView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="snn-Nk-846">
+                                <rect key="frame" x="166.5" y="323" width="42" height="21"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="mgg-DJ-1eM" firstAttribute="bottom" secondItem="aZM-ON-dOh" secondAttribute="bottom" id="Hg9-KI-l57"/>
+                            <constraint firstItem="mgg-DJ-1eM" firstAttribute="trailing" secondItem="aZM-ON-dOh" secondAttribute="trailing" id="WKE-aK-KKx"/>
+                            <constraint firstItem="snn-Nk-846" firstAttribute="centerY" secondItem="6EN-Sv-IaY" secondAttribute="centerY" id="iJ5-Aq-JBN"/>
+                            <constraint firstItem="aZM-ON-dOh" firstAttribute="top" secondItem="mgg-DJ-1eM" secondAttribute="top" id="pga-ty-wEv"/>
+                            <constraint firstItem="aZM-ON-dOh" firstAttribute="leading" secondItem="mgg-DJ-1eM" secondAttribute="leading" id="tlh-8H-GEJ"/>
+                            <constraint firstItem="snn-Nk-846" firstAttribute="centerX" secondItem="6EN-Sv-IaY" secondAttribute="centerX" id="x5S-Bz-23K"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="mgg-DJ-1eM"/>
+                    </view>
+                    <connections>
+                        <outlet property="noDataLabel" destination="snn-Nk-846" id="yIF-vN-6vW"/>
+                        <outlet property="tableView" destination="aZM-ON-dOh" id="lRa-XS-XYa"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dsX-A8-FNZ" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-3562" y="2587"/>
+        </scene>
+        <!--Book List View Controller-->
+        <scene sceneID="dXB-M0-0B8">
+            <objects>
+                <viewController storyboardIdentifier="BookListViewController" id="O3q-zB-NJo" customClass="BookListViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="fAm-2B-U6s">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="tQC-jf-UCf">
+                                <rect key="frame" x="0.0" y="20" width="375" height="647"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <view key="tableFooterView" contentMode="scaleToFill" id="QDZ-lM-XoL">
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+                                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                    <subviews>
+                                        <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="fWx-YR-gan">
+                                            <rect key="frame" x="177.5" y="12" width="20" height="20"/>
+                                        </activityIndicatorView>
+                                    </subviews>
+                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                    <constraints>
+                                        <constraint firstItem="fWx-YR-gan" firstAttribute="centerX" secondItem="QDZ-lM-XoL" secondAttribute="centerX" id="Rp9-YQ-Dw1"/>
+                                        <constraint firstItem="fWx-YR-gan" firstAttribute="centerY" secondItem="QDZ-lM-XoL" secondAttribute="centerY" id="jgC-Rs-vSo"/>
+                                    </constraints>
+                                </view>
+                            </tableView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KZo-jI-iBe">
+                                <rect key="frame" x="166.5" y="323" width="42" height="21"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="C61-NR-iff" customClass="ActivityIndicatorButton" customModule="WolneLektury" customModuleProvider="target">
+                                <rect key="frame" x="162.5" y="308.5" width="50" height="50"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="50" id="CYy-Lw-DYs"/>
+                                    <constraint firstAttribute="height" constant="50" id="LY0-Pj-urI"/>
+                                </constraints>
+                                <color key="tintColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/>
+                                <connections>
+                                    <action selector="refreshDataButtonAction:" destination="O3q-zB-NJo" eventType="touchUpInside" id="jx5-5y-YnX"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="KZo-jI-iBe" firstAttribute="centerY" secondItem="fAm-2B-U6s" secondAttribute="centerY" id="QzF-WE-7lR"/>
+                            <constraint firstItem="gjR-UW-DgB" firstAttribute="trailing" secondItem="tQC-jf-UCf" secondAttribute="trailing" id="R1V-YW-G1Z"/>
+                            <constraint firstItem="C61-NR-iff" firstAttribute="centerX" secondItem="fAm-2B-U6s" secondAttribute="centerX" id="XYe-wn-Goa"/>
+                            <constraint firstItem="C61-NR-iff" firstAttribute="centerY" secondItem="fAm-2B-U6s" secondAttribute="centerY" id="YZJ-EB-b6Q"/>
+                            <constraint firstItem="tQC-jf-UCf" firstAttribute="leading" secondItem="gjR-UW-DgB" secondAttribute="leading" id="dBy-hP-MEm"/>
+                            <constraint firstAttribute="bottom" secondItem="tQC-jf-UCf" secondAttribute="bottom" id="gEU-Ty-Q1G"/>
+                            <constraint firstItem="KZo-jI-iBe" firstAttribute="centerX" secondItem="fAm-2B-U6s" secondAttribute="centerX" id="iLB-rU-f4c"/>
+                            <constraint firstItem="tQC-jf-UCf" firstAttribute="top" secondItem="gjR-UW-DgB" secondAttribute="top" id="pyE-oM-nW6"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="gjR-UW-DgB"/>
+                    </view>
+                    <connections>
+                        <outlet property="footerViewActivityIndicator" destination="fWx-YR-gan" id="jRO-Td-BCl"/>
+                        <outlet property="noDataLabel" destination="KZo-jI-iBe" id="D2R-QR-y3g"/>
+                        <outlet property="refreshDataButton" destination="C61-NR-iff" id="nf8-n1-15a"/>
+                        <outlet property="tableView" destination="tQC-jf-UCf" id="CKw-Of-Wh5"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="uV2-Bo-Wc6" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-3562" y="2587"/>
+        </scene>
+        <!--News View Controller-->
+        <scene sceneID="0Ye-uh-boa">
+            <objects>
+                <viewController storyboardIdentifier="NewsViewController" id="rrY-za-qx7" customClass="NewsViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="Gcq-Xg-VxI">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="14h-9k-LYj">
+                                <rect key="frame" x="0.0" y="20" width="375" height="647"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <view key="tableFooterView" contentMode="scaleToFill" id="Kxx-Et-4dj">
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+                                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                    <subviews>
+                                        <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="KuZ-W4-SQg">
+                                            <rect key="frame" x="177.5" y="12" width="20" height="20"/>
+                                        </activityIndicatorView>
+                                    </subviews>
+                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                    <constraints>
+                                        <constraint firstItem="KuZ-W4-SQg" firstAttribute="centerX" secondItem="Kxx-Et-4dj" secondAttribute="centerX" id="klQ-qx-Thh"/>
+                                        <constraint firstItem="KuZ-W4-SQg" firstAttribute="centerY" secondItem="Kxx-Et-4dj" secondAttribute="centerY" id="zkz-da-GoY"/>
+                                    </constraints>
+                                </view>
+                            </tableView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xcd-zY-l5e">
+                                <rect key="frame" x="166.5" y="323" width="42" height="21"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DYZ-pL-yiT" customClass="ActivityIndicatorButton" customModule="WolneLektury" customModuleProvider="target">
+                                <rect key="frame" x="162.5" y="308.5" width="50" height="50"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="50" id="BEo-SV-eni"/>
+                                    <constraint firstAttribute="height" constant="50" id="iNd-5V-o1H"/>
+                                </constraints>
+                                <color key="tintColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/>
+                                <connections>
+                                    <action selector="refreshDataButtonAction:" destination="rrY-za-qx7" eventType="touchUpInside" id="waH-xV-9AO"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="xcd-zY-l5e" firstAttribute="centerX" secondItem="Gcq-Xg-VxI" secondAttribute="centerX" id="13u-U4-6GT"/>
+                            <constraint firstItem="14h-9k-LYj" firstAttribute="top" secondItem="Z1K-EZ-dQW" secondAttribute="top" id="5eP-bB-B8C"/>
+                            <constraint firstItem="14h-9k-LYj" firstAttribute="leading" secondItem="Z1K-EZ-dQW" secondAttribute="leading" id="BzT-Ue-JM2"/>
+                            <constraint firstItem="DYZ-pL-yiT" firstAttribute="centerX" secondItem="Gcq-Xg-VxI" secondAttribute="centerX" id="S45-7C-93s"/>
+                            <constraint firstItem="Z1K-EZ-dQW" firstAttribute="trailing" secondItem="14h-9k-LYj" secondAttribute="trailing" id="gwm-rP-bDH"/>
+                            <constraint firstItem="DYZ-pL-yiT" firstAttribute="centerY" secondItem="Gcq-Xg-VxI" secondAttribute="centerY" id="jOQ-fj-C7X"/>
+                            <constraint firstAttribute="bottom" secondItem="14h-9k-LYj" secondAttribute="bottom" id="nHv-Hn-Nbt"/>
+                            <constraint firstItem="xcd-zY-l5e" firstAttribute="centerY" secondItem="Gcq-Xg-VxI" secondAttribute="centerY" id="rh6-8P-NHN"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="Z1K-EZ-dQW"/>
+                    </view>
+                    <connections>
+                        <outlet property="footerViewActivityIndicator" destination="KuZ-W4-SQg" id="apS-Ap-MmK"/>
+                        <outlet property="noDataLabel" destination="xcd-zY-l5e" id="DYI-lg-Njr"/>
+                        <outlet property="refreshDataButton" destination="DYZ-pL-yiT" id="8F2-J1-Uhl"/>
+                        <outlet property="tableView" destination="14h-9k-LYj" id="Ag6-Pn-19g"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="DWW-td-4IY" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-4306" y="2587"/>
+        </scene>
+        <!--Login View Controller-->
+        <scene sceneID="BiL-H1-Afp">
+            <objects>
+                <viewController storyboardIdentifier="LoginViewController" id="v6k-DO-BLg" customClass="LoginViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="Fol-sk-ZV4">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aUZ-aS-kCh">
+                                <rect key="frame" x="0.0" y="175.5" width="375" height="316.5"/>
+                                <subviews>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bEI-yF-PlY">
+                                        <rect key="frame" x="27.5" y="71.5" width="320" height="181"/>
+                                        <subviews>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="dodawać książki i audiobooki do ulubionych," lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tY2-9z-OvZ">
+                                                <rect key="frame" x="46" y="22" width="274" height="36"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_accept2" translatesAutoresizingMaskIntoConstraints="NO" id="drb-fC-Are">
+                                                <rect key="frame" x="0.0" y="28" width="36" height="25"/>
+                                                <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="width" constant="36" id="cHS-p0-WWH"/>
+                                                    <constraint firstAttribute="height" constant="25" id="q8v-Px-bBe"/>
+                                                </constraints>
+                                            </imageView>
+                                            <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_accept2" translatesAutoresizingMaskIntoConstraints="NO" id="gpr-52-KJK">
+                                                <rect key="frame" x="0.0" y="78" width="36" height="25"/>
+                                                <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="25" id="ozg-is-Oif"/>
+                                                    <constraint firstAttribute="width" constant="36" id="q42-F8-JFP"/>
+                                                </constraints>
+                                            </imageView>
+                                            <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_accept2" translatesAutoresizingMaskIntoConstraints="NO" id="CPR-OW-SAC">
+                                                <rect key="frame" x="0.0" y="128" width="36" height="25"/>
+                                                <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="25" id="gzU-Pu-IJm"/>
+                                                    <constraint firstAttribute="width" constant="36" id="oB6-ck-xFA"/>
+                                                </constraints>
+                                            </imageView>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="wrócić do czytania rozpoczętej już książki," lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="prt-Pu-y4f">
+                                                <rect key="frame" x="46" y="72" width="274" height="36"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="zostać naszym przyjacielem i pomóc nam rozwijać Wolne Lektury." lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="V1k-wR-7Pg">
+                                                <rect key="frame" x="46" y="122" width="274" height="36"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                        </subviews>
+                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <constraints>
+                                            <constraint firstItem="tY2-9z-OvZ" firstAttribute="centerY" secondItem="drb-fC-Are" secondAttribute="centerY" id="0d5-c2-5cF"/>
+                                            <constraint firstItem="tY2-9z-OvZ" firstAttribute="leading" secondItem="drb-fC-Are" secondAttribute="trailing" constant="10" id="Aqj-sw-PQf"/>
+                                            <constraint firstItem="prt-Pu-y4f" firstAttribute="centerY" secondItem="gpr-52-KJK" secondAttribute="centerY" id="EJ9-hz-vZM"/>
+                                            <constraint firstItem="prt-Pu-y4f" firstAttribute="leading" secondItem="gpr-52-KJK" secondAttribute="trailing" constant="10" id="EY0-yK-zMz"/>
+                                            <constraint firstItem="gpr-52-KJK" firstAttribute="top" secondItem="drb-fC-Are" secondAttribute="bottom" constant="25" id="LVk-tl-6TI"/>
+                                            <constraint firstItem="gpr-52-KJK" firstAttribute="leading" secondItem="drb-fC-Are" secondAttribute="leading" id="QEf-Ti-2FQ"/>
+                                            <constraint firstAttribute="trailing" secondItem="prt-Pu-y4f" secondAttribute="trailing" id="RQr-px-47k"/>
+                                            <constraint firstItem="CPR-OW-SAC" firstAttribute="leading" secondItem="gpr-52-KJK" secondAttribute="leading" id="aaS-r8-d20"/>
+                                            <constraint firstAttribute="width" constant="320" id="fNq-h7-a5z"/>
+                                            <constraint firstAttribute="height" constant="181" id="jQB-6f-uwe"/>
+                                            <constraint firstAttribute="trailing" secondItem="V1k-wR-7Pg" secondAttribute="trailing" id="kOk-MX-9Nx"/>
+                                            <constraint firstItem="CPR-OW-SAC" firstAttribute="top" secondItem="gpr-52-KJK" secondAttribute="bottom" constant="25" id="liM-9z-0q2"/>
+                                            <constraint firstAttribute="trailing" secondItem="tY2-9z-OvZ" secondAttribute="trailing" id="lk2-2f-j8h"/>
+                                            <constraint firstItem="V1k-wR-7Pg" firstAttribute="leading" secondItem="CPR-OW-SAC" secondAttribute="trailing" constant="10" id="rWh-7e-QqR"/>
+                                            <constraint firstItem="drb-fC-Are" firstAttribute="leading" secondItem="bEI-yF-PlY" secondAttribute="leading" id="stH-JX-tNW"/>
+                                            <constraint firstItem="V1k-wR-7Pg" firstAttribute="centerY" secondItem="CPR-OW-SAC" secondAttribute="centerY" id="v8j-cT-G8M"/>
+                                            <constraint firstItem="drb-fC-Are" firstAttribute="top" secondItem="bEI-yF-PlY" secondAttribute="top" constant="28" id="z2T-UH-n0O"/>
+                                        </constraints>
+                                    </view>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Załóż konto / Zaloguj się w Wolnych Lekturach, aby:" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eyV-Ni-fXP">
+                                        <rect key="frame" x="16" y="0.0" width="343" height="60"/>
+                                        <fontDescription key="fontDescription" type="system" weight="light" pointSize="25"/>
+                                        <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4yb-Jb-68k">
+                                        <rect key="frame" x="90.5" y="280.5" width="194" height="36"/>
+                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="36" id="Ji0-jq-U1o"/>
+                                        </constraints>
+                                        <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
+                                        <inset key="contentEdgeInsets" minX="50" minY="0.0" maxX="50" maxY="0.0"/>
+                                        <state key="normal" title="ZALOGUJ SIĘ">
+                                            <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        </state>
+                                        <connections>
+                                            <action selector="loginButtonAction:" destination="v6k-DO-BLg" eventType="touchUpInside" id="Cw4-M2-JaH"/>
+                                        </connections>
+                                    </button>
+                                </subviews>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstItem="eyV-Ni-fXP" firstAttribute="top" secondItem="aUZ-aS-kCh" secondAttribute="top" id="255-3V-bwl"/>
+                                    <constraint firstItem="bEI-yF-PlY" firstAttribute="top" secondItem="eyV-Ni-fXP" secondAttribute="bottom" constant="11.5" id="FLm-tc-S5S"/>
+                                    <constraint firstAttribute="width" constant="375" id="Lki-G0-07z"/>
+                                    <constraint firstItem="bEI-yF-PlY" firstAttribute="centerX" secondItem="aUZ-aS-kCh" secondAttribute="centerX" id="Mom-hW-Xjn"/>
+                                    <constraint firstAttribute="bottom" secondItem="4yb-Jb-68k" secondAttribute="bottom" id="PWM-Ke-htP"/>
+                                    <constraint firstItem="4yb-Jb-68k" firstAttribute="top" secondItem="bEI-yF-PlY" secondAttribute="bottom" constant="28" id="Vnf-Vj-8kK"/>
+                                    <constraint firstItem="4yb-Jb-68k" firstAttribute="centerX" secondItem="aUZ-aS-kCh" secondAttribute="centerX" id="XAh-Kd-Dv1"/>
+                                    <constraint firstAttribute="trailing" secondItem="eyV-Ni-fXP" secondAttribute="trailing" constant="16" id="ogF-ju-Nou"/>
+                                    <constraint firstItem="eyV-Ni-fXP" firstAttribute="leading" secondItem="aUZ-aS-kCh" secondAttribute="leading" constant="16" id="xSh-Bn-AZP"/>
+                                </constraints>
+                            </view>
+                        </subviews>
+                        <color key="backgroundColor" red="0.0" green="0.4039215686" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="aUZ-aS-kCh" firstAttribute="centerY" secondItem="Fol-sk-ZV4" secondAttribute="centerY" id="iDX-11-2wS"/>
+                            <constraint firstItem="eCQ-Fd-ihy" firstAttribute="trailing" secondItem="aUZ-aS-kCh" secondAttribute="trailing" id="mx2-na-ECP"/>
+                            <constraint firstItem="aUZ-aS-kCh" firstAttribute="leading" secondItem="eCQ-Fd-ihy" secondAttribute="leading" id="qHh-cF-Oyk"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="eCQ-Fd-ihy"/>
+                    </view>
+                    <connections>
+                        <outlet property="desc1" destination="tY2-9z-OvZ" id="OtC-tc-Qbk"/>
+                        <outlet property="desc1Checkmark" destination="drb-fC-Are" id="3ZS-Ip-oq1"/>
+                        <outlet property="desc2" destination="prt-Pu-y4f" id="k1v-2z-l73"/>
+                        <outlet property="desc2Checkmark" destination="gpr-52-KJK" id="6Jl-OB-puP"/>
+                        <outlet property="desc3" destination="V1k-wR-7Pg" id="fWk-3u-Whv"/>
+                        <outlet property="desc3Checkmark" destination="CPR-OW-SAC" id="hsF-Gu-NKh"/>
+                        <outlet property="loginButton" destination="4yb-Jb-68k" id="Tly-DQ-IGZ"/>
+                        <outlet property="titleLabel" destination="eyV-Ni-fXP" id="ucH-xq-OJM"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="SXh-Mg-cm2" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-6580" y="139.880059970015"/>
+        </scene>
+        <!--About View Controller-->
+        <scene sceneID="Wom-sf-B0r">
+            <objects>
+                <viewController storyboardIdentifier="AboutViewController" id="MCF-eK-GXX" customClass="AboutViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="ZEL-1w-L6s">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1Ns-Ho-rtM">
+                                <rect key="frame" x="0.0" y="20" width="375" height="647"/>
+                                <subviews>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="CF3-Tq-6Ic">
+                                        <rect key="frame" x="0.0" y="0.0" width="375" height="687"/>
+                                        <subviews>
+                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TiN-vs-5TA">
+                                                <rect key="frame" x="83" y="28" width="209" height="36"/>
+                                                <color key="backgroundColor" red="1" green="0.64705882349999999" blue="0.1843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="36" id="KJC-o8-zJk"/>
+                                                </constraints>
+                                                <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
+                                                <inset key="contentEdgeInsets" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
+                                                <state key="normal" title="ZOSTAŃ PRZYJACIELEM">
+                                                    <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                </state>
+                                                <connections>
+                                                    <action selector="becomeFriendButtonAction:" destination="MCF-eK-GXX" eventType="touchUpInside" id="WlY-nz-Tiv"/>
+                                                </connections>
+                                            </button>
+                                            <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="wl_logo" translatesAutoresizingMaskIntoConstraints="NO" id="cdT-2p-m6e">
+                                                <rect key="frame" x="54.5" y="84" width="265" height="104"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="width" constant="265" id="Nvw-dq-cZx"/>
+                                                    <constraint firstAttribute="height" constant="104" id="ov1-Iq-FR2"/>
+                                                </constraints>
+                                            </imageView>
+                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4kr-rk-oKl">
+                                                <rect key="frame" x="20" y="250" width="46" height="30"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="30" id="7zF-uG-iDr"/>
+                                                </constraints>
+                                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                                <state key="normal" title="Button">
+                                                    <color key="titleColor" red="0.0" green="0.4039215686" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                </state>
+                                                <connections>
+                                                    <action selector="contactButtonAction:" destination="MCF-eK-GXX" eventType="touchUpInside" id="fwV-R8-YlI"/>
+                                                </connections>
+                                            </button>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WXz-mb-f4i">
+                                                <rect key="frame" x="20" y="300" width="335" height="18"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                                <nil key="textColor"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logo_fnp" translatesAutoresizingMaskIntoConstraints="NO" id="MH3-hb-UiE">
+                                                <rect key="frame" x="63.5" y="339" width="248" height="100"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="100" id="cYY-tn-GEc"/>
+                                                    <constraint firstAttribute="width" constant="248" id="zQI-rp-Lpb"/>
+                                                </constraints>
+                                            </imageView>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ufP-j3-aMt">
+                                                <rect key="frame" x="20" y="454" width="335" height="18"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                                <nil key="textColor"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logo_mkidn" translatesAutoresizingMaskIntoConstraints="NO" id="nfw-vp-cnJ">
+                                                <rect key="frame" x="93" y="482" width="188" height="200"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="width" constant="188" id="M1o-nE-7oF"/>
+                                                    <constraint firstAttribute="height" constant="200" id="Wo6-9f-6Wp"/>
+                                                </constraints>
+                                            </imageView>
+                                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ggd-Cp-hlb">
+                                                <rect key="frame" x="20" y="272.5" width="46" height="1"/>
+                                                <color key="backgroundColor" red="0.0" green="0.4039215686" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="1" id="Ucq-52-gQP"/>
+                                                </constraints>
+                                            </view>
+                                            <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="text" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="Zrh-HN-2rD">
+                                                <rect key="frame" x="20" y="218" width="335" height="34"/>
+                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                                <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
+                                            </textView>
+                                        </subviews>
+                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <constraints>
+                                            <constraint firstItem="MH3-hb-UiE" firstAttribute="centerX" secondItem="CF3-Tq-6Ic" secondAttribute="centerX" id="1Hx-GF-Kak"/>
+                                            <constraint firstItem="Zrh-HN-2rD" firstAttribute="leading" secondItem="CF3-Tq-6Ic" secondAttribute="leading" constant="20" id="4v4-VB-Iiy"/>
+                                            <constraint firstItem="WXz-mb-f4i" firstAttribute="top" secondItem="4kr-rk-oKl" secondAttribute="bottom" constant="20" id="82R-1J-4L2"/>
+                                            <constraint firstItem="Zrh-HN-2rD" firstAttribute="top" secondItem="cdT-2p-m6e" secondAttribute="bottom" constant="30" id="8wB-cu-8Kr"/>
+                                            <constraint firstAttribute="trailing" secondItem="ufP-j3-aMt" secondAttribute="trailing" constant="20" id="Bus-TD-9NB"/>
+                                            <constraint firstItem="cdT-2p-m6e" firstAttribute="top" secondItem="TiN-vs-5TA" secondAttribute="bottom" constant="20" id="P2d-rW-Zhd"/>
+                                            <constraint firstItem="Ggd-Cp-hlb" firstAttribute="trailing" secondItem="4kr-rk-oKl" secondAttribute="trailing" id="T2t-kx-xuN"/>
+                                            <constraint firstItem="ufP-j3-aMt" firstAttribute="leading" secondItem="CF3-Tq-6Ic" secondAttribute="leading" constant="20" id="TOn-ol-2xo"/>
+                                            <constraint firstAttribute="trailing" secondItem="Zrh-HN-2rD" secondAttribute="trailing" constant="20" id="Tyg-kZ-2gj"/>
+                                            <constraint firstItem="nfw-vp-cnJ" firstAttribute="centerX" secondItem="CF3-Tq-6Ic" secondAttribute="centerX" id="V56-lH-ckn"/>
+                                            <constraint firstItem="Ggd-Cp-hlb" firstAttribute="centerY" secondItem="4kr-rk-oKl" secondAttribute="centerY" constant="8" id="W2s-Rc-5U5"/>
+                                            <constraint firstItem="cdT-2p-m6e" firstAttribute="top" secondItem="CF3-Tq-6Ic" secondAttribute="top" constant="84" id="XUE-fC-qhE"/>
+                                            <constraint firstItem="WXz-mb-f4i" firstAttribute="leading" secondItem="CF3-Tq-6Ic" secondAttribute="leading" constant="20" id="Zit-SY-9Ej"/>
+                                            <constraint firstItem="nfw-vp-cnJ" firstAttribute="top" secondItem="ufP-j3-aMt" secondAttribute="bottom" constant="10" id="ag8-D8-uI6"/>
+                                            <constraint firstItem="TiN-vs-5TA" firstAttribute="centerX" secondItem="CF3-Tq-6Ic" secondAttribute="centerX" id="atp-6h-bNY"/>
+                                            <constraint firstItem="ufP-j3-aMt" firstAttribute="top" secondItem="MH3-hb-UiE" secondAttribute="bottom" constant="15" id="dwr-HT-338"/>
+                                            <constraint firstItem="4kr-rk-oKl" firstAttribute="leading" secondItem="CF3-Tq-6Ic" secondAttribute="leading" constant="20" id="ekQ-IA-FRh"/>
+                                            <constraint firstItem="MH3-hb-UiE" firstAttribute="top" secondItem="WXz-mb-f4i" secondAttribute="bottom" constant="21" id="gRb-zU-eAC"/>
+                                            <constraint firstItem="cdT-2p-m6e" firstAttribute="centerX" secondItem="CF3-Tq-6Ic" secondAttribute="centerX" id="ggZ-R6-ecL"/>
+                                            <constraint firstAttribute="bottom" secondItem="nfw-vp-cnJ" secondAttribute="bottom" constant="5" id="hMe-IM-sb1"/>
+                                            <constraint firstItem="4kr-rk-oKl" firstAttribute="top" secondItem="Zrh-HN-2rD" secondAttribute="bottom" constant="-2" id="kN9-Kp-nCU"/>
+                                            <constraint firstItem="4kr-rk-oKl" firstAttribute="leading" secondItem="Ggd-Cp-hlb" secondAttribute="leading" id="nJq-UG-dXz"/>
+                                            <constraint firstAttribute="trailing" secondItem="WXz-mb-f4i" secondAttribute="trailing" constant="20" id="tF1-2D-2rB"/>
+                                        </constraints>
+                                    </view>
+                                </subviews>
+                                <constraints>
+                                    <constraint firstItem="CF3-Tq-6Ic" firstAttribute="leading" secondItem="1Ns-Ho-rtM" secondAttribute="leading" id="TyQ-rh-HvE"/>
+                                    <constraint firstItem="CF3-Tq-6Ic" firstAttribute="width" secondItem="1Ns-Ho-rtM" secondAttribute="width" id="fqy-pi-9sx"/>
+                                    <constraint firstAttribute="trailing" secondItem="CF3-Tq-6Ic" secondAttribute="trailing" id="pdJ-84-nXr"/>
+                                    <constraint firstItem="CF3-Tq-6Ic" firstAttribute="top" secondItem="1Ns-Ho-rtM" secondAttribute="top" id="vYA-nI-YU1"/>
+                                    <constraint firstAttribute="bottom" secondItem="CF3-Tq-6Ic" secondAttribute="bottom" id="zpo-g9-Igy"/>
+                                </constraints>
+                            </scrollView>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstAttribute="bottom" secondItem="1Ns-Ho-rtM" secondAttribute="bottom" id="Cnl-VX-3FV"/>
+                            <constraint firstItem="iK1-uj-3I4" firstAttribute="trailing" secondItem="1Ns-Ho-rtM" secondAttribute="trailing" id="dDH-p7-sGB"/>
+                            <constraint firstItem="1Ns-Ho-rtM" firstAttribute="leading" secondItem="iK1-uj-3I4" secondAttribute="leading" id="hhf-T8-EWa"/>
+                            <constraint firstItem="1Ns-Ho-rtM" firstAttribute="top" secondItem="iK1-uj-3I4" secondAttribute="top" id="j3n-Ov-wWS"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="iK1-uj-3I4"/>
+                    </view>
+                    <connections>
+                        <outlet property="becomeFriendButton" destination="TiN-vs-5TA" id="lYS-z1-8O3"/>
+                        <outlet property="contactButton" destination="4kr-rk-oKl" id="IG5-RT-WXo"/>
+                        <outlet property="firstTextView" destination="Zrh-HN-2rD" id="ElH-SN-UJ0"/>
+                        <outlet property="logoTopConstraint" destination="XUE-fC-qhE" id="BM5-Hn-QEG"/>
+                        <outlet property="secondLabel" destination="WXz-mb-f4i" id="e1s-sB-9xj"/>
+                        <outlet property="thirdLabel" destination="ufP-j3-aMt" id="PGX-rT-kFZ"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="zVf-2s-K3j" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-5860" y="139.880059970015"/>
+        </scene>
+        <!--Settings View Controller-->
+        <scene sceneID="EZK-Cx-gFq">
+            <objects>
+                <viewController storyboardIdentifier="SettingsViewController" id="Bce-jF-KaK" customClass="SettingsViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="29C-1V-7n2">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="smA-TD-Ghm">
+                                <rect key="frame" x="16" y="30" width="238" height="70"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="70" id="iDy-ps-INi"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3Wa-Up-40Y">
+                                <rect key="frame" x="16" y="100" width="343" height="1"/>
+                                <color key="backgroundColor" white="1" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="1" id="Nan-f6-1Ng"/>
+                                </constraints>
+                            </view>
+                            <label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gwu-4W-M37">
+                                <rect key="frame" x="16" y="30" width="238" height="70"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="70" id="fCs-0q-Zls"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="zUC-ke-2eS">
+                                <rect key="frame" x="16" y="100" width="343" height="1"/>
+                                <color key="backgroundColor" white="1" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="1" id="y5e-8t-HjV"/>
+                                </constraints>
+                            </view>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4hZ-iH-JZc">
+                                <rect key="frame" x="16" y="30" width="238" height="70"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="70" id="NrR-v8-7W6"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uDV-Gf-Xcn">
+                                <rect key="frame" x="16" y="100" width="343" height="1"/>
+                                <color key="backgroundColor" white="1" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="1" id="DV7-lq-8UO"/>
+                                </constraints>
+                            </view>
+                            <label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="e6Z-xh-Li4">
+                                <rect key="frame" x="259" y="40" width="100" height="50"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="50" id="QCv-7T-1Vk"/>
+                                    <constraint firstAttribute="width" constant="100" id="bC2-1F-JD4"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <switch hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YyS-18-85d">
+                                <rect key="frame" x="312" y="50" width="49" height="31"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="31" id="HrN-BY-aSt"/>
+                                    <constraint firstAttribute="width" constant="47" id="qTh-0A-XkL"/>
+                                </constraints>
+                                <color key="onTintColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/>
+                                <connections>
+                                    <action selector="switchAction" destination="Bce-jF-KaK" eventType="valueChanged" id="Ob0-yz-FB7"/>
+                                </connections>
+                            </switch>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tkL-UY-9a2">
+                                <rect key="frame" x="289" y="50" width="70" height="30"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="70" id="B5A-6b-i9Y"/>
+                                    <constraint firstAttribute="height" constant="30" id="iNX-qg-OI9"/>
+                                </constraints>
+                                <state key="normal" title="USUŃ">
+                                    <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                </state>
+                                <connections>
+                                    <action selector="deleteFilesButtonAction" destination="Bce-jF-KaK" eventType="touchUpInside" id="1vt-Eh-0CW"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CdJ-5f-wtT">
+                                <rect key="frame" x="83" y="616" width="209" height="36"/>
+                                <color key="backgroundColor" red="1" green="0.64705882349999999" blue="0.1843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="36" id="WBg-0d-kbD"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
+                                <inset key="contentEdgeInsets" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
+                                <state key="normal" title="ZOSTAŃ PRZYJACIELEM">
+                                    <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                </state>
+                                <connections>
+                                    <action selector="becomeFriendButtonAction" destination="Bce-jF-KaK" eventType="touchUpInside" id="yTl-wb-D54"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <color key="backgroundColor" red="0.0" green="0.4039215686" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="3Wa-Up-40Y" firstAttribute="top" secondItem="smA-TD-Ghm" secondAttribute="bottom" id="1dp-eQ-iAp"/>
+                            <constraint firstItem="smA-TD-Ghm" firstAttribute="leading" secondItem="VZt-rd-6xH" secondAttribute="leading" constant="16" id="2iC-77-cgx"/>
+                            <constraint firstItem="4hZ-iH-JZc" firstAttribute="top" secondItem="zUC-ke-2eS" secondAttribute="bottom" constant="-71" id="2iY-aA-Voc"/>
+                            <constraint firstItem="VZt-rd-6xH" firstAttribute="trailing" secondItem="smA-TD-Ghm" secondAttribute="trailing" constant="121" id="2iq-26-J7w"/>
+                            <constraint firstItem="zUC-ke-2eS" firstAttribute="top" secondItem="Gwu-4W-M37" secondAttribute="bottom" id="2xz-Zv-Aki"/>
+                            <constraint firstItem="smA-TD-Ghm" firstAttribute="top" secondItem="VZt-rd-6xH" secondAttribute="top" constant="10" id="4FR-PB-c8B"/>
+                            <constraint firstItem="VZt-rd-6xH" firstAttribute="trailing" secondItem="4hZ-iH-JZc" secondAttribute="trailing" constant="121" id="4dz-LM-g5V"/>
+                            <constraint firstItem="zUC-ke-2eS" firstAttribute="leading" secondItem="29C-1V-7n2" secondAttribute="leading" constant="16" id="7C1-Nr-GbR"/>
+                            <constraint firstItem="4hZ-iH-JZc" firstAttribute="leading" secondItem="VZt-rd-6xH" secondAttribute="leading" constant="16" id="7FC-kK-pBl"/>
+                            <constraint firstItem="VZt-rd-6xH" firstAttribute="trailing" secondItem="3Wa-Up-40Y" secondAttribute="trailing" constant="16" id="81C-bT-Fxi"/>
+                            <constraint firstItem="VZt-rd-6xH" firstAttribute="bottom" secondItem="CdJ-5f-wtT" secondAttribute="bottom" constant="15" id="BGV-ar-EEM"/>
+                            <constraint firstItem="uDV-Gf-Xcn" firstAttribute="top" secondItem="4hZ-iH-JZc" secondAttribute="bottom" id="CVe-8D-JPG"/>
+                            <constraint firstItem="Gwu-4W-M37" firstAttribute="leading" secondItem="VZt-rd-6xH" secondAttribute="leading" constant="16" id="E55-Oc-Ked"/>
+                            <constraint firstItem="VZt-rd-6xH" firstAttribute="trailing" secondItem="tkL-UY-9a2" secondAttribute="trailing" constant="16" id="G2Y-Wm-vB1"/>
+                            <constraint firstItem="VZt-rd-6xH" firstAttribute="trailing" secondItem="YyS-18-85d" secondAttribute="trailing" constant="16" id="RO1-ty-mxt"/>
+                            <constraint firstItem="YyS-18-85d" firstAttribute="centerY" secondItem="smA-TD-Ghm" secondAttribute="centerY" id="RqK-RQ-Md3"/>
+                            <constraint firstItem="CdJ-5f-wtT" firstAttribute="centerX" secondItem="29C-1V-7n2" secondAttribute="centerX" id="S7h-jQ-uR1"/>
+                            <constraint firstAttribute="trailing" secondItem="uDV-Gf-Xcn" secondAttribute="trailing" constant="16" id="SKi-2j-PXH"/>
+                            <constraint firstAttribute="trailing" secondItem="zUC-ke-2eS" secondAttribute="trailing" constant="16" id="Wb7-tK-c0T"/>
+                            <constraint firstItem="Gwu-4W-M37" firstAttribute="top" secondItem="3Wa-Up-40Y" secondAttribute="bottom" constant="-71" id="bpd-sm-tld"/>
+                            <constraint firstItem="uDV-Gf-Xcn" firstAttribute="leading" secondItem="29C-1V-7n2" secondAttribute="leading" constant="16" id="d19-fL-Ptc"/>
+                            <constraint firstItem="VZt-rd-6xH" firstAttribute="trailing" secondItem="e6Z-xh-Li4" secondAttribute="trailing" constant="16" id="edn-AF-IWk"/>
+                            <constraint firstItem="tkL-UY-9a2" firstAttribute="centerY" secondItem="4hZ-iH-JZc" secondAttribute="centerY" id="erK-7G-Bz5"/>
+                            <constraint firstItem="3Wa-Up-40Y" firstAttribute="leading" secondItem="VZt-rd-6xH" secondAttribute="leading" constant="16" id="skN-gT-DxW"/>
+                            <constraint firstItem="e6Z-xh-Li4" firstAttribute="centerY" secondItem="Gwu-4W-M37" secondAttribute="centerY" id="stV-aG-VAx"/>
+                            <constraint firstItem="VZt-rd-6xH" firstAttribute="trailing" secondItem="Gwu-4W-M37" secondAttribute="trailing" constant="121" id="vYq-9m-uIb"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="VZt-rd-6xH"/>
+                    </view>
+                    <connections>
+                        <outlet property="becomeFriendButton" destination="CdJ-5f-wtT" id="FRV-6y-yPC"/>
+                        <outlet property="deleteFilesButton" destination="tkL-UY-9a2" id="Kor-8M-G0P"/>
+                        <outlet property="deleteFilesLabel" destination="4hZ-iH-JZc" id="Cho-fO-xT0"/>
+                        <outlet property="notificationsLabel" destination="smA-TD-Ghm" id="ewN-fi-aV3"/>
+                        <outlet property="notificationsSwitch" destination="YyS-18-85d" id="l1K-aR-ggf"/>
+                        <outlet property="subscriptionLabel" destination="Gwu-4W-M37" id="yMf-lf-0e3"/>
+                        <outlet property="subscriptionStatusLabel" destination="e6Z-xh-Li4" id="I5Y-aA-YKc"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="gFH-qL-g8W" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-5121" y="140"/>
+        </scene>
+        <!--News Details View Controller-->
+        <scene sceneID="AN3-N5-Uv8">
+            <objects>
+                <viewController storyboardIdentifier="NewsDetailsViewController" automaticallyAdjustsScrollViewInsets="NO" id="Iw8-BX-jdF" customClass="NewsDetailsViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="s2h-c1-sFM">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ngk-xN-Pn1">
+                                <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                                <subviews>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ejz-vO-0M3">
+                                        <rect key="frame" x="0.0" y="0.0" width="375" height="446"/>
+                                        <subviews>
+                                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="HGm-1d-DlY">
+                                                <rect key="frame" x="0.0" y="0.0" width="375" height="250"/>
+                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="width" secondItem="HGm-1d-DlY" secondAttribute="height" multiplier="3:2" id="Ewn-Jx-VVh"/>
+                                                </constraints>
+                                                <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="pHa-iC-WNm">
+                                                    <size key="itemSize" width="50" height="50"/>
+                                                    <size key="headerReferenceSize" width="0.0" height="0.0"/>
+                                                    <size key="footerReferenceSize" width="0.0" height="0.0"/>
+                                                    <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
+                                                </collectionViewFlowLayout>
+                                                <cells>
+                                                    <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gce-ib-B0N">
+                                                        <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
+                                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                                        <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
+                                                            <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
+                                                            <autoresizingMask key="autoresizingMask"/>
+                                                        </view>
+                                                    </collectionViewCell>
+                                                </cells>
+                                            </collectionView>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6Nc-fI-Qlu">
+                                                <rect key="frame" x="16" y="258" width="343" height="23"/>
+                                                <fontDescription key="fontDescription" type="system" weight="light" pointSize="19"/>
+                                                <color key="textColor" red="0.0" green="0.4039215686" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VRC-1H-2Fv">
+                                                <rect key="frame" x="303" y="229" width="42" height="42"/>
+                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="42" id="QXA-cE-PEq"/>
+                                                    <constraint firstAttribute="width" constant="42" id="U0i-6C-uSF"/>
+                                                </constraints>
+                                                <color key="tintColor" red="1" green="0.64705882349999999" blue="0.1843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                <state key="normal" image="share"/>
+                                                <connections>
+                                                    <action selector="shareButtonAction:" destination="Iw8-BX-jdF" eventType="touchUpInside" id="X0V-VR-cUE"/>
+                                                </connections>
+                                            </button>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6Y2-Rd-JZZ">
+                                                <rect key="frame" x="16" y="316" width="343" height="18"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                                <nil key="textColor"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Se2-xR-ioS">
+                                                <rect key="frame" x="16" y="339" width="343" height="18"/>
+                                                <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                                                <color key="textColor" red="0.0" green="0.4039215686" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c7J-Sd-PXq">
+                                                <rect key="frame" x="16" y="362" width="343" height="18"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                                <nil key="textColor"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IP9-0c-Gco">
+                                                <rect key="frame" x="16" y="385" width="343" height="18"/>
+                                                <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                                                <color key="textColor" red="0.0" green="0.4039215686" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bui-47-sm4">
+                                                <rect key="frame" x="16" y="418" width="343" height="18"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                                <nil key="textColor"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <pageControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" numberOfPages="3" translatesAutoresizingMaskIntoConstraints="NO" id="7ey-lO-1fi">
+                                                <rect key="frame" x="168" y="213" width="39" height="37"/>
+                                                <color key="pageIndicatorTintColor" white="0.84887861389999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <color key="currentPageIndicatorTintColor" red="0.0" green="0.50196078430000002" blue="0.53333333329999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            </pageControl>
+                                        </subviews>
+                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <constraints>
+                                            <constraint firstItem="7ey-lO-1fi" firstAttribute="centerX" secondItem="HGm-1d-DlY" secondAttribute="centerX" id="1bs-oX-9TL"/>
+                                            <constraint firstItem="c7J-Sd-PXq" firstAttribute="top" secondItem="Se2-xR-ioS" secondAttribute="bottom" constant="5" id="1np-Gj-rnZ"/>
+                                            <constraint firstItem="6Nc-fI-Qlu" firstAttribute="top" secondItem="HGm-1d-DlY" secondAttribute="bottom" constant="8" id="8a0-SP-2GS"/>
+                                            <constraint firstAttribute="bottom" secondItem="bui-47-sm4" secondAttribute="bottom" constant="10" id="9dE-Hp-vsg"/>
+                                            <constraint firstAttribute="trailing" secondItem="6Y2-Rd-JZZ" secondAttribute="trailing" constant="16" id="BiT-MU-BWO"/>
+                                            <constraint firstItem="6Y2-Rd-JZZ" firstAttribute="leading" secondItem="ejz-vO-0M3" secondAttribute="leading" constant="16" id="CD7-pQ-h08"/>
+                                            <constraint firstItem="bui-47-sm4" firstAttribute="leading" secondItem="ejz-vO-0M3" secondAttribute="leading" constant="16" id="CoE-sU-Sxs"/>
+                                            <constraint firstItem="bui-47-sm4" firstAttribute="top" secondItem="IP9-0c-Gco" secondAttribute="bottom" constant="15" id="DQa-Nj-xxj"/>
+                                            <constraint firstAttribute="trailing" secondItem="HGm-1d-DlY" secondAttribute="trailing" id="EYc-o0-zO7"/>
+                                            <constraint firstAttribute="trailing" secondItem="IP9-0c-Gco" secondAttribute="trailing" constant="16" id="Po1-73-MTg"/>
+                                            <constraint firstAttribute="trailing" secondItem="Se2-xR-ioS" secondAttribute="trailing" constant="16" id="TJO-ke-z2x"/>
+                                            <constraint firstItem="7ey-lO-1fi" firstAttribute="bottom" secondItem="HGm-1d-DlY" secondAttribute="bottom" id="UHl-3n-d4c"/>
+                                            <constraint firstAttribute="trailing" secondItem="bui-47-sm4" secondAttribute="trailing" constant="16" id="UN5-UR-snF"/>
+                                            <constraint firstItem="IP9-0c-Gco" firstAttribute="leading" secondItem="ejz-vO-0M3" secondAttribute="leading" constant="16" id="UZd-lb-rNb"/>
+                                            <constraint firstItem="6Nc-fI-Qlu" firstAttribute="leading" secondItem="ejz-vO-0M3" secondAttribute="leading" constant="16" id="VjM-zV-k4g"/>
+                                            <constraint firstItem="Se2-xR-ioS" firstAttribute="leading" secondItem="ejz-vO-0M3" secondAttribute="leading" constant="16" id="Wgg-LV-sBc"/>
+                                            <constraint firstItem="6Y2-Rd-JZZ" firstAttribute="top" secondItem="6Nc-fI-Qlu" secondAttribute="bottom" constant="35" id="XqC-Kr-EK2"/>
+                                            <constraint firstAttribute="trailing" secondItem="c7J-Sd-PXq" secondAttribute="trailing" constant="16" id="Xsq-Bh-6Ih"/>
+                                            <constraint firstAttribute="trailing" secondItem="6Nc-fI-Qlu" secondAttribute="trailing" constant="16" id="YZN-yb-vjU"/>
+                                            <constraint firstItem="c7J-Sd-PXq" firstAttribute="leading" secondItem="ejz-vO-0M3" secondAttribute="leading" constant="16" id="bij-hC-wwg"/>
+                                            <constraint firstItem="Se2-xR-ioS" firstAttribute="top" secondItem="6Y2-Rd-JZZ" secondAttribute="bottom" constant="5" id="eGW-xs-ywu"/>
+                                            <constraint firstItem="HGm-1d-DlY" firstAttribute="leading" secondItem="ejz-vO-0M3" secondAttribute="leading" id="eJ6-4o-EQu"/>
+                                            <constraint firstItem="IP9-0c-Gco" firstAttribute="top" secondItem="c7J-Sd-PXq" secondAttribute="bottom" constant="5" id="mrm-uV-ftE"/>
+                                            <constraint firstItem="HGm-1d-DlY" firstAttribute="top" secondItem="ejz-vO-0M3" secondAttribute="top" id="oMO-GF-gzp"/>
+                                            <constraint firstItem="VRC-1H-2Fv" firstAttribute="centerY" secondItem="HGm-1d-DlY" secondAttribute="bottom" id="tdl-aq-w6J"/>
+                                            <constraint firstAttribute="trailing" secondItem="VRC-1H-2Fv" secondAttribute="trailing" constant="30" id="vcl-M6-5Fg"/>
+                                        </constraints>
+                                    </view>
+                                </subviews>
+                                <constraints>
+                                    <constraint firstAttribute="bottom" secondItem="ejz-vO-0M3" secondAttribute="bottom" id="D1U-36-xc8"/>
+                                    <constraint firstAttribute="trailing" secondItem="ejz-vO-0M3" secondAttribute="trailing" id="WEe-se-xii"/>
+                                    <constraint firstItem="ejz-vO-0M3" firstAttribute="leading" secondItem="Ngk-xN-Pn1" secondAttribute="leading" id="fSa-1E-5em"/>
+                                    <constraint firstItem="ejz-vO-0M3" firstAttribute="top" secondItem="Ngk-xN-Pn1" secondAttribute="top" id="hJa-B5-GXA"/>
+                                </constraints>
+                            </scrollView>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="Ngk-xN-Pn1" firstAttribute="bottom" secondItem="Nhp-Un-ubu" secondAttribute="bottom" id="3Vx-lo-vvT"/>
+                            <constraint firstItem="Ngk-xN-Pn1" firstAttribute="top" secondItem="s2h-c1-sFM" secondAttribute="top" id="711-7B-QFJ"/>
+                            <constraint firstItem="Ngk-xN-Pn1" firstAttribute="trailing" secondItem="Nhp-Un-ubu" secondAttribute="trailing" id="cbh-ge-hD1"/>
+                            <constraint firstItem="Ngk-xN-Pn1" firstAttribute="leading" secondItem="Nhp-Un-ubu" secondAttribute="leading" id="hrK-GB-Rxv"/>
+                            <constraint firstItem="ejz-vO-0M3" firstAttribute="width" secondItem="s2h-c1-sFM" secondAttribute="width" id="rDE-Tk-GFG"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="Nhp-Un-ubu"/>
+                    </view>
+                    <extendedEdge key="edgesForExtendedLayout"/>
+                    <navigationItem key="navigationItem" id="xAD-EV-qLx"/>
+                    <connections>
+                        <outlet property="descLabel" destination="bui-47-sm4" id="aFa-9J-Vcj"/>
+                        <outlet property="galleryCollectionView" destination="HGm-1d-DlY" id="HZ7-YH-f1w"/>
+                        <outlet property="pageControl" destination="7ey-lO-1fi" id="eIO-kd-NQg"/>
+                        <outlet property="scrollView" destination="Ngk-xN-Pn1" id="IBt-Y0-oK0"/>
+                        <outlet property="shareButton" destination="VRC-1H-2Fv" id="OUy-mI-4N1"/>
+                        <outlet property="titleLabel" destination="6Nc-fI-Qlu" id="Lsq-bd-B6Q"/>
+                        <outlet property="whenDescLabel" destination="Se2-xR-ioS" id="hBC-z1-CYS"/>
+                        <outlet property="whenTitleLabel" destination="6Y2-Rd-JZZ" id="uJQ-vT-FrQ"/>
+                        <outlet property="whereDescLabel" destination="IP9-0c-Gco" id="rPv-1s-gbX"/>
+                        <outlet property="whereTitleLabel" destination="c7J-Sd-PXq" id="6Bz-hJ-ZcE"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="Jx6-gh-Xgt" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-4306.3999999999996" y="139.880059970015"/>
+        </scene>
+        <!--Gallery View Controller-->
+        <scene sceneID="Efe-sO-QpJ">
+            <objects>
+                <viewController storyboardIdentifier="GalleryViewController" id="Fql-md-OCG" customClass="GalleryViewController" customModule="WolneLektury" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="xVh-sn-hYi">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="1Es-7m-lDZ">
+                                <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="0.0" id="bmR-MG-vgy">
+                                    <size key="itemSize" width="50" height="50"/>
+                                    <size key="headerReferenceSize" width="0.0" height="0.0"/>
+                                    <size key="footerReferenceSize" width="0.0" height="0.0"/>
+                                    <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
+                                </collectionViewFlowLayout>
+                                <cells/>
+                            </collectionView>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZKF-Qf-RNZ">
+                                <rect key="frame" x="325" y="20" width="50" height="50"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="50" id="1dg-Gp-cDN"/>
+                                    <constraint firstAttribute="width" constant="50" id="EUS-84-qS3"/>
+                                </constraints>
+                                <color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                <state key="normal" image="navbar_close"/>
+                                <connections>
+                                    <action selector="closeButtonAction:" destination="Fql-md-OCG" eventType="touchUpInside" id="za0-DG-eml"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <color key="backgroundColor" red="0.0" green="0.50196078430000002" blue="0.53333333329999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="1Es-7m-lDZ" firstAttribute="trailing" secondItem="cjQ-T7-38R" secondAttribute="trailing" id="GiM-Ue-UyM"/>
+                            <constraint firstItem="1Es-7m-lDZ" firstAttribute="top" secondItem="xVh-sn-hYi" secondAttribute="top" id="Ipz-GM-8EW"/>
+                            <constraint firstItem="1Es-7m-lDZ" firstAttribute="bottom" secondItem="xVh-sn-hYi" secondAttribute="bottom" id="cj5-bl-qGU"/>
+                            <constraint firstItem="ZKF-Qf-RNZ" firstAttribute="top" secondItem="cjQ-T7-38R" secondAttribute="top" id="dAL-Yf-xm7"/>
+                            <constraint firstItem="cjQ-T7-38R" firstAttribute="trailing" secondItem="ZKF-Qf-RNZ" secondAttribute="trailing" id="dyM-mA-dPD"/>
+                            <constraint firstItem="1Es-7m-lDZ" firstAttribute="leading" secondItem="cjQ-T7-38R" secondAttribute="leading" id="lUb-wI-UUi"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="cjQ-T7-38R"/>
+                    </view>
+                    <connections>
+                        <outlet property="closeButton" destination="ZKF-Qf-RNZ" id="xoq-Rz-xxw"/>
+                        <outlet property="galleryCollectionView" destination="1Es-7m-lDZ" id="8he-9x-iB5"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="msg-bi-um9" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-5447.1999999999998" y="2586.6566716641682"/>
+        </scene>
+        <!--Support Us View Controller-->
+        <scene sceneID="zFS-F2-ahg">
+            <objects>
+                <viewController storyboardIdentifier="SupportUsViewController" id="T7b-0U-3j9" customClass="SupportUsViewController" customModule="WolneLektury" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="D5e-fY-nvw">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="w40-em-pqj">
+                                <rect key="frame" x="0.0" y="20" width="375" height="647"/>
+                                <subviews>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="PUN-8F-Cid">
+                                        <rect key="frame" x="0.0" y="0.0" width="375" height="554"/>
+                                        <subviews>
+                                            <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="wl_logo" translatesAutoresizingMaskIntoConstraints="NO" id="IrT-1B-ZC8">
+                                                <rect key="frame" x="54.5" y="30" width="265" height="104"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="104" id="00O-7W-r8Y"/>
+                                                    <constraint firstAttribute="width" constant="265" id="FYV-8P-tv3"/>
+                                                </constraints>
+                                            </imageView>
+                                            <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logo_opp" translatesAutoresizingMaskIntoConstraints="NO" id="9du-hp-PJR">
+                                                <rect key="frame" x="71" y="352" width="233" height="152"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="width" constant="233" id="8su-Xi-Zm8"/>
+                                                    <constraint firstAttribute="height" constant="152" id="sMQ-UE-e2M"/>
+                                                </constraints>
+                                            </imageView>
+                                            <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" bouncesZoom="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="MAP-I0-XU5">
+                                                <rect key="frame" x="16" y="142" width="343" height="200"/>
+                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                                <string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
+                                                <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                                <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
+                                            </textView>
+                                        </subviews>
+                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                        <constraints>
+                                            <constraint firstItem="9du-hp-PJR" firstAttribute="top" secondItem="MAP-I0-XU5" secondAttribute="bottom" constant="10" id="K0p-nS-bTG"/>
+                                            <constraint firstItem="IrT-1B-ZC8" firstAttribute="top" secondItem="PUN-8F-Cid" secondAttribute="top" constant="30" id="LFZ-6Z-IJ4"/>
+                                            <constraint firstItem="IrT-1B-ZC8" firstAttribute="centerX" secondItem="PUN-8F-Cid" secondAttribute="centerX" id="MIm-KL-gF8"/>
+                                            <constraint firstAttribute="bottom" secondItem="9du-hp-PJR" secondAttribute="bottom" constant="50" id="OvJ-eO-6JM"/>
+                                            <constraint firstItem="MAP-I0-XU5" firstAttribute="top" secondItem="IrT-1B-ZC8" secondAttribute="bottom" constant="8" id="S9N-vw-fz3"/>
+                                            <constraint firstAttribute="trailing" secondItem="MAP-I0-XU5" secondAttribute="trailing" constant="16" id="STG-Tr-in7"/>
+                                            <constraint firstItem="MAP-I0-XU5" firstAttribute="leading" secondItem="PUN-8F-Cid" secondAttribute="leading" constant="16" id="Sn8-Ea-XlY"/>
+                                            <constraint firstItem="9du-hp-PJR" firstAttribute="centerX" secondItem="PUN-8F-Cid" secondAttribute="centerX" id="d7Z-Ul-7iQ"/>
+                                        </constraints>
+                                    </view>
+                                </subviews>
+                                <constraints>
+                                    <constraint firstAttribute="trailing" secondItem="PUN-8F-Cid" secondAttribute="trailing" id="M8p-cI-ab4"/>
+                                    <constraint firstAttribute="bottom" secondItem="PUN-8F-Cid" secondAttribute="bottom" id="b1I-s7-017"/>
+                                    <constraint firstItem="PUN-8F-Cid" firstAttribute="leading" secondItem="w40-em-pqj" secondAttribute="leading" id="bJC-Yt-D4a"/>
+                                    <constraint firstItem="PUN-8F-Cid" firstAttribute="top" secondItem="w40-em-pqj" secondAttribute="top" id="dvz-F1-c9U"/>
+                                    <constraint firstItem="PUN-8F-Cid" firstAttribute="width" secondItem="w40-em-pqj" secondAttribute="width" id="gSS-vm-aEG"/>
+                                </constraints>
+                            </scrollView>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="w40-em-pqj" firstAttribute="top" secondItem="ifH-SZ-0xT" secondAttribute="top" id="7aF-hB-XSm"/>
+                            <constraint firstItem="w40-em-pqj" firstAttribute="leading" secondItem="ifH-SZ-0xT" secondAttribute="leading" id="d7h-ST-8SE"/>
+                            <constraint firstItem="ifH-SZ-0xT" firstAttribute="bottom" secondItem="w40-em-pqj" secondAttribute="bottom" id="fZQ-gT-mgC"/>
+                            <constraint firstItem="ifH-SZ-0xT" firstAttribute="trailing" secondItem="w40-em-pqj" secondAttribute="trailing" id="tsN-RY-Otc"/>
+                        </constraints>
+                        <viewLayoutGuide key="safeArea" id="ifH-SZ-0xT"/>
+                    </view>
+                    <connections>
+                        <outlet property="textView" destination="MAP-I0-XU5" id="1S8-By-4Na"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="QMW-Qe-4Vc" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-7255.1999999999998" y="139.880059970015"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="ic_accept2" width="24" height="16"/>
+        <image name="ic_play_arrow" width="24" height="24"/>
+        <image name="ic_toggle" width="17" height="11"/>
+        <image name="icon_heart-outline-big" width="21" height="19"/>
+        <image name="logo_fnp" width="298" height="124"/>
+        <image name="logo_mkidn" width="238" height="238"/>
+        <image name="logo_opp" width="233" height="152"/>
+        <image name="menu_premium" width="16" height="14"/>
+        <image name="navbar_back" width="9" height="16"/>
+        <image name="navbar_close" width="14" height="14"/>
+        <image name="navbar_close-small" width="7" height="7"/>
+        <image name="navbar_filter" width="17" height="13"/>
+        <image name="navbar_menu" width="21" height="13"/>
+        <image name="navbar_tick" width="21" height="14"/>
+        <image name="player_chapter_next" width="24" height="22"/>
+        <image name="player_chapter_previous" width="24" height="22"/>
+        <image name="player_controls_forward" width="33" height="32"/>
+        <image name="player_controls_play" width="21" height="41"/>
+        <image name="player_controls_rewind" width="33" height="32"/>
+        <image name="search" width="16" height="16"/>
+        <image name="share" width="20" height="22"/>
+        <image name="wl_logo" width="265" height="104"/>
+    </resources>
+</document>
diff --git a/iOS/WolneLektury/Config.swift b/iOS/WolneLektury/Config.swift
new file mode 100644 (file)
index 0000000..7004552
--- /dev/null
@@ -0,0 +1,26 @@
+//
+//  Config.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+struct Config{
+    static let MEDIA_URL = ""
+    static let MEDIA_URL_HTTPS = ""
+    static let BASE_URL = ""
+    
+    static let CONSUMER_KEY = "";
+    static let CONSUMER_SECRET = "";
+    static let OAUTH_REQUEST_TOKEN = BASE_URL + "";
+    static let OAUTH_ACCESS_TOKEN = BASE_URL + "";
+    static let OAUTH_AUTHORIZE = BASE_URL + "";
+    
+    //piwik
+    static let PIWIK_URL = ""
+    static let PIWIK_TRACKER_NAME = ""
+    static let PIWIK_SITE_ID = ""
+}
diff --git a/iOS/WolneLektury/Connection/DownloadManager.swift b/iOS/WolneLektury/Connection/DownloadManager.swift
new file mode 100644 (file)
index 0000000..f72e81f
--- /dev/null
@@ -0,0 +1,344 @@
+//
+//  DownloadManager.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 22/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import MZDownloadManager
+
+enum FileStatus{
+    case notDownloaded
+    case downloading
+    case downloaded
+}
+
+enum FileType{
+    case ebook
+    case audiobook
+    
+    private var destinationPath: String{
+        switch self {
+        case .ebook:
+            return Constants.ebookPath
+        case .audiobook:
+            return Constants.audiobookPath
+        }
+    }
+    
+    func destinationPathWithSlug(bookSlug: String) -> String {
+        return destinationPath + "/" + bookSlug + "/"
+    }
+    
+    func pathForFileName(filename: String, bookSlug: String) -> String{
+        return self.destinationPathWithSlug(bookSlug: bookSlug) + filename
+    }
+}
+
+protocol DownloadManagerDelegate: class{
+    func downloadManagerDownloadProgressChanged(model: MZDownloadModel, allProgress: Float, bookSlug: String)
+    func downloadManagerDownloadFinished(model: MZDownloadModel, bookSlug: String)
+    func downloadManagerDownloadFailed(model: MZDownloadModel, bookSlug: String)
+}
+
+extension MZDownloadModel {
+    func isAudiobook() -> Bool {
+        return self.destinationPath.starts(with: Constants.audiobookPath)
+    }
+    func isEbook() -> Bool {
+        return self.destinationPath.starts(with: Constants.ebookPath)
+    }
+
+}
+
+class DownloadingAudiobook{
+    var allUrlsCount: Float = 0
+    var waitingToDownloadUrls: [String]
+    var downloadedUrls: [String]
+    var bookSlug: String
+    init(bookDetailsModel: BookDetailsModel) {
+        
+        waitingToDownloadUrls = [String]()
+        downloadedUrls = [String]()
+        
+        let audiobookUrls = bookDetailsModel.getAudiobookFilesUrls()
+        bookSlug = bookDetailsModel.slug
+        allUrlsCount = Float(audiobookUrls.count)
+        
+        for url in audiobookUrls{
+            if NSObject.audiobookExists(audioBookUrlString: url, bookSlug: bookSlug){
+                downloadedUrls.append(url)
+            }
+            else{
+                waitingToDownloadUrls.append(url)
+            }
+        }
+    }
+    
+    func getProgress() -> Float{
+        let allCount = waitingToDownloadUrls.count + downloadedUrls.count
+        if allCount > 0{
+            if downloadedUrls.count > 0{
+                return Float(downloadedUrls.count) / Float(allCount)
+            }
+        }
+        return 0.0
+    }
+}
+
+class DownloadManager: NSObject, MZDownloadManagerDelegate{
+    
+    weak var delegate: DownloadManagerDelegate?
+    var downloadingAudiobooks = [DownloadingAudiobook]()
+    
+    //Shared instance of DownloadManager
+    static let sharedInstance : DownloadManager = {
+        return DownloadManager()
+    }()
+    
+    
+    lazy var downloadManager: MZDownloadManager = {
+        [unowned self] in
+        let sessionIdentifer: String = "com.moiseum.WolneLektury.BackgroundSession"
+        
+        let appDelegate = UIApplication.shared.delegate as! AppDelegate
+        var completion = appDelegate.backgroundSessionCompletionHandler
+        
+        let downloadmanager = MZDownloadManager(session: sessionIdentifer, delegate: self, completion: completion)
+        return downloadmanager
+        }()
+    
+    func checkEbookStatus(bookSlug: String) -> FileStatus{
+        if getEbookProgress(bookSlug: bookSlug) != nil{
+            return .downloading
+        }
+        
+        if ebookExists(bookSlug: bookSlug){
+            return .downloaded
+        }
+        
+        return .notDownloaded
+    }
+    
+    func getDownloadingAudiobook(bookSlug: String) -> DownloadingAudiobook? {
+        for downloadingAudiobook in downloadingAudiobooks {
+            if downloadingAudiobook.bookSlug == bookSlug {
+                return downloadingAudiobook
+            }
+        }
+        return nil
+    }
+    
+    func clearDownloadingAudiobookFromQueue(bookSlug: String){
+        
+        var i = 0
+        var found = false
+        for downloadingAudiobook in downloadingAudiobooks {
+            if downloadingAudiobook.bookSlug == bookSlug {
+                found = true
+                break
+            }
+            
+            i += 1
+        }
+        if found {
+            downloadingAudiobooks.remove(at: i)
+        }
+        
+        if let index = downloadManager.downloadingArray.index(where: {$0.destinationPath == FileType.audiobook.destinationPathWithSlug(bookSlug: bookSlug)}) {
+            downloadManager.cancelTaskAtIndex(index)
+        }
+    }
+    
+    func checkAudiobookStatus(bookDetailsModel: BookDetailsModel) -> FileStatus{
+        
+        guard bookDetailsModel.slug.count > 0 else { return .notDownloaded}
+        
+        if getDownloadingAudiobook(bookSlug: bookDetailsModel.slug) != nil {
+            return .downloading
+        }
+        
+        if bookDetailsModel.checkIfAllAudiobookFilesAreDownloaded() {
+            return .downloaded
+        }
+        
+        return .notDownloaded
+    }
+
+    func deleteEbook(bookSlug: String){
+        
+        let fileType = FileType.ebook
+        
+        if let index = downloadManager.downloadingArray.index(where: {$0.fileName == bookSlug && $0.destinationPath == fileType.destinationPathWithSlug(bookSlug: bookSlug)}) {
+            downloadManager.cancelTaskAtIndex(index)
+        }
+        else {
+            let path = fileType.destinationPathWithSlug(bookSlug: bookSlug)// pathForFileName(filename: bookSlug,bookSlug: bookSlug)
+            if FileManager.default.fileExists(atPath: path) {
+                try! FileManager.default.removeItem(atPath: path)
+            }
+        }
+        UserDefaults.standard.removeObject(forKey: bookSlug)
+    }
+    
+    func deleteAudiobook(bookSlug: String){
+        
+        let fileType = FileType.audiobook
+        
+        try? FileManager.default.removeItem(atPath: fileType.destinationPathWithSlug(bookSlug: bookSlug))
+    }
+    
+    func getEbookProgress(bookSlug: String) -> Float? {
+        if let model = downloadManager.downloadingArray.first(where: {$0.fileName == bookSlug && $0.destinationPath == FileType.ebook.destinationPathWithSlug(bookSlug: bookSlug)}){
+            return model.progress
+        }
+        return nil
+    }
+    
+    func getAudiobookProgress(bookDetailsModel: BookDetailsModel) -> Float? {
+        
+        guard let downloadingAudiobook = getDownloadingAudiobook(bookSlug: bookDetailsModel.slug) else {return nil}
+        
+        return downloadingAudiobook.getProgress()
+    }
+
+    func downloadEbook(bookDetailsModel: BookDetailsModel) {
+        
+        guard bookDetailsModel.epub.count > 0, bookDetailsModel.slug.count > 0 else {
+            return
+        }
+        
+        downloadManager.addDownloadTask(bookDetailsModel.slug, fileURL: bookDetailsModel.epub, destinationPath: FileType.ebook.destinationPathWithSlug(bookSlug: bookDetailsModel.slug))
+    }
+    
+    func downloadAudiobooks(bookDetailsModel: BookDetailsModel) {
+        
+        let bookSlug = bookDetailsModel.slug
+        guard bookDetailsModel.getAudiobookFilesUrls().count > 0, bookSlug.count > 0 else {
+            return
+        }
+        
+        if let downloadingAudiobook = getDownloadingAudiobook(bookSlug:bookSlug){
+            
+            if let firstUrl = downloadingAudiobook.waitingToDownloadUrls.first{
+                addDownloadAudiobookTask(bookSlug: bookSlug, fileUrl: firstUrl)
+                return
+            }
+            else{
+                clearDownloadingAudiobookFromQueue(bookSlug: bookSlug)
+            }
+        }
+        
+        let downloadingAudiobook = DownloadingAudiobook(bookDetailsModel: bookDetailsModel)
+        if let firstUrl = downloadingAudiobook.waitingToDownloadUrls.first {
+            downloadingAudiobooks.append(downloadingAudiobook)
+            addDownloadAudiobookTask(bookSlug: bookSlug, fileUrl: firstUrl)
+        }
+    }
+    
+    func addDownloadAudiobookTask(bookSlug: String, fileUrl: String){
+        downloadManager.addDownloadTask((fileUrl as NSString).lastPathComponent, fileURL: fileUrl, destinationPath: FileType.audiobook.destinationPathWithSlug(bookSlug: bookSlug))
+    }
+
+    func notifyDelegateThatProgressChanged(downloadModel: MZDownloadModel){
+        guard let delegate = delegate else { return }
+        
+        if downloadModel.isAudiobook(){
+            for downloadAudiobook in downloadingAudiobooks {
+                if downloadAudiobook.waitingToDownloadUrls.index(of: downloadModel.fileURL) != nil {
+                    delegate.downloadManagerDownloadProgressChanged(model: downloadModel, allProgress: downloadAudiobook.getProgress() + downloadModel.progress/downloadAudiobook.allUrlsCount, bookSlug: downloadAudiobook.bookSlug)
+                    return
+                }
+            }
+        }
+        delegate.downloadManagerDownloadProgressChanged(model: downloadModel, allProgress: downloadModel.progress, bookSlug: downloadModel.fileName)
+    }
+    
+    func downloadRequestStarted(_ downloadModel: MZDownloadModel, index: Int) {
+        
+        notifyDelegateThatProgressChanged(downloadModel: downloadModel)
+    }
+    
+    func downloadRequestDidPopulatedInterruptedTasks(_ downloadModels: [MZDownloadModel]) {
+    }
+    
+    func downloadRequestDidUpdateProgress(_ downloadModel: MZDownloadModel, index: Int) {
+        notifyDelegateThatProgressChanged(downloadModel: downloadModel)
+    }
+    
+    func downloadRequestDidPaused(_ downloadModel: MZDownloadModel, index: Int) {
+    }
+    
+    func downloadRequestDidResumed(_ downloadModel: MZDownloadModel, index: Int) {
+    }
+    
+    func downloadRequestCanceled(_ downloadModel: MZDownloadModel, index: Int) {
+        if downloadModel.isAudiobook(){
+            for downloadAudiobook in downloadingAudiobooks {
+                if downloadAudiobook.waitingToDownloadUrls.index(of: downloadModel.fileURL) != nil {
+                    clearDownloadingAudiobookFromQueue(bookSlug: downloadAudiobook.bookSlug)
+
+                    delegate?.downloadManagerDownloadFailed(model: downloadModel, bookSlug:downloadAudiobook.bookSlug )
+                    return
+                }
+            }
+        }
+
+        delegate?.downloadManagerDownloadFailed(model: downloadModel, bookSlug: downloadModel.fileName)
+    }
+    
+    func downloadRequestFinished(_ downloadModel: MZDownloadModel, index: Int) {
+        
+        // audiobook
+        if downloadModel.isAudiobook() {
+            for downloadAudiobook in downloadingAudiobooks {
+                if let index = downloadAudiobook.waitingToDownloadUrls.index(of: downloadModel.fileURL) {
+                    downloadAudiobook.waitingToDownloadUrls.remove(at: index)
+                    downloadAudiobook.downloadedUrls.append(downloadModel.fileURL)
+                    let slug = downloadAudiobook.bookSlug
+                    // check if there is any waiting to download url, and start downloading
+                    if let firstUrl = downloadAudiobook.waitingToDownloadUrls.first(where: {$0.count > 0}) {
+                        addDownloadAudiobookTask(bookSlug: downloadAudiobook.bookSlug, fileUrl: firstUrl)
+                    }
+                    else { // otherwise, downloading is finished, notify delegates
+                        clearDownloadingAudiobookFromQueue(bookSlug: slug)
+                        delegate?.downloadManagerDownloadFinished(model: downloadModel, bookSlug: slug)
+                    }
+                }
+            }
+        }
+        else {//ebook
+            delegate?.downloadManagerDownloadFinished(model: downloadModel, bookSlug: downloadModel.fileName)
+        }
+    }
+    
+    func downloadRequestDidFailedWithError(_ error: NSError, downloadModel: MZDownloadModel, index: Int) {
+        var bookSlug = downloadModel.fileName
+        if downloadModel.isAudiobook() {
+            for downloadAudiobook in downloadingAudiobooks {
+                if downloadAudiobook.waitingToDownloadUrls.index(of: downloadModel.fileURL) != nil {
+                    bookSlug = downloadAudiobook.bookSlug
+                    clearDownloadingAudiobookFromQueue(bookSlug: bookSlug ?? "")
+                    continue
+                }
+            }
+        }
+        delegate?.downloadManagerDownloadFailed(model: downloadModel, bookSlug: downloadModel.fileName)
+    }
+    
+    //Oppotunity to handle destination does not exists error
+    //This delegate will be called on the session queue so handle it appropriately
+    func downloadRequestDestinationDoestNotExists(_ downloadModel: MZDownloadModel, index: Int, location: URL) {
+        let myDownloadPath = downloadModel.destinationPath
+        if !FileManager.default.fileExists(atPath: myDownloadPath) {
+            try! FileManager.default.createDirectory(atPath: myDownloadPath, withIntermediateDirectories: true, attributes: nil)
+        }        
+        let filePath = myDownloadPath + "/" + downloadModel.fileName
+        if FileManager.default.fileExists(atPath: filePath){
+           try! FileManager.default.removeItem(atPath: filePath)
+        }
+        try! FileManager.default.moveItem(at: location, to: URL(fileURLWithPath: filePath))
+    }
+}
diff --git a/iOS/WolneLektury/Connection/NetworkService.swift b/iOS/WolneLektury/Connection/NetworkService.swift
new file mode 100644 (file)
index 0000000..c95ba6c
--- /dev/null
@@ -0,0 +1,348 @@
+//
+//  NetworkService.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Alamofire
+import AlamofireActivityLogger
+import OAuthSwift
+import SwiftKeychainWrapper
+
+enum Result<Model> {
+    case success(Model)
+    case failure(Error)
+}
+
+typealias ConnectionCompletionHandler = (_ result: Result<Any>) -> ()
+
+let keychainWrapperCredentialString = "credential"
+
+final class NetworkService {
+    
+    private let REQUEST_TOKEN_HEADER = "Token-Requested"
+    private let AUTH_REQUIRED_HEADER = "Authentication-Required"
+    private let AUTHORIZATION_HEADER = "Authorization"
+    
+    private let OAUTH_REALM = "realm=\"API\", "
+    private let OAUTH_CONSUMER_KEY = "oauth_consumer_key"
+    private let OAUTH_NONCE = "oauth_nonce"
+    private let OAUTH_SIGNATURE = "oauth_signature"
+    private let OAUTH_SIGNATURE_METHOD = "oauth_signature_method"
+    private let OAUTH_SIGNATURE_METHOD_VALUE = "HMAC-SHA1"
+    private let OAUTH_TIMESTAMP = "oauth_timestamp"
+    private let OAUTH_ACCESS_TOKEN = "oauth_token"
+    private let OAUTH_VERSION = "oauth_version"
+    private let OAUTH_VERSION_VALUE = "1.0"
+    private let ONE_SECOND = 1000
+    
+    // Temporarly token used only for request access token
+    private var oAuthRequestToken: OAuthTokenModel?
+    
+    // Place with sensitive data such as authentication tokens
+    private var credentials: Credentials
+
+    // Values used for OAuth authentication
+    private var signCredentials = OAuthSwiftCredential(consumerKey: Config.CONSUMER_KEY, consumerSecret: Config.CONSUMER_SECRET)
+    
+    let baseURL: String
+    
+    private var _privateManager: SessionManager?
+    var manager:SessionManager{
+        get{
+            if let manager = _privateManager{
+                return manager
+            }
+            
+            let configuration = URLSessionConfiguration.default
+            configuration.timeoutIntervalForRequest = 60
+            configuration.timeoutIntervalForResource = 60
+            
+            _privateManager = Alamofire.SessionManager(configuration: configuration)
+            //            _privateManager!.retrier = self
+            //            _privateManager!.adapter = self
+            return _privateManager!
+        }
+        set(newVal){
+            _privateManager = manager
+        }
+    }
+    
+    init() {
+        credentials = (KeychainWrapper.standard.object(forKey: keychainWrapperCredentialString) as? Credentials) ?? Credentials()
+        self.baseURL = Config.BASE_URL
+        
+        if SharedGlobals.shared.isFirstUse(){
+            logout()
+        }
+        setSignCredentials(oauthToken: credentials.oauthTokenModel?.token ?? "", oauthTokenSecret: credentials.oauthTokenModel?.tokenSecret ?? "")
+    }
+
+    func logout(){
+        updateUserCredentials(oAuthTokenModel: nil)
+    }
+    
+    func isLoggedIn() -> Bool {
+        return credentials.isLoggedIn()
+    }
+    
+    func setSignCredentials(oauthToken: String, oauthTokenSecret: String){
+        signCredentials.oauthToken = oauthToken
+        signCredentials.oauthTokenSecret = oauthTokenSecret
+    }
+    
+    func updateUserCredentials(oAuthTokenModel: OAuthTokenModel?){
+        
+        credentials.oauthTokenModel = oAuthTokenModel
+        if let oAuthTokenModel = oAuthTokenModel, oAuthTokenModel.isValid(){
+            setSignCredentials(oauthToken: oAuthTokenModel.token, oauthTokenSecret: oAuthTokenModel.tokenSecret)
+        }
+        else{
+            setSignCredentials(oauthToken: "", oauthTokenSecret: "")
+        }
+        
+        KeychainWrapper.standard.set(credentials, forKey:keychainWrapperCredentialString)
+    }
+
+    
+    private func getNonce() -> String {
+        let uuid: String = UUID().uuidString
+        return uuid[0..<8]
+    }
+    
+    private func URLString(restAction: RestAction) ->String
+    {
+        let url = baseURL + restAction.endpoint
+        return url
+    }
+    
+    
+    func performRequest<responseModel: Decodable>(with action:RestAction, responseModelType: responseModel.Type, params: Parameters? = nil, completionHandler: ConnectionCompletionHandler?)
+    {
+        
+        performRequest(with: action, responseModelType: responseModelType, urlSuffix: "", params: params, completionHandler: completionHandler)
+    }
+    
+    func performRequest<responseModel: Decodable>(with action:RestAction, responseModelType: responseModel.Type, urlSuffix: String, params: Parameters? = nil, completionHandler: ConnectionCompletionHandler?)
+    {
+        var components: [(String, String)] = []
+        
+        let baseURLString = URLString(restAction: action) + urlSuffix
+
+        if let params = params{
+            for key in params.keys.sorted(by: <) {
+                let value = params[key]!
+                components += URLEncoding.default.queryComponents(fromKey: key, value: value)
+            }
+        }
+        
+        let componentsString = components.map { "\($0)=\($1)" }.joined(separator: "&")
+        var urlParameters = ""
+        if componentsString.count > 0{
+            urlParameters = urlParameters + "?" + componentsString
+        }
+
+        let url = baseURLString + urlParameters
+        
+        let method = action.httpMethod
+        
+        var headers: HTTPHeaders? = nil
+        let acceptableContentType = "application/json"
+        
+        if /*action.authenticationRequiredHeader || */isLoggedIn(){
+            
+            let baseUrl = URL(string: baseURLString)!
+
+            var oAuthParameters = getOAuthParams()
+            
+            if let params = params{
+                for key in params.keys.sorted(by: <) {
+                    
+                    oAuthParameters[key] = params[key]!
+                }
+            }
+           
+            let signature = signCredentials.signature(method: action.httpMethod == .post ? .POST : .GET, url: baseUrl, parameters: oAuthParameters)
+            
+            var authorizationString = "OAuth " + OAUTH_REALM
+            authorizationString +=  OAUTH_CONSUMER_KEY + "=\"" + ((oAuthParameters[OAUTH_CONSUMER_KEY] as? String) ?? "") + "\", "
+            authorizationString +=  OAUTH_NONCE + "=\"" + ((oAuthParameters[OAUTH_NONCE] as? String) ?? "") + "\", "
+            authorizationString +=  OAUTH_SIGNATURE + "=\"" + signature + "\", "
+            authorizationString +=  OAUTH_SIGNATURE_METHOD + "=\"" + OAUTH_SIGNATURE_METHOD_VALUE + "\", "
+            authorizationString +=  OAUTH_TIMESTAMP + "=\"" + ((oAuthParameters[OAUTH_TIMESTAMP] as? String) ?? "") + "\", "
+            authorizationString +=  OAUTH_ACCESS_TOKEN + "=\"" + signCredentials.oauthToken + "\", "
+            authorizationString +=  OAUTH_VERSION + "=\"" + OAUTH_VERSION_VALUE + "\"";
+            
+            headers = [
+                "Accept": "application/json",
+            ]
+            
+            headers![AUTHORIZATION_HEADER] = authorizationString
+
+            print(authorizationString)
+        }
+        else{
+            headers = ["Accept": "application/json"]
+        }
+        
+        
+        let encoding : ParameterEncoding = URLEncoding.default //(method == .get || method == .delete) ? URLEncoding.default : JSONEncoding.default
+        self.manager.request(url, method: method, parameters: nil, encoding: encoding, headers: headers)
+            .validate(contentType: [acceptableContentType])
+            .validate()
+            .log(level: .all, options: [.onlyDebug, .jsonPrettyPrint, .includeSeparator], printer: NativePrinter())
+            .responseJSON { response in
+                
+                guard response.error == nil else {
+                    completionHandler?(.failure(response.error!))
+                    return
+                }
+                
+                if  let data = response.data,
+                    let model = try? JSONDecoder().decode(responseModel.self, from: data) {
+                    completionHandler?(.success(model))
+                } else {
+                    completionHandler?(.failure(NSError(  domain: "wolnelektury",
+                                                          code: 1000,
+                                                          userInfo: ["error":"wrong model"])))
+                }
+        }
+    }
+    
+    func getOAuthParams() -> [String: Any]{
+        let timestamp = String(Int64(Date().timeIntervalSince1970))
+        let nonce = OAuthSwiftCredential.generateNonce()
+        let oAuthParameters = signCredentials.authorizationParameters(nil, timestamp: timestamp, nonce: nonce)
+        return oAuthParameters
+    }
+    
+    func urlStringWithRequestTokenParameters(action: RestAction) -> String{
+        var url = URLString(restAction: action)
+        
+        let urlUrl = URL(string: url)!
+        
+        if action == .accessToken && oAuthRequestToken != nil{
+            signCredentials.oauthToken = oAuthRequestToken!.token
+            signCredentials.oauthTokenSecret = oAuthRequestToken!.tokenSecret
+        }
+        else{
+            signCredentials.oauthToken = ""
+            signCredentials.oauthTokenSecret = ""
+            oAuthRequestToken = nil
+        }
+        
+        var oAuthParameters =  getOAuthParams()
+        let signature = signCredentials.signature(method: .GET, url: urlUrl, parameters: oAuthParameters)
+        
+        url = url + "?" +
+            OAUTH_CONSUMER_KEY.urlEncoded() + "=" + Config.CONSUMER_KEY.urlEncoded() + "&" +
+            OAUTH_NONCE.urlEncoded() + "=" + (oAuthParameters[OAUTH_NONCE] as! String).urlEncoded() + "&" +
+            OAUTH_SIGNATURE_METHOD.urlEncoded() + "=" + OAUTH_SIGNATURE_METHOD_VALUE.urlEncoded() + "&" +
+            OAUTH_TIMESTAMP.urlEncoded() + "=" + (oAuthParameters[OAUTH_TIMESTAMP] as! String) + "&" +
+            OAUTH_VERSION.urlEncoded() + "=" + OAUTH_VERSION_VALUE.urlEncoded() + "&" +
+            OAUTH_SIGNATURE.urlEncoded() + "=" + signature.urlEncoded()
+        
+        if action == .accessToken && oAuthRequestToken != nil{
+            url = url + "&" + OAUTH_ACCESS_TOKEN.urlEncoded() + "=" + oAuthRequestToken!.token.urlEncoded()
+        }
+        
+        return url
+    }
+    
+    func requestToken( completionHandler: ConnectionCompletionHandler?)
+    {
+        let action = RestAction.requestToken
+        let method = action.httpMethod
+
+        let acceptableContentType = "text/html"
+        let url = urlStringWithRequestTokenParameters(action: action)
+        print(url)
+        
+        let parameters:Parameters = [:]
+        let headers: HTTPHeaders? = nil
+        
+        let encoding : ParameterEncoding = (method == .get || method == .delete) ? URLEncoding.default : JSONEncoding.default
+        self.manager.request(url, method: method, parameters: parameters, encoding: encoding, headers: headers)
+            .validate(contentType: [acceptableContentType])
+            .validate()
+            .log(level: .all, options: [.onlyDebug, .jsonPrettyPrint, .includeSeparator], printer: NativePrinter())
+            .response { response in
+                
+                guard response.error == nil else {
+                    completionHandler?(.failure(response.error!))
+                    return
+                }
+                
+                if let data = response.data, let string = String(data: data, encoding: .utf8){
+                    print(string)
+                    
+                    var tokenModel: OAuthTokenModel?
+                    let parameters = string.parametersFromQueryString
+                    if let oauthToken = parameters["oauth_token"], let oauthTokenSecret = parameters["oauth_token_secret"]  {
+                        tokenModel = OAuthTokenModel(token: oauthToken, tokenSecret: oauthTokenSecret)
+                    }
+                    
+                    if let tokenModel = tokenModel, tokenModel.isValid(){
+                        self.oAuthRequestToken = tokenModel
+                        completionHandler?(.success(tokenModel))
+                    }
+                    else{
+                        self.oAuthRequestToken = nil
+                        completionHandler?(.failure(NSError(  domain: "wolnelektury",
+                                                              code: 1000,
+                                                              userInfo: ["error":"wrong model"])))
+                    }
+                }
+        }
+    }
+    
+    func requestAccessToken( completionHandler: ConnectionCompletionHandler?)
+    {
+        let action = RestAction.accessToken
+        let method = action.httpMethod
+        
+        let acceptableContentType = "text/html"
+        let url = urlStringWithRequestTokenParameters(action: action)
+        print(url)
+        
+        let parameters:Parameters = [:]
+        let headers: HTTPHeaders? = nil
+        
+        let encoding : ParameterEncoding = (method == .get || method == .delete) ? URLEncoding.default : JSONEncoding.default
+        self.manager.request(url, method: method, parameters: parameters, encoding: encoding, headers: headers)
+            .validate(contentType: [acceptableContentType])
+            .validate()
+            .log(level: .all, options: [.onlyDebug, .jsonPrettyPrint, .includeSeparator], printer: NativePrinter())
+            .response { response in
+                
+                guard response.error == nil else {
+                    completionHandler?(.failure(response.error!))
+                    return
+                }
+                
+                if let data = response.data, let string = String(data: data, encoding: .utf8){
+                    print(string)
+                    
+                    let parameters = string.parametersFromQueryString
+
+                    var tokenModel: OAuthTokenModel?
+                    if let oauthToken = parameters["oauth_token"], let oauthTokenSecret = parameters["oauth_token_secret"]{
+                        tokenModel = OAuthTokenModel(token: oauthToken, tokenSecret: oauthTokenSecret)
+                    }
+                    
+                    if let tokenModel = tokenModel, tokenModel.isValid(){
+                        
+                        completionHandler?(.success(tokenModel))
+                    }
+                    else{
+                        completionHandler?(.failure(NSError(  domain: "wolnelektury",
+                                                              code: 1000,
+                                                              userInfo: ["error":"wrong model"])))
+                    }
+                }
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Connection/RestAction.swift b/iOS/WolneLektury/Connection/RestAction.swift
new file mode 100644 (file)
index 0000000..f7dcd6f
--- /dev/null
@@ -0,0 +1,98 @@
+//
+//  RestAction.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Alamofire
+
+enum RestAction{
+
+    //books
+    case filterBooks
+    case books
+    case newest
+    case recommended
+    case audiobooks
+    case setReadingState
+    case getReadingState
+    case getReadingBooks
+    case getReadenBooks
+    case setFavouriteState
+    case getFavouriteState
+    case preview
+    case getFavourites
+
+    case epochs
+    case genres
+    case kinds
+    case blog
+
+    //user
+    case requestToken
+    case accessToken
+    case username
+
+    
+    
+    var endpoint: String{
+        switch self {
+        //books
+        case .filterBooks:
+            return "filter-books/"
+        case .books:
+            return "books/"
+        case .newest:
+            return "newest/"
+        case .recommended:
+            return "recommended/"
+        case .audiobooks:
+            return "audiobooks/"
+        case .setReadingState, .getReadingState:
+            return "reading/"
+        case .getReadingBooks:
+            return "shelf/reading/"
+        case .getReadenBooks:
+            return "shelf/complete/"
+        case .setFavouriteState, .getFavouriteState:
+            return "like/"
+        case .getFavourites:
+            return "shelf/likes/"
+
+        case .epochs:
+            return "epochs"
+        case .genres:
+            return "genres"
+        case .kinds:
+            return "kinds"
+        case .preview:
+            return "preview"
+        case .blog:
+            return "blog"
+
+        //user
+        case .requestToken:
+            return "oauth/request_token/"
+        case .accessToken:
+            return "oauth/access_token/"
+        case .username:
+            return "username/"
+        }
+    }
+    
+    var httpMethod: Alamofire.HTTPMethod{
+        switch self {
+        case .setReadingState, .setFavouriteState:
+            return .post
+        default:
+            return .get
+        }
+    }
+    
+    var tokenRequestedHeader: Bool{
+        return self == .requestToken || self == .accessToken
+    }
+}
diff --git a/iOS/WolneLektury/Connection/Routes.swift b/iOS/WolneLektury/Connection/Routes.swift
new file mode 100644 (file)
index 0000000..e1dd8df
--- /dev/null
@@ -0,0 +1,26 @@
+//
+//  Routes.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+struct Route<Model> {
+    let endpoint: String
+    var id: Int?
+    var params: [String: AnyObject]?
+    
+    init (endpoint: String, id: Int? = nil, parameters: [String: AnyObject]? = nil) {
+        self.endpoint = endpoint
+        self.id = id
+        self.params = parameters
+    }
+}
+
+struct Routes {
+    static let filterBooks = Route<[BookModel]>(endpoint: "filter-books")
+    
+}
diff --git a/iOS/WolneLektury/Connection/SyncManager.swift b/iOS/WolneLektury/Connection/SyncManager.swift
new file mode 100644 (file)
index 0000000..3061b1b
--- /dev/null
@@ -0,0 +1,144 @@
+//
+//  SyncManager.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+
+class SyncManager: NSObject {
+    private var networkService = NetworkService()
+
+    override init() {
+        
+    }
+    
+    func isLoggedIn() -> Bool {
+        return networkService.isLoggedIn()
+    }
+    
+    func logout() {
+        networkService.logout()
+        DatabaseManager.shared.updateUser(usernameModel: nil)
+    }
+    
+    func updateUserCredentials(oAuthTokenModel: OAuthTokenModel?){
+        networkService.updateUserCredentials(oAuthTokenModel: oAuthTokenModel)
+    }
+    
+    func requestToken(completionHandler: ConnectionCompletionHandler?){
+        networkService.requestToken(completionHandler: completionHandler)
+    }
+    
+    func accessToken(completionHandler: ConnectionCompletionHandler?){
+        networkService.requestAccessToken(completionHandler: completionHandler)
+    }
+
+    func getUsername(completionHandler: ConnectionCompletionHandler?){
+        networkService.performRequest(with: .username, responseModelType: UsernameModel.self, params: nil, completionHandler: completionHandler)
+    }
+    
+    func filterBooks(params: FilterBooksParameters, completionHandler: ConnectionCompletionHandler?){
+        networkService.performRequest(with: .filterBooks, responseModelType: [BookModel].self, params: params.parameters(), completionHandler: completionHandler)
+    }
+
+    func getNews(after: String?, completionHandler: ConnectionCompletionHandler?){
+        
+        var params = [String: Any]()
+        if let after = after {
+            params["after"] = after
+        }
+        params["count"] = 20
+        
+        networkService.performRequest(with: .blog, responseModelType: [NewsModel].self, params: params, completionHandler: completionHandler)
+    }
+
+    func getCategories(filterSection: FilterSection, bookOnly: Bool, completionHandler: ConnectionCompletionHandler?){
+        
+        var method = RestAction.epochs
+        if filterSection == .genres{
+            method = .genres
+        }
+        else if filterSection == .kinds{
+            method = .kinds
+        }
+        networkService.performRequest(with: method, responseModelType: [CategoryModel].self, params: ["book_only": bookOnly ? "true" : "false"], completionHandler: completionHandler)
+    }
+    
+    func getDataForLibrary(libraryCollectionType: LibraryCollectionType, completionHandler: ConnectionCompletionHandler?){
+        var method: RestAction!
+        
+        switch libraryCollectionType {
+        case .newest:
+            method = .newest
+        case .recommended:
+            method = .recommended
+        case .reading_now:
+            method = .getReadingBooks
+        }
+        
+        networkService.performRequest(with: method, responseModelType: [BookModel].self, params: nil, completionHandler: completionHandler)
+    }
+    
+    func getPreview(completionHandler: ConnectionCompletionHandler?){
+        
+        networkService.performRequest(with: .preview, responseModelType: [BookModel].self, params: nil, completionHandler: completionHandler)
+    }
+
+
+    func getDataForListType(listViewControllerType: ListViewControllerType, params: FilterBooksParameters? = nil, completionHandler: ConnectionCompletionHandler?){
+        var method: RestAction!
+        
+        switch listViewControllerType {
+        case .newest:
+            method = .newest
+        case .recommended:
+            method = .recommended
+        case .reading_now:
+            method = .getReadingBooks
+        case .audiobooks:
+            method = .audiobooks
+        case .news:
+            method = .blog
+        case .favourites:
+            method = .getFavourites
+        case .completed:
+            method = .getReadenBooks
+        }
+        
+        if method == .blog {
+            networkService.performRequest(with: method, responseModelType: [NewsModel].self, params: params?.parameters(), completionHandler: completionHandler)
+        }
+        else {
+            networkService.performRequest(with: method, responseModelType: [BookModel].self, params: params?.parameters(), completionHandler: completionHandler)
+        }
+    }
+
+    func getBookDetails(bookSlug: String, completionHandler: ConnectionCompletionHandler?){
+        
+        networkService.performRequest(with: .books, responseModelType: BookDetailsModel.self, urlSuffix: bookSlug + "/", completionHandler: completionHandler)
+    }
+
+    func getFavouriteState(slug: String, completionHandler: ConnectionCompletionHandler?){
+        
+        networkService.performRequest(with: .getFavouriteState, responseModelType: LikeModel.self, urlSuffix: slug + "/", params: nil, completionHandler: completionHandler)
+    }
+    
+    func setFavouriteState(slug: String, favourite: Bool, completionHandler: ConnectionCompletionHandler?){
+        
+        networkService.performRequest(with: .setFavouriteState, responseModelType: LikeModel.self, urlSuffix: slug + "/", params: ["action" : favourite ? "like" : "unlike"], completionHandler: completionHandler)
+    }
+    
+    func setReadingState(slug: String, readingState: ReadingStateModel.ReadingState, completionHandler: ConnectionCompletionHandler?){
+        
+        networkService.performRequest(with: .setReadingState, responseModelType: ReadingStateModel.self, urlSuffix: slug + "/" + readingState.rawValue + "/", params: nil, completionHandler: completionHandler)
+    }
+    
+    func getReadingState(slug: String, completionHandler: ConnectionCompletionHandler?){
+        
+        networkService.performRequest(with: .getReadingState, responseModelType: ReadingStateModel.self, urlSuffix: slug + "/", params: nil, completionHandler: completionHandler)
+    }
+}
diff --git a/iOS/WolneLektury/Constants.swift b/iOS/WolneLektury/Constants.swift
new file mode 100644 (file)
index 0000000..9c5d3d9
--- /dev/null
@@ -0,0 +1,69 @@
+//
+//  Constants.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+
+import UIKit
+import MZDownloadManager
+
+struct Constants {
+    
+    static let SAMPLE_FLOAT: Float = 4.25
+    static let ebookPath = MZUtility.baseFilePath + "/ebooks"
+    static let audiobookPath = MZUtility.baseFilePath + "/audiobooks"
+    static let callbackOauthHost = "oauth-callback"
+    static let callbackPaypalSuccessHost = "paypal_return"
+    static let callbackPaypalErrorHost = "paypal_error"
+    static let authorizationUrlFormat = "https://wolnelektury.pl/api/oauth/authorize/?oauth_token=%@&oauth_callback=wolnelekturyapp://oauth-callback/"
+    static let webPaypalFormUrl = "https://wolnelektury.pl/paypal/app-form/"
+    static let donateEnabled: Bool = false
+
+    struct StoryboardIds {
+        static let SampleController = "SampleControllerID"
+    }
+    
+    struct CellIds {
+        //        static let SampleCell = "SampleCellId"
+    }
+    
+    struct Segues {
+        static let SampleSegue = "sampleSegue"
+    }
+    
+    struct Notifications {
+        static let SampleNotification = "SampleNotification"
+    }
+    
+    struct Colors {
+        static let menuTintColor = {
+            return Constants.Colors.lightGreenBgColor()
+        }
+        
+        static let navbarBgColor = {
+            return Constants.Colors.lightGreenBgColor()
+        }
+        
+        static let lightGreenBgColor = {
+            return UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
+        }
+
+        static let darkGreenBgColor = {
+            return UIColor(red:0.00, green:0.40, blue:0.42, alpha:1.00)
+        }
+        
+        static let grayTextColor = {
+            return UIColor(red:0.32, green:0.32, blue:0.32, alpha:1.00)
+        }
+
+        static let orangeColor = {
+            return UIColor(red:1.0, green:165.0/255.0, blue:0, alpha:1.00)
+        }
+
+    }
+}
diff --git a/iOS/WolneLektury/Controls/ActivityIndicatorButton.swift b/iOS/WolneLektury/Controls/ActivityIndicatorButton.swift
new file mode 100644 (file)
index 0000000..65fe2fb
--- /dev/null
@@ -0,0 +1,61 @@
+//
+//  ActivityIndicatorButton.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 12/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+enum ActivityIndicatorButtonState{
+    case loading
+    case button
+    case hidden
+}
+
+class ActivityIndicatorButton: UIButton {
+
+    private var indicatorButtonState: ActivityIndicatorButtonState = .loading
+    
+    private var activityIndicatorView: UIActivityIndicatorView!
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        customInit()
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        customInit()
+    }
+    
+    func customInit() {
+        activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
+        activityIndicatorView.hidesWhenStopped = true
+        activityIndicatorView.tintColor = tintColor
+        activityIndicatorView.color = tintColor
+
+        activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
+        self.addSubview(activityIndicatorView)
+        activityIndicatorView.centerInSuperview()
+        setIndicatorButtonState(state: .loading)
+        titleLabel?.text = ""
+    }
+    
+    func setIndicatorButtonState(state: ActivityIndicatorButtonState){
+        indicatorButtonState = state
+        
+        switch state {
+        case .loading:
+            isHidden = false
+            activityIndicatorView.startAnimating()
+            setImage(nil, for: .normal)
+        case .button:
+            isHidden = false
+            activityIndicatorView.stopAnimating()
+            setImage(#imageLiteral(resourceName: "ic_reload"), for: .normal)
+        case .hidden:
+            isHidden = true
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Controls/ProgressButton.swift b/iOS/WolneLektury/Controls/ProgressButton.swift
new file mode 100644 (file)
index 0000000..8534e85
--- /dev/null
@@ -0,0 +1,51 @@
+//
+//  ProgressButton.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 20/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class ProgressButton: UIView {
+
+    var progressView: UIView!
+    var titleLabel: UILabel!
+    var button: UIBarButtonItem!
+    var progressWidthConstraint: NSLayoutConstraint!
+    
+    var bgColor: UIColor = .black{
+        didSet {
+            progressView.backgroundColor = bgColor
+        }
+    }
+    
+    var textColor: UIColor = .black{
+        didSet {
+            progressView.backgroundColor = bgColor
+        }
+    }
+
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        
+        setupView()
+        setupConstraints()
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    func setBackgroundColor(color: UIColor) {
+        
+    }
+    
+    func setupView() {
+    }
+    
+    func setupConstraints() {
+        
+    }
+}
diff --git a/iOS/WolneLektury/Controls/ProgressLabel.swift b/iOS/WolneLektury/Controls/ProgressLabel.swift
new file mode 100644 (file)
index 0000000..735d6ce
--- /dev/null
@@ -0,0 +1,87 @@
+//
+//  ProgressLabel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 20/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+// from https://stackoverflow.com/a/46047570/871425
+class ProgressLabel: UIView {
+    
+    private var privateColor = UIColor.green
+    @IBInspectable var color: UIColor {
+        get {
+            return privateColor
+        }set {
+            privateColor = newValue
+        }
+    }
+
+    var textColor = UIColor.white
+    var font = UIFont.systemFont(ofSize: 15)
+    var fullProgress: Bool = false
+    var customText: String? {
+        didSet{
+            if customText != nil{
+                setNeedsDisplay()
+            }
+        }
+    }
+    var progress: Float = 0 {
+        didSet {
+            customText = nil
+            progress = Float.minimum(100.0, Float.maximum(progress, 0.0))
+            self.setNeedsDisplay()
+        }
+    }
+    
+    override func draw(_ rect: CGRect) {
+        let context = UIGraphicsGetCurrentContext()!
+        
+        // Set up environment.
+        let size = self.bounds.size
+
+        layer.borderColor = privateColor.cgColor
+        layer.borderWidth = 1.0
+        layer.cornerRadius = 10
+        clipsToBounds = true
+        
+        var prg = progress
+        var progressMessage = NSString(format:"Pobieranie %d%%", Int(prg))
+
+        if let customText = customText{
+            prg = fullProgress ? 100 : 0
+            progressMessage = customText as NSString
+        }
+        
+        // Prepare progress as a string.
+        var attributes: [NSAttributedStringKey:Any] = [ NSAttributedStringKey.font : font ]
+        let textSize = progressMessage.size(withAttributes: attributes)
+        let progressX = ceil(CGFloat(prg) / 100 * size.width)
+        let textPoint = CGPoint(x: ceil((size.width - textSize.width) / 2.0), y: ceil((size.height - textSize.height) / 2.0))
+        
+        // Draw background + foreground text
+        privateColor.setFill()
+        context.fill(self.bounds)
+        attributes[NSAttributedStringKey.foregroundColor] = textColor
+        let rct = CGRect(x: 20, y: 12, width: bounds.size.width - 20, height: bounds.size.height - 13)
+        progressMessage.draw(in: rct, withAttributes: attributes)
+
+        // Clip the drawing that follows to the remaining progress' frame.
+        context.saveGState()
+        let remainingProgressRect = CGRect(x: progressX, y: 0.0, width: size.width - progressX, height: size.height)
+        context.addRect(remainingProgressRect)
+        context.clip()
+        
+        // Draw again with inverted colors.
+        textColor.setFill()
+        context.fill(self.bounds)
+        attributes[NSAttributedStringKey.foregroundColor] = privateColor
+        progressMessage.draw(in: rct, withAttributes: attributes)
+
+        context.restoreGState()
+    }
+}
diff --git a/iOS/WolneLektury/Extensions/DateFormatter+Ext.swift b/iOS/WolneLektury/Extensions/DateFormatter+Ext.swift
new file mode 100644 (file)
index 0000000..92e7b75
--- /dev/null
@@ -0,0 +1,18 @@
+//
+//  DateFormatter+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+
+extension DateFormatter
+{
+    @nonobjc static let yyyy_mm_dd_dateFormatter: DateFormatter = {
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateFormat = "yyyy-MM-dd"
+        return dateFormatter
+    }()
+}
diff --git a/iOS/WolneLektury/Extensions/Dictionary+Ext.swift b/iOS/WolneLektury/Extensions/Dictionary+Ext.swift
new file mode 100644 (file)
index 0000000..f9e96c3
--- /dev/null
@@ -0,0 +1,24 @@
+//
+//  Dictionary+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 10/07/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+
+extension Dictionary{
+    var urlEncoded: String {
+        var parts = [String]()
+        
+        for (key, value) in self {
+            let keyString = "\(key)".urlEncoded()
+            let valueString = "\(value)".urlEncoded()
+            let query = "\(keyString)=\(valueString)"
+            parts.append(query)
+        }
+        
+        return parts.joined(separator: "&")
+    }
+}
diff --git a/iOS/WolneLektury/Extensions/MatomoTracker+Ext.swift b/iOS/WolneLektury/Extensions/MatomoTracker+Ext.swift
new file mode 100644 (file)
index 0000000..1941075
--- /dev/null
@@ -0,0 +1,14 @@
+//
+//  MatomoTracker+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 25/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+import MatomoTracker
+
+extension MatomoTracker {
+    static let shared: MatomoTracker = MatomoTracker(siteId: Config.PIWIK_SITE_ID, baseURL: URL(string: Config.PIWIK_URL)!)
+}
diff --git a/iOS/WolneLektury/Extensions/NSAttributedString+Ext.swift b/iOS/WolneLektury/Extensions/NSAttributedString+Ext.swift
new file mode 100644 (file)
index 0000000..ff6f8c9
--- /dev/null
@@ -0,0 +1,118 @@
+//
+//  NSAttributedString+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+
+import UIKit
+
+extension NSAttributedString{
+    
+    
+    static func AttributedString(font: UIFont, text: String, lineSpacing: CGFloat, textAlignment: NSTextAlignment) -> NSAttributedString{
+        
+        let paragraphStyle = NSMutableParagraphStyle()
+        paragraphStyle.lineSpacing = 1.0
+        paragraphStyle.lineHeightMultiple = lineSpacing
+        paragraphStyle.alignment = textAlignment
+        
+        let attrString = NSMutableAttributedString(string: text)
+        attrString.addAttribute(NSAttributedStringKey.font, value: font, range: NSMakeRange(0, attrString.length))
+        attrString.addAttribute(NSAttributedStringKey.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attrString.length))
+        return attrString;
+    }
+    
+    static func AttributedString(font: UIFont, text: String, color: UIColor) -> NSAttributedString{
+        
+        let attrString = NSMutableAttributedString(string: text)
+        attrString.addAttribute(NSAttributedStringKey.font, value: font, range: NSMakeRange(0, attrString.length))
+        attrString.addAttribute(NSAttributedStringKey.foregroundColor, value: color, range: NSMakeRange(0, attrString.length))
+        return attrString;
+    }
+    
+    
+    public convenience init(html:String) throws {
+        
+        let htmString = "<html><head><style> " +
+            "\nbody \n{\n" +
+            //"text-align: justify;\n" +
+                        "font-size: 11.00pt;\n" +
+                        "font-family: '-apple-system';\n" +
+                        "line-height: 6px;\n" +
+            "} \n" +
+            "</style></head><body>\(html)</body>" +
+        "</html>"
+        
+        
+        guard let data =  htmString.data(using: .unicode, allowLossyConversion: true) else {
+            throw NSError(domain: "Invalid HTML", code: -500, userInfo: nil)
+        }
+        
+        try self.init(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil)
+    }
+    
+    
+    public convenience init(string: String?, font: UIFont?) {
+        self.init(string: string, font: font, color: nil, textAlignment: nil, lineSpacing: nil)
+    }
+    
+    public convenience init(string: String?, font: UIFont?, color: UIColor?) {
+        self.init(string: string, font: font, color: color, textAlignment: nil, lineSpacing: nil)
+    }
+    
+    public convenience init(string: String?, font: UIFont?, color: UIColor?, textAlignment: NSTextAlignment?) {
+        self.init(string: string, font: font, color: color, textAlignment: textAlignment, lineSpacing: nil)
+    }
+    
+    public convenience init(string: String?, font: UIFont?, textAlignment: NSTextAlignment?) {
+        self.init(string: string, font: font, color: nil, textAlignment: textAlignment, lineSpacing: nil)
+    }
+    
+    public convenience init(string: String?, font: UIFont?, color: UIColor?, textAlignment: NSTextAlignment?, lineSpacing: CGFloat?) {
+        
+        let style = NSMutableParagraphStyle()
+        var addStyle = false
+        
+        
+        style.lineBreakMode = .byWordWrapping;
+        
+        var str = ""
+        if let value = string{
+            str = value
+        }
+        
+        var dict = [NSAttributedStringKey : AnyObject]()
+        if let fnt = font{
+            dict[NSAttributedStringKey.font] = fnt
+        }
+        
+        if let clr = color{
+            dict[NSAttributedStringKey.foregroundColor] = clr
+        }
+        
+        if let tal = textAlignment{
+            style.alignment = tal
+            if(tal == .justified){
+                style.firstLineHeadIndent = 0.05;
+            }
+            addStyle = true
+        }
+        
+        if let ls = lineSpacing{
+            style.lineSpacing = ls
+            addStyle = true
+        }
+        
+        
+        if(addStyle){
+            dict[NSAttributedStringKey.paragraphStyle] = style
+        }
+        
+        self.init(string: str, attributes: dict)
+        return
+    }
+}
diff --git a/iOS/WolneLektury/Extensions/NSObject+Ext.swift b/iOS/WolneLektury/Extensions/NSObject+Ext.swift
new file mode 100644 (file)
index 0000000..8e1be95
--- /dev/null
@@ -0,0 +1,39 @@
+//
+//  NSObject+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+extension NSObject
+{
+    var appDelegate:AppDelegate {
+        return UIApplication.shared.delegate as! AppDelegate
+    }
+    
+    func ebookExists(bookSlug: String) -> Bool {
+        let path = FileType.ebook.pathForFileName(filename: bookSlug, bookSlug: bookSlug)
+        print(path)
+        return FileManager.default.fileExists(atPath: path)
+    }
+    
+    static func audiobookExists(audioBookUrlString: String, bookSlug: String) -> Bool {
+        let fileName = (audioBookUrlString as NSString).lastPathComponent
+        return FileManager.default.fileExists(atPath: FileType.audiobook.pathForFileName(filename: fileName, bookSlug: bookSlug))
+    }
+    
+    static func audiobookPathIfExists(audioBookUrlString: String, bookSlug: String) -> URL? {
+        let fileName = (audioBookUrlString as NSString).lastPathComponent
+        let path = FileType.audiobook.pathForFileName(filename: fileName, bookSlug: bookSlug)
+        
+        if FileManager.default.fileExists(atPath: path) {
+            return URL(fileURLWithPath: path)
+        }
+        return nil
+    }
+
+}
diff --git a/iOS/WolneLektury/Extensions/String+Ext.swift b/iOS/WolneLektury/Extensions/String+Ext.swift
new file mode 100644 (file)
index 0000000..ad85be4
--- /dev/null
@@ -0,0 +1,211 @@
+//
+//  String+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+
+import UIKit
+
+extension String{
+    
+    var trimmed: String {
+        return trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
+    }
+    
+    var trimmedLength: Int{
+        get {
+            return trimmed.count
+        }
+    }
+    
+    var isBlank: Bool {
+        get {
+            return trimmedLength == 0
+        }
+    }
+    
+    //Validate Email
+    var isEmail: Bool {
+        do {
+            let regex = try NSRegularExpression(pattern: "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", options: .caseInsensitive)
+            let selfTrimmed = self.trimmed
+            return regex.firstMatch(in: selfTrimmed, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, selfTrimmed.characters.count)) != nil
+        } catch {
+            return false
+        }
+    }
+    
+    var isPostCode: Bool{
+        
+        let pinRegex =  "^\\d{2}-\\d{3}$" //"^[0-9]{6}$"
+        let predicate = NSPredicate(format: "SELF MATCHES %@", pinRegex)
+        
+        let result =  predicate.evaluate(with:self)
+        return result
+    }
+    
+    var localized: String {
+        return NSLocalizedString(self, comment: "")
+    }
+    
+    func base64Encoded() -> String? {
+        if let data = self.data(using: .utf8) {
+            return data.base64EncodedString()
+        }
+        return nil
+    }
+    
+    func base64Decoded() -> String? {
+        if let data = Data(base64Encoded: self) {
+            return String(data: data, encoding: .utf8)
+        }
+        return nil
+    }
+    
+    var numericOnly: Bool{
+        get{
+            return CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: self))
+        }
+    }
+    
+    func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
+        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
+        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil)
+        
+        return ceil(boundingBox.height)
+    }
+    
+    func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
+        let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
+        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil)
+        
+        return ceil(boundingBox.width)
+    }
+    
+    subscript(_ range: CountableRange<Int>) -> String {
+        let idx1 = index(startIndex, offsetBy: range.lowerBound)
+        let idx2 = index(startIndex, offsetBy: range.upperBound)
+        return String(self[idx1..<idx2])
+    }
+    
+    func getUrl() -> URL? {
+        if let escapedUrl = self.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: escapedUrl){
+            return url
+        }
+        return nil
+    }
+    
+    func utf8DecodedString()-> String {
+        let data = self.data(using: .utf8)
+        if let message = String(data: data!, encoding: .nonLossyASCII){
+            return message
+        }
+        return ""
+    }
+    
+    func utf8EncodedString()-> String {
+        let messageData = self.data(using: .nonLossyASCII)
+        let text = String(data: messageData!, encoding: .utf8)
+        return text!
+    }
+    
+    //
+    //  from oauhSwift
+    //
+    
+    func urlEncoded() -> String {
+        let customAllowedSet = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
+        return self.addingPercentEncoding(withAllowedCharacters: customAllowedSet)!
+    }
+    
+    var parametersFromQueryString: [String: String] {
+        return dictionaryBySplitting("&", keyValueSeparator: "=")
+    }
+    
+    var urlQueryEncoded: String? {
+        return self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
+    }
+    
+    /// Returns new url query string by appending query parameter encoding it first, if specified.
+    func urlQueryByAppending(parameter name: String, value: String, encode: Bool = true, _ encodeError: ((String, String) -> Void)? = nil) -> String? {
+        if value.isEmpty {
+            return self
+        } else if let value = encode ? value.urlQueryEncoded : value {
+            return "\(self)\(self.isEmpty ? "" : "&")\(name)=\(value)"
+        } else {
+            encodeError?(name, value)
+            return nil
+        }
+    }
+    
+    /// Returns new url string by appending query string at the end.
+    func urlByAppending(query: String) -> String {
+        return "\(self)\(self.contains("?") ? "&" : "?")\(query)"
+    }
+    
+    fileprivate func dictionaryBySplitting(_ elementSeparator: String, keyValueSeparator: String) -> [String: String] {
+        var string = self
+        
+        if hasPrefix(elementSeparator) {
+            string = String(dropFirst(1))
+        }
+        
+        var parameters = [String: String]()
+        
+        let scanner = Scanner(string: string)
+        
+        var key: NSString?
+        var value: NSString?
+        
+        while !scanner.isAtEnd {
+            key = nil
+            scanner.scanUpTo(keyValueSeparator, into: &key)
+            scanner.scanString(keyValueSeparator, into: nil)
+            
+            value = nil
+            scanner.scanUpTo(elementSeparator, into: &value)
+            scanner.scanString(elementSeparator, into: nil)
+            
+            if let key = key as String? {
+                if let value = value as String? {
+                    if key.contains(elementSeparator) {
+                        var keys = key.components(separatedBy: elementSeparator)
+                        if let key = keys.popLast() {
+                            parameters.updateValue(value, forKey: String(key))
+                        }
+                        for flag in keys {
+                            parameters.updateValue("", forKey: flag)
+                        }
+                    } else {
+                        parameters.updateValue(value, forKey: key)
+                    }
+                } else {
+                    parameters.updateValue("", forKey: key)
+                }
+            }
+        }
+        
+        return parameters
+    }
+
+    func getPhotoUrl() -> URL?{
+        
+        if self.count > 0{
+            var str = self
+            
+            if (!self.contains(Config.MEDIA_URL) && !self.contains(Config.MEDIA_URL_HTTPS)) {
+                str = Config.MEDIA_URL + str
+            }
+            return str.getUrl()
+        }
+        return nil
+    }
+    
+    func sizeOf(_ font: UIFont) -> CGSize {
+        return self.size(withAttributes: [NSAttributedStringKey.font: font])
+    }
+}
diff --git a/iOS/WolneLektury/Extensions/UIButton+Ext.swift b/iOS/WolneLektury/Extensions/UIButton+Ext.swift
new file mode 100644 (file)
index 0000000..d15c6eb
--- /dev/null
@@ -0,0 +1,23 @@
+//
+//  UIButton+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+
+import UIKit
+
+extension UIButton {
+    
+    var text: String{
+        get{
+            return title(for: .normal) ?? ""
+        }
+        set(newVal){
+            setTitle(newVal, for: .normal)
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Extensions/UIColor+Ext.swift b/iOS/WolneLektury/Extensions/UIColor+Ext.swift
new file mode 100644 (file)
index 0000000..367296a
--- /dev/null
@@ -0,0 +1,29 @@
+//
+//  UIColor+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/08/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+extension UIColor{
+    
+    convenience init(hex:String) {
+        
+        var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
+        var rgbValue:UInt32 = 0
+        
+        if cString.hasPrefix("#") {
+            cString.remove(at: cString.startIndex)
+        }
+        
+        if cString.count == 6 {
+            Scanner(string: cString).scanHexInt32(&rgbValue)
+        }
+        
+        self.init(red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, blue: CGFloat(rgbValue & 0x0000FF) / 255.0, alpha: 1.0 )
+    }
+}
diff --git a/iOS/WolneLektury/Extensions/UIImage+Ext.swift b/iOS/WolneLektury/Extensions/UIImage+Ext.swift
new file mode 100644 (file)
index 0000000..b339384
--- /dev/null
@@ -0,0 +1,27 @@
+//
+//  UIImage+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+extension UIImage{
+    
+    static func imageFromColor(colour: UIColor) -> UIImage? {
+        let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
+        UIGraphicsBeginImageContext(rect.size)
+        if let context = UIGraphicsGetCurrentContext() {
+            context.setFillColor(colour.cgColor)
+            context.fill(rect)
+            
+            let image = UIGraphicsGetImageFromCurrentImageContext()
+            UIGraphicsEndImageContext()
+            return image
+        }
+        return nil
+    }
+}
diff --git a/iOS/WolneLektury/Extensions/UITableView+Ext.swift b/iOS/WolneLektury/Extensions/UITableView+Ext.swift
new file mode 100644 (file)
index 0000000..d0362d0
--- /dev/null
@@ -0,0 +1,21 @@
+//
+//  UITableView+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+extension UITableView{
+    
+    func registerNib(name: String){
+        registerNib(name: name, forCellReuseIdentifier: name)
+    }
+    
+    func registerNib(name: String, forCellReuseIdentifier: String){
+        self.register(UINib(nibName: name, bundle: nil), forCellReuseIdentifier: forCellReuseIdentifier)
+    }
+}
diff --git a/iOS/WolneLektury/Extensions/UIView+Ext.swift b/iOS/WolneLektury/Extensions/UIView+Ext.swift
new file mode 100644 (file)
index 0000000..2cb0a02
--- /dev/null
@@ -0,0 +1,56 @@
+//
+//  UIView+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+
+import UIKit
+
+extension UIView{
+    func addConstraintsFllingContainer(toView: UIView){
+        
+        toView.translatesAutoresizingMaskIntoConstraints = false
+        let viewsDictionary:[String: AnyObject] = ["toView": toView]
+        
+        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[toView]|", options: [], metrics: nil, views: viewsDictionary))
+        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[toView]|", options: [], metrics: nil, views: viewsDictionary))
+    }
+    
+    func centerInSuperview() {
+        self.centerHorizontallyInSuperview()
+        self.centerVerticallyInSuperview()
+    }
+    
+    func addConstraingWidth(width: CGFloat){
+        let c: NSLayoutConstraint = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: width)
+        addConstraint(c)
+    }
+    
+    func centerHorizontallyInSuperview(){
+        let c: NSLayoutConstraint = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: self.superview, attribute: .centerX, multiplier: 1, constant: 0)
+        self.superview?.addConstraint(c)
+    }
+    
+    func centerVerticallyInSuperview(){
+        let c: NSLayoutConstraint = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: self.superview, attribute: .centerY, multiplier: 1, constant: 0)
+        self.superview?.addConstraint(c)
+    }
+    
+    func addDropShadow(height: CGFloat = 2, radius: CGFloat = 3) {
+        layer.masksToBounds = false
+        layer.shadowColor = UIColor.black.cgColor
+        layer.shadowOffset = CGSize(width: 0, height: height)
+        layer.shadowOpacity = 0.2
+        layer.shadowRadius = radius
+    }
+    
+    func rotate(angle: Double) {
+        let radians = angle / 180.0 * Double.pi
+        let rotation = CGAffineTransform.init(rotationAngle:CGFloat(radians))// CGAffineTransformRotate(self.transform, CGFloat(radians));
+        self.transform = rotation
+    }
+}
diff --git a/iOS/WolneLektury/Extensions/UIViewController+Ext.swift b/iOS/WolneLektury/Extensions/UIViewController+Ext.swift
new file mode 100644 (file)
index 0000000..487ef07
--- /dev/null
@@ -0,0 +1,131 @@
+//
+//  UIViewController+Ext.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import Foundation
+import UIKit
+import MBProgressHUD
+
+extension UIViewController
+{
+    var syncManager:SyncManager{
+        return appDelegate.syncManager
+    }
+}
+
+extension UINavigationController{
+    open override var preferredStatusBarStyle : UIStatusBarStyle {
+        return .lightContent
+    }
+}
+
+extension UIViewController{
+    
+
+    static func createViewControllerFromStoryboard(storyboardName:String = "Login") -> UIViewController {
+        
+        let viewController:UIViewController = UIStoryboard(name:storyboardName, bundle: nil).instantiateViewController(withIdentifier: String(describing: self)) as UIViewController
+        return viewController
+    }
+    
+    class func instance() -> Self
+    {
+        return instantiateFromStoryboardHelper()
+    }
+    
+    private class func instantiateFromStoryboardHelper<T>() -> T
+    {
+        let storyboard = UIStoryboard(name: "Main", bundle: nil)
+        let controller = storyboard.instantiateViewController(withIdentifier: "\(self)") as! T
+        return controller
+    }
+}
+
+extension UIViewController{
+    
+    func presentAlertViewController(title:String,message:String, okAction:UIAlertAction?)
+    {
+        let alertController = UIAlertController(title:title, message:message, preferredStyle: .alert)
+        
+        var cancelTitle = "button_cancel".localized
+        if okAction == nil{
+            cancelTitle = "button_ok".localized
+        }
+        
+        let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel, handler: nil)
+        alertController.addAction(cancelAction)
+        
+        if okAction != nil
+        {
+            alertController.addAction(okAction!)
+        }
+        self.present(alertController, animated: true, completion:nil)
+    }
+    
+    func presentProblemOccuredAlert(message: String){
+        presentAlertViewController(title: "problem_occurred".localized, message: message, okAction: nil)
+    }
+    
+    func presentToast(message: String) {
+        view.makeToast(message, duration: 3.0, position: .bottom)
+    }
+    
+    func share(string: String, button: UIButton) {
+        let items = [string]
+        let activity = UIActivityViewController(activityItems: items, applicationActivities: nil)
+        
+        // Pop style on iPad
+        if let actv = activity.popoverPresentationController {
+            actv.sourceView = button
+        }
+
+        self.present(activity, animated: true, completion: nil)
+    }
+    
+    func onBecomeFriendButtonTapped(){
+        guard Constants.donateEnabled else { return }
+
+        if syncManager.isLoggedIn() {
+            appDelegate.mainNavigator.presentPayPalForm()
+        }
+        else {
+            appDelegate.mainNavigator.presentLogin(push: true)
+        }
+    }
+}
+
+extension UIViewController{
+    func setIsPopup(){
+        providesPresentationContextTransitionStyle = true
+        definesPresentationContext = true
+        modalPresentationStyle = .overCurrentContext
+        modalTransitionStyle = .crossDissolve
+    }
+}
+
+extension UIViewController{
+    
+    func containerAdd(childViewController: UIViewController, toView: UIView){
+        
+        addChildViewController(childViewController)
+        childViewController.view.frame = toView.bounds
+        toView.addSubview(childViewController.view)
+        childViewController.view.translatesAutoresizingMaskIntoConstraints = false
+        childViewController.view.topAnchor.constraint(equalTo: toView.topAnchor).isActive = true
+        childViewController.view.leftAnchor.constraint(equalTo: toView.leftAnchor).isActive = true
+        childViewController.view.rightAnchor.constraint(equalTo: toView.rightAnchor).isActive = true
+        childViewController.view.bottomAnchor.constraint(equalTo: toView.bottomAnchor).isActive = true
+        childViewController.didMove(toParentViewController: self)
+    }
+    
+    func containerRemove(childViewController: UIViewController){
+        
+        childViewController.willMove(toParentViewController: nil)
+        childViewController.view.removeFromSuperview()
+        childViewController.removeFromParentViewController()
+    }
+}
diff --git a/iOS/WolneLektury/Info.plist b/iOS/WolneLektury/Info.plist
new file mode 100644 (file)
index 0000000..6e0c406
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>$(DEVELOPMENT_LANGUAGE)</string>
+       <key>CFBundleExecutable</key>
+       <string>$(EXECUTABLE_NAME)</string>
+       <key>CFBundleIdentifier</key>
+       <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>$(PRODUCT_NAME)</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleURLTypes</key>
+       <array>
+               <dict>
+                       <key>CFBundleTypeRole</key>
+                       <string>Editor</string>
+                       <key>CFBundleURLSchemes</key>
+                       <array>
+                               <string>wolnelekturyapp</string>
+                       </array>
+               </dict>
+       </array>
+       <key>CFBundleVersion</key>
+       <string>10</string>
+       <key>Fabric</key>
+       <dict>
+               <key>APIKey</key>
+               <string>67296ed66bf2e7658d0e3c71701e4fa0db8a85b0</string>
+               <key>Kits</key>
+               <array>
+                       <dict>
+                               <key>KitInfo</key>
+                               <dict/>
+                               <key>KitName</key>
+                               <string>Crashlytics</string>
+                       </dict>
+               </array>
+       </dict>
+       <key>LSRequiresIPhoneOS</key>
+       <true/>
+       <key>NSAppTransportSecurity</key>
+       <dict>
+               <key>NSAllowsArbitraryLoads</key>
+               <true/>
+       </dict>
+       <key>UIAppFonts</key>
+       <array>
+               <string>Roboto-Bold.ttf</string>
+               <string>Roboto-Light.ttf</string>
+               <string>Roboto-Medium.ttf</string>
+               <string>Roboto-Regular.ttf</string>
+               <string>Roboto-Thin.ttf</string>
+       </array>
+       <key>UIBackgroundModes</key>
+       <array>
+               <string>audio</string>
+       </array>
+       <key>UILaunchStoryboardName</key>
+       <string>LaunchScreen</string>
+       <key>UIMainStoryboardFile</key>
+       <string>Main</string>
+       <key>UIRequiredDeviceCapabilities</key>
+       <array>
+               <string>armv7</string>
+       </array>
+       <key>UISupportedInterfaceOrientations</key>
+       <array>
+               <string>UIInterfaceOrientationPortrait</string>
+               <string>UIInterfaceOrientationLandscapeLeft</string>
+               <string>UIInterfaceOrientationLandscapeRight</string>
+       </array>
+       <key>UISupportedInterfaceOrientations~ipad</key>
+       <array>
+               <string>UIInterfaceOrientationPortrait</string>
+               <string>UIInterfaceOrientationPortraitUpsideDown</string>
+               <string>UIInterfaceOrientationLandscapeLeft</string>
+               <string>UIInterfaceOrientationLandscapeRight</string>
+       </array>
+</dict>
+</plist>
diff --git a/iOS/WolneLektury/MainNavigator.swift b/iOS/WolneLektury/MainNavigator.swift
new file mode 100644 (file)
index 0000000..7fea2ba
--- /dev/null
@@ -0,0 +1,118 @@
+//
+//  MainNavigator.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 29/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import SideMenu
+import OAuthSwift
+import SafariServices
+
+class MainNavigator: NSObject {
+    
+    fileprivate var window: UIWindow
+    fileprivate var navigator: WLNavigationController!
+    private var menuViewController: MenuViewController!
+
+    init(window: UIWindow){
+        self.window = window
+        super.init()
+        
+        SideMenuManager.default.menuPresentMode = .menuSlideIn
+        SideMenuManager.default.menuFadeStatusBar = false
+        SideMenuManager.default.menuWidth = 245
+        
+        navigator = WLNavigationController(rootViewController: LibraryViewController.instance())
+        window.rootViewController = navigator
+        window.makeKeyAndVisible()
+        
+        let menuLeftNavigationController = UISideMenuNavigationController.instance()
+        SideMenuManager.default.menuLeftNavigationController = menuLeftNavigationController
+        menuViewController = menuLeftNavigationController.viewControllers[0] as! MenuViewController
+    }
+    
+    func updateSettingsViewIfPresented() {
+        if let settingsViewController = navigator.viewControllers.last as? SettingsViewController {
+            settingsViewController.updateNotificationsSwitchValue()
+        }
+    }
+    
+    func setLoggedIn(){
+        menuViewController.setup()
+    }
+    
+    func reset(){
+        menuViewController.setup()
+        presentLibrary(dismissSideMenu: false)
+    }
+
+    
+    func presentLibrary(dismissSideMenu: Bool){
+        setRootViewController(controller: LibraryViewController.instance(), dismissSideMenu: dismissSideMenu, menuItem: .wolne_lektury)
+    }
+    
+    func presentSearch() {
+        setRootViewController(controller: SearchViewController.instance(type: .search), menuItem: .catalog)
+    }
+    
+    func presentNews() {
+        setRootViewController(controller: NewsViewController.instance(dataSource: nil), menuItem: .news)
+    }
+    
+    func presentPremiumBook(bookSlug: String) {
+        //TODO:
+        setRootViewController(controller: BookDetailsViewController.instance(bookSlug: bookSlug), menuItem: .premium)
+    }
+    
+    func presentPayPalForm() {
+        appDelegate.presentPaypal(fromViewController: navigator)
+    }
+    
+    func presentSettings() {
+        setRootViewController(controller: SettingsViewController.instance(), menuItem: .settings)
+    }
+    
+    func presentAbout() {
+        setRootViewController(controller: AboutViewController.instance(), menuItem: .about)
+    }
+    
+    func presentSupportUs() {
+        setRootViewController(controller: SupportUsViewController.instance())
+    }
+
+    func presentBookList(listViewControllerType: ListViewControllerType){
+        let vc = BookListViewController.instance(listViewControllerType: listViewControllerType, dataSource: nil)
+        
+        setRootViewController(controller: vc, menuItem: listViewControllerType.menuItem)
+    }
+    
+    func presentDownloaded(){
+        let vc = DownloadedListViewController.instance()
+        setRootViewController(controller: vc, menuItem: .downloaded)
+    }
+    
+    func presentLogin(push: Bool){
+        let vc = LoginViewController.instance()
+        if push {
+            navigator.pushViewController(vc, animated: true)
+        }
+        else {
+            setRootViewController(controller: vc)
+        }
+    }
+    
+    
+    public func setRootViewController(controller: UIViewController, dismissSideMenu: Bool = true, menuItem: MenuItem? = nil){
+        if appDelegate.sideMenuNavigationController != nil && dismissSideMenu{
+            appDelegate.sideMenuNavigationController?.dismiss(animated: true, completion: nil)
+        }
+        
+        if let menuItem = menuItem {
+            menuViewController.selectItem(menuItem: menuItem)
+        }
+        navigator.setViewControllers([controller], animated: false)
+    }
+}
diff --git a/iOS/WolneLektury/Model/BookDetailsModel.swift b/iOS/WolneLektury/Model/BookDetailsModel.swift
new file mode 100644 (file)
index 0000000..08da5e7
--- /dev/null
@@ -0,0 +1,256 @@
+//
+//  BookDetailsModel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import RealmSwift
+
+protocol BookBase {
+    var simple_thumb: String { get set }
+    var title: String { get set }
+    
+    func getAuthor() -> String
+}
+
+extension BookBase{
+    
+    // returns image url
+    func getCoverThumbUrl() -> URL?{
+        
+        if simple_thumb.count > 0{
+            var str = simple_thumb
+            
+            if (!str.contains(Config.MEDIA_URL) && !str.contains(Config.MEDIA_URL_HTTPS)) {
+                str = Config.MEDIA_URL + str
+            }
+            
+            return str.getUrl()
+        }
+        return nil
+    }
+    
+    func getAttributedAuthorAndTitle(titleFont: UIFont, descFont: UIFont) -> NSAttributedString{
+        let titleAttributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: getAuthor() + "\n", font: titleFont))
+        titleAttributedText.append(NSAttributedString(string: title, font: descFont))
+        return titleAttributedText
+    }
+}
+
+class BookDetailsModel: Object, BookBase, Decodable, NSCopying {
+    @objc dynamic var title: String = ""
+    @objc dynamic var simple_thumb: String = ""
+    
+    var genres = List<CategoryModel>()
+    var kinds = List<CategoryModel>()
+    @objc dynamic var url: String = ""
+    var media = List<MediaModel>()
+    @objc dynamic var simple_cover: String = ""
+    var epochs = List<CategoryModel>()
+    var authors = List<CategoryModel>()
+    @objc dynamic var pdf: String = ""
+    @objc dynamic var epub: String = ""
+    @objc dynamic var fragmentTitle = ""
+    @objc dynamic var fragmentHtml = ""
+    
+    @objc dynamic var audio_length: String = ""
+    @objc dynamic var cover_color: String = ""
+
+    //additional
+    @objc dynamic var currentChapter = 0
+    @objc dynamic var totalChapters = 0
+    @objc dynamic var currentAudioChapter = 0
+    @objc dynamic var totalAudioChapters = 0
+    @objc dynamic var slug = ""
+    
+    private var privateBgColor: UIColor?
+    var bgColor: UIColor{
+        get {
+            if let privateBgColor = privateBgColor {
+                return privateBgColor
+            }
+            privateBgColor = UIColor(hex: cover_color)
+            return privateBgColor!
+        }
+        set {
+            privateBgColor = newValue
+        }
+    }
+    
+    override class func primaryKey() -> String? {
+        return "slug"
+    }
+
+    private enum BookDetailsModelCodingKeys: String, CodingKey {
+        case title
+        case simple_thumb
+        case genres
+        case kinds
+        case url
+        case media
+        case simple_cover
+        case epochs
+        case authors
+        case pdf
+        case epub
+        case fragment_data
+        case audio_length
+        case cover_color
+    }
+
+    convenience init(title: String, simple_thumb: String, url: String, simple_cover: String, pdf: String, epub: String, audio_length: String, cover_color: String, genres: [CategoryModel], kinds: [CategoryModel], media: [MediaModel], epochs: [CategoryModel], authors: [CategoryModel], fragmentTitle: String, fragmentHtml: String, slug: String) {
+        
+        self.init()
+        self.title = title
+        self.simple_thumb = simple_thumb
+        self.url = url
+        self.simple_cover = simple_cover
+        self.pdf = pdf
+        self.epub = epub
+        self.audio_length = audio_length
+        self.cover_color = cover_color
+        self.fragmentHtml = fragmentHtml
+        self.fragmentTitle = fragmentTitle
+        self.slug = slug
+        
+        self.genres = List<CategoryModel>()
+        self.kinds = List<CategoryModel>()
+        self.epochs = List<CategoryModel>()
+        self.authors = List<CategoryModel>()
+        self.media = List<MediaModel>()
+        
+        self.genres.append(objectsIn: genres)
+        self.kinds.append(objectsIn: kinds)
+        self.epochs.append(objectsIn: epochs)
+        self.authors.append(objectsIn: authors)
+        self.media.append(objectsIn: media)
+    }
+
+    convenience required init(from decoder: Decoder) throws {
+        let container = try decoder.container(keyedBy: BookDetailsModelCodingKeys.self)
+        
+        let title = try container.decode(String.self, forKey: .title)
+        let simple_thumb = try container.decode(String.self, forKey: .simple_thumb)
+        let url = try container.decode(String.self, forKey: .url)
+        let simple_cover = try container.decode(String.self, forKey: .simple_cover)
+        let pdf = try container.decode(String.self, forKey: .pdf)
+        let epub = try container.decode(String.self, forKey: .epub)
+        let audio_length = try container.decode(String.self, forKey: .audio_length)
+        let cover_color = try container.decode(String.self, forKey: .cover_color)
+
+        let genres = try container.decodeIfPresent([CategoryModel].self, forKey: .genres)
+        let kinds = try container.decodeIfPresent([CategoryModel].self, forKey: .kinds)
+        let epochs = try container.decodeIfPresent([CategoryModel].self, forKey: .epochs)
+        let authors = try container.decodeIfPresent([CategoryModel].self, forKey: .authors)
+        let media = try container.decodeIfPresent([MediaModel].self, forKey: .media)
+        
+        let fragment_data = try container.decodeIfPresent(FragmentModel.self, forKey: .fragment_data)
+        
+        let genresArray = genres ?? []
+        let kindsArray = kinds ?? []
+        let mediaArray = media ?? []
+        let epochsArray = epochs ?? []
+        let authorsArray = authors ?? []
+        
+        self.init(title: title, simple_thumb: simple_thumb, url: url, simple_cover: simple_cover, pdf: pdf, epub: epub, audio_length: audio_length, cover_color: cover_color, genres: genresArray, kinds: kindsArray, media: mediaArray, epochs: epochsArray, authors: authorsArray, fragmentTitle: fragment_data?.title ?? "", fragmentHtml: fragment_data?.html ?? "", slug: "")
+    }
+    
+    
+    func getAuthor() -> String {
+        return authors.map({$0.name}).joined(separator: ", ")
+    }
+    
+    func getGenres() -> String {
+        return genres.map({$0.name}).joined(separator: ", ")
+    }
+    
+    func getKinds() -> String {
+        return kinds.map({$0.name}).joined(separator: ", ")
+    }
+    
+    func getEpochs() -> String {
+        return epochs.map({$0.name}).joined(separator: ", ")
+    }
+    
+    func getAudiobookMediaModels() -> [MediaModel] {
+        var mediaModels = [MediaModel]()
+        
+        for mediaFile in media {
+            if mediaFile.type == "mp3" {
+                mediaModels.append(mediaFile)
+            }
+        }
+        return mediaModels;
+    }
+    
+    func getAudiobookFilesUrls() -> [String] {
+        
+        return getAudiobookMediaModels().map({$0.url})
+    }
+    
+    func checkIfAllAudiobookFilesAreDownloaded() -> Bool{
+        
+        guard slug.count > 0 else {return false}
+        let audiobookUrls = getAudiobookFilesUrls()
+        
+        for url in audiobookUrls{
+            if !NSObject.audiobookExists(audioBookUrlString: url, bookSlug: slug) {
+                return false
+            }
+        }
+        return true
+    }
+    
+    func copy(with zone: NSZone? = nil) -> Any {
+        
+        var genresArray = [CategoryModel]()
+        var kindsArray = [CategoryModel]()
+        var epochsArray = [CategoryModel]()
+        var authorsArray = [CategoryModel]()
+        var mediaArray = [MediaModel]()
+        
+        for object in genres{
+            genresArray.append(object.copy() as! CategoryModel)
+        }
+        
+        for object in kinds{
+            kindsArray.append(object.copy() as! CategoryModel)
+        }
+
+        for object in epochs{
+            epochsArray.append(object.copy() as! CategoryModel)
+        }
+        
+        for object in authors{
+            authorsArray.append(object.copy() as! CategoryModel)
+        }
+        
+        for object in media{
+            mediaArray.append(object.copy() as! MediaModel)
+        }
+        
+        let model = BookDetailsModel(title: title, simple_thumb: simple_thumb, url: url, simple_cover: simple_cover, pdf: pdf, epub: epub, audio_length: audio_length, cover_color: cover_color, genres: genresArray, kinds: kindsArray, media: mediaArray, epochs: epochsArray, authors: authorsArray, fragmentTitle: fragmentTitle, fragmentHtml: fragmentHtml, slug: slug)
+        if currentChapter > 0 {
+            model.currentChapter = currentChapter
+        }
+        if currentAudioChapter > 0 {
+            model.currentAudioChapter = currentAudioChapter
+        }
+        if totalAudioChapters > 0 {
+            model.totalAudioChapters = totalAudioChapters
+        }
+        if totalChapters > 0 {
+            model.totalAudioChapters = totalAudioChapters
+        }
+        return model
+    }
+    
+    func setInitialAudiobookChaptersValuesIfNeeded() {
+        if totalAudioChapters == 0 && currentAudioChapter == 0 {
+            DatabaseManager.shared.updateCurrentChapters(bookDetailsModel: self, currentChapter: nil, totalChapters: nil, currentAudioChapter: 0, totalAudioChapters: getAudiobookFilesUrls().count)
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Model/BookModel.swift b/iOS/WolneLektury/Model/BookModel.swift
new file mode 100644 (file)
index 0000000..3ec697d
--- /dev/null
@@ -0,0 +1,69 @@
+//
+//  BookModel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+struct BookModel: Codable{
+
+    var localId: Int?
+
+    var kind: String!
+    var author: String!
+    var url: String!
+    var title: String!
+    var cover: String!
+    var epoch: String!
+    var href: String!
+    var genre: String!
+    var slug: String!
+    var key: String!
+    var simple_thumb: String!
+    var fileName: String?
+    var epub: String?
+    var has_audio: Bool!
+    var cover_color: String!
+    var full_sort_key: String!
+    
+    var bgColor: UIColor {
+        return UIColor(hex: cover_color)
+    }
+
+    private enum CodingKeys: String, CodingKey {
+        case localId
+        case kind
+        case author
+        case url
+        case title
+        case cover
+        case epoch
+        case href
+        case genre
+        case slug
+        case key
+        case simple_thumb
+        case fileName
+        case epub
+        case has_audio
+        case cover_color
+        case full_sort_key
+    }
+
+    
+    // returns image url
+    func getCoverThumbUrl() -> URL?{
+        return simple_thumb.getPhotoUrl()
+    }
+    
+    func getAttributedAuthorAndTitle(titleFont: UIFont, descFont: UIFont) -> NSAttributedString{
+        let titleAttributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: author, font: titleFont))
+        titleAttributedText.append(NSAttributedString(string:  "\n\n", font: UIFont.systemFont(ofSize: 2)))
+
+        titleAttributedText.append(NSAttributedString(string:  title, font: descFont))
+        return titleAttributedText
+    }
+}
diff --git a/iOS/WolneLektury/Model/CategoryModel.swift b/iOS/WolneLektury/Model/CategoryModel.swift
new file mode 100644 (file)
index 0000000..0ae8246
--- /dev/null
@@ -0,0 +1,40 @@
+//
+//  CategoryModel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import RealmSwift
+
+class CategoryModel: Object, Decodable, NSCopying {
+
+    @objc dynamic var url: String = ""
+    @objc dynamic var href: String = ""
+    @objc dynamic var name: String = ""
+    @objc dynamic var slug: String = ""
+    
+    var checked: Bool = false
+    
+    private enum CodingKeys: String, CodingKey {
+        case url
+        case href
+        case name
+        case slug
+    }
+    
+    convenience init(url: String, href: String, name: String, slug: String) {
+        self.init()
+        
+        self.url = url
+        self.href = href
+        self.name = name
+        self.slug = slug
+    }
+    
+    func copy(with zone: NSZone? = nil) -> Any {
+        return CategoryModel(url: url, href: href, name: name, slug: slug)
+    }
+}
diff --git a/iOS/WolneLektury/Model/Credentials.swift b/iOS/WolneLektury/Model/Credentials.swift
new file mode 100644 (file)
index 0000000..2851ded
--- /dev/null
@@ -0,0 +1,43 @@
+//
+//  Credentials.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 14/07/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class Credentials: NSObject, NSCoding{
+    
+    let credentialOAuthTokenSecretKey = "credentialOAuthTokenSecretKey"
+    let credentialOAuthTokenKey = "credentialOAuthTokenKey"
+    
+    var oauthTokenModel: OAuthTokenModel?
+    
+    override init(){
+        
+    }
+
+    required init?(coder decoder: NSCoder) {
+        
+        if let token = decoder.decodeObject(forKey: credentialOAuthTokenKey) as? String, let secret = decoder.decodeObject(forKey: credentialOAuthTokenSecretKey) as? String  {
+            self.oauthTokenModel = OAuthTokenModel(token: token, tokenSecret: secret)
+        }
+    }
+    
+    func encode(with encoder: NSCoder) {
+        
+        if let tokenModel = oauthTokenModel{
+            encoder.encode(tokenModel.token, forKey: credentialOAuthTokenKey)
+            encoder.encode(tokenModel.tokenSecret, forKey: credentialOAuthTokenSecretKey)
+        }
+    }
+    
+    func isLoggedIn() -> Bool {
+        guard let tokenModel = oauthTokenModel, tokenModel.isValid() else {
+            return false
+        }
+        return true
+    }
+}
diff --git a/iOS/WolneLektury/Model/FragmentModel.swift b/iOS/WolneLektury/Model/FragmentModel.swift
new file mode 100644 (file)
index 0000000..357195a
--- /dev/null
@@ -0,0 +1,15 @@
+//
+//  FragmentModel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class FragmentModel: Decodable {
+
+    @objc var html: String = ""
+    @objc var title: String = ""
+}
diff --git a/iOS/WolneLektury/Model/LikeModel.swift b/iOS/WolneLektury/Model/LikeModel.swift
new file mode 100644 (file)
index 0000000..2589150
--- /dev/null
@@ -0,0 +1,13 @@
+//
+//  LikeModel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 05/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class LikeModel: Codable {
+    var likes: Bool!
+}
diff --git a/iOS/WolneLektury/Model/MediaModel.swift b/iOS/WolneLektury/Model/MediaModel.swift
new file mode 100644 (file)
index 0000000..06b1ac7
--- /dev/null
@@ -0,0 +1,37 @@
+//
+//  MediaModel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import RealmSwift
+
+class MediaModel: Object, Decodable, NSCopying {
+
+    @objc dynamic var url: String = ""
+    @objc dynamic var director: String = ""
+    @objc dynamic var type: String = ""
+    @objc dynamic var name: String = ""
+    @objc dynamic var artist: String = ""
+    
+    convenience init(url: String, director: String, type: String, name: String, artist: String) {
+        self.init()
+        
+        self.url = url
+        self.director = director
+        self.type = type
+        self.name = name
+        self.artist = artist
+    }
+
+    func copy(with zone: NSZone? = nil) -> Any {
+        return MediaModel(url: url, director: director, type: type, name: name, artist: artist)
+    }
+    
+    func titleForPlayer() -> String {
+        return "\(artist), \(name)"
+    }
+}
diff --git a/iOS/WolneLektury/Model/NewsModel.swift b/iOS/WolneLektury/Model/NewsModel.swift
new file mode 100644 (file)
index 0000000..92b1c94
--- /dev/null
@@ -0,0 +1,29 @@
+//
+//  NewsModel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 15/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class NewsModel: Codable {
+    var body: String!
+    var lead: String!
+    var title: String!
+    var url: String!
+    var image_url: String!
+    var key: String!
+    var time: String!
+    var place: String!
+    var image_thumb: String!
+    var gallery_urls: [String]!
+    var type: String!
+    
+    // returns image url
+    func getCoverThumbUrl() -> URL?{
+      
+        return image_thumb.getPhotoUrl()
+    }
+}
diff --git a/iOS/WolneLektury/Model/OAuthTokenModel.swift b/iOS/WolneLektury/Model/OAuthTokenModel.swift
new file mode 100644 (file)
index 0000000..a1f941c
--- /dev/null
@@ -0,0 +1,24 @@
+//
+//  OAuthTokenModel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 09/07/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class OAuthTokenModel {
+
+    init(token: String, tokenSecret: String) {
+        self.token = token
+        self.tokenSecret = tokenSecret
+    }
+    
+    var tokenSecret = ""
+    var token = ""
+    
+    func isValid() -> Bool{
+        return tokenSecret.count > 0 && token.count > 0
+    }
+}
diff --git a/iOS/WolneLektury/Model/Parameters/FilterBooksParameters.swift b/iOS/WolneLektury/Model/Parameters/FilterBooksParameters.swift
new file mode 100644 (file)
index 0000000..4dab9d6
--- /dev/null
@@ -0,0 +1,58 @@
+//
+//  FilterBooksParameters.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class FilterBooksParameters{
+    static let SEARCH_ITEMS_COUNT = 30
+    
+    static func ==(lhs: FilterBooksParameters, rhs: FilterBooksParameters) -> Bool {
+        return lhs.search == rhs.search && lhs.epochs == rhs.epochs && lhs.genres == rhs.genres && lhs.kinds == rhs.kinds && lhs.after == rhs.after
+    }
+
+    var onlyLectures: Bool?
+    var hasAudiobook: Bool?
+
+    var search: String?
+    var epochs: String?
+    var genres: String?
+    var kinds: String?
+    var after: String?
+
+    func parameters() -> [String: Any]{
+        var dict: [String: Any] =  ["count": FilterBooksParameters.SEARCH_ITEMS_COUNT]
+        
+        if let v = search{
+            dict["search"] = v
+        }
+
+        if let v = after{
+            dict["after"] = v
+        }
+        if let v = epochs{
+            dict["epochs"] = v
+        }
+        if let v = genres{
+            dict["genres"] = v
+        }
+        if let v = kinds{
+            dict["kinds"] = v
+        }
+        
+        if let value = onlyLectures, value == true {
+            dict["lektura"] = "true"
+        }
+
+        if let value = hasAudiobook, value == true {
+            dict["audiobook"] = "true"
+        }
+
+        dict["new_api"] = "true"
+        return dict
+    }
+}
diff --git a/iOS/WolneLektury/Model/ReadingStateModel.swift b/iOS/WolneLektury/Model/ReadingStateModel.swift
new file mode 100644 (file)
index 0000000..0ff9f05
--- /dev/null
@@ -0,0 +1,44 @@
+//
+//  ReadingStateModel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 29/08/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class ReadingStateModel: Decodable{
+    
+    public enum ReadingState : String {
+        case unknown        = "unknown"
+        case not_started    = "not_started"
+        case reading        = "reading"
+        case complete       = "complete"
+    }
+    
+    var state = ReadingState.unknown
+    
+    private enum ReadingStateModelCodingKeys: String, CodingKey {
+        case state
+    }
+
+    convenience required init(from decoder: Decoder) throws {
+        let container = try decoder.container(keyedBy: ReadingStateModelCodingKeys.self)
+        
+        let stateString = try container.decode(String.self, forKey: .state)
+        
+        var returnState: ReadingState = .unknown
+        
+        if let readingState  = ReadingState(rawValue: stateString) {
+            returnState = readingState
+        }
+        
+        self.init(state: returnState)
+    }
+    
+    convenience init(state: ReadingState) {
+        self.init()
+        self.state = state
+    }
+}
diff --git a/iOS/WolneLektury/Model/Realm/RLMApplication.swift b/iOS/WolneLektury/Model/Realm/RLMApplication.swift
new file mode 100644 (file)
index 0000000..e0f436f
--- /dev/null
@@ -0,0 +1,103 @@
+//
+//  RLMApplication.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 24/08/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import RealmSwift
+
+class RLMApplication: Object {
+    
+    @objc dynamic var user: RLMUser?
+    var downloadedBooks = List<BookDetailsModel>()
+    @objc dynamic var premium: Bool = false
+
+    
+    func updateUser(usernameModel: UsernameModel?){
+        let realm = try! Realm()
+        
+        if let usernameModel = usernameModel{
+            try! realm.write {
+                let usr = RLMUser()
+                usr.username = usernameModel.username
+                usr.premium = usernameModel.premium
+                self.user = usr
+            }
+        }
+        else {
+            if user != nil {
+                try! realm.write {
+                    realm.delete( user!)
+                    self.user = nil
+                }
+            }
+        }
+    }
+    
+    func addBookToDownloaded(bookDetailsModel: BookDetailsModel){
+        let realm = try! Realm()
+        realm.beginWrite()
+
+        var bookExists = false
+        if let b = getBookFromDownloaded(bookSlug: bookDetailsModel.slug) {
+            bookDetailsModel.currentChapter = b.currentChapter
+            bookDetailsModel.totalChapters = b.totalChapters
+            bookDetailsModel.currentAudioChapter = b.currentAudioChapter
+            bookDetailsModel.totalAudioChapters = b.totalAudioChapters
+            
+            bookDetailsModel.authors = b.authors
+            bookDetailsModel.epochs = b.epochs
+            bookDetailsModel.genres = b.genres
+            bookDetailsModel.kinds = b.kinds
+            bookDetailsModel.media = b.media
+
+            bookExists = true
+        }
+        
+        realm.add(bookDetailsModel, update: true)
+        if !bookExists{
+            self.downloadedBooks.append(bookDetailsModel)
+        }
+        try! realm.commitWrite()
+    }
+    
+    func removeAllBooksFromDownloaded() {
+        
+        guard downloadedBooks.count > 0 else { return }
+        
+        let realm = try! Realm()
+        realm.beginWrite()
+        realm.delete(downloadedBooks)
+        try! realm.commitWrite()
+    }
+    
+    func removeBookFromDownloaded(bookSlug: String) -> Bool {
+        
+        let books = downloadedBooks.filter({$0.slug == bookSlug})
+        
+        if books.count > 0 {
+            let realm = try! Realm()
+            realm.beginWrite()
+            realm.delete(books)
+            try! realm.commitWrite()
+            return true
+        }
+        return false
+    }
+    
+    func getBookFromDownloaded(bookSlug: String) -> BookDetailsModel? {
+        return downloadedBooks.first(where: {$0.slug == bookSlug})
+    }
+    
+    func setUserPermium() {
+        guard let user = user else { return }
+        
+        let realm = try! Realm()
+        realm.beginWrite()
+        user.premium = true
+        try! realm.commitWrite()
+    }
+}
diff --git a/iOS/WolneLektury/Model/Realm/RLMUser.swift b/iOS/WolneLektury/Model/Realm/RLMUser.swift
new file mode 100644 (file)
index 0000000..ecd950e
--- /dev/null
@@ -0,0 +1,16 @@
+//
+//  RLMUser.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 24/08/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import RealmSwift
+
+class RLMUser: Object {
+    @objc dynamic var username: String!
+    @objc dynamic var premium: Bool = false
+
+}
diff --git a/iOS/WolneLektury/Model/UsernameModel.swift b/iOS/WolneLektury/Model/UsernameModel.swift
new file mode 100644 (file)
index 0000000..b82ae86
--- /dev/null
@@ -0,0 +1,14 @@
+//
+//  UsernameModel.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 24/08/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class UsernameModel: Codable {
+    var username: String!
+    var premium: Bool!
+}
diff --git a/iOS/WolneLektury/Reader/WLReaderConfig.swift b/iOS/WolneLektury/Reader/WLReaderConfig.swift
new file mode 100644 (file)
index 0000000..4103bf8
--- /dev/null
@@ -0,0 +1,22 @@
+//
+//  WLReaderConfig.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 25/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import FolioReaderKit
+
+class WLReaderConfig: FolioReaderConfig {
+
+    override init() {
+        super.init()
+        self.tintColor = Constants.Colors.lightGreenBgColor()
+        self.displayTitle = true
+        self.allowSharing = false
+        self.enableTTS = false
+        self.useReaderMenuController = false
+    }
+}
diff --git a/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Bold.ttf b/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Bold.ttf
new file mode 100644 (file)
index 0000000..89e1c0f
Binary files /dev/null and b/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Bold.ttf differ
diff --git a/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Light.ttf b/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Light.ttf
new file mode 100644 (file)
index 0000000..5e3c2a3
Binary files /dev/null and b/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Light.ttf differ
diff --git a/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Medium.ttf b/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Medium.ttf
new file mode 100644 (file)
index 0000000..941af06
Binary files /dev/null and b/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Medium.ttf differ
diff --git a/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Regular.ttf b/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Regular.ttf
new file mode 100644 (file)
index 0000000..bf725b3
Binary files /dev/null and b/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Regular.ttf differ
diff --git a/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Thin.ttf b/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Thin.ttf
new file mode 100644 (file)
index 0000000..4a71fb3
Binary files /dev/null and b/iOS/WolneLektury/Resources/Font/Roboto/Roboto-Thin.ttf differ
diff --git a/iOS/WolneLektury/Screens/About/AboutViewController.swift b/iOS/WolneLektury/Screens/About/AboutViewController.swift
new file mode 100644 (file)
index 0000000..2b4106d
--- /dev/null
@@ -0,0 +1,69 @@
+//
+//  AboutViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 18/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import MessageUI
+
+class AboutViewController: MainViewController, MFMailComposeViewControllerDelegate {
+    
+    @IBOutlet weak var firstTextView: UITextView!
+    @IBOutlet weak var secondLabel: UILabel!
+    @IBOutlet weak var thirdLabel: UILabel!
+    @IBOutlet weak var contactButton: UIButton!
+    @IBOutlet weak var becomeFriendButton: UIButton!
+    @IBOutlet weak var logoTopConstraint: NSLayoutConstraint!
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        title = "nav_about".localized
+
+        firstTextView.textContainerInset = UIEdgeInsets.zero
+        firstTextView.textContainer.lineFragmentPadding = 0
+        becomeFriendButton.layer.cornerRadius = 18
+        
+        if DatabaseManager.shared.isUserPremium() || Constants.donateEnabled == false{
+            becomeFriendButton.isHidden = true
+            logoTopConstraint.constant = 30
+        }
+        else {
+            becomeFriendButton.isHidden = false
+            logoTopConstraint.constant = 70
+        }
+        
+        
+        if let htmlString = try? NSAttributedString(html: "about_text".localized){
+            firstTextView.attributedText =  htmlString
+        }
+        else {
+            firstTextView.text = "about_text".localized
+        }
+        firstTextView.tintColor = Constants.Colors.darkGreenBgColor()
+
+        secondLabel.text = "about_text_fundation".localized
+        thirdLabel.text = "about_text_mkdn".localized
+        contactButton.text = "about_text_contact".localized
+    }
+    
+    @IBAction func becomeFriendButtonAction(_ sender: Any) {
+        onBecomeFriendButtonTapped()
+    }
+    
+    @IBAction func contactButtonAction(_ sender: Any) {
+
+        let email = contactButton.text
+        if let url = URL(string: "mailto:\(email)") {
+            if #available(iOS 10.0, *) {
+                UIApplication.shared.open(url)
+            } else {
+                UIApplication.shared.openURL(url)
+            }
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Authorization/AuthorizationWebViewController.swift b/iOS/WolneLektury/Screens/Authorization/AuthorizationWebViewController.swift
new file mode 100644 (file)
index 0000000..bab8af6
--- /dev/null
@@ -0,0 +1,60 @@
+//
+//  AuthorizationWebViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 14/07/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import OAuthSwift
+typealias WebView = UIWebView // WKWebView
+
+class AuthorizationWebViewController: OAuthWebViewController {
+    
+    var targetURL: URL?
+    let webView: WebView = WebView()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        self.webView.frame = UIScreen.main.bounds
+        self.webView.scalesPageToFit = true
+        self.webView.delegate = self
+        self.view.addSubview(self.webView)
+        loadAddressURL()
+    }
+    
+    override func handle(_ url: URL) {
+        targetURL = url
+        super.handle(url)
+        self.loadAddressURL()
+    }
+    
+    func loadAddressURL() {
+        guard let url = targetURL else {
+            return
+        }
+        let req = URLRequest(url: url)
+        self.webView.loadRequest(req)
+    }
+    
+    open override func doHandle(_ url: URL) {
+        let completion: () -> Void = { [unowned self] in
+            self.delegate?.oauthWebViewControllerDidPresent()
+        }
+        appDelegate.mainNavigator.setRootViewController(controller: self)        
+    }
+}
+
+// MARK: delegate
+extension AuthorizationWebViewController: UIWebViewDelegate {
+    func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
+        if let url = request.url, url.scheme == "oauth-swift" {
+            // Call here AppDelegate.sharedInstance.applicationHandleOpenURL(url) if necessary ie. if AppDelegate not configured to handle URL scheme
+            // compare the url with your own custom provided one in `authorizeWithCallbackURL`
+            self.dismissWebViewController()
+        }
+        return true
+    }
+}
diff --git a/iOS/WolneLektury/Screens/BookDetails/BookDetailsViewController.swift b/iOS/WolneLektury/Screens/BookDetails/BookDetailsViewController.swift
new file mode 100644 (file)
index 0000000..3c8685f
--- /dev/null
@@ -0,0 +1,539 @@
+//
+//  BookDetailsViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 19/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import MZDownloadManager
+import FolioReaderKit
+import MatomoTracker
+
+class BookDetailsViewController: WLViewController {
+    
+    var bookDetailsModel: BookDetailsModel?
+    private var bookSlug: String!
+    private var isFavourite = false
+    private var isBookPremium: Bool = false
+    @IBOutlet weak var headerView: UIView!
+    @IBOutlet weak var refreshButton: ActivityIndicatorButton!
+    @IBOutlet weak var backButton: UIButton!
+    @IBOutlet weak var tableView: UITableView!
+    
+    @IBOutlet weak var favouriteButton: UIButton!
+    @IBOutlet weak var shareButton: UIButton!
+    @IBOutlet weak var buttonsContainer: UIView!
+    @IBOutlet weak var headerHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var buttonsContainerWidthConstraint: NSLayoutConstraint!
+
+    @IBOutlet weak var becomeFriendView: UIView!
+    @IBOutlet weak var becomeFriendLabel: UILabel!
+    @IBOutlet weak var becomeFriendButton: UIButton!
+    @IBOutlet weak var becomeFriendStarImageView: UIImageView!
+    @IBOutlet weak var headerLabel: UILabel!
+    
+    var topColor = UIColor(red:0.91, green:0.31, blue:0.20, alpha:1.00)
+    var headerProgress: CGFloat = 100
+
+    var cellsArray = [WLTableViewCell]()
+    var readCell: BookDetailsButtonTableViewCell?
+    var audiobookCell: BookDetailsButtonTableViewCell?
+    var readingState: ReadingStateModel.ReadingState = .unknown
+    
+    @IBAction func refreshButtonAction(_ sender: Any) {
+        refreshData()
+    }
+    
+    @IBAction func becomeFriendButtonAction(_ sender: Any) {
+        onBecomeFriendButtonTapped()
+    }
+    
+    @IBAction func backButtonAction(_ sender: Any) {
+        navigationController?.popViewController(animated: true)
+    }
+    
+    @IBAction func favouriteButtonAction(_ sender: Any) {
+        setFavourite(favourite: !isFavourite)
+    }
+
+    @IBAction func sharebuttonAction(_ sender: UIButton) {
+        
+        if let url = bookDetailsModel?.url {
+            self.share(string: url, button: sender)
+        }
+    }
+    
+    static func instance(bookSlug: String, isBookPremium: Bool = false) -> BookDetailsViewController{
+        let controller = BookDetailsViewController.instance()
+        controller.bookSlug = bookSlug
+        controller.isBookPremium = isBookPremium
+        return controller
+    }
+
+    var bigHeaderHeight: CGFloat = 200
+    var smallHeaderHeight: CGFloat = 200
+    
+    open override var preferredStatusBarStyle : UIStatusBarStyle {
+        return .lightContent
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        headerLabel.text = ""
+        tableView.isHidden = true
+        buttonsContainer.isHidden = true
+        becomeFriendStarImageView.tintColor = UIColor.white
+        becomeFriendButton.layer.cornerRadius = 18
+        refreshButton.tintColor = UIColor.white
+
+        bigHeaderHeight = 190 + UIApplication.shared.statusBarFrame.size.height
+        smallHeaderHeight = 44 + UIApplication.shared.statusBarFrame.size.height
+        headerHeightConstraint.constant = bigHeaderHeight
+        
+        headerView.backgroundColor = Constants.Colors.navbarBgColor()
+        tableView.rowHeight = UITableViewAutomaticDimension
+        tableView.estimatedRowHeight = 200
+        tableView.separatorStyle = .none
+        tableView.registerNib(name: "BookDetailsHeaderTableViewCell")
+        tableView.registerNib(name: "BookDetailsInfoTableViewCell")
+        refreshData()
+        
+        if syncManager.isLoggedIn() == false{
+            favouriteButton.isHidden = true
+            buttonsContainerWidthConstraint.constant = 42
+        }
+        
+        let tintColor = UIColor(red:0.91, green:0.31, blue:0.20, alpha:1.00)
+        favouriteButton.tintColor = tintColor
+        shareButton.tintColor = tintColor
+        favouriteButton.layer.cornerRadius = 21
+        shareButton.layer.cornerRadius = 21
+        let buttonBorderColor = UIColor(red:0.96, green:0.96, blue:0.96, alpha:1.00)
+        favouriteButton.layer.borderColor = buttonBorderColor.cgColor
+        shareButton.layer.borderColor = buttonBorderColor.cgColor
+        favouriteButton.layer.borderWidth = 1.0
+        shareButton.layer.borderWidth = 1.0
+
+        edgesForExtendedLayout = []
+        extendedLayoutIncludesOpaqueBars = false
+        if #available(iOS 11.0, *) {
+            tableView.contentInsetAdjustmentBehavior = .never
+        } else {
+            automaticallyAdjustsScrollViewInsets = false
+        }
+        
+        if isBookPremium && DatabaseManager.shared.isUserPremium() == false {
+            becomeFriendView.isHidden = false
+            becomeFriendLabel.text = "become_friend_desc".localized
+            becomeFriendButton.text = "become_friend_button".localized.uppercased()
+        }
+        else {
+            becomeFriendView.isHidden = true
+        }
+        
+        refreshIsFavourite()
+        refreshReadingState()
+    }
+    
+    func createCells(bookDetails: BookDetailsModel) {
+        cellsArray = [WLTableViewCell]()
+        self.bookDetailsModel = bookDetails
+        let titleCell = BookDetailsHeaderTableViewCell.instance(height: bigHeaderHeight)
+        titleCell.setup(bookModel: bookDetails, topColor: topColor)
+        cellsArray.append(titleCell)
+        let infoCell = BookDetailsInfoTableViewCell.instance()
+        infoCell.setup(bookModel: bookDetails)
+        cellsArray.append(infoCell)
+        cellsArray.append(BookDetailsSeparatorTableViewCell.instance())
+        
+        if bookDetails.fragmentHtml.count > 0{
+            let fragmentCell = BookDetailsFragmentTableViewCell.instance()
+            fragmentCell.setup(fragmentTitle: bookDetails.fragmentTitle, fragmentHtml: bookDetails.fragmentHtml)
+            cellsArray.append( fragmentCell)
+            cellsArray.append(BookDetailsSeparatorTableViewCell.instance())
+
+        }
+        
+        if isBookPremium && DatabaseManager.shared.isUserPremium() == false{ // dont show buttons when user is not premium
+            
+        }
+        else{
+            
+            if bookDetails.epub.count > 0{
+                var buttonType = BookDetailsButtonType.download_ebook
+                switch DownloadManager.sharedInstance.checkEbookStatus(bookSlug: bookDetails.slug){
+                case .downloaded:
+                    buttonType = .download_ebook_read
+                case .downloading:
+                    DownloadManager.sharedInstance.delegate = self
+                    buttonType = .download_ebook_loading
+                default:
+                    break
+                }
+                readCell = BookDetailsButtonTableViewCell.instance(delegate: self, bookDetailsButtonType: buttonType, bookDetailsModel: bookDetails)
+                cellsArray.append(readCell!)
+            }
+            
+            if bookDetails.getAudiobookFilesUrls().count > 0 {
+                var buttonType = BookDetailsButtonType.download_audiobook
+                
+                switch DownloadManager.sharedInstance.checkAudiobookStatus(bookDetailsModel: bookDetails){
+                case .downloaded:
+                    buttonType = .download_audiobook_listen
+                case .downloading:
+                    DownloadManager.sharedInstance.delegate = self
+                    buttonType = .download_audiobook_loading
+                default:
+                    break
+                }
+                audiobookCell = BookDetailsButtonTableViewCell.instance(delegate: self, bookDetailsButtonType: buttonType, bookDetailsModel: bookDetails)
+                cellsArray.append(audiobookCell!)
+            }
+            
+//            cellsArray.append(BookDetailsButtonTableViewCell.instance(delegate: self, bookDetailsButtonType: .support_us, bookDetailsModel: bookDetails))
+        }
+    }
+    
+    func bookDetailsDownloaded(bookDetails: BookDetailsModel){
+        self.bookDetailsModel = bookDetails
+        tableView.isHidden = false
+        buttonsContainer.isHidden = false
+        headerView.alpha = 0
+        topColor = self.bookDetailsModel!.bgColor
+        headerLabel.text = bookDetails.title
+        createCells(bookDetails: self.bookDetailsModel!)
+        tableView.reloadData()
+    }
+    
+    func setFavourite(favourite: Bool){
+        isFavourite = favourite
+        refreshFavouriteButton()
+        
+        syncManager.setFavouriteState(slug: bookSlug, favourite: favourite) {[weak self] (result) in
+            
+            guard let strongSelf = self else{
+                return
+            }
+            
+            strongSelf.refreshButton.setIndicatorButtonState(state: .hidden)
+            switch result {
+            case .success/*(let model)*/:
+                strongSelf.isFavourite = favourite
+            case .failure/*(let error)*/:
+                
+                break
+            }
+        }
+    }
+    
+    func refreshFavouriteButton(){
+        favouriteButton.setImage(isFavourite ? #imageLiteral(resourceName: "icon_heart-fill-big") : #imageLiteral(resourceName: "icon_heart-outline-big"), for: .normal)
+    }
+    
+    func refreshIsFavourite(){
+        guard syncManager.isLoggedIn() else { return }
+        
+        syncManager.getFavouriteState(slug: bookSlug) {[weak self] (result) in
+            
+            guard let strongSelf = self else{
+                return
+            }
+            
+            switch result {
+            case .success(let model):
+                strongSelf.isFavourite = (model as! LikeModel).likes
+                strongSelf.refreshFavouriteButton()
+            case .failure/*(let error)*/:
+                break
+            }
+        }
+
+    }
+    
+    func refreshReadingState(){
+        
+        guard syncManager.isLoggedIn() else {
+            readingState = .unknown
+            return
+        }
+        
+        syncManager.getReadingState(slug: bookSlug, completionHandler: { [weak self] (result) in
+        
+            guard let strongSelf = self else{
+                return
+            }
+            
+            switch result {
+            case .success(let model):
+                strongSelf.readingState = (model as! ReadingStateModel).state
+            case .failure/*(let error)*/:
+                break
+            }
+        })
+    }
+
+    
+    func refreshData(){
+        var storedBook = false
+        if let downloadedModel = DatabaseManager.shared.getBookFromDownloaded(bookSlug: bookSlug) {
+            storedBook = true
+            bookDetailsDownloaded(bookDetails: downloadedModel)
+        }
+
+        if storedBook == false {
+            refreshButton.setIndicatorButtonState(state: .loading)
+        }
+        
+        syncManager.getBookDetails(bookSlug: bookSlug) {[weak self] (result) in
+            
+            guard let strongSelf = self else{
+                return
+            }
+            
+            strongSelf.refreshButton.setIndicatorButtonState(state: .hidden)
+            switch result {
+            case .success(let model):
+                if let model = model as? BookDetailsModel{
+                    model.slug = strongSelf.bookSlug
+                    if storedBook {
+                        DatabaseManager.shared.addBookToDownloaded(bookDetailsModel: model)
+                    }
+                    strongSelf.bookDetailsDownloaded(bookDetails: model)
+                }
+                
+            case .failure/*(let error)*/:
+                if storedBook == false {
+                    strongSelf.refreshButton.setIndicatorButtonState(state: .button)
+                    self?.view.makeToast("book_loading_error".localized, duration: 3.0, position: .bottom)
+                }
+            }
+        }
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        openedPlayer = false
+        navigationController?.setNavigationBarHidden(true, animated: true)
+        DownloadManager.sharedInstance.delegate = self
+    }
+    
+    override func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+        if !openedPlayer {
+            navigationController?.setNavigationBarHidden(false, animated: true)
+        }
+        DownloadManager.sharedInstance.delegate = nil
+    }
+    
+    func downloadEbook(){
+        guard let bookDetailsModel = bookDetailsModel, bookDetailsModel.epub.count > 0 else {return}
+        
+        DatabaseManager.shared.addBookToDownloaded(bookDetailsModel: bookDetailsModel)
+        DownloadManager.sharedInstance.delegate = self
+        DownloadManager.sharedInstance.downloadEbook(bookDetailsModel: bookDetailsModel)
+    }
+    
+    var openedPlayer = false
+    func downloadAudiobooks(){
+        guard let bookDetailsModel = bookDetailsModel, bookDetailsModel.getAudiobookFilesUrls().count > 0 else {return}
+        
+        DatabaseManager.shared.addBookToDownloaded(bookDetailsModel: bookDetailsModel)
+
+        DownloadManager.sharedInstance.delegate = self
+        DownloadManager.sharedInstance.downloadAudiobooks(bookDetailsModel: bookDetailsModel)
+    }
+
+    func updateReadingStateIfNeeded(state: ReadingStateModel.ReadingState) {
+        if state == .reading && readingState != .not_started {
+            return
+        }
+        
+        if state == .complete && readingState == .complete {
+            return
+        }
+
+        syncManager.setReadingState(slug: bookSlug, readingState: state, completionHandler: nil)
+    }
+    
+    func openFolioReader() {
+        guard ebookExists(bookSlug: bookSlug) else {
+            return
+        }
+
+        updateReadingStateIfNeeded(state: .reading)
+        
+        var array = parentNames()
+        array.append("Reader")
+        MatomoTracker.shared.track(view: array)
+
+        let config = WLReaderConfig()
+        let bookPath = FileType.ebook.pathForFileName(filename: bookSlug, bookSlug: bookSlug)
+        let bookDirectory = FileType.ebook.destinationPathWithSlug(bookSlug: bookSlug) + "unzipped/"
+
+        if !FileManager.default.fileExists(atPath: bookDirectory) {
+            try! FileManager.default.createDirectory(atPath: bookDirectory, withIntermediateDirectories: true, attributes: nil)
+        }
+
+        let folioReader = FolioReader()
+        folioReader.delegate = self
+        
+        folioReader.presentReader(parentViewController: self.navigationController!, withEpubPath: bookPath, unzipPath: bookDirectory, andConfig: config, shouldRemoveEpub: false)
+    }
+    
+    func openAudiobook(afterDownload: Bool) {
+        guard let bookDetailsModel = bookDetailsModel else { return }
+        
+        updateReadingStateIfNeeded(state: .reading)
+
+        openedPlayer = true
+        navigationController?.pushViewController(PlayerViewController.instance(bookDetailsModel: bookDetailsModel), animated: true)
+    }
+    
+    deinit {
+    }
+}
+
+extension BookDetailsViewController: UITableViewDataSource{
+    
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return cellsArray.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        return cellsArray[indexPath.row]
+    }
+}
+
+extension BookDetailsViewController: UITableViewDelegate{
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return cellsArray[indexPath.row].getHeight()
+    }
+    
+    func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        print(scrollView.contentOffset)
+        
+        var progress: CGFloat = 100
+
+        var newValue = bigHeaderHeight - scrollView.contentOffset.y
+        if newValue < smallHeaderHeight{
+            newValue = smallHeaderHeight
+            progress = 0
+        }
+        else{
+            progress = (bigHeaderHeight - smallHeaderHeight - scrollView.contentOffset.y) / (bigHeaderHeight - smallHeaderHeight)
+            if progress > 100{
+                progress = 100
+            }
+        }
+        
+        headerView.alpha = 1 - progress
+        buttonsContainer.alpha = progress
+        headerHeightConstraint.constant = newValue
+        headerProgress = progress
+    }
+    
+    func showHeader(){
+        headerView.alpha = 1
+        buttonsContainer.alpha = 0
+        headerHeightConstraint.constant = smallHeaderHeight
+        headerProgress = 0
+    }
+}
+
+extension BookDetailsViewController: BookDetailsButtonTableViewCellDelegate{
+    
+    func bookDetailsButtonTableViewCellButtonTapped(buttonType: BookDetailsButtonType){
+        switch buttonType {
+        case .download_ebook:
+            downloadEbook()
+        case .download_ebook_read:
+            openFolioReader()
+        case .download_audiobook:
+            downloadAudiobooks()
+        case .download_audiobook_listen:
+            openAudiobook(afterDownload: false)
+        default:
+            break
+        }
+    }
+    
+    func bookDetailsButtonTableViewCellDeleteButtonTapped(buttonType: BookDetailsButtonType){
+        switch buttonType {
+        case .download_ebook_read, .download_ebook_loading:
+            DownloadManager.sharedInstance.deleteEbook(bookSlug: bookSlug)
+            readCell?.setup(bookDetailsButtonType: .download_ebook, progress: nil, bookDetailsModel: bookDetailsModel)
+            bookDetailsModel = (bookDetailsModel?.copy() as! BookDetailsModel)
+            let _ = DatabaseManager.shared.removeBookFromDownloaded(bookSlug: bookSlug)
+        case .download_audiobook_listen, .download_audiobook_loading:
+            if let bookDetails = PlayerController.shared.currentBookDetails, bookDetails.slug == bookSlug{
+                PlayerController.shared.stopAndClear()
+            }
+            DownloadManager.sharedInstance.clearDownloadingAudiobookFromQueue(bookSlug: bookSlug)
+            DownloadManager.sharedInstance.deleteAudiobook(bookSlug: bookSlug)
+            audiobookCell?.setup(bookDetailsButtonType: .download_audiobook, progress:nil, bookDetailsModel: bookDetailsModel)
+            bookDetailsModel = (bookDetailsModel?.copy() as! BookDetailsModel)
+            let _ = DatabaseManager.shared.removeBookFromDownloaded(bookSlug: bookSlug)
+        default:
+            break
+        }
+    }
+}
+
+extension BookDetailsViewController: DownloadManagerDelegate{
+    
+    func downloadManagerDownloadProgressChanged(model: MZDownloadModel, allProgress: Float, bookSlug: String) {
+        guard let bookDetailsModel = bookDetailsModel, bookDetailsModel.slug == bookSlug else { return        }
+
+        if model.isAudiobook() {
+            audiobookCell?.setup(bookDetailsButtonType: .download_audiobook_loading, progress: allProgress,  bookDetailsModel: bookDetailsModel)
+        }
+        else if model.isEbook() {
+            readCell?.setup(bookDetailsButtonType: .download_ebook_loading, progress: model.progress,  bookDetailsModel: bookDetailsModel)
+        }
+    }
+    
+    func downloadManagerDownloadFinished(model: MZDownloadModel, bookSlug: String) {
+        guard let bookDetailsModel = bookDetailsModel, bookDetailsModel.slug == bookSlug else { return        }
+        
+        if model.isAudiobook() {
+            
+            bookDetailsModel.setInitialAudiobookChaptersValuesIfNeeded()
+            
+            audiobookCell?.setup(bookDetailsButtonType: .download_audiobook_listen, progress: nil,  bookDetailsModel: bookDetailsModel)
+            openAudiobook(afterDownload: true)
+        }
+        else if model.isEbook() {
+            readCell?.setup(bookDetailsButtonType: .download_ebook_read, progress: nil,  bookDetailsModel: bookDetailsModel)
+            openFolioReader()
+        }
+    }
+    
+    func downloadManagerDownloadFailed(model: MZDownloadModel, bookSlug: String) {
+        guard let bookDetailsModel = bookDetailsModel, bookDetailsModel.slug == bookSlug else { return }
+
+        if model.isAudiobook() {
+            view.makeToast("audiobook_download_error".localized, duration: 3.0, position: .bottom)
+            audiobookCell?.setup(bookDetailsButtonType: .download_audiobook, progress: nil, bookDetailsModel: bookDetailsModel)
+        }
+        else if model.isEbook() {
+            view.makeToast("book_download_error".localized, duration: 3.0, position: .bottom)
+            readCell?.setup(bookDetailsButtonType: .download_ebook, progress: nil, bookDetailsModel: bookDetailsModel)
+        }
+    }
+}
+
+extension BookDetailsViewController: FolioReaderDelegate{
+    @objc func folioReaderDidClose(_ folioReader: FolioReader) {
+        guard let bookDetailsModel = bookDetailsModel, let progressValues = folioReader.getProgressValues(), progressValues.currentPage > 0, progressValues.currentPage <= progressValues.totalPages else { return }
+        
+        DatabaseManager.shared.updateCurrentChapters(bookDetailsModel: bookDetailsModel, currentChapter: progressValues.currentPage, totalChapters: progressValues.totalPages, currentAudioChapter: nil, totalAudioChapters: nil)
+        readCell?.setup(bookDetailsButtonType: .download_ebook_read, progress: nil, bookDetailsModel: bookDetailsModel)
+        
+        if progressValues.currentPage == progressValues.totalPages && readingState == ReadingStateModel.ReadingState.reading{
+           updateReadingStateIfNeeded(state: .complete)
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsButtonTableViewCell.swift b/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsButtonTableViewCell.swift
new file mode 100644 (file)
index 0000000..a715bc1
--- /dev/null
@@ -0,0 +1,145 @@
+//
+//  BookDetailsButtonTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 20/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+
+
+enum BookDetailsButtonType{
+    case download_ebook
+    case download_ebook_loading
+    case download_ebook_read
+    case download_audiobook
+    case download_audiobook_listen
+    case download_audiobook_loading
+    case support_us
+    
+    var color: UIColor{
+        switch self{
+        case .download_ebook, .download_ebook_loading, .download_ebook_read:
+            return UIColor(red:0.91, green:0.31, blue:0.20, alpha:1.00)
+        case .download_audiobook, .download_audiobook_listen, .download_audiobook_loading:
+            return UIColor(red:0.00, green:0.53, blue:0.56, alpha:1.00)
+        case .support_us:
+            return UIColor(red:1.00, green:0.64, blue:0.26, alpha:1.00)
+        }
+    }
+    
+    func getTitle() -> String{
+        return "\(self)".localized.uppercased()
+    }
+    
+    var icon: UIImage{
+        switch self {
+        case .download_ebook, .download_ebook_loading, .download_ebook_read:
+            return #imageLiteral(resourceName: "icon_glasses-mid")
+        case .download_audiobook, .download_audiobook_listen, .download_audiobook_loading:
+            return #imageLiteral(resourceName: "icon_speaker-mid")
+        case .support_us:
+            return #imageLiteral(resourceName: "icon_star-mid")
+        }
+    }
+    
+    var tintColor: UIColor{
+        
+        switch self {
+        case .download_ebook_read, .download_audiobook_listen, .support_us:
+            return UIColor.white
+        default:
+            return color
+        }
+    }
+    
+    var deleteButtonHidden: Bool{
+        switch self {
+        case .download_ebook, .download_audiobook, .support_us:
+            return true
+        default:
+            return false
+        }
+    }
+}
+
+protocol BookDetailsButtonTableViewCellDelegate: class{
+    func bookDetailsButtonTableViewCellButtonTapped(buttonType: BookDetailsButtonType)
+    func bookDetailsButtonTableViewCellDeleteButtonTapped(buttonType: BookDetailsButtonType)
+}
+
+class BookDetailsButtonTableViewCell: WLTableViewCell {
+    
+    weak var delegate: BookDetailsButtonTableViewCellDelegate?
+//    @IBOutlet weak var button: UIButton!
+    @IBOutlet weak var button: ProgressLabel!
+    @IBOutlet weak var buttonIcon: UIImageView!
+    @IBOutlet weak var deleteButton: UIButton!
+    var buttonType: BookDetailsButtonType!
+
+    class func instance(delegate: BookDetailsButtonTableViewCellDelegate, bookDetailsButtonType: BookDetailsButtonType, bookDetailsModel: BookDetailsModel) -> BookDetailsButtonTableViewCell{
+        let cell = BookDetailsButtonTableViewCell.instance(type: BookDetailsButtonTableViewCell.self)
+        cell.delegate = delegate
+        cell.setup(bookDetailsButtonType: bookDetailsButtonType, progress: nil, bookDetailsModel: bookDetailsModel)
+        return cell
+    }
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        deleteButton.isHidden = true
+//        button.layer.cornerRadius = 10
+//        button.layer.borderWidth = 1
+        button.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(buttonAction)))
+    }
+
+    func setup(bookDetailsButtonType: BookDetailsButtonType, progress:Float?, bookDetailsModel: BookDetailsModel?){
+        self.buttonType = bookDetailsButtonType
+        let tintColor = bookDetailsButtonType.tintColor
+        button.color = bookDetailsButtonType.color
+//        button.backgroundColor = bookDetailsButtonType.bgColor
+//        button.layer.borderColor = bookDetailsButtonType.color.cgColor
+        buttonIcon.tintColor = tintColor
+        buttonIcon.image = bookDetailsButtonType.icon
+        button.fullProgress = false
+        if let progress = progress{
+            button.progress = progress * 100
+        }
+        else {
+            var customText = bookDetailsButtonType.getTitle()
+            if bookDetailsButtonType == .download_audiobook_listen {
+                button.fullProgress = true
+                if let bookDetailsModel = bookDetailsModel, bookDetailsModel.totalAudioChapters > 0 {
+                    customText += " \(bookDetailsModel.currentAudioChapter + 1)/\(bookDetailsModel.totalAudioChapters)"
+                }
+            }
+            else if bookDetailsButtonType == .download_ebook_read {
+                button.fullProgress = true
+                if let bookDetailsModel = bookDetailsModel, bookDetailsModel.totalChapters > 0 {
+                    customText += " \(bookDetailsModel.currentChapter)/\(bookDetailsModel.totalChapters)"
+                }
+            }
+
+            button.customText = customText
+        }
+        
+        deleteButton.isHidden = bookDetailsButtonType.deleteButtonHidden
+    }
+    
+    @objc func buttonAction(_ sender: Any) {
+        delegate?.bookDetailsButtonTableViewCellButtonTapped(buttonType: buttonType)
+    }
+    
+    @IBAction func deleteButtonAction(_ sender: Any) {
+        delegate?.bookDetailsButtonTableViewCellDeleteButtonTapped(buttonType: buttonType)
+    }
+    
+    override func getHeight() -> CGFloat {
+        return 55
+    }
+    
+    deinit {
+        print("deinit \(self)")
+    }
+}
diff --git a/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsButtonTableViewCell.xib b/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsButtonTableViewCell.xib
new file mode 100644 (file)
index 0000000..2c11206
--- /dev/null
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="56" id="KGk-i7-Jjw" customClass="BookDetailsButtonTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="56"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="375" height="55.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NKM-iu-F90" customClass="ProgressLabel" customModule="WolneLektury" customModuleProvider="target">
+                        <rect key="frame" x="53" y="6" width="269" height="43"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="43" id="oxl-fo-PbF"/>
+                            <constraint firstAttribute="width" relation="lessThanOrEqual" constant="300" id="vDU-Ci-XcW"/>
+                        </constraints>
+                    </view>
+                    <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="IQ5-kE-Rse">
+                        <rect key="frame" x="287" y="18" width="20" height="20"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="20" id="deM-ID-dej"/>
+                            <constraint firstAttribute="width" constant="20" id="zt2-I8-lLF"/>
+                        </constraints>
+                    </imageView>
+                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yPz-SB-GDW">
+                        <rect key="frame" x="327" y="7" width="40" height="40"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="40" id="7GT-gY-qQK"/>
+                            <constraint firstAttribute="height" constant="40" id="djw-yh-hHi"/>
+                        </constraints>
+                        <color key="tintColor" red="0.0" green="0.50588235294117645" blue="0.53333333333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <state key="normal" image="icon_trash"/>
+                        <connections>
+                            <action selector="deleteButtonAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="Yh5-G0-3hm"/>
+                        </connections>
+                    </button>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="NKM-iu-F90" firstAttribute="centerX" secondItem="H2p-sc-9uM" secondAttribute="centerX" id="9U1-bV-eoe"/>
+                    <constraint firstItem="NKM-iu-F90" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="H8D-BV-Uan"/>
+                    <constraint firstItem="IQ5-kE-Rse" firstAttribute="leading" secondItem="NKM-iu-F90" secondAttribute="trailing" constant="-35" id="Mfn-lR-zYa"/>
+                    <constraint firstItem="yPz-SB-GDW" firstAttribute="leading" secondItem="NKM-iu-F90" secondAttribute="trailing" constant="5" id="WVM-oD-XuR"/>
+                    <constraint firstAttribute="trailing" secondItem="NKM-iu-F90" secondAttribute="trailing" priority="999" constant="53" id="XrQ-r6-Bt8"/>
+                    <constraint firstItem="NKM-iu-F90" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" priority="999" constant="53" id="iKN-hm-Frq"/>
+                    <constraint firstItem="yPz-SB-GDW" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="k0S-cg-2Yc"/>
+                    <constraint firstItem="IQ5-kE-Rse" firstAttribute="centerY" secondItem="NKM-iu-F90" secondAttribute="centerY" id="kA2-XB-3zm"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="button" destination="NKM-iu-F90" id="LWx-zF-gC3"/>
+                <outlet property="buttonIcon" destination="IQ5-kE-Rse" id="SBb-PA-pTf"/>
+                <outlet property="deleteButton" destination="yPz-SB-GDW" id="ZXx-9e-5kV"/>
+            </connections>
+            <point key="canvasLocation" x="25" y="58"/>
+        </tableViewCell>
+    </objects>
+    <resources>
+        <image name="icon_trash" width="14" height="16"/>
+    </resources>
+</document>
diff --git a/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsFragmentTableViewCell.swift b/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsFragmentTableViewCell.swift
new file mode 100644 (file)
index 0000000..8f9dd4b
--- /dev/null
@@ -0,0 +1,35 @@
+//
+//  BookDetailsFragmentTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 20/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class BookDetailsFragmentTableViewCell: WLTableViewCell {
+
+    @IBOutlet weak var htmlLabel: UILabel!
+    @IBOutlet weak var titleLabel: UILabel!
+    
+    class func instance() -> BookDetailsFragmentTableViewCell{
+        let cell = BookDetailsFragmentTableViewCell.instance(type: BookDetailsFragmentTableViewCell.self)
+        return cell
+    }
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+    }
+    
+    func setup(fragmentTitle: String, fragmentHtml: String){
+        
+        if let htmlString = try? NSAttributedString(html: fragmentHtml){
+            let mutableAttributedString = NSMutableAttributedString(attributedString: htmlString)
+            mutableAttributedString.addAttribute(NSAttributedStringKey.font, value: UIFont.italicSystemFont(ofSize: 12), range: NSMakeRange(0, mutableAttributedString.length))
+            htmlLabel.attributedText = mutableAttributedString
+        }
+        
+        titleLabel.text = fragmentTitle
+    }
+}
diff --git a/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsFragmentTableViewCell.xib b/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsFragmentTableViewCell.xib
new file mode 100644 (file)
index 0000000..61caca1
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="36" id="KGk-i7-Jjw" customClass="BookDetailsFragmentTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="27"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="26.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MfF-w5-ayf">
+                        <rect key="frame" x="29" y="0.0" width="262" height="14.5"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                        <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="calibratedRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sgx-pe-HBv">
+                        <rect key="frame" x="29" y="15" width="262" height="11"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="9"/>
+                        <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="calibratedRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="MfF-w5-ayf" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="Cbr-dn-DuA"/>
+                    <constraint firstItem="MfF-w5-ayf" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="29" id="Pq5-7B-rtS"/>
+                    <constraint firstAttribute="bottom" secondItem="sgx-pe-HBv" secondAttribute="bottom" constant="0.5" id="UFD-AZ-1VW"/>
+                    <constraint firstItem="sgx-pe-HBv" firstAttribute="top" secondItem="MfF-w5-ayf" secondAttribute="bottom" id="ZzQ-5H-AjH"/>
+                    <constraint firstAttribute="trailing" secondItem="MfF-w5-ayf" secondAttribute="trailing" constant="29" id="l2d-0X-5wq"/>
+                    <constraint firstAttribute="trailing" secondItem="sgx-pe-HBv" secondAttribute="trailing" constant="29" id="seY-jP-fNa"/>
+                    <constraint firstItem="sgx-pe-HBv" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="29" id="w1L-ia-WQf"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="htmlLabel" destination="MfF-w5-ayf" id="LGd-Hj-RMX"/>
+                <outlet property="titleLabel" destination="sgx-pe-HBv" id="OY6-FJ-fgg"/>
+            </connections>
+            <point key="canvasLocation" x="23" y="39.5"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsHeaderTableViewCell.swift b/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsHeaderTableViewCell.swift
new file mode 100644 (file)
index 0000000..7c01640
--- /dev/null
@@ -0,0 +1,52 @@
+//
+//  BookDetailsHeaderTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 19/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Kingfisher
+
+class BookDetailsHeaderTableViewCell: WLTableViewCell {
+    
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var bgImageView: UIImageView!
+    @IBOutlet weak var bgOverlayView: UIView!
+    @IBOutlet weak var miniatureImageView: UIImageView!
+
+    var height: CGFloat!
+    
+    class func instance(height: CGFloat) -> BookDetailsHeaderTableViewCell{
+        let cell = BookDetailsHeaderTableViewCell.instance(type: BookDetailsHeaderTableViewCell.self)
+        cell.height = height
+        return cell
+    }
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        bgOverlayView.alpha = 0.7
+    }
+    
+    func setup(bookModel: BookDetailsModel, topColor: UIColor){
+        
+        bgOverlayView.backgroundColor = topColor
+        let titleAttributedText = bookModel.getAttributedAuthorAndTitle(titleFont: UIFont.systemFont(ofSize: 22, weight: .light), descFont: UIFont.systemFont(ofSize: 28, weight: .light))
+        titleLabel.attributedText = titleAttributedText
+        
+        if let url = bookModel.getCoverThumbUrl(){
+            ImageDownloader.default.downloadImage(with: url, options: [], progressBlock: nil) {
+                [weak self] (image, error, url, data) in
+                if let image = image{
+                    self?.bgImageView.image = image
+                    self?.miniatureImageView.image = image
+                }
+            }
+        }
+    }
+
+    override func getHeight() -> CGFloat {
+        return height
+    }
+}
diff --git a/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsHeaderTableViewCell.xib b/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsHeaderTableViewCell.xib
new file mode 100644 (file)
index 0000000..d9459fa
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="210" id="KGk-i7-Jjw" customClass="BookDetailsHeaderTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="210"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="209.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="VY8-qH-xdZ">
+                        <rect key="frame" x="0.0" y="0.0" width="320" height="209.5"/>
+                    </imageView>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XXx-gY-5qA">
+                        <rect key="frame" x="0.0" y="0.0" width="320" height="209.5"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    </view>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="iqV-Nj-f00">
+                        <rect key="frame" x="189" y="43.5" width="124" height="166"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="124" id="Kkv-tq-be9"/>
+                            <constraint firstAttribute="height" constant="166" id="avB-l9-7X9"/>
+                        </constraints>
+                    </imageView>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gae-jc-rla">
+                        <rect key="frame" x="20" y="48.5" width="159" height="156"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                        <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="VY8-qH-xdZ" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="4V0-pf-7Md"/>
+                    <constraint firstItem="gae-jc-rla" firstAttribute="centerY" secondItem="iqV-Nj-f00" secondAttribute="centerY" id="4rM-Qh-Ivb"/>
+                    <constraint firstItem="VY8-qH-xdZ" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="5DB-yL-jTE"/>
+                    <constraint firstItem="gae-jc-rla" firstAttribute="height" secondItem="iqV-Nj-f00" secondAttribute="height" constant="-10" id="8jE-hN-I7N"/>
+                    <constraint firstAttribute="trailing" secondItem="XXx-gY-5qA" secondAttribute="trailing" id="8kJ-Fu-cQp"/>
+                    <constraint firstItem="XXx-gY-5qA" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="IKK-pR-tOf"/>
+                    <constraint firstItem="XXx-gY-5qA" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="MF4-j6-FKy"/>
+                    <constraint firstAttribute="bottom" secondItem="VY8-qH-xdZ" secondAttribute="bottom" id="N5A-J6-CoX"/>
+                    <constraint firstAttribute="bottom" secondItem="XXx-gY-5qA" secondAttribute="bottom" id="OZD-TP-QBK"/>
+                    <constraint firstItem="iqV-Nj-f00" firstAttribute="leading" secondItem="gae-jc-rla" secondAttribute="trailing" constant="10" id="V44-29-Mh9"/>
+                    <constraint firstItem="gae-jc-rla" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="bZP-gB-I5d"/>
+                    <constraint firstAttribute="trailing" secondItem="VY8-qH-xdZ" secondAttribute="trailing" id="cSJ-4j-qWu"/>
+                    <constraint firstAttribute="bottom" secondItem="iqV-Nj-f00" secondAttribute="bottom" id="dfB-Fv-mTM"/>
+                    <constraint firstAttribute="trailing" secondItem="iqV-Nj-f00" secondAttribute="trailing" constant="7" id="wJM-27-fMe"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="bgImageView" destination="VY8-qH-xdZ" id="euA-AP-oI5"/>
+                <outlet property="bgOverlayView" destination="XXx-gY-5qA" id="Bb3-96-Pfx"/>
+                <outlet property="miniatureImageView" destination="iqV-Nj-f00" id="anL-eX-Uxv"/>
+                <outlet property="titleLabel" destination="gae-jc-rla" id="a87-Mb-RPR"/>
+            </connections>
+            <point key="canvasLocation" x="25" y="135"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsInfoTableViewCell.swift b/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsInfoTableViewCell.swift
new file mode 100644 (file)
index 0000000..ced483f
--- /dev/null
@@ -0,0 +1,37 @@
+//
+//  BookDetailsInfoTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 19/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class BookDetailsInfoTableViewCell: WLTableViewCell {
+
+    @IBOutlet weak var epochTitleLabel: UILabel!
+    @IBOutlet weak var epochDescLabel: UILabel!
+    @IBOutlet weak var kindTitleLabel: UILabel!
+    @IBOutlet weak var kindDescLabel: UILabel!
+    @IBOutlet weak var genreTitleLabel: UILabel!
+    @IBOutlet weak var genreDescLabel: UILabel!
+    class func instance() -> BookDetailsInfoTableViewCell{
+        let cell = BookDetailsInfoTableViewCell.instance(type: BookDetailsInfoTableViewCell.self)
+        return cell
+    }
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        epochTitleLabel.text = "book_epoch".localized.uppercased()
+        kindTitleLabel.text = "book_kind".localized.uppercased()
+        genreTitleLabel.text = "book_genre".localized.uppercased()
+    }
+
+    func setup(bookModel: BookDetailsModel){
+        epochDescLabel.text = bookModel.getEpochs()
+        kindDescLabel.text = bookModel.getKinds()
+        genreDescLabel.text = bookModel.getGenres()
+    }
+}
diff --git a/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsInfoTableViewCell.xib b/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsInfoTableViewCell.xib
new file mode 100644 (file)
index 0000000..cc97391
--- /dev/null
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="BookDetailsInfoTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="137"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="136.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ooS-n1-ebo">
+                        <rect key="frame" x="29" y="23" width="24" height="11"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="9"/>
+                        <color key="textColor" red="0.32156862745098036" green="0.32156862745098036" blue="0.32156862745098036" alpha="1" colorSpace="calibratedRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Pg7-Rh-DQy">
+                        <rect key="frame" x="29" y="64" width="24" height="11"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="9"/>
+                        <color key="textColor" red="0.32156862745098036" green="0.32156862745098036" blue="0.32156862745098036" alpha="1" colorSpace="calibratedRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HLr-z8-cAa">
+                        <rect key="frame" x="29" y="105" width="24" height="11"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="9"/>
+                        <color key="textColor" red="0.32156862745098036" green="0.32156862745098036" blue="0.32156862745098036" alpha="1" colorSpace="calibratedRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumScaleFactor="0.59999999999999998" translatesAutoresizingMaskIntoConstraints="NO" id="BSw-B6-Ozu">
+                        <rect key="frame" x="29" y="35" width="271" height="19.5"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="16"/>
+                        <color key="textColor" red="0.0" green="0.50588235294117645" blue="0.53333333333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumScaleFactor="0.59999999999999998" translatesAutoresizingMaskIntoConstraints="NO" id="MrI-Kk-RQm">
+                        <rect key="frame" x="29" y="76" width="271" height="19.5"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="16"/>
+                        <color key="textColor" red="0.0" green="0.50588235294117645" blue="0.53333333333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.59999999999999998" translatesAutoresizingMaskIntoConstraints="NO" id="LpQ-uz-UsD">
+                        <rect key="frame" x="29" y="117" width="271" height="19.5"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="16"/>
+                        <color key="textColor" red="0.0" green="0.50588235294117645" blue="0.53333333333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="MrI-Kk-RQm" firstAttribute="leading" secondItem="Pg7-Rh-DQy" secondAttribute="leading" id="6GI-4r-dxX"/>
+                    <constraint firstAttribute="trailing" secondItem="BSw-B6-Ozu" secondAttribute="trailing" constant="20" id="B2j-gc-lBa"/>
+                    <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="LpQ-uz-UsD" secondAttribute="bottom" id="BLG-RS-E3c"/>
+                    <constraint firstItem="Pg7-Rh-DQy" firstAttribute="top" relation="greaterThanOrEqual" secondItem="BSw-B6-Ozu" secondAttribute="bottom" id="HYU-aJ-ldd"/>
+                    <constraint firstItem="MrI-Kk-RQm" firstAttribute="top" secondItem="Pg7-Rh-DQy" secondAttribute="bottom" constant="1" id="KeH-gB-3Mm"/>
+                    <constraint firstItem="HLr-z8-cAa" firstAttribute="leading" secondItem="ooS-n1-ebo" secondAttribute="leading" id="MUV-Uu-Rr4"/>
+                    <constraint firstItem="HLr-z8-cAa" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="105" id="O06-Ej-lxU"/>
+                    <constraint firstItem="ooS-n1-ebo" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="23" id="RRw-8B-m8j"/>
+                    <constraint firstItem="BSw-B6-Ozu" firstAttribute="top" secondItem="ooS-n1-ebo" secondAttribute="bottom" constant="1" id="Wm8-t2-1GW"/>
+                    <constraint firstItem="ooS-n1-ebo" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="29" id="Zzg-HQ-a9t"/>
+                    <constraint firstItem="HLr-z8-cAa" firstAttribute="top" relation="greaterThanOrEqual" secondItem="MrI-Kk-RQm" secondAttribute="bottom" id="hkI-FI-EPr"/>
+                    <constraint firstItem="Pg7-Rh-DQy" firstAttribute="leading" secondItem="ooS-n1-ebo" secondAttribute="leading" id="mn1-Hi-Udd"/>
+                    <constraint firstItem="LpQ-uz-UsD" firstAttribute="top" secondItem="HLr-z8-cAa" secondAttribute="bottom" constant="1" id="qJ5-KY-YXQ"/>
+                    <constraint firstAttribute="trailing" secondItem="MrI-Kk-RQm" secondAttribute="trailing" constant="20" id="rXW-cD-JlY"/>
+                    <constraint firstItem="LpQ-uz-UsD" firstAttribute="leading" secondItem="ooS-n1-ebo" secondAttribute="leading" id="rZw-nZ-gTE"/>
+                    <constraint firstItem="Pg7-Rh-DQy" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="64" id="z3K-K7-zDh"/>
+                    <constraint firstAttribute="trailing" secondItem="LpQ-uz-UsD" secondAttribute="trailing" constant="20" id="zfR-i1-WJY"/>
+                    <constraint firstItem="BSw-B6-Ozu" firstAttribute="leading" secondItem="ooS-n1-ebo" secondAttribute="leading" id="zn1-96-TSM"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="epochDescLabel" destination="BSw-B6-Ozu" id="JXl-n3-zp2"/>
+                <outlet property="epochTitleLabel" destination="ooS-n1-ebo" id="gm4-rJ-jMe"/>
+                <outlet property="genreDescLabel" destination="LpQ-uz-UsD" id="YMn-bF-FsQ"/>
+                <outlet property="genreTitleLabel" destination="HLr-z8-cAa" id="Kte-Sg-JsP"/>
+                <outlet property="kindDescLabel" destination="MrI-Kk-RQm" id="pHe-qW-GfO"/>
+                <outlet property="kindTitleLabel" destination="Pg7-Rh-DQy" id="5AS-5P-SMA"/>
+            </connections>
+            <point key="canvasLocation" x="26" y="51.5"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsSeparatorTableViewCell.swift b/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsSeparatorTableViewCell.swift
new file mode 100644 (file)
index 0000000..5715906
--- /dev/null
@@ -0,0 +1,25 @@
+//
+//  BookDetailsSeparatorTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 19/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class BookDetailsSeparatorTableViewCell: WLTableViewCell {
+
+    class func instance() -> BookDetailsSeparatorTableViewCell{
+        let cell = BookDetailsSeparatorTableViewCell.instance(type: BookDetailsSeparatorTableViewCell.self)
+        return cell
+    }
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+    }
+    
+    override func getHeight() -> CGFloat {
+        return 21
+    }
+}
diff --git a/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsSeparatorTableViewCell.xib b/iOS/WolneLektury/Screens/BookDetails/Cells/BookDetailsSeparatorTableViewCell.xib
new file mode 100644 (file)
index 0000000..65979fa
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="BookDetailsSeparatorTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="21"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="20.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="YWL-jG-hbF">
+                        <rect key="frame" x="29" y="10" width="262" height="1"/>
+                        <color key="backgroundColor" red="0.50196078431372548" green="0.50196078431372548" blue="0.50196078431372548" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="1" id="hgK-6v-dbZ"/>
+                        </constraints>
+                    </view>
+                </subviews>
+                <constraints>
+                    <constraint firstAttribute="trailing" secondItem="YWL-jG-hbF" secondAttribute="trailing" constant="29" id="0Yg-pg-KDV"/>
+                    <constraint firstItem="YWL-jG-hbF" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="cnc-9T-jPn"/>
+                    <constraint firstItem="YWL-jG-hbF" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="29" id="y4A-eM-o8q"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/BookList/BookListViewController.swift b/iOS/WolneLektury/Screens/BookList/BookListViewController.swift
new file mode 100644 (file)
index 0000000..3c41737
--- /dev/null
@@ -0,0 +1,44 @@
+//
+//  BookListViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/08/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class BookListViewController: ListViewController {
+    
+    static func instance(listViewControllerType: ListViewControllerType, dataSource: [Any]?) -> BookListViewController {
+        let controller = BookListViewController.instance()
+        controller.dataSource = dataSource ?? [BookModel]()
+        controller.listViewControllerType = listViewControllerType
+        return controller
+    }
+    
+    override func setupTableView() {
+        tableView.registerNib(name: "BookTableViewCell")
+        super.setupTableView()
+    }
+
+    override func getLastObjectAfterParameter() -> String? {
+
+        if let last = dataSource.last as? BookModel {
+            return last.full_sort_key
+        }
+        return nil
+    }
+    
+    override func getTableViewCell(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "BookTableViewCell", for: indexPath) as! BookTableViewCell
+        cell.setup(bookModel: dataSource[indexPath.row] as! BookModel)
+        return cell
+    }
+    
+    override func didSelectRow(row: Int) {
+        if dataSource.count > row {
+            navigationController?.pushViewController(BookDetailsViewController.instance(bookSlug: (dataSource[row] as! BookModel).slug) , animated: true)
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Common/Cells/BookTableViewCell.swift b/iOS/WolneLektury/Screens/Common/Cells/BookTableViewCell.swift
new file mode 100644 (file)
index 0000000..cdfd4bf
--- /dev/null
@@ -0,0 +1,92 @@
+//
+//  BookTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 14/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Kingfisher
+
+protocol BookTableViewCellDelegate: class{
+    func bookTableViewCellDelegateDidTapTrashButton(bookSlug: String, indexPath: IndexPath?)
+}
+
+class BookTableViewCell: UITableViewCell {
+    
+    weak var delegate: BookTableViewCellDelegate?
+    
+    @IBOutlet weak var coverImageView: UIImageView!
+    @IBOutlet weak var authorLabel: UILabel!
+    @IBOutlet weak var titleLabel: UILabel!
+    
+    @IBOutlet weak var epochTitleLabel: UILabel!
+    @IBOutlet weak var kindTitleLabel: UILabel!
+    @IBOutlet weak var genreTitleLabel: UILabel!
+    
+    @IBOutlet weak var epochLabel: UILabel!
+    @IBOutlet weak var kindLabel: UILabel!
+    @IBOutlet weak var genreLabel: UILabel!
+    
+    @IBOutlet weak var descFirstImageView: UIImageView!
+    @IBOutlet weak var descFirstLabel: UILabel!
+    @IBOutlet weak var descSecondImageView: UIImageView!
+    @IBOutlet weak var descSecondLabel: UILabel!
+    
+    @IBOutlet weak var epochTopConstraint: NSLayoutConstraint!
+    
+    @IBOutlet weak var bookDescriptionView: BookDescriptionView!
+    
+    private var bookSlug = ""
+    private var indexPath: IndexPath?
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        coverImageView.layer.cornerRadius = 4
+    }
+
+    func setup(bookModel: BookModel){
+        
+        bookDescriptionView.setup(bookModel: bookModel)
+        
+        coverImageView.image = nil
+        bookSlug = bookModel.slug
+        if let url = bookModel.getCoverThumbUrl(){
+            
+            coverImageView.kf.setImage(with: ImageResource(downloadURL: url),
+                                           placeholder: #imageLiteral(resourceName: "list_nocover"),
+                                           options: [.transition(.fade(1))],
+                                           progressBlock: nil,
+                                           completionHandler: { (image, error, cacheType, url) in
+            })
+        }
+    }
+    
+    func setup(bookDetailsModel: BookDetailsModel, delegate: BookTableViewCellDelegate, indexPath: IndexPath){
+
+        self.delegate = delegate
+        self.indexPath = indexPath
+        
+        bookSlug = bookDetailsModel.slug
+        bookDescriptionView.setup(bookDetailsModel: bookDetailsModel)
+        bookDescriptionView.trashButton.removeTarget(nil, action: nil, for: .allEvents)
+        bookDescriptionView.trashButton.addTarget(self, action: #selector(trashButtonAction), for: .touchUpInside)
+        
+        coverImageView.image = nil
+        
+        if let url = bookDetailsModel.getCoverThumbUrl(){
+            
+            coverImageView.kf.setImage(with: ImageResource(downloadURL: url),
+                                       placeholder: #imageLiteral(resourceName: "list_nocover"),
+                                       options: [.transition(.fade(1))],
+                                       progressBlock: nil,
+                                       completionHandler: { (image, error, cacheType, url) in
+            })
+        }
+    }
+    
+    @objc func trashButtonAction(){
+        delegate?.bookTableViewCellDelegateDidTapTrashButton(bookSlug: bookSlug, indexPath: indexPath)
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Common/Cells/BookTableViewCell.xib b/iOS/WolneLektury/Screens/Common/Cells/BookTableViewCell.xib
new file mode 100644 (file)
index 0000000..1064ae9
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="BookTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="137"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="136.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="TKE-6b-nvc">
+                        <rect key="frame" x="11" y="3" width="90" height="129.5"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="90" id="Ryj-Lc-zUs"/>
+                        </constraints>
+                    </imageView>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sqy-pB-CIg" customClass="BookDescriptionView" customModule="WolneLektury" customModuleProvider="target">
+                        <rect key="frame" x="117" y="1" width="187" height="133"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="133" placeholder="YES" id="buW-ph-N24"/>
+                        </constraints>
+                    </view>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="sqy-pB-CIg" firstAttribute="height" relation="lessThanOrEqual" secondItem="TKE-6b-nvc" secondAttribute="height" constant="4" id="4ro-Ss-78Y"/>
+                    <constraint firstItem="TKE-6b-nvc" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="3" id="BmQ-7n-9hq"/>
+                    <constraint firstAttribute="bottom" secondItem="TKE-6b-nvc" secondAttribute="bottom" constant="4" id="Bps-nc-7vz"/>
+                    <constraint firstAttribute="trailing" secondItem="sqy-pB-CIg" secondAttribute="trailing" constant="16" id="OHs-hb-MnS"/>
+                    <constraint firstItem="sqy-pB-CIg" firstAttribute="leading" secondItem="TKE-6b-nvc" secondAttribute="trailing" constant="16" id="QyD-Pq-h5U"/>
+                    <constraint firstItem="TKE-6b-nvc" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="11" id="RMC-zO-C2x"/>
+                    <constraint firstItem="sqy-pB-CIg" firstAttribute="centerY" secondItem="TKE-6b-nvc" secondAttribute="centerY" id="iYB-Up-SDB"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="bookDescriptionView" destination="sqy-pB-CIg" id="52T-PD-5yw"/>
+                <outlet property="coverImageView" destination="TKE-6b-nvc" id="yuK-Sd-wxF"/>
+            </connections>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/Common/Cells/WLTableViewCell.swift b/iOS/WolneLektury/Screens/Common/Cells/WLTableViewCell.swift
new file mode 100644 (file)
index 0000000..68bd973
--- /dev/null
@@ -0,0 +1,54 @@
+//
+//  WLTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class WLTableViewCell: UITableViewCell {
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        selectionStyle = .none
+    }
+
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        super.setSelected(selected, animated: animated)
+
+        // Configure the view for the selected state
+    }
+    
+    static var fullNameIdentifier: String {
+        var fullName: String = NSStringFromClass(self)
+        let range = fullName.range(of: ".", options: .backwards)
+        if let range = range {
+            fullName = fullName.substring(from: range.upperBound)
+        }
+        return fullName
+    }
+
+    static var reuseIdentifier: String {
+        return fullNameIdentifier
+    }
+    
+    class func instance<T>(type: T.Type) -> T{
+        let cell = Bundle.main.loadNibNamed(fullNameIdentifier, owner: nil, options: nil)!.first as! T
+        return cell
+    }
+    
+    static var nib: UINib? {
+        if((Bundle.main.path(forResource: fullNameIdentifier, ofType: "nib")) != nil)
+        {
+            return UINib(nibName: String(fullNameIdentifier), bundle: nil)
+        }
+        return nil
+    }
+    
+    func getHeight() -> CGFloat {
+        return UITableViewAutomaticDimension
+    }
+
+}
diff --git a/iOS/WolneLektury/Screens/Common/Cells/WLTableViewCell.xib b/iOS/WolneLektury/Screens/Common/Cells/WLTableViewCell.xib
new file mode 100644 (file)
index 0000000..6c43444
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="WLTableViewCell" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
+                <autoresizingMask key="autoresizingMask"/>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/Common/ListViewController.swift b/iOS/WolneLektury/Screens/Common/ListViewController.swift
new file mode 100644 (file)
index 0000000..a48e836
--- /dev/null
@@ -0,0 +1,193 @@
+//
+//  ListViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 15/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+enum ListViewControllerType{
+    case newest
+    case recommended
+    case reading_now
+    case audiobooks
+    case news
+    case favourites
+    case completed
+
+    var title: String{
+        return "\(self)".localized
+    }
+    
+    var emptyTitle: String{
+        return "\(self)_empty_list".localized
+    }
+        
+    var canLoadMore: Bool{
+        switch self {
+        case .reading_now, .audiobooks, .news, .favourites, .completed:
+            return true
+        default:
+            return false
+        }
+    }
+    
+    var menuItem: MenuItem? {
+        switch self {
+        case .reading_now:
+            return .reading
+        case .audiobooks:
+            return .audiobooks
+        case .news:
+            return .news
+        case .favourites:
+            return .favourites
+        case .completed:
+            return .completed
+        default:
+            return nil
+        }
+    }
+}
+
+class ListViewController: MainViewController  {
+    
+    @IBOutlet weak var footerViewActivityIndicator: UIActivityIndicatorView!
+    @IBOutlet weak var tableView: UITableView!
+    @IBOutlet weak var noDataLabel: UILabel!
+    @IBOutlet weak var refreshDataButton: ActivityIndicatorButton!
+    
+    var currentParams =  FilterBooksParameters()
+    var canLoadMore = false
+    var loadingMore = false
+    var dataSource: [Any]!
+    var listViewControllerType: ListViewControllerType!
+    
+    override func name() -> String {
+        return "\(listViewControllerType!)List"
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        if listViewControllerType! == .audiobooks {
+            navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "navbar_search"), style: .plain, target: self, action: #selector(presentSearch))
+        }
+        
+        title = listViewControllerType.title
+        
+        noDataLabel.text = listViewControllerType.emptyTitle
+        noDataLabel.isHidden = true
+        
+        refreshDataButton.tintColor = .black
+        setupTableView()
+        
+        refreshDataButton.setIndicatorButtonState(state: .hidden)
+        if dataSource.count == 0{
+            loadData(more: false)
+        }
+    }
+    
+    func setupTableView(){
+        tableView.separatorStyle = .none
+        tableView.delegate = self
+        tableView.dataSource = self
+        tableView.rowHeight = 137
+        var insets = tableView.contentInset
+        insets.top = 11
+        tableView.contentInset = insets
+        footerViewActivityIndicator.color = Constants.Colors.darkGreenBgColor()
+        footerViewActivityIndicator.hidesWhenStopped = true
+    }
+    
+    @objc func presentSearch() {
+        appDelegate.mainNavigator.presentSearch()
+    }
+
+    @IBAction func refreshDataButtonAction(_ sender: Any) {
+        loadData(more: false)
+    }
+    
+    func getLastObjectAfterParameter() -> String? {
+        assertionFailure("This method should be overriden")
+        return nil
+    }
+    
+    func getTableViewCell(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
+        assertionFailure("This method should be overriden")
+        return UITableViewCell()
+    }
+    
+    func didSelectRow(row: Int) {
+        assertionFailure("This method should be overriden")
+    }
+    
+    func loadData(more: Bool){
+        
+        if more{
+            loadingMore = true
+            currentParams.after = getLastObjectAfterParameter()
+        }
+        
+        noDataLabel.isHidden = true
+        footerViewActivityIndicator.startAnimating()
+
+        syncManager.getDataForListType(listViewControllerType: listViewControllerType, params: listViewControllerType.canLoadMore ? currentParams : nil) { [weak self] (result) in
+            
+            guard let strongSelf = self else{
+                return
+            }
+            
+            strongSelf.loadingMore = false
+            strongSelf.footerViewActivityIndicator.stopAnimating()
+
+            switch result {
+            case .success(let model):
+                
+                let array = model as! [Any]
+                strongSelf.canLoadMore =
+                    strongSelf.listViewControllerType.canLoadMore
+                    && array.count == FilterBooksParameters.SEARCH_ITEMS_COUNT
+                strongSelf.dataSource.append(contentsOf: array)
+                strongSelf.refreshDataButton.setIndicatorButtonState(state: .hidden)
+                
+                strongSelf.noDataLabel.isHidden = strongSelf.dataSource.count > 0
+                strongSelf.tableView.reloadData()
+                
+            case .failure/*(let error)*/:
+                if strongSelf.dataSource.count > 0{
+                    strongSelf.canLoadMore = false
+                    strongSelf.refreshDataButton.setIndicatorButtonState(state: .hidden)
+                }
+                else{
+                    strongSelf.refreshDataButton.setIndicatorButtonState(state: .button)
+                }
+            }
+        }
+    }
+}
+
+extension ListViewController: UITableViewDataSource{
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
+        return dataSource.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
+        
+        return getTableViewCell(tableView: tableView, indexPath: indexPath)
+    }
+    
+    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
+        if indexPath.row > 0 && indexPath.row == (dataSource.count-1) && canLoadMore && !loadingMore{
+            let _ = loadData(more: true)
+        }
+    }
+}
+
+extension ListViewController: UITableViewDelegate{
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        tableView.deselectRow(at: indexPath, animated: true)
+        didSelectRow(row: indexPath.row)
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Common/MainNavigationViewController.swift b/iOS/WolneLektury/Screens/Common/MainNavigationViewController.swift
new file mode 100644 (file)
index 0000000..0396005
--- /dev/null
@@ -0,0 +1,23 @@
+//
+//  MainNavigationViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 29/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class MainNavigationViewController: UINavigationController {
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        // Do any additional setup after loading the view.
+    }
+
+    override func didReceiveMemoryWarning() {
+        super.didReceiveMemoryWarning()
+        // Dispose of any resources that can be recreated.
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Common/MainViewController.swift b/iOS/WolneLektury/Screens/Common/MainViewController.swift
new file mode 100644 (file)
index 0000000..24d9746
--- /dev/null
@@ -0,0 +1,31 @@
+//
+//  MainViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 18/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import SideMenu
+
+class MainViewController: WLViewController{
+    
+    var leftBarButtonItemShouldOpenMenu = true
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        if leftBarButtonItemShouldOpenMenu {
+            navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "navbar_menu"), style: .plain, target: self, action: #selector(presentMenu))
+        }
+    }
+    
+    deinit {
+        print("deinit \(self)")
+    }
+    
+    @objc func presentMenu() {
+        present(SideMenuManager.default.menuLeftNavigationController!, animated: true, completion: nil)
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Common/Views/BookDescriptionView.swift b/iOS/WolneLektury/Screens/Common/Views/BookDescriptionView.swift
new file mode 100644 (file)
index 0000000..07a57b7
--- /dev/null
@@ -0,0 +1,129 @@
+//
+//  BookDescriptionView.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 18/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class BookDescriptionView: DesignableXibView {
+
+    @IBOutlet weak var authorLabel: UILabel!
+    @IBOutlet weak var titleLabel: UILabel!
+    
+    @IBOutlet weak var epochTitleLabel: UILabel!
+    @IBOutlet weak var kindTitleLabel: UILabel!
+    @IBOutlet weak var genreTitleLabel: UILabel!
+    
+    @IBOutlet weak var epochLabel: UILabel!
+    @IBOutlet weak var kindLabel: UILabel!
+    @IBOutlet weak var genreLabel: UILabel!
+    
+    @IBOutlet weak var lineView: UIView!
+    
+    @IBOutlet weak var descFirstImageView: UIImageView!
+    @IBOutlet weak var descFirstLabel: UILabel!
+    @IBOutlet weak var descSecondImageView: UIImageView!
+    @IBOutlet weak var descSecondLabel: UILabel!
+    
+    @IBOutlet weak var epochTopConstraint: NSLayoutConstraint!
+    @IBOutlet weak var titleLabelTrailingConstraint: NSLayoutConstraint!
+    
+    @IBOutlet weak var trashButton: UIButton!
+    
+    let orangeTextColor = UIColor(red: 255.0/255.0, green: 165/255.0, blue: 0, alpha: 1.0)
+    let whiteTextColor = UIColor.white
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        epochTitleLabel.text = "book_epoch".localized.uppercased()
+        kindTitleLabel.text = "book_kind".localized.uppercased()
+        genreTitleLabel.text = "book_genre".localized.uppercased()
+        descFirstImageView.tintColor = lineView.backgroundColor
+        descSecondImageView.tintColor = lineView.backgroundColor
+        descFirstImageView.image = #imageLiteral(resourceName: "icon_glasses-mid")
+        descSecondImageView.image = #imageLiteral(resourceName: "icon_speaker-mid")
+        setTrashButtonHidden(isHidden: true)
+    }
+    
+    func setDescViewsVisibility(hidden: Bool){
+        descFirstImageView.isHidden = hidden
+        descFirstLabel.isHidden = hidden
+        descSecondImageView.isHidden = hidden
+        descSecondLabel.isHidden = hidden
+        
+        epochTopConstraint.constant = hidden ? 10 : 30
+    }
+    
+    func setup(bookModel: BookModel, isPremium: Bool = false){
+        setDescViewsVisibility(hidden: false)
+        
+        descSecondImageView.isHidden = !bookModel.has_audio
+        descSecondLabel.isHidden = !bookModel.has_audio
+        if isPremium{
+            
+            authorLabel.textColor = whiteTextColor
+            epochTitleLabel.textColor = whiteTextColor
+            kindTitleLabel.textColor = whiteTextColor
+            genreTitleLabel.textColor = whiteTextColor
+            
+            descFirstImageView.tintColor = whiteTextColor
+            descFirstLabel.textColor = whiteTextColor
+            descSecondImageView.tintColor = whiteTextColor
+            descSecondLabel.textColor = whiteTextColor
+            
+            lineView.backgroundColor = whiteTextColor
+            
+            titleLabel.textColor = orangeTextColor
+            epochLabel.textColor = orangeTextColor
+            kindLabel.textColor = orangeTextColor
+            genreLabel.textColor = orangeTextColor
+        }
+        
+        authorLabel.text = bookModel.author
+        titleLabel.text = bookModel.title
+        epochLabel.text = bookModel.epoch
+        kindLabel.text = bookModel.kind
+        genreLabel.text = bookModel.genre
+    }
+    
+    func setup(bookDetailsModel: BookDetailsModel){
+        setDescViewsVisibility(hidden: false)
+        
+        authorLabel.text = bookDetailsModel.getAuthor()
+        titleLabel.text = bookDetailsModel.title
+        epochLabel.text = bookDetailsModel.getEpochs()
+        kindLabel.text = bookDetailsModel.getKinds()
+        genreLabel.text = bookDetailsModel.getGenres()
+        
+        if bookDetailsModel.totalChapters > 0 && bookDetailsModel.currentChapter <= bookDetailsModel.totalChapters{
+            let progress = Double(bookDetailsModel.currentChapter)/Double(bookDetailsModel.totalChapters) * 100.0
+            descFirstLabel.text = String(format: "reading_progress".localized, progress).uppercased()
+        }
+        else {
+            descFirstLabel.text = ""
+        }
+        
+        descSecondImageView.isHidden = bookDetailsModel.audio_length.count == 0
+        descSecondLabel.isHidden = bookDetailsModel.audio_length.count == 0
+
+        /*
+        if bookDetailsModel.totalAudioChapters > 0 && bookDetailsModel.currentAudioChapter < bookDetailsModel.totalAudioChapters{
+            let progress = Double(bookDetailsModel.currentAudioChapter + 1)/Double(bookDetailsModel.totalAudioChapters) * 100.0
+            descSecondLabel.text = String(format: "listening_progress".localized, progress).uppercased()
+        }
+        else {
+            descSecondLabel.text = ""
+        }
+        */
+        setTrashButtonHidden(isHidden: false)
+    }
+    
+    func setTrashButtonHidden(isHidden: Bool){
+        trashButton.isHidden = isHidden
+        titleLabelTrailingConstraint.constant = isHidden ? 0 : 30
+    }
+
+}
diff --git a/iOS/WolneLektury/Screens/Common/Views/BookDescriptionView.xib b/iOS/WolneLektury/Screens/Common/Views/BookDescriptionView.xib
new file mode 100644 (file)
index 0000000..6e86fc0
--- /dev/null
@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="BookDescriptionView" customModule="WolneLektury" customModuleProvider="target">
+            <connections>
+                <outlet property="authorLabel" destination="fsW-si-b7i" id="7t3-Ql-Tgm"/>
+                <outlet property="descFirstImageView" destination="Yxo-yF-eCp" id="Y6g-Wm-3JT"/>
+                <outlet property="descFirstLabel" destination="AcO-xj-Off" id="dvI-Z4-jps"/>
+                <outlet property="descSecondImageView" destination="41D-ra-qHH" id="t3U-Qo-CTE"/>
+                <outlet property="descSecondLabel" destination="7y8-t1-1KS" id="cUm-LI-Szm"/>
+                <outlet property="epochLabel" destination="pH6-DL-h5s" id="k1k-Dm-Eao"/>
+                <outlet property="epochTitleLabel" destination="Wle-PE-UAF" id="Lms-xB-wd6"/>
+                <outlet property="epochTopConstraint" destination="5is-9a-3dC" id="WDO-w9-NAf"/>
+                <outlet property="genreLabel" destination="4qA-25-xYA" id="l8v-Vw-03p"/>
+                <outlet property="genreTitleLabel" destination="dj2-Nu-iyN" id="hqH-Dm-kOC"/>
+                <outlet property="kindLabel" destination="Eow-PN-jDn" id="FwP-w3-CEO"/>
+                <outlet property="kindTitleLabel" destination="1Le-Sw-TDx" id="iPf-H7-3Ej"/>
+                <outlet property="lineView" destination="gn6-0J-7uZ" id="ph1-Ar-okX"/>
+                <outlet property="titleLabel" destination="ctr-a3-CWd" id="oxI-P3-QeA"/>
+                <outlet property="titleLabelTrailingConstraint" destination="j5t-60-JfE" id="ECX-us-RNi"/>
+                <outlet property="trashButton" destination="Epm-RG-hdV" id="XOI-Gh-vZV"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iyC-zG-cEJ">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="116"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <subviews>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Adam Mickiewicz" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fsW-si-b7i">
+                    <rect key="frame" x="0.0" y="0.0" width="375" height="14"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="11"/>
+                    <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gn6-0J-7uZ">
+                    <rect key="frame" x="0.0" y="36.5" width="375" height="1"/>
+                    <color key="backgroundColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="1" id="no7-9B-CLS"/>
+                    </constraints>
+                </view>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Dziady" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ctr-a3-CWd">
+                    <rect key="frame" x="0.0" y="15" width="375" height="19.5"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="16"/>
+                    <color key="textColor" red="0.85882352939999995" green="0.29411764709999999" blue="0.086274509799999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Yxo-yF-eCp">
+                    <rect key="frame" x="0.0" y="42.5" width="16" height="16"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="16" id="CfP-zw-F43"/>
+                        <constraint firstAttribute="width" constant="16" id="LlY-8b-Wmx"/>
+                    </constraints>
+                </imageView>
+                <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="41D-ra-qHH">
+                    <rect key="frame" x="30" y="42.5" width="16" height="16"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="16" id="TqM-oF-f10"/>
+                        <constraint firstAttribute="width" constant="16" id="YSI-ug-lHS"/>
+                    </constraints>
+                </imageView>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AcO-xj-Off">
+                    <rect key="frame" x="18" y="50.5" width="10" height="0.0"/>
+                    <constraints>
+                        <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="10" id="62k-1e-M6F"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" pointSize="11"/>
+                    <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7y8-t1-1KS">
+                    <rect key="frame" x="48" y="50.5" width="0.0" height="0.0"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="11"/>
+                    <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="EPOKA" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wle-PE-UAF">
+                    <rect key="frame" x="0.0" y="67.5" width="25" height="8.5"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="8.5" id="Xqt-lP-nM6"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" weight="medium" pointSize="7"/>
+                    <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="EPOKA" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1Le-Sw-TDx">
+                    <rect key="frame" x="0.0" y="86.5" width="25" height="8.5"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="8.5" id="Krw-MC-JGY"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" weight="medium" pointSize="7"/>
+                    <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="EPOKA" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dj2-Nu-iyN">
+                    <rect key="frame" x="0.0" y="105.5" width="25" height="8.5"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="8.5" id="ePu-5r-Pr1"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" weight="medium" pointSize="7"/>
+                    <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="EPOKA" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pH6-DL-h5s">
+                    <rect key="frame" x="38" y="64" width="337" height="14"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                    <color key="textColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="EPOKA" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Eow-PN-jDn">
+                    <rect key="frame" x="38" y="83" width="337" height="14"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                    <color key="textColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="EPOKA" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4qA-25-xYA">
+                    <rect key="frame" x="38" y="102" width="337" height="14"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                    <color key="textColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Epm-RG-hdV">
+                    <rect key="frame" x="345" y="0.0" width="30" height="36.5"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="30" id="VBe-nL-lGw"/>
+                    </constraints>
+                    <color key="tintColor" red="0.0" green="0.50196078430000002" blue="0.53333333329999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <state key="normal" image="icon_trash"/>
+                </button>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="Wle-PE-UAF" firstAttribute="top" secondItem="gn6-0J-7uZ" secondAttribute="bottom" constant="30" id="5is-9a-3dC"/>
+                <constraint firstItem="7y8-t1-1KS" firstAttribute="centerY" secondItem="41D-ra-qHH" secondAttribute="centerY" id="6e0-U5-VaX"/>
+                <constraint firstItem="4qA-25-xYA" firstAttribute="centerY" secondItem="dj2-Nu-iyN" secondAttribute="centerY" id="6m9-zn-tY1"/>
+                <constraint firstItem="dj2-Nu-iyN" firstAttribute="leading" secondItem="1Le-Sw-TDx" secondAttribute="leading" id="7Hr-Lq-bYB"/>
+                <constraint firstItem="1Le-Sw-TDx" firstAttribute="leading" secondItem="Wle-PE-UAF" secondAttribute="leading" id="7Uj-Hs-qsn"/>
+                <constraint firstAttribute="trailing" secondItem="4qA-25-xYA" secondAttribute="trailing" id="7dS-JH-vJM"/>
+                <constraint firstAttribute="trailing" secondItem="Eow-PN-jDn" secondAttribute="trailing" id="ARP-II-IuX"/>
+                <constraint firstAttribute="bottom" secondItem="4qA-25-xYA" secondAttribute="bottom" id="Fg3-sP-0CP"/>
+                <constraint firstItem="4qA-25-xYA" firstAttribute="leading" secondItem="Eow-PN-jDn" secondAttribute="leading" id="GFB-GB-dyn"/>
+                <constraint firstItem="Eow-PN-jDn" firstAttribute="centerY" secondItem="1Le-Sw-TDx" secondAttribute="centerY" id="GxC-GF-pcz"/>
+                <constraint firstItem="fsW-si-b7i" firstAttribute="top" secondItem="iyC-zG-cEJ" secondAttribute="top" id="IYP-ZC-7To"/>
+                <constraint firstItem="41D-ra-qHH" firstAttribute="leading" secondItem="AcO-xj-Off" secondAttribute="trailing" constant="2" id="Kkd-UY-em3"/>
+                <constraint firstItem="PhG-rG-28a" firstAttribute="trailing" secondItem="Epm-RG-hdV" secondAttribute="trailing" id="Lpb-ix-jJk"/>
+                <constraint firstItem="1Le-Sw-TDx" firstAttribute="top" secondItem="Wle-PE-UAF" secondAttribute="bottom" constant="10" id="N1N-HW-gLj"/>
+                <constraint firstAttribute="trailing" secondItem="pH6-DL-h5s" secondAttribute="trailing" id="OiG-FA-1TQ"/>
+                <constraint firstItem="gn6-0J-7uZ" firstAttribute="top" secondItem="Epm-RG-hdV" secondAttribute="bottom" id="RaJ-bL-9QX"/>
+                <constraint firstItem="fsW-si-b7i" firstAttribute="leading" secondItem="gn6-0J-7uZ" secondAttribute="leading" id="VSq-nQ-spb"/>
+                <constraint firstItem="AcO-xj-Off" firstAttribute="leading" secondItem="Yxo-yF-eCp" secondAttribute="trailing" constant="2" id="WxK-X7-ZA9"/>
+                <constraint firstItem="Epm-RG-hdV" firstAttribute="top" secondItem="PhG-rG-28a" secondAttribute="top" id="Zi1-Cz-VZf"/>
+                <constraint firstItem="pH6-DL-h5s" firstAttribute="leading" secondItem="gn6-0J-7uZ" secondAttribute="leading" constant="38" id="Zmb-AE-RSX"/>
+                <constraint firstItem="ctr-a3-CWd" firstAttribute="leading" secondItem="gn6-0J-7uZ" secondAttribute="leading" id="cET-SF-D5X"/>
+                <constraint firstItem="Yxo-yF-eCp" firstAttribute="leading" secondItem="gn6-0J-7uZ" secondAttribute="leading" id="cqU-tX-9yC"/>
+                <constraint firstItem="dj2-Nu-iyN" firstAttribute="top" secondItem="1Le-Sw-TDx" secondAttribute="bottom" constant="10" id="eA1-le-KTr"/>
+                <constraint firstItem="Eow-PN-jDn" firstAttribute="leading" secondItem="pH6-DL-h5s" secondAttribute="leading" id="fXd-h0-TZK"/>
+                <constraint firstAttribute="trailing" secondItem="fsW-si-b7i" secondAttribute="trailing" id="j5t-60-JfE"/>
+                <constraint firstItem="Yxo-yF-eCp" firstAttribute="top" secondItem="gn6-0J-7uZ" secondAttribute="bottom" constant="5" id="la3-dR-38Y"/>
+                <constraint firstItem="ctr-a3-CWd" firstAttribute="trailing" secondItem="fsW-si-b7i" secondAttribute="trailing" id="mbY-nn-AE9"/>
+                <constraint firstItem="gn6-0J-7uZ" firstAttribute="top" secondItem="ctr-a3-CWd" secondAttribute="bottom" constant="2" id="mkc-F7-PlX"/>
+                <constraint firstItem="AcO-xj-Off" firstAttribute="centerY" secondItem="Yxo-yF-eCp" secondAttribute="centerY" id="pWl-So-sUD"/>
+                <constraint firstItem="41D-ra-qHH" firstAttribute="top" secondItem="Yxo-yF-eCp" secondAttribute="top" id="q2J-4w-iI4"/>
+                <constraint firstItem="ctr-a3-CWd" firstAttribute="top" secondItem="fsW-si-b7i" secondAttribute="bottom" constant="1" id="s0D-n1-Pee"/>
+                <constraint firstItem="pH6-DL-h5s" firstAttribute="centerY" secondItem="Wle-PE-UAF" secondAttribute="centerY" id="txc-Tx-0hh"/>
+                <constraint firstAttribute="trailing" secondItem="gn6-0J-7uZ" secondAttribute="trailing" id="wYK-da-rOF"/>
+                <constraint firstItem="7y8-t1-1KS" firstAttribute="leading" secondItem="41D-ra-qHH" secondAttribute="trailing" constant="2" id="xCj-lW-VJs"/>
+                <constraint firstItem="Wle-PE-UAF" firstAttribute="leading" secondItem="gn6-0J-7uZ" secondAttribute="leading" id="yBC-yb-YPl"/>
+                <constraint firstItem="fsW-si-b7i" firstAttribute="leading" secondItem="iyC-zG-cEJ" secondAttribute="leading" id="yDZ-tY-Ocg"/>
+                <constraint firstItem="gn6-0J-7uZ" firstAttribute="leading" secondItem="iyC-zG-cEJ" secondAttribute="leading" id="yZj-dj-ke9"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <viewLayoutGuide key="safeArea" id="PhG-rG-28a"/>
+            <point key="canvasLocation" x="24.5" y="-215"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="icon_trash" width="14" height="16"/>
+    </resources>
+</document>
diff --git a/iOS/WolneLektury/Screens/Common/Views/BookImageOverlayView.swift b/iOS/WolneLektury/Screens/Common/Views/BookImageOverlayView.swift
new file mode 100644 (file)
index 0000000..13d659f
--- /dev/null
@@ -0,0 +1,53 @@
+//
+//  BookImageOverlayView.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 18/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class BookImageOverlayView: DesignableXibView {
+    
+        @IBOutlet weak var iconsView: UIView!
+        @IBOutlet weak var readImageView: UIImageView!
+        @IBOutlet weak var audiobookImageView: UIImageView!
+        @IBOutlet weak var iconsWidthConstraint: NSLayoutConstraint!
+        @IBOutlet weak var titleLabel: UILabel!
+        @IBOutlet weak var textBgColorView: UIView!
+
+
+        override func awakeFromNib() {
+            super.awakeFromNib()
+            readImageView.layer.cornerRadius = 9
+            audiobookImageView.layer.cornerRadius = 9
+            readImageView.tintColor = UIColor.white
+            audiobookImageView.tintColor = UIColor.white
+            
+            let color = UIColor(red:0.84, green:0.29, blue:0.19, alpha:0.9)
+            readImageView.backgroundColor = color
+            audiobookImageView.backgroundColor = color
+            textBgColorView.alpha = 0.9
+
+        }
+        
+        func setup(bookModel: BookModel){
+            let titleAttributedText = bookModel.getAttributedAuthorAndTitle(titleFont: UIFont.systemFont(ofSize: 9, weight: .medium), descFont: UIFont.systemFont(ofSize: 11, weight: .bold))
+            
+            readImageView.backgroundColor = bookModel.bgColor
+            audiobookImageView.backgroundColor = bookModel.bgColor
+            textBgColorView.backgroundColor = bookModel.bgColor
+            titleLabel.attributedText = titleAttributedText
+
+            if bookModel.has_audio{
+                audiobookImageView.isHidden = false
+                iconsWidthConstraint.constant = 39
+            }
+            else{
+                audiobookImageView.isHidden = true
+                iconsWidthConstraint.constant = 18
+            }
+        }
+}
+
diff --git a/iOS/WolneLektury/Screens/Common/Views/BookImageOverlayView.xib b/iOS/WolneLektury/Screens/Common/Views/BookImageOverlayView.xib
new file mode 100644 (file)
index 0000000..f056c78
--- /dev/null
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="BookImageOverlayView" customModule="WolneLektury" customModuleProvider="target">
+            <connections>
+                <outlet property="audiobookImageView" destination="Ed1-aX-xBH" id="f6d-EH-hjL"/>
+                <outlet property="iconsView" destination="Spc-Kx-5eG" id="ihL-cl-yYW"/>
+                <outlet property="iconsWidthConstraint" destination="QaM-yO-Osa" id="maL-dD-ny4"/>
+                <outlet property="readImageView" destination="tNy-0M-xKb" id="jKF-Zm-Icu"/>
+                <outlet property="textBgColorView" destination="WX8-OJ-ZhC" id="wlb-PO-nbF"/>
+                <outlet property="titleLabel" destination="Gve-bi-73b" id="8qH-un-cwl"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
+            <rect key="frame" x="0.0" y="0.0" width="192" height="232"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="c3L-SM-fkF">
+                    <rect key="frame" x="0.0" y="190" width="192" height="42"/>
+                    <subviews>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WX8-OJ-ZhC">
+                            <rect key="frame" x="0.0" y="0.0" width="192" height="42"/>
+                            <color key="backgroundColor" red="1" green="0.25154124150000001" blue="1" alpha="1" colorSpace="calibratedRGB"/>
+                        </view>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="5" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gve-bi-73b">
+                            <rect key="frame" x="2" y="5" width="188" height="32"/>
+                            <fontDescription key="fontDescription" type="boldSystem" pointSize="11"/>
+                            <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="trailing" secondItem="WX8-OJ-ZhC" secondAttribute="trailing" id="4wE-dH-JLJ"/>
+                        <constraint firstItem="Gve-bi-73b" firstAttribute="top" secondItem="c3L-SM-fkF" secondAttribute="top" constant="5" id="74T-LY-1RC"/>
+                        <constraint firstItem="Gve-bi-73b" firstAttribute="leading" secondItem="c3L-SM-fkF" secondAttribute="leading" constant="2" id="Ga8-Vb-yva"/>
+                        <constraint firstAttribute="bottom" secondItem="Gve-bi-73b" secondAttribute="bottom" constant="5" id="S7S-b1-Eb3"/>
+                        <constraint firstItem="WX8-OJ-ZhC" firstAttribute="leading" secondItem="c3L-SM-fkF" secondAttribute="leading" id="U6h-2t-1OT"/>
+                        <constraint firstAttribute="trailing" secondItem="Gve-bi-73b" secondAttribute="trailing" constant="2" id="V0O-X6-t3M"/>
+                        <constraint firstItem="WX8-OJ-ZhC" firstAttribute="top" secondItem="c3L-SM-fkF" secondAttribute="top" id="pqU-hT-3pO"/>
+                        <constraint firstAttribute="bottom" secondItem="WX8-OJ-ZhC" secondAttribute="bottom" id="qwQ-qE-aAS"/>
+                        <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="42" id="was-OT-TqD"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Spc-Kx-5eG">
+                    <rect key="frame" x="87" y="169" width="18" height="18"/>
+                    <subviews>
+                        <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_glasses-small" translatesAutoresizingMaskIntoConstraints="NO" id="tNy-0M-xKb">
+                            <rect key="frame" x="0.0" y="0.0" width="18" height="18"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="18" id="grW-2g-du1"/>
+                                <constraint firstAttribute="width" constant="18" id="wc8-fD-C3b"/>
+                            </constraints>
+                        </imageView>
+                        <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_speaker-small" translatesAutoresizingMaskIntoConstraints="NO" id="Ed1-aX-xBH">
+                            <rect key="frame" x="0.0" y="0.0" width="18" height="18"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="18" id="7mc-Eo-FT3"/>
+                                <constraint firstAttribute="width" constant="18" id="Rhw-Ts-9BT"/>
+                            </constraints>
+                        </imageView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="Ed1-aX-xBH" firstAttribute="top" secondItem="Spc-Kx-5eG" secondAttribute="top" id="2ef-JB-uEV"/>
+                        <constraint firstAttribute="height" constant="18" id="5ro-lB-18b"/>
+                        <constraint firstAttribute="width" constant="18" id="QaM-yO-Osa"/>
+                        <constraint firstItem="tNy-0M-xKb" firstAttribute="leading" secondItem="Spc-Kx-5eG" secondAttribute="leading" id="kjl-ZL-i90"/>
+                        <constraint firstItem="tNy-0M-xKb" firstAttribute="top" secondItem="Spc-Kx-5eG" secondAttribute="top" id="onZ-LJ-upC"/>
+                        <constraint firstAttribute="trailing" secondItem="Ed1-aX-xBH" secondAttribute="trailing" id="yqj-SA-eqS"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="c3L-SM-fkF" secondAttribute="bottom" id="RIb-PP-JD1"/>
+                <constraint firstItem="c3L-SM-fkF" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="VrC-AX-5UV"/>
+                <constraint firstItem="c3L-SM-fkF" firstAttribute="top" secondItem="Spc-Kx-5eG" secondAttribute="bottom" constant="3" id="XOF-zn-qXR"/>
+                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="c3L-SM-fkF" secondAttribute="trailing" id="biG-Fe-wbK"/>
+                <constraint firstItem="Spc-Kx-5eG" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="rYU-2B-Pxu"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
+            <point key="canvasLocation" x="-151" y="-39"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="icon_glasses-small" width="11" height="9"/>
+        <image name="icon_speaker-small" width="10" height="8"/>
+    </resources>
+</document>
diff --git a/iOS/WolneLektury/Screens/Common/Views/DesignableXibView.swift b/iOS/WolneLektury/Screens/Common/Views/DesignableXibView.swift
new file mode 100644 (file)
index 0000000..e1e1091
--- /dev/null
@@ -0,0 +1,39 @@
+//
+//  DesignableXibView.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 18/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class DesignableXibView: UIView {
+    
+    var contentView : UIView!
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        xibSetup()
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        xibSetup()
+    }
+    
+    func xibSetup() {
+        contentView = loadViewFromNib()
+        contentView.frame = bounds
+        contentView.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
+        addSubview(contentView)
+    }
+    
+    func loadViewFromNib() -> UIView! {
+        let bundle = Bundle(for: type(of: self))
+        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
+        let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
+        
+        return view
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Common/WLNavigationController.swift b/iOS/WolneLektury/Screens/Common/WLNavigationController.swift
new file mode 100644 (file)
index 0000000..dd69fc8
--- /dev/null
@@ -0,0 +1,22 @@
+//
+//  WLNavigationController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 28/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class WLNavigationController: UINavigationController {
+
+    override var shouldAutorotate: Bool {
+        return true
+    }
+    
+    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
+        
+        return UIDevice.current.userInterfaceIdiom == .pad ? .all : .portrait
+    }
+
+}
diff --git a/iOS/WolneLektury/Screens/Common/WLSideMenuNavigationController.swift b/iOS/WolneLektury/Screens/Common/WLSideMenuNavigationController.swift
new file mode 100644 (file)
index 0000000..74de320
--- /dev/null
@@ -0,0 +1,20 @@
+//
+//  WLSideMenuNavigationController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 28/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import SideMenu
+class WLSideMenuNavigationController:  UISideMenuNavigationController{
+
+    override var shouldAutorotate: Bool {
+        return true
+    }
+    
+    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
+        return UIDevice.current.userInterfaceIdiom == .pad ? .all : .portrait
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Common/WLViewController.swift b/iOS/WolneLektury/Screens/Common/WLViewController.swift
new file mode 100644 (file)
index 0000000..8df91fa
--- /dev/null
@@ -0,0 +1,54 @@
+//
+//  WLViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 25/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import MatomoTracker
+
+class WLViewController: UIViewController {
+
+    func name() -> String {
+        
+        return String(describing: type(of: self)).replacingOccurrences(of: "ViewController", with: "")
+    }
+    
+    func parentNames() -> [String] {
+        var namesList = [String]()
+        if let controller = self.parent as? WLViewController {
+            namesList.append(contentsOf: controller.parentNames())
+            namesList.append(name())
+        }
+        else if let controller = self.parent as? UINavigationController {
+            for vc in controller.childViewControllers{
+                if let vc = vc as? WLViewController {
+                    namesList.append(vc.name())
+                }
+            }
+        }
+        else {
+            namesList.append(name())
+        }
+        return namesList
+    }
+    
+    func trackScreen() {
+        
+        print("trackScreen \(parentNames())")
+        MatomoTracker.shared.track(view: parentNames())
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        trackScreen()
+    }
+
+    override func didReceiveMemoryWarning() {
+        super.didReceiveMemoryWarning()
+        // Dispose of any resources that can be recreated.
+    }
+    
+}
diff --git a/iOS/WolneLektury/Screens/DownloadedList/DownloadedListViewController.swift b/iOS/WolneLektury/Screens/DownloadedList/DownloadedListViewController.swift
new file mode 100644 (file)
index 0000000..cf933e5
--- /dev/null
@@ -0,0 +1,99 @@
+//
+//  DownloadedListViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 12/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class DownloadedListViewController: MainViewController {
+
+    @IBOutlet weak var tableView: UITableView!
+    @IBOutlet weak var noDataLabel: UILabel!
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        title = "nav_downloaded".localized
+        
+        noDataLabel.text = "downloaded_empty_list".localized
+        noDataLabel.isHidden = true
+        setupTableView()
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        reloaData()
+    }
+    func setupTableView(){
+        tableView.registerNib(name: "BookTableViewCell")
+        tableView.separatorStyle = .none
+        tableView.delegate = self
+        tableView.dataSource = self
+        tableView.rowHeight = 137
+        var insets = tableView.contentInset
+        insets.top = 11
+        tableView.contentInset = insets
+
+    }
+    
+    func reloaData() {
+        DatabaseManager.shared.refresh()
+        tableView.reloadData()
+        noDataLabel.isHidden = (DatabaseManager.shared.rlmApplication?.downloadedBooks.count ?? 0) > 0
+    }
+}
+
+extension DownloadedListViewController: UITableViewDataSource{
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
+        return DatabaseManager.shared.rlmApplication?.downloadedBooks.count ?? 0
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
+        
+        let cell = tableView.dequeueReusableCell(withIdentifier: "BookTableViewCell", for: indexPath) as! BookTableViewCell
+        
+        if let downloadedBooks = DatabaseManager.shared.rlmApplication?.downloadedBooks, downloadedBooks.count > indexPath.row {
+            cell.setup(bookDetailsModel: downloadedBooks[indexPath.row], delegate: self, indexPath: indexPath)
+        } 
+        
+        return cell
+    }
+    
+    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
+
+    }
+}
+
+extension DownloadedListViewController: UITableViewDelegate{
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        tableView.deselectRow(at: indexPath, animated: true)
+        
+        guard let dataSource = DatabaseManager.shared.rlmApplication?.downloadedBooks else {
+            return
+        }
+
+        if dataSource.count > indexPath.row{
+            navigationController?.pushViewController(BookDetailsViewController.instance(bookSlug: dataSource[indexPath.row].slug) , animated: true)
+        }
+    }
+}
+
+extension DownloadedListViewController: BookTableViewCellDelegate{
+    func bookTableViewCellDelegateDidTapTrashButton(bookSlug: String, indexPath: IndexPath?) {
+        
+        if let slug = PlayerController.shared.currentBookDetails?.slug, slug == bookSlug {
+            PlayerController.shared.stopAndClear()
+        }
+        if let index = DatabaseManager.shared.rlmApplication?.downloadedBooks.index(where: {$0.slug == bookSlug}), DatabaseManager.shared.removeBookFromDownloaded(bookSlug: bookSlug){
+            tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
+            presentToast(message: "book_deleted_message".localized)
+        }
+        else{
+            reloaData()
+        }        
+    }
+}
+
diff --git a/iOS/WolneLektury/Screens/Filter/Cells/FilterCollectionViewCell.swift b/iOS/WolneLektury/Screens/Filter/Cells/FilterCollectionViewCell.swift
new file mode 100644 (file)
index 0000000..d64c203
--- /dev/null
@@ -0,0 +1,33 @@
+//
+//  FilterCollectionViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 12/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class FilterCollectionViewCell: UICollectionViewCell {
+    
+    @IBOutlet weak var checkboxBgImageView: UIImageView!
+    @IBOutlet weak var checkboxTickImageView: UIImageView!
+    @IBOutlet weak var titleLabel: UILabel!
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        checkboxTickImageView.tintColor = .white
+    }
+    
+    func setup(categoryModel: CategoryModel){
+        
+        titleLabel.text = categoryModel.name.uppercased()
+        setChecked(value: categoryModel.checked)
+    }
+    
+    func setChecked(value: Bool){
+        
+        checkboxTickImageView.isHidden = !value
+        checkboxBgImageView.tintColor = value ? .white : UIColor(red:0.51, green:0.73, blue:0.73, alpha:1.00)
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Filter/Cells/FilterCollectionViewCell.xib b/iOS/WolneLektury/Screens/Filter/Cells/FilterCollectionViewCell.xib
new file mode 100644 (file)
index 0000000..84f7263
--- /dev/null
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="B18-fA-3GF" customClass="FilterCollectionViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="136" height="30"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
+                <rect key="frame" x="0.0" y="0.0" width="136" height="30"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="navbar_checkbox" translatesAutoresizingMaskIntoConstraints="NO" id="Z6W-4v-Yq4">
+                        <rect key="frame" x="5" y="0.0" width="30" height="30"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="30" id="7U1-08-jU6"/>
+                            <constraint firstAttribute="height" constant="30" id="Kvv-3L-82e"/>
+                        </constraints>
+                    </imageView>
+                    <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="navbar_checkbox-tick" translatesAutoresizingMaskIntoConstraints="NO" id="uNk-LA-tZJ">
+                        <rect key="frame" x="5" y="0.0" width="30" height="30"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="30" id="Q7Q-eX-XQ7"/>
+                            <constraint firstAttribute="height" constant="30" id="VUa-dN-QT5"/>
+                        </constraints>
+                    </imageView>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dJM-8z-Yi6">
+                        <rect key="frame" x="37" y="7" width="99" height="17"/>
+                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                        <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                </subviews>
+            </view>
+            <constraints>
+                <constraint firstItem="Z6W-4v-Yq4" firstAttribute="leading" secondItem="B18-fA-3GF" secondAttribute="leading" constant="5" id="0Th-7t-c60"/>
+                <constraint firstAttribute="trailing" secondItem="dJM-8z-Yi6" secondAttribute="trailing" id="DNQ-5m-uFH"/>
+                <constraint firstItem="Z6W-4v-Yq4" firstAttribute="top" secondItem="B18-fA-3GF" secondAttribute="top" id="Rml-up-YM5"/>
+                <constraint firstItem="dJM-8z-Yi6" firstAttribute="centerY" secondItem="B18-fA-3GF" secondAttribute="centerY" id="UBt-f1-jYw"/>
+                <constraint firstItem="dJM-8z-Yi6" firstAttribute="leading" secondItem="Z6W-4v-Yq4" secondAttribute="trailing" constant="2" id="cW7-zh-Ud0"/>
+                <constraint firstItem="uNk-LA-tZJ" firstAttribute="centerY" secondItem="B18-fA-3GF" secondAttribute="centerY" id="i3g-NQ-NNJ"/>
+                <constraint firstItem="uNk-LA-tZJ" firstAttribute="trailing" secondItem="Z6W-4v-Yq4" secondAttribute="trailing" id="kWn-LR-ClY"/>
+                <constraint firstItem="Z6W-4v-Yq4" firstAttribute="centerY" secondItem="B18-fA-3GF" secondAttribute="centerY" id="kxy-gp-kFY"/>
+                <constraint firstItem="uNk-LA-tZJ" firstAttribute="leading" secondItem="Z6W-4v-Yq4" secondAttribute="leading" id="oLh-zc-X7O"/>
+                <constraint firstAttribute="bottom" secondItem="Z6W-4v-Yq4" secondAttribute="bottom" id="wmy-iT-qts"/>
+            </constraints>
+            <viewLayoutGuide key="safeArea" id="NnD-tu-bfx"/>
+            <connections>
+                <outlet property="checkboxBgImageView" destination="Z6W-4v-Yq4" id="Dh8-9z-zxp"/>
+                <outlet property="checkboxTickImageView" destination="uNk-LA-tZJ" id="jrk-eg-oaz"/>
+                <outlet property="titleLabel" destination="dJM-8z-Yi6" id="zBu-Of-3DO"/>
+            </connections>
+        </collectionViewCell>
+    </objects>
+    <resources>
+        <image name="navbar_checkbox" width="21" height="21"/>
+        <image name="navbar_checkbox-tick" width="13" height="10"/>
+    </resources>
+</document>
diff --git a/iOS/WolneLektury/Screens/Filter/Cells/FilterOnlyLecturesReusableView.swift b/iOS/WolneLektury/Screens/Filter/Cells/FilterOnlyLecturesReusableView.swift
new file mode 100644 (file)
index 0000000..c2d5461
--- /dev/null
@@ -0,0 +1,37 @@
+//
+//  FilterOnlyLecturesReusableView.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 12/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+protocol FilterOnlyLecturesReusableViewDelegate: class {
+    func filterOnlyLecturesReusableViewSwitchValueChanged(value: Bool, isAudiobook: Bool)
+}
+
+class FilterOnlyLecturesReusableView: UICollectionReusableView {
+    var delegate: FilterOnlyLecturesReusableViewDelegate?
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var onSwitch: UISwitch!
+    var isAudiobook: Bool = false {
+        didSet{
+            titleLabel.text = isAudiobook ? "has_audiobook".localized.uppercased() : "only_lecture".localized.uppercased()
+        }
+    }
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        titleLabel.text = "only_lecture".localized.uppercased()
+    }
+    
+    func setup(isAudiobook: Bool){
+        self.isAudiobook = isAudiobook
+    }
+
+    @IBAction func switchValueChanged(_ sender: Any) {
+        delegate?.filterOnlyLecturesReusableViewSwitchValueChanged(value: onSwitch.isOn, isAudiobook: isAudiobook)
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Filter/Cells/FilterOnlyLecturesReusableView.xib b/iOS/WolneLektury/Screens/Filter/Cells/FilterOnlyLecturesReusableView.xib
new file mode 100644 (file)
index 0000000..8ba0520
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="GQ9-1I-9TM" customClass="FilterOnlyLecturesReusableView" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <subviews>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4yT-PG-dlF">
+                    <rect key="frame" x="21" y="20" width="36" height="17"/>
+                    <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Am5-fN-9ml">
+                    <rect key="frame" x="302" y="13" width="49" height="31"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="47" id="8og-vb-OnE"/>
+                        <constraint firstAttribute="height" constant="31" id="FyR-g6-fK7"/>
+                    </constraints>
+                    <color key="onTintColor" red="0.4823529412" green="0.69803921570000005" blue="0.71372549019999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <connections>
+                        <action selector="switchValueChanged:" destination="GQ9-1I-9TM" eventType="valueChanged" id="vpq-nq-BmO"/>
+                    </connections>
+                </switch>
+            </subviews>
+            <constraints>
+                <constraint firstItem="4yT-PG-dlF" firstAttribute="centerY" secondItem="Am5-fN-9ml" secondAttribute="centerY" id="H8P-ib-WSp"/>
+                <constraint firstItem="4yT-PG-dlF" firstAttribute="leading" secondItem="GQ9-1I-9TM" secondAttribute="leading" constant="21" id="g3F-lH-Gl3"/>
+                <constraint firstAttribute="trailing" secondItem="Am5-fN-9ml" secondAttribute="trailing" constant="26" id="jG1-Nb-EAb"/>
+                <constraint firstItem="g1z-sa-HbR" firstAttribute="bottom" secondItem="Am5-fN-9ml" secondAttribute="bottom" id="yVC-Sp-H8J"/>
+            </constraints>
+            <viewLayoutGuide key="safeArea" id="g1z-sa-HbR"/>
+            <connections>
+                <outlet property="onSwitch" destination="Am5-fN-9ml" id="g41-Mp-gJx"/>
+                <outlet property="titleLabel" destination="4yT-PG-dlF" id="c0w-to-gfT"/>
+            </connections>
+        </collectionReusableView>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/Filter/Cells/FilterSectionHeaderCollectionReusableView.swift b/iOS/WolneLektury/Screens/Filter/Cells/FilterSectionHeaderCollectionReusableView.swift
new file mode 100644 (file)
index 0000000..b6a314f
--- /dev/null
@@ -0,0 +1,31 @@
+//
+//  FilterSectionHeaderCollectionReusableView.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 12/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+protocol FilterSectionHeaderCollectionReusableViewDelegate: class {
+    func filterSectionRefreshButtonTapped(section: FilterSection)
+}
+
+class FilterSectionHeaderCollectionReusableView: UICollectionReusableView {
+    var delegate: FilterSectionHeaderCollectionReusableViewDelegate?
+
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var refreshButton: ActivityIndicatorButton!
+    var filterSection : FilterSection!
+    
+    func setup(filterSection: FilterSection, isDownloading: Bool){
+        self.filterSection = filterSection
+        titleLabel.text = filterSection.title
+        refreshButton.setIndicatorButtonState(state: isDownloading ?  .loading : .button)
+    }
+    
+    @IBAction func refreshButtonAction(_ sender: Any) {
+        delegate?.filterSectionRefreshButtonTapped(section: filterSection)
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Filter/Cells/FilterSectionHeaderCollectionReusableView.xib b/iOS/WolneLektury/Screens/Filter/Cells/FilterSectionHeaderCollectionReusableView.xib
new file mode 100644 (file)
index 0000000..6a28b7b
--- /dev/null
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="FilterSectionHeaderCollectionReusableView" id="O3K-ZT-8Gz" customClass="FilterSectionHeaderCollectionReusableView" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="179"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <subviews>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cce-r6-xvO">
+                    <rect key="frame" x="21" y="14.5" width="36" height="17"/>
+                    <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xuV-St-3U1">
+                    <rect key="frame" x="67" y="22.5" width="282" height="1"/>
+                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="1" id="TCi-fA-OFJ"/>
+                    </constraints>
+                </view>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vkW-mp-4ey" customClass="ActivityIndicatorButton" customModule="WolneLektury" customModuleProvider="target">
+                    <rect key="frame" x="162.5" y="77.5" width="50" height="50"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="50" id="SFs-ao-EN3"/>
+                        <constraint firstAttribute="height" constant="50" id="YhN-df-ef8"/>
+                    </constraints>
+                    <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <connections>
+                        <action selector="refreshButtonAction:" destination="O3K-ZT-8Gz" eventType="touchUpInside" id="EVS-di-VUe"/>
+                    </connections>
+                </button>
+            </subviews>
+            <constraints>
+                <constraint firstItem="xuV-St-3U1" firstAttribute="centerY" secondItem="cce-r6-xvO" secondAttribute="centerY" id="ErS-jk-nbT"/>
+                <constraint firstItem="xuV-St-3U1" firstAttribute="leading" secondItem="cce-r6-xvO" secondAttribute="trailing" constant="10" id="Gej-fQ-NHx"/>
+                <constraint firstItem="cce-r6-xvO" firstAttribute="leading" secondItem="O3K-ZT-8Gz" secondAttribute="leading" constant="21" id="QcB-dl-Gpc"/>
+                <constraint firstItem="vkW-mp-4ey" firstAttribute="top" secondItem="xuV-St-3U1" secondAttribute="bottom" constant="54" id="SqF-bK-Ga7"/>
+                <constraint firstItem="cce-r6-xvO" firstAttribute="top" secondItem="a2c-qx-dAX" secondAttribute="top" constant="14.5" id="UHB-i6-Up0"/>
+                <constraint firstItem="a2c-qx-dAX" firstAttribute="trailing" secondItem="xuV-St-3U1" secondAttribute="trailing" constant="26" id="ez5-Xw-aYX"/>
+                <constraint firstItem="vkW-mp-4ey" firstAttribute="centerX" secondItem="O3K-ZT-8Gz" secondAttribute="centerX" id="qwk-7A-od1"/>
+            </constraints>
+            <viewLayoutGuide key="safeArea" id="a2c-qx-dAX"/>
+            <connections>
+                <outlet property="refreshButton" destination="vkW-mp-4ey" id="rqK-IB-Yaf"/>
+                <outlet property="titleLabel" destination="cce-r6-xvO" id="Vvd-Bf-TGX"/>
+            </connections>
+            <point key="canvasLocation" x="25.5" y="-34.5"/>
+        </collectionReusableView>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/Filter/FilterHeaderView.swift b/iOS/WolneLektury/Screens/Filter/FilterHeaderView.swift
new file mode 100644 (file)
index 0000000..4fc02de
--- /dev/null
@@ -0,0 +1,14 @@
+//
+//  FilterHeaderView.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 12/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class FilterHeaderView: UIView {
+
+
+}
diff --git a/iOS/WolneLektury/Screens/Filter/FilterViewController.swift b/iOS/WolneLektury/Screens/Filter/FilterViewController.swift
new file mode 100644 (file)
index 0000000..dda7189
--- /dev/null
@@ -0,0 +1,370 @@
+//
+//  FilterViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Toast_Swift
+
+enum FilterSection: Int{
+    case onlyLectures = 0
+    case hasAudiobook
+    case epochs
+    case kinds
+    case genres
+    
+    var title: String{
+        return "filter_\(self)".localized.uppercased()
+    }
+    
+    var failedText: String{
+        return "load_\(self)_failed".localized
+    }
+}
+
+class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
+    
+    override init() {
+        super.init()
+        minimumLineSpacing = 0
+        
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
+        let attributes = super.layoutAttributesForElements(in: rect)
+        
+        var leftMargin = sectionInset.left
+        var maxY: CGFloat = -1.0
+        attributes?.forEach { layoutAttribute in
+            
+            if(layoutAttribute.representedElementCategory == .cell){
+                if layoutAttribute.frame.origin.y >= maxY {
+                    leftMargin = sectionInset.left
+                }
+                
+                layoutAttribute.frame.origin.x = leftMargin
+                
+                leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing
+                maxY = max(layoutAttribute.frame.maxY , maxY)
+            }
+        }
+        return attributes
+    }
+}
+
+protocol FilterViewControllerDelegate: class {
+    func filterViewControllerDidSelectItems(kindsArray: [CategoryModel], epochsArray: [CategoryModel], genresArray: [CategoryModel], onlyLectures: Bool, hasAudiobook: Bool, filterChanged: Bool)
+}
+
+class FilterViewController: WLViewController {
+    
+    class func instance(delegate: FilterViewControllerDelegate, selectedKindsArray: [CategoryModel], selectedEpochsArray: [CategoryModel], selectedGenresArray: [CategoryModel], onlyLectures: Bool, hasAudiobook: Bool) -> FilterViewController{
+        let controller = FilterViewController.instance()
+        controller.initialSelectedKindsArray = selectedKindsArray
+        controller.initialSelectedEpochsArray = selectedEpochsArray
+        controller.initialSelectedGenresArray = selectedGenresArray
+        controller.onlyLectures = onlyLectures
+        controller.hasAudiobook = hasAudiobook
+        controller.delegate = delegate
+        return controller
+    }
+    
+    weak var delegate: FilterViewControllerDelegate!
+    @IBOutlet weak var collectionView: UICollectionView!
+
+    var initialSelectedKindsArray: [CategoryModel]!
+    var initialSelectedEpochsArray: [CategoryModel]!
+    var initialSelectedGenresArray: [CategoryModel]!
+
+    var kindsArray: [CategoryModel]?
+    var epochsArray: [CategoryModel]?
+    var genresArray: [CategoryModel]?
+    
+    var kindsSection: FilterSectionHeaderCollectionReusableView?
+    var epochsSection: FilterSectionHeaderCollectionReusableView?
+    var genresSection: FilterSectionHeaderCollectionReusableView?
+    
+    var isKindsDownloading = false
+    var isEpochsDownloading = false
+    var isGenresDownloading = false
+    
+    var filterChanged = false
+    var onlyLectures = false
+    var hasAudiobook = false
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        title = "filters".localized
+        
+        setupCollectionView()
+        
+        getData(filterSection: .epochs)
+        getData(filterSection: .kinds)
+        getData(filterSection: .genres)
+    }
+    
+    @IBAction func closeAction(_ sender: Any) {
+        navigationController?.dismiss(animated: true, completion: nil)
+    }
+    
+    @IBAction func confirmAction(_ sender: Any) {
+        delegate.filterViewControllerDidSelectItems(kindsArray: kindsArray?.filter({$0.checked == true}) ?? [CategoryModel](), epochsArray: epochsArray?.filter({$0.checked == true}) ?? [CategoryModel](), genresArray: genresArray?.filter({$0.checked == true}) ?? [CategoryModel](), onlyLectures: onlyLectures, hasAudiobook: hasAudiobook, filterChanged: filterChanged)
+    }
+    
+    func getSection(filterSection: FilterSection) -> FilterSectionHeaderCollectionReusableView? {
+        switch filterSection {
+        case .epochs:
+            return epochsSection
+        case .genres:
+            return genresSection
+        case .kinds:
+            return kindsSection
+        default:
+            return nil
+        }
+    }
+    
+    func setDataSource(filterSection:FilterSection, dSource: [CategoryModel]?) {
+        switch filterSection {
+        case .epochs:
+            if let arr = dSource{
+                for obj in initialSelectedEpochsArray{
+                    arr.first(where: {$0.slug == obj.slug})?.checked = true
+                }
+            }
+            epochsArray = dSource
+        case .genres:
+            if let arr = dSource{
+                for obj in initialSelectedGenresArray{
+                    arr.first(where: {$0.slug == obj.slug})?.checked = true
+                }
+            }
+            genresArray = dSource
+        case .kinds:
+            if let arr = dSource{
+                for obj in initialSelectedKindsArray{
+                    arr.first(where: {$0.slug == obj.slug})?.checked = true
+                }
+            }
+            kindsArray = dSource
+        default:
+            break
+        }
+    }
+    
+    func setIsDownloading(isDownloading: Bool, section: FilterSection){
+        switch section {
+        case .epochs:
+            isEpochsDownloading = isDownloading
+        case .genres:
+            isGenresDownloading = isDownloading
+        case .kinds:
+            isKindsDownloading = isDownloading
+        default:
+            break
+        }
+    }
+
+    func getIsDownloading(section: FilterSection) -> Bool{
+        switch section {
+        case .epochs:
+            return isEpochsDownloading
+        case .genres:
+            return isGenresDownloading
+        case .kinds:
+            return isKindsDownloading
+        default:
+            return false
+        }
+    }
+
+    func getData(filterSection: FilterSection){
+        
+        getSection(filterSection: filterSection)?.refreshButton.setIndicatorButtonState(state: .loading)
+        setIsDownloading(isDownloading: true, section: filterSection)
+        
+        syncManager.getCategories(filterSection: filterSection, bookOnly: true) { [weak self] (result) in
+            self?.setIsDownloading(isDownloading: false, section: filterSection)
+            switch result {
+            case .success(let model):
+                self?.getSection(filterSection: filterSection)?.refreshButton.setIndicatorButtonState(state: .hidden)
+                self?.setDataSource(filterSection: filterSection, dSource: model as? [CategoryModel])
+                self?.collectionView.reloadSections([filterSection.rawValue])
+            case .failure/*(let *error)*/:
+                self?.getSection(filterSection: filterSection)?.refreshButton.setIndicatorButtonState(state: .button)
+                self?.view.makeToast(filterSection.failedText, duration: 3.0, position: .bottom)
+            }
+        }
+    }
+    
+    func setupCollectionView(){
+        
+        collectionView.backgroundColor = UIColor.clear
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.register(UINib.init(nibName: "FilterCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "FilterCollectionViewCell")
+        collectionView.register(UINib.init(nibName: "FilterSectionHeaderCollectionReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "FilterSectionHeaderCollectionReusableView")
+        collectionView.register(UINib.init(nibName: "FilterOnlyLecturesReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "FilterOnlyLecturesReusableView")
+        
+        if #available(iOS 11.0, *) {
+            collectionView.contentInsetAdjustmentBehavior = .always
+        }
+        
+        collectionView.setCollectionViewLayout(LeftAlignedCollectionViewFlowLayout(), animated: true)
+    }
+    
+    func getArrayForSection(filterSection: FilterSection) -> [CategoryModel]?{
+        switch filterSection{
+        case .onlyLectures, .hasAudiobook:
+            return nil
+        case .epochs:
+            return epochsArray
+        case .genres:
+            return genresArray
+        case .kinds:
+            return kindsArray
+        }
+    }
+    
+    func numberOfRowsInSection(filterSection: FilterSection) -> Int{
+        return getArrayForSection(filterSection: filterSection)?.count ?? 0
+    }
+    
+    func clearSectionHeaderReference(section: FilterSectionHeaderCollectionReusableView) {
+        if genresSection == section{
+            genresSection = nil
+        }
+        if epochsSection == section{
+            epochsSection = nil
+        }
+        if kindsSection == section{
+            kindsSection = nil
+        }
+    }
+}
+
+extension FilterViewController: UICollectionViewDelegateFlowLayout{
+   
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+        
+        if let items = getArrayForSection(filterSection: FilterSection(rawValue: indexPath.section)!){
+            let item = items[indexPath.row]
+            return CGSize(width: item.name.uppercased().width(withConstrainedHeight: 20, font: UIFont.systemFont(ofSize: 14, weight: .medium)) + 40, height: 30)
+        }
+        return CGSize(width: 50, height: 30)
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
+        
+        if indexPath.section == 0{
+            let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FilterOnlyLecturesReusableView", for: indexPath) as? FilterOnlyLecturesReusableView
+            sectionHeader?.delegate = self
+            sectionHeader?.onSwitch.isOn = onlyLectures
+            sectionHeader?.setup(isAudiobook: false)
+            return sectionHeader!
+        }
+        if indexPath.section == 1{
+            let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FilterOnlyLecturesReusableView", for: indexPath) as? FilterOnlyLecturesReusableView
+            sectionHeader?.delegate = self
+            sectionHeader?.onSwitch.isOn = hasAudiobook
+            sectionHeader?.setup(isAudiobook: true)
+            return sectionHeader!
+        }
+
+        else{
+            let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FilterSectionHeaderCollectionReusableView", for: indexPath) as! FilterSectionHeaderCollectionReusableView
+            clearSectionHeaderReference(section: sectionHeader)
+            
+            let filterSection = FilterSection(rawValue: indexPath.section)!
+            sectionHeader.setup(filterSection: filterSection, isDownloading: getIsDownloading(section: filterSection))
+            sectionHeader.delegate = self
+
+            if indexPath.section == FilterSection.epochs.rawValue{
+                epochsSection = sectionHeader
+            }
+            else if indexPath.section == FilterSection.genres.rawValue{
+                genresSection = sectionHeader
+            }
+            else if indexPath.section == FilterSection.kinds.rawValue{
+                kindsSection = sectionHeader
+            }
+            return sectionHeader
+        }
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
+        
+        if section == 0 || section == 1{
+            return CGSize(width: UIScreen.main.bounds.width, height: 44)
+        }
+        else{
+            if getArrayForSection(filterSection: FilterSection(rawValue: section)!) != nil{
+                return CGSize(width: UIScreen.main.bounds.width, height: 44)
+            }
+            else{
+                return CGSize(width: UIScreen.main.bounds.width, height: 180)
+            }
+        }
+    }
+}
+
+extension FilterViewController: UICollectionViewDataSource{ //UICollectionViewDataSource
+    
+    func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 5
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
+        return numberOfRowsInSection(filterSection: FilterSection(rawValue: section)!)
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
+        
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCollectionViewCell", for: indexPath) as! FilterCollectionViewCell
+        
+        if let items = getArrayForSection(filterSection: FilterSection(rawValue: indexPath.section)!){
+            cell.setup(categoryModel: items[indexPath.row])
+        }
+        return cell
+    }
+}
+
+extension FilterViewController: UICollectionViewDelegate{
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        
+        if let items = getArrayForSection(filterSection: FilterSection(rawValue: indexPath.section)!), items.count > indexPath.row{
+            filterChanged = true
+            let item = items[indexPath.row]
+            item.checked = !item.checked
+            if let cell = collectionView.cellForItem(at: indexPath) as? FilterCollectionViewCell{
+                cell.setChecked(value: item.checked)
+            }
+        }
+    }
+}
+
+extension FilterViewController: FilterOnlyLecturesReusableViewDelegate{
+    func filterOnlyLecturesReusableViewSwitchValueChanged(value: Bool, isAudiobook: Bool){
+        if isAudiobook {
+            hasAudiobook = value
+        }
+        else {
+            onlyLectures = value
+        }
+        filterChanged = true
+    }
+}
+
+extension FilterViewController: FilterSectionHeaderCollectionReusableViewDelegate{
+    func filterSectionRefreshButtonTapped(section: FilterSection){
+        getData(filterSection: section)
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Library/Cells/BecomeFriendTableViewCell.swift b/iOS/WolneLektury/Screens/Library/Cells/BecomeFriendTableViewCell.swift
new file mode 100644 (file)
index 0000000..8a2fa8f
--- /dev/null
@@ -0,0 +1,43 @@
+//
+//  BecomeFriendTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 31/08/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+protocol BecomeFriendTableViewCellDelegate: class{
+    func becomeFriendTableViewCellTapped()
+}
+
+class BecomeFriendTableViewCell: WLTableViewCell {
+
+    @IBOutlet weak var arrowImageView: UIImageView!
+    @IBOutlet weak var button: UIButton!
+    weak var delegate: BecomeFriendTableViewCellDelegate?
+    
+    class func instance(delegate: BecomeFriendTableViewCellDelegate?) -> BecomeFriendTableViewCell{
+        let cell = BecomeFriendTableViewCell.instance(type: BecomeFriendTableViewCell.self)
+        cell.delegate = delegate
+        return cell
+    }
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        button.layer.cornerRadius = 15
+        button.text = "support_us".localized.uppercased()
+        arrowImageView.tintColor = UIColor.white
+        contentView.backgroundColor = UIColor.white
+    }
+    
+    override func getHeight() -> CGFloat {
+        
+        return Constants.donateEnabled ? 40 : 44
+    }
+    
+    @IBAction func buttonAction(_ sender: Any) {
+        delegate?.becomeFriendTableViewCellTapped()
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Library/Cells/BecomeFriendTableViewCell.xib b/iOS/WolneLektury/Screens/Library/Cells/BecomeFriendTableViewCell.xib
new file mode 100644 (file)
index 0000000..24a0229
--- /dev/null
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="49" id="KGk-i7-Jjw" customClass="BecomeFriendTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="48.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aku-c8-PMi">
+                        <rect key="frame" x="14" y="24" width="168" height="1"/>
+                        <color key="backgroundColor" red="1" green="0.64313725489999995" blue="0.1843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="1" id="wdg-r6-MAU"/>
+                        </constraints>
+                    </view>
+                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q32-ON-y8J">
+                        <rect key="frame" x="188" y="9.5" width="117" height="30"/>
+                        <color key="backgroundColor" red="1" green="0.64313725489999995" blue="0.1843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="30" id="E6B-s7-Gup"/>
+                        </constraints>
+                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="11"/>
+                        <inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="20" maxY="0.0"/>
+                        <state key="normal" title="WESPRZYJ NAS"/>
+                        <connections>
+                            <action selector="buttonAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="UqV-gn-cRh"/>
+                        </connections>
+                    </button>
+                    <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrrow_right" translatesAutoresizingMaskIntoConstraints="NO" id="W49-hs-SFj">
+                        <rect key="frame" x="286" y="19" width="12" height="12"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="12" id="20K-aT-mQF"/>
+                            <constraint firstAttribute="height" constant="12" id="j7l-tG-vx8"/>
+                        </constraints>
+                    </imageView>
+                </subviews>
+                <color key="backgroundColor" red="0.0" green="0.4039215686" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                <constraints>
+                    <constraint firstItem="W49-hs-SFj" firstAttribute="centerY" secondItem="q32-ON-y8J" secondAttribute="centerY" id="0Hy-ck-EDu"/>
+                    <constraint firstItem="q32-ON-y8J" firstAttribute="leading" secondItem="aku-c8-PMi" secondAttribute="trailing" constant="6" id="Ngu-MK-Vap"/>
+                    <constraint firstItem="q32-ON-y8J" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="9.5" id="UXY-aH-TWF"/>
+                    <constraint firstItem="aku-c8-PMi" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="14" id="i5q-eo-K0t"/>
+                    <constraint firstAttribute="trailing" secondItem="q32-ON-y8J" secondAttribute="trailing" constant="15" id="k92-qY-InC"/>
+                    <constraint firstAttribute="trailingMargin" secondItem="W49-hs-SFj" secondAttribute="trailing" constant="6" id="uSW-xa-FlK"/>
+                    <constraint firstItem="aku-c8-PMi" firstAttribute="centerY" secondItem="q32-ON-y8J" secondAttribute="centerY" id="vi7-wc-X8e"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="arrowImageView" destination="W49-hs-SFj" id="Im5-gg-V7i"/>
+                <outlet property="button" destination="q32-ON-y8J" id="Cby-W2-MIP"/>
+            </connections>
+            <point key="canvasLocation" x="26" y="57.5"/>
+        </tableViewCell>
+    </objects>
+    <resources>
+        <image name="arrrow_right" width="7" height="12"/>
+    </resources>
+</document>
diff --git a/iOS/WolneLektury/Screens/Library/Cells/BookCollectionViewCell.swift b/iOS/WolneLektury/Screens/Library/Cells/BookCollectionViewCell.swift
new file mode 100644 (file)
index 0000000..060d3ef
--- /dev/null
@@ -0,0 +1,41 @@
+//
+//  BookCollectionViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 18/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Kingfisher
+
+class BookCollectionViewCell: UICollectionViewCell {
+
+    @IBOutlet weak var bgView: UIView!
+    @IBOutlet weak var coverImageView: UIImageView!
+    @IBOutlet weak var overlayView: BookImageOverlayView!
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        bgView.layer.cornerRadius = 5
+    }
+    
+    func setup(bookModel: BookModel){
+
+        coverImageView.kf.cancelDownloadTask()
+        coverImageView.image = #imageLiteral(resourceName: "list_nocover")
+        
+        if let url = bookModel.getCoverThumbUrl(){
+            
+            coverImageView.kf.setImage(with: ImageResource(downloadURL: url),
+                                       placeholder: #imageLiteral(resourceName: "list_nocover"),
+                                       options: [.transition(.fade(1))],
+                                       progressBlock: nil,
+                                       completionHandler: { (image, error, cacheType, url) in
+            })
+        }
+        overlayView.setup(bookModel: bookModel)
+    }
+    
+    
+}
diff --git a/iOS/WolneLektury/Screens/Library/Cells/BookCollectionViewCell.xib b/iOS/WolneLektury/Screens/Library/Cells/BookCollectionViewCell.xib
new file mode 100644 (file)
index 0000000..87cb964
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="BookCollectionViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="120" height="177"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                <rect key="frame" x="0.0" y="0.0" width="120" height="177"/>
+                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                <subviews>
+                    <view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WdZ-Zj-o2C">
+                        <rect key="frame" x="0.0" y="0.0" width="120" height="177"/>
+                        <subviews>
+                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Szm-VA-cOa">
+                                <rect key="frame" x="0.0" y="0.0" width="120" height="177"/>
+                            </imageView>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ANs-UY-S9s" customClass="BookImageOverlayView" customModule="WolneLektury" customModuleProvider="target">
+                                <rect key="frame" x="0.0" y="0.0" width="120" height="177"/>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            </view>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="ANs-UY-S9s" firstAttribute="top" secondItem="WdZ-Zj-o2C" secondAttribute="top" id="4V4-ks-5DN"/>
+                            <constraint firstItem="Szm-VA-cOa" firstAttribute="top" secondItem="WdZ-Zj-o2C" secondAttribute="top" id="BBq-Jz-nhP"/>
+                            <constraint firstItem="Szm-VA-cOa" firstAttribute="leading" secondItem="WdZ-Zj-o2C" secondAttribute="leading" id="IgI-63-NmP"/>
+                            <constraint firstItem="ANs-UY-S9s" firstAttribute="leading" secondItem="WdZ-Zj-o2C" secondAttribute="leading" id="JhP-0Q-bxs"/>
+                            <constraint firstAttribute="bottom" secondItem="ANs-UY-S9s" secondAttribute="bottom" id="Lqk-wk-M0L"/>
+                            <constraint firstAttribute="trailing" secondItem="ANs-UY-S9s" secondAttribute="trailing" id="acf-5r-Rq0"/>
+                            <constraint firstAttribute="trailing" secondItem="Szm-VA-cOa" secondAttribute="trailing" id="caQ-KN-YeF"/>
+                            <constraint firstAttribute="bottom" secondItem="Szm-VA-cOa" secondAttribute="bottom" id="qt4-vM-fJB"/>
+                        </constraints>
+                    </view>
+                </subviews>
+            </view>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="WdZ-Zj-o2C" secondAttribute="trailing" id="QBE-gO-ugl"/>
+                <constraint firstItem="WdZ-Zj-o2C" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="SEv-HY-8oh"/>
+                <constraint firstAttribute="bottom" secondItem="WdZ-Zj-o2C" secondAttribute="bottom" id="awn-OT-5yB"/>
+                <constraint firstItem="WdZ-Zj-o2C" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="ff7-iD-yeX"/>
+            </constraints>
+            <viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
+            <connections>
+                <outlet property="bgView" destination="WdZ-Zj-o2C" id="bj0-kK-F7M"/>
+                <outlet property="coverImageView" destination="Szm-VA-cOa" id="d4A-Kf-4z1"/>
+                <outlet property="overlayView" destination="ANs-UY-S9s" id="Ov9-Sx-B4k"/>
+            </connections>
+            <point key="canvasLocation" x="34" y="134.5"/>
+        </collectionViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/Library/Cells/LibraryCollectionTableViewCell.swift b/iOS/WolneLektury/Screens/Library/Cells/LibraryCollectionTableViewCell.swift
new file mode 100644 (file)
index 0000000..922aab4
--- /dev/null
@@ -0,0 +1,119 @@
+//
+//  LibraryCollectionTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 18/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+enum LibraryCollectionType{
+    case reading_now
+    case newest
+    case recommended
+    
+    var title: String{
+        return "\(self)".localized.uppercased()
+    }
+    
+    var noDataTitle: String{
+        switch self {
+        case .reading_now:
+            return "read_now_library_empty".localized
+        default:
+            return ""
+        }
+    }
+}
+
+protocol LibraryCollectionTableViewCellDelegate: class {
+    func libraryCollectionTableViewCellDelegateRefreshButtonTapped(collectionViewType: LibraryCollectionType)
+    func libraryCollectionTableViewCellDelegateShowAllButtonTapped(collectionViewType: LibraryCollectionType)
+    func libraryCollectionTableViewCellDelegateDidSelect(bookModel: BookModel)
+
+}
+
+class LibraryCollectionTableViewCell: WLTableViewCell {
+    var delegate: LibraryCollectionTableViewCellDelegate?
+    
+    class func instance(libraryCollectionType: LibraryCollectionType) -> LibraryCollectionTableViewCell{
+        let cell = LibraryCollectionTableViewCell.instance(type: LibraryCollectionTableViewCell.self)
+        cell.libraryCollectionType = libraryCollectionType
+        cell.titleLabel.text = libraryCollectionType.title
+        cell.noDataLabel.text = libraryCollectionType.noDataTitle
+        return cell
+    }
+    
+    var libraryCollectionType: LibraryCollectionType!
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var showAllButton: UIButton!
+    @IBOutlet weak var collectionView: UICollectionView!
+    @IBOutlet weak var refreshButton: ActivityIndicatorButton!
+    @IBOutlet weak var showAllArrowImageView: UIImageView!
+    @IBOutlet weak var noDataLabel: UILabel!
+    
+    var dataSource = [BookModel]()
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        showAllButton.text = "see_all".localized
+        
+        collectionView.register(UINib(nibName: "BookCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "BookCollectionViewCell")
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        showAllArrowImageView.tintColor = Constants.Colors.grayTextColor()
+        noDataLabel.isHidden = true
+    }
+    
+    @IBAction func refreshButtonAction(_ sender: Any) {
+        delegate?.libraryCollectionTableViewCellDelegateRefreshButtonTapped(collectionViewType: libraryCollectionType)
+    }
+    
+    @IBAction func showAllButtonAction(_ sender: Any) {
+        delegate?.libraryCollectionTableViewCellDelegateShowAllButtonTapped(collectionViewType: libraryCollectionType)
+    }
+    
+    func setup(state: ActivityIndicatorButtonState, dataSource: [BookModel]?) {
+        
+        refreshButton.setIndicatorButtonState(state: state)
+        switch state{
+        case .hidden:
+            self.dataSource = dataSource ?? [BookModel]()
+            collectionView.reloadData()
+            collectionView.isHidden = false
+            noDataLabel.isHidden = self.dataSource.count > 0
+        case .button, .loading:
+            collectionView.isHidden = true
+        }
+    }
+    
+    override func getHeight() -> CGFloat {
+        return 221
+    }
+}
+
+extension LibraryCollectionTableViewCell: UICollectionViewDataSource{
+    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
+        
+        return dataSource.count
+    }
+    
+    // The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
+    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
+        
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BookCollectionViewCell", for: indexPath) as! BookCollectionViewCell
+        
+        cell.setup(bookModel: dataSource[indexPath.row])
+        
+        return cell
+    }
+}
+
+extension LibraryCollectionTableViewCell: UICollectionViewDelegate{
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        delegate?.libraryCollectionTableViewCellDelegateDidSelect(bookModel: dataSource[indexPath.row])
+    }
+}
+
+
diff --git a/iOS/WolneLektury/Screens/Library/Cells/LibraryCollectionTableViewCell.xib b/iOS/WolneLektury/Screens/Library/Cells/LibraryCollectionTableViewCell.xib
new file mode 100644 (file)
index 0000000..05de33c
--- /dev/null
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="LibraryCollectionTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="221"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="220.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsVerticalScrollIndicator="NO" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="DeU-Rq-JUf">
+                        <rect key="frame" x="0.0" y="30" width="319.5" height="181"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="181" id="rph-GB-uTa"/>
+                        </constraints>
+                        <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="14" minimumInteritemSpacing="14" id="kpk-kg-4K9">
+                            <size key="itemSize" width="120" height="177"/>
+                            <size key="headerReferenceSize" width="0.0" height="0.0"/>
+                            <size key="footerReferenceSize" width="0.0" height="0.0"/>
+                            <inset key="sectionInset" minX="13" minY="0.0" maxX="13" maxY="0.0"/>
+                        </collectionViewFlowLayout>
+                    </collectionView>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Zacznij czytać" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="i3E-Sg-8vO">
+                        <rect key="frame" x="16" y="114.5" width="288" height="12"/>
+                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="10"/>
+                        <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="TERAZ CZYTAM" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lfX-gv-Wlm">
+                        <rect key="frame" x="13" y="8" width="79.5" height="12"/>
+                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="10"/>
+                        <color key="textColor" red="0.32156862745098036" green="0.32156862745098036" blue="0.32156862745098036" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iJt-1p-QVF" customClass="ActivityIndicatorButton" customModule="WolneLektury" customModuleProvider="target">
+                        <rect key="frame" x="135" y="95.5" width="50" height="50"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="50" id="DNU-Dw-myo"/>
+                            <constraint firstAttribute="height" constant="50" id="tKJ-wR-83m"/>
+                        </constraints>
+                        <color key="tintColor" red="0.0" green="0.4039215686" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <connections>
+                            <action selector="refreshButtonAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="cTL-fq-c9v"/>
+                        </connections>
+                    </button>
+                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZiP-Xp-Dpt">
+                        <rect key="frame" x="187" y="0.0" width="119" height="28"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="28" id="XGQ-6b-Tcv"/>
+                        </constraints>
+                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="9"/>
+                        <inset key="contentEdgeInsets" minX="0.0" minY="0.0" maxX="15" maxY="0.0"/>
+                        <state key="normal" title="ZOBACZ WSZYSTKIE &gt;">
+                            <color key="titleColor" red="0.32156862745098036" green="0.32156862745098036" blue="0.32156862745098036" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        </state>
+                        <connections>
+                            <action selector="showAllButtonAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="XB0-78-4rB"/>
+                        </connections>
+                    </button>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Lwc-dI-BDx">
+                        <rect key="frame" x="98.5" y="13.5" width="82.5" height="1"/>
+                        <color key="backgroundColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="1" id="pIz-e1-mme"/>
+                        </constraints>
+                    </view>
+                    <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrrow_right" translatesAutoresizingMaskIntoConstraints="NO" id="UQL-Up-OFn">
+                        <rect key="frame" x="294" y="8" width="12" height="12"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="12" id="two-uf-teq"/>
+                            <constraint firstAttribute="height" constant="12" id="vpC-ls-coh"/>
+                        </constraints>
+                    </imageView>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="DeU-Rq-JUf" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="3wK-wl-h6I"/>
+                    <constraint firstAttribute="trailing" secondItem="ZiP-Xp-Dpt" secondAttribute="trailing" constant="14" id="FjL-Ht-cJv"/>
+                    <constraint firstItem="i3E-Sg-8vO" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="HQu-N7-EJJ"/>
+                    <constraint firstItem="Lwc-dI-BDx" firstAttribute="leading" secondItem="lfX-gv-Wlm" secondAttribute="trailing" constant="6" id="JMr-7w-iVg"/>
+                    <constraint firstItem="lfX-gv-Wlm" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="13" id="M6g-wJ-OCs"/>
+                    <constraint firstItem="iJt-1p-QVF" firstAttribute="centerY" secondItem="DeU-Rq-JUf" secondAttribute="centerY" id="M8p-Ix-j4H"/>
+                    <constraint firstItem="UQL-Up-OFn" firstAttribute="trailing" secondItem="ZiP-Xp-Dpt" secondAttribute="trailing" id="O3I-ZF-cHe"/>
+                    <constraint firstItem="ZiP-Xp-Dpt" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="V5c-50-63W"/>
+                    <constraint firstItem="DeU-Rq-JUf" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="30" id="YiI-rO-4Ta"/>
+                    <constraint firstItem="lfX-gv-Wlm" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="8" id="bsl-Wg-ifx"/>
+                    <constraint firstItem="i3E-Sg-8vO" firstAttribute="centerX" secondItem="DeU-Rq-JUf" secondAttribute="centerX" id="dPF-VC-cO3"/>
+                    <constraint firstItem="UQL-Up-OFn" firstAttribute="centerY" secondItem="ZiP-Xp-Dpt" secondAttribute="centerY" id="dhr-FN-Pza"/>
+                    <constraint firstAttribute="trailing" secondItem="i3E-Sg-8vO" secondAttribute="trailing" constant="16" id="e6u-7a-owQ"/>
+                    <constraint firstItem="Lwc-dI-BDx" firstAttribute="centerY" secondItem="lfX-gv-Wlm" secondAttribute="centerY" id="gem-pM-BLG"/>
+                    <constraint firstItem="ZiP-Xp-Dpt" firstAttribute="leading" secondItem="Lwc-dI-BDx" secondAttribute="trailing" constant="6" id="lCS-4D-Swv"/>
+                    <constraint firstItem="iJt-1p-QVF" firstAttribute="centerX" secondItem="DeU-Rq-JUf" secondAttribute="centerX" id="vgt-Hg-raP"/>
+                    <constraint firstAttribute="trailing" secondItem="DeU-Rq-JUf" secondAttribute="trailing" id="vsS-gS-26c"/>
+                    <constraint firstItem="i3E-Sg-8vO" firstAttribute="centerY" secondItem="DeU-Rq-JUf" secondAttribute="centerY" id="wHe-ha-ecF"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="collectionView" destination="DeU-Rq-JUf" id="Acc-cL-bfy"/>
+                <outlet property="noDataLabel" destination="i3E-Sg-8vO" id="xlG-lz-xwX"/>
+                <outlet property="refreshButton" destination="iJt-1p-QVF" id="iyM-cF-agr"/>
+                <outlet property="showAllArrowImageView" destination="UQL-Up-OFn" id="Dun-rW-9qV"/>
+                <outlet property="showAllButton" destination="ZiP-Xp-Dpt" id="0NW-fT-qFn"/>
+                <outlet property="titleLabel" destination="lfX-gv-Wlm" id="yx8-MJ-hXC"/>
+            </connections>
+            <point key="canvasLocation" x="26" y="51.5"/>
+        </tableViewCell>
+    </objects>
+    <resources>
+        <image name="arrrow_right" width="7" height="12"/>
+    </resources>
+</document>
diff --git a/iOS/WolneLektury/Screens/Library/Cells/LibraryEarlyAccessTableViewCell.swift b/iOS/WolneLektury/Screens/Library/Cells/LibraryEarlyAccessTableViewCell.swift
new file mode 100644 (file)
index 0000000..a879f23
--- /dev/null
@@ -0,0 +1,84 @@
+//
+//  LibraryEarlyAccessTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 18/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Kingfisher
+
+protocol LibraryEarlyAccessTableViewCellDelegate: class {
+    func libraryEarlyAccessTableViewCellRefreshButtonTapped()
+}
+
+class LibraryEarlyAccessTableViewCell: WLTableViewCell {
+    var delegate: LibraryEarlyAccessTableViewCellDelegate?
+
+    class func instance() -> LibraryEarlyAccessTableViewCell{
+        let cell = LibraryEarlyAccessTableViewCell.instance(type: LibraryEarlyAccessTableViewCell.self)
+        return cell
+    }
+    
+    @IBOutlet weak var containerView: UIView!
+    @IBOutlet weak var bookDescriptionView: BookDescriptionView!
+    @IBOutlet weak var miniatureImageView: UIImageView!
+    @IBOutlet weak var miniatureOverlayView: BookImageOverlayView!
+    @IBOutlet weak var miniatureBgView: UIView!
+    @IBOutlet weak var refreshButton: ActivityIndicatorButton!
+    var book: BookModel?
+    @IBOutlet weak var noPremiumBookLabel: UILabel!
+    private var height: CGFloat = 199
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        miniatureBgView.layer.cornerRadius = 5
+        noPremiumBookLabel.text = "library_empty_header".localized
+    }
+    
+    func setup(state: ActivityIndicatorButtonState, bookModel: BookModel?) {
+        
+        self.book = bookModel
+        
+        noPremiumBookLabel.isHidden = true
+        containerView.isHidden = true
+        refreshButton.setIndicatorButtonState(state: state)
+        height = 199
+        switch state{
+        case .hidden:
+            if let book = self.book {
+                containerView.isHidden = false
+                bookDescriptionView.setup(bookModel: book, isPremium: true)
+                miniatureOverlayView.setup(bookModel: book)
+                miniatureImageView.kf.cancelDownloadTask()
+                miniatureImageView.image = #imageLiteral(resourceName: "list_nocover")
+                
+                if let url = book.getCoverThumbUrl() {
+                    
+                    miniatureImageView.kf.setImage(with: ImageResource(downloadURL: url),
+                                               placeholder: #imageLiteral(resourceName: "list_nocover"),
+                                               options: [.transition(.fade(1))],
+                                               progressBlock: nil,
+                                               completionHandler: { (image, error, cacheType, url) in
+                    })
+                }
+            }
+            else {
+                noPremiumBookLabel.isHidden = false
+                height = 100
+            }
+        case .button, .loading:
+            break
+        }
+    }
+    
+    @IBAction func refreshButtonAction(_ sender: Any) {
+        delegate?.libraryEarlyAccessTableViewCellRefreshButtonTapped()
+    }
+    
+    override func getHeight() -> CGFloat {
+
+        return height
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Library/Cells/LibraryEarlyAccessTableViewCell.xib b/iOS/WolneLektury/Screens/Library/Cells/LibraryEarlyAccessTableViewCell.xib
new file mode 100644 (file)
index 0000000..f1d4f15
--- /dev/null
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="203" id="KGk-i7-Jjw" customClass="LibraryEarlyAccessTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="203"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="202.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AaB-N9-zs0">
+                        <rect key="frame" x="0.0" y="0.0" width="320" height="193"/>
+                        <color key="backgroundColor" red="0.0" green="0.4039215686" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    </view>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Nys-P3-hdk">
+                        <rect key="frame" x="14" y="8" width="290" height="177"/>
+                        <subviews>
+                            <view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wUL-dS-jE6">
+                                <rect key="frame" x="0.0" y="0.0" width="120" height="177"/>
+                                <subviews>
+                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="B9G-fP-jBa">
+                                        <rect key="frame" x="0.0" y="0.0" width="120" height="177"/>
+                                    </imageView>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aAu-YM-v71" customClass="BookImageOverlayView" customModule="WolneLektury" customModuleProvider="target">
+                                        <rect key="frame" x="0.0" y="0.0" width="120" height="177"/>
+                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                    </view>
+                                </subviews>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstAttribute="trailing" secondItem="B9G-fP-jBa" secondAttribute="trailing" id="1ZO-Ub-3jn"/>
+                                    <constraint firstAttribute="bottom" secondItem="B9G-fP-jBa" secondAttribute="bottom" id="1ll-Gu-G8V"/>
+                                    <constraint firstItem="B9G-fP-jBa" firstAttribute="leading" secondItem="wUL-dS-jE6" secondAttribute="leading" id="7MD-cS-S8l"/>
+                                    <constraint firstItem="aAu-YM-v71" firstAttribute="top" secondItem="wUL-dS-jE6" secondAttribute="top" id="Bm7-Wa-GCL"/>
+                                    <constraint firstAttribute="trailing" secondItem="aAu-YM-v71" secondAttribute="trailing" id="Jw7-bS-eCr"/>
+                                    <constraint firstAttribute="bottom" secondItem="aAu-YM-v71" secondAttribute="bottom" id="WHI-TD-GwL"/>
+                                    <constraint firstItem="aAu-YM-v71" firstAttribute="leading" secondItem="wUL-dS-jE6" secondAttribute="leading" id="Zhl-DQ-lHx"/>
+                                    <constraint firstAttribute="width" constant="120" id="dnz-Yl-hST"/>
+                                    <constraint firstItem="B9G-fP-jBa" firstAttribute="top" secondItem="wUL-dS-jE6" secondAttribute="top" id="zQg-cV-e6O"/>
+                                </constraints>
+                            </view>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="b3t-3h-4gU" customClass="BookDescriptionView" customModule="WolneLektury" customModuleProvider="target">
+                                <rect key="frame" x="136" y="0.0" width="154" height="177"/>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="177" placeholder="YES" id="0oB-7R-0r4"/>
+                                </constraints>
+                            </view>
+                        </subviews>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstAttribute="trailing" secondItem="b3t-3h-4gU" secondAttribute="trailing" id="Afn-cM-oMb"/>
+                            <constraint firstItem="wUL-dS-jE6" firstAttribute="leading" secondItem="Nys-P3-hdk" secondAttribute="leading" id="RWK-td-VXa"/>
+                            <constraint firstItem="wUL-dS-jE6" firstAttribute="top" secondItem="Nys-P3-hdk" secondAttribute="top" id="XMn-Sq-8KK"/>
+                            <constraint firstItem="b3t-3h-4gU" firstAttribute="height" relation="lessThanOrEqual" secondItem="aAu-YM-v71" secondAttribute="height" id="Y1u-m7-I8G"/>
+                            <constraint firstItem="b3t-3h-4gU" firstAttribute="centerY" secondItem="wUL-dS-jE6" secondAttribute="centerY" id="fAR-iF-8IU"/>
+                            <constraint firstItem="b3t-3h-4gU" firstAttribute="leading" secondItem="wUL-dS-jE6" secondAttribute="trailing" constant="16" id="lZ7-1O-yHu"/>
+                            <constraint firstAttribute="height" constant="177" id="lbg-5g-R32"/>
+                            <constraint firstAttribute="bottom" secondItem="wUL-dS-jE6" secondAttribute="bottom" id="mGc-ID-aqv"/>
+                        </constraints>
+                    </view>
+                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ivr-0B-PF4" customClass="ActivityIndicatorButton" customModule="WolneLektury" customModuleProvider="target">
+                        <rect key="frame" x="135" y="76.5" width="50" height="50"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="50" id="NoK-Nh-RrW"/>
+                            <constraint firstAttribute="height" constant="50" id="Rb4-dA-lqb"/>
+                        </constraints>
+                        <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    </button>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QJy-sj-07z">
+                        <rect key="frame" x="16" y="88" width="288" height="17"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                        <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                </subviews>
+                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                <constraints>
+                    <constraint firstItem="AaB-N9-zs0" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="0Nn-Ol-Z5r"/>
+                    <constraint firstAttribute="bottom" secondItem="AaB-N9-zs0" secondAttribute="bottom" constant="10" id="0c6-ZZ-SyO"/>
+                    <constraint firstItem="AaB-N9-zs0" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="IgI-xH-klX"/>
+                    <constraint firstItem="QJy-sj-07z" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="K8e-5g-N80"/>
+                    <constraint firstAttribute="trailing" secondItem="Nys-P3-hdk" secondAttribute="trailing" constant="16" id="SEQ-13-hSZ"/>
+                    <constraint firstAttribute="trailing" secondItem="QJy-sj-07z" secondAttribute="trailing" constant="16" id="YTB-vG-Udr"/>
+                    <constraint firstItem="Nys-P3-hdk" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="14" id="buh-Pk-gMX"/>
+                    <constraint firstItem="QJy-sj-07z" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" constant="-5" id="crL-vX-bGD"/>
+                    <constraint firstItem="ivr-0B-PF4" firstAttribute="centerX" secondItem="H2p-sc-9uM" secondAttribute="centerX" id="k9r-72-4rk"/>
+                    <constraint firstItem="Nys-P3-hdk" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="8" id="lvk-QE-9bi"/>
+                    <constraint firstAttribute="trailing" secondItem="AaB-N9-zs0" secondAttribute="trailing" id="shU-17-qyn"/>
+                    <constraint firstItem="ivr-0B-PF4" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="wdf-vg-ogu"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="bookDescriptionView" destination="b3t-3h-4gU" id="pOc-Sv-k8l"/>
+                <outlet property="containerView" destination="Nys-P3-hdk" id="5hN-4S-ZC8"/>
+                <outlet property="miniatureBgView" destination="wUL-dS-jE6" id="FoZ-sa-d9k"/>
+                <outlet property="miniatureImageView" destination="B9G-fP-jBa" id="gbs-YV-y4k"/>
+                <outlet property="miniatureOverlayView" destination="aAu-YM-v71" id="x78-Aa-t9F"/>
+                <outlet property="noPremiumBookLabel" destination="QJy-sj-07z" id="yuE-41-p4j"/>
+                <outlet property="refreshButton" destination="ivr-0B-PF4" id="7Lr-fW-7EK"/>
+            </connections>
+            <point key="canvasLocation" x="26" y="63.5"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/Library/LibraryViewController.swift b/iOS/WolneLektury/Screens/Library/LibraryViewController.swift
new file mode 100644 (file)
index 0000000..0787a6d
--- /dev/null
@@ -0,0 +1,262 @@
+//
+//  LibraryViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 13/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+
+class LibraryViewController: MainViewController {
+    enum LibraryCellState {
+        case not_loaded
+        case loading
+        case loaded
+    }
+    
+    @IBOutlet weak var tableView: UITableView!
+
+    var cellsArray = [WLTableViewCell]()
+    
+    let earlyAccessCell = LibraryEarlyAccessTableViewCell.instance()
+    let newestCell = LibraryCollectionTableViewCell.instance(libraryCollectionType: .newest)
+    let recommendedCell = LibraryCollectionTableViewCell.instance(libraryCollectionType: .recommended)
+    let readingNowCell = LibraryCollectionTableViewCell.instance(libraryCollectionType: .reading_now)
+
+    var earlyAccessCellState: LibraryCellState = .not_loaded
+    var newestCellState: LibraryCellState = .not_loaded
+    var recommendedCellState: LibraryCellState = .not_loaded
+    var readingCellState: LibraryCellState = .not_loaded
+    
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        title = "nav_library".localized
+        navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "navbar_search"), style: .plain, target: self, action: #selector(presentSearch))
+        
+        tableView.registerNib(name: "LibraryEarlyAccessTableViewCell")
+        tableView.registerNib(name: "LibraryCollectionTableViewCell")
+        tableView.registerNib(name: "BecomeFriendTableViewCell")
+        tableView.separatorStyle = .none
+        
+        if DatabaseManager.shared.isUserPremium() == false /* && Constants.donateEnabled*/{
+            cellsArray.append(BecomeFriendTableViewCell.instance(delegate: self))
+        }
+        
+        if Constants.donateEnabled {
+            earlyAccessCell.delegate = self
+            cellsArray.append(earlyAccessCell)
+        }
+        
+        if syncManager.isLoggedIn() {
+            readingNowCell.delegate = self
+            cellsArray.append(readingNowCell)
+        }
+        
+        newestCell.delegate = self
+        cellsArray.append(newestCell)
+
+        recommendedCell.delegate = self
+        cellsArray.append(recommendedCell)
+
+    }
+    
+    @objc func presentSearch() {
+        appDelegate.mainNavigator.presentSearch()
+    }
+    
+    func getDataFor(libraryCollectionType: LibraryCollectionType) {
+
+        var libraryCellState = LibraryCellState.not_loaded
+        switch libraryCollectionType {
+        case .reading_now:
+            libraryCellState = readingCellState
+        case .newest:
+            libraryCellState = newestCellState
+        case .recommended:
+            libraryCellState = recommendedCellState
+        }
+        
+        guard libraryCellState == .not_loaded else {
+            return
+        }
+        
+        switch libraryCollectionType {
+        case .reading_now:
+            readingCellState = .loading
+            readingNowCell.setup(state: .loading, dataSource: nil)
+        case .newest:
+            newestCellState = .loading
+            newestCell.setup(state: .loading, dataSource: nil)
+        case .recommended:
+            recommendedCellState = .loading
+            recommendedCell.setup(state: .loading, dataSource: nil)
+        }
+        
+        syncManager.getDataForLibrary(libraryCollectionType: libraryCollectionType) { [weak self] (result) in
+           
+            guard let strongSelf = self else { return }
+            
+            switch result {
+            case .success(let model):
+                
+                let array = model as! [BookModel]
+                
+                switch libraryCollectionType {
+                case .reading_now:
+                    strongSelf.readingCellState = .loaded
+                    strongSelf.readingNowCell.setup(state: .hidden, dataSource: array)
+                case .newest:
+                    strongSelf.newestCellState = .loaded
+                    strongSelf.newestCell.setup(state: .hidden, dataSource: array)
+                    strongSelf.tableView.reloadData()
+                    
+                case .recommended:
+                    strongSelf.recommendedCellState = .loaded
+                    strongSelf.recommendedCell.setup(state: .hidden, dataSource: array)
+                }
+                
+            case .failure(let error):
+                switch libraryCollectionType {
+                case .reading_now:
+                    strongSelf.readingCellState = .not_loaded
+                    strongSelf.readingNowCell.setup(state: .button, dataSource: nil)
+                case .newest:
+                    strongSelf.newestCellState = .not_loaded
+                    strongSelf.newestCell.setup(state: .button, dataSource: nil)
+                case .recommended:
+                    strongSelf.recommendedCellState = .not_loaded
+                    strongSelf.recommendedCell.setup(state: .button, dataSource: nil)
+                }
+            }
+        }
+    }
+    
+    func getPreview() {
+        guard earlyAccessCellState == .not_loaded else {
+            return
+        }
+        
+        if Constants.donateEnabled {
+            earlyAccessCellState = .loaded
+            earlyAccessCell.setup(state: .hidden, bookModel: nil)
+        }
+        
+        
+        
+        earlyAccessCellState = .loading
+        earlyAccessCell.setup(state: .loading, bookModel: nil)
+        
+        syncManager.getPreview { [weak self] (result) in
+            
+            guard let strongSelf = self else { return }
+            
+            switch result {
+            case .success(let model):
+                let array = model as! [BookModel]
+                strongSelf.earlyAccessCellState = .loaded
+                strongSelf.earlyAccessCell.setup(state: .hidden, bookModel: array.count > 0 ? array[0] : nil)
+            case .failure(let error):
+                strongSelf.earlyAccessCellState = .not_loaded
+                strongSelf.earlyAccessCell.setup(state: .button, bookModel: nil)
+            }
+            strongSelf.tableView.reloadData()
+        }
+    }
+}
+
+extension LibraryViewController: UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return cellsArray.count
+    }
+    
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        
+        return cellsArray[indexPath.row].getHeight()
+    }
+    
+    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
+        let cell = cellsArray[indexPath.row]
+        if cell == earlyAccessCell {
+            getPreview()
+        }
+        else if cell == newestCell {
+            getDataFor(libraryCollectionType: .newest)
+        }
+        else if cell == recommendedCell {
+            getDataFor(libraryCollectionType: .recommended)
+        }
+        else if cell == readingNowCell {
+            getDataFor(libraryCollectionType: .reading_now)
+        }
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+
+        return cellsArray[indexPath.row]
+    }
+}
+
+extension LibraryViewController: UITableViewDelegate {
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        if let cell = cellsArray[indexPath.row] as? LibraryEarlyAccessTableViewCell, earlyAccessCellState == .loaded {
+            if let book = cell.book {
+                let controller = BookDetailsViewController.instance(bookSlug: book.slug, isBookPremium: true)
+                navigationController?.pushViewController(controller, animated: true)
+            }
+            else {
+                // no action
+            }
+        }
+    }
+}
+
+extension LibraryViewController: LibraryEarlyAccessTableViewCellDelegate {
+    func libraryEarlyAccessTableViewCellRefreshButtonTapped() {
+        getPreview()
+    }
+}
+
+extension LibraryViewController: LibraryCollectionTableViewCellDelegate {
+    func libraryCollectionTableViewCellDelegateRefreshButtonTapped(collectionViewType: LibraryCollectionType) {
+        getDataFor(libraryCollectionType: collectionViewType)
+    }
+    
+    func libraryCollectionTableViewCellDelegateShowAllButtonTapped(collectionViewType: LibraryCollectionType) {
+        
+        var bookListViewControllerType: ListViewControllerType?
+        var dataSource: [BookModel]?
+        switch collectionViewType {
+        case .newest:
+            bookListViewControllerType = .newest
+            dataSource = newestCell.dataSource
+        case .reading_now:
+            bookListViewControllerType = .reading_now
+            dataSource = readingNowCell.dataSource
+        case .recommended:
+            bookListViewControllerType = .recommended
+            dataSource = recommendedCell.dataSource
+        }
+    
+        if let controllerType = bookListViewControllerType {
+            let controller = BookListViewController.instance(listViewControllerType: controllerType, dataSource: dataSource)
+            controller.leftBarButtonItemShouldOpenMenu = false
+            navigationController?.pushViewController(controller, animated: true)
+        }
+    }
+    
+    func libraryCollectionTableViewCellDelegateDidSelect(bookModel: BookModel) {
+        let controller = BookDetailsViewController.instance(bookSlug: bookModel.slug)
+        navigationController?.pushViewController(controller, animated: true)
+    }
+}
+
+extension LibraryViewController: BecomeFriendTableViewCellDelegate {
+    func becomeFriendTableViewCellTapped() {
+        
+        appDelegate.mainNavigator.presentSupportUs()
+    }
+}
+
diff --git a/iOS/WolneLektury/Screens/Login/LoginViewController.swift b/iOS/WolneLektury/Screens/Login/LoginViewController.swift
new file mode 100644 (file)
index 0000000..8f2e0b4
--- /dev/null
@@ -0,0 +1,45 @@
+//
+//  LoginViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 18/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class LoginViewController: WLViewController {
+
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var desc1: UILabel!
+    @IBOutlet weak var desc2: UILabel!
+    @IBOutlet weak var desc3: UILabel!
+    @IBOutlet weak var desc1Checkmark: UIImageView!
+    @IBOutlet weak var desc2Checkmark: UIImageView!
+    @IBOutlet weak var desc3Checkmark: UIImageView!
+
+    @IBOutlet weak var loginButton: UIButton!
+    
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        titleLabel.text = "login_title".localized
+        desc1.text = "login_benefits".localized
+        desc2.text = "login_benefits_2".localized
+        desc3.text = "login_benefits_3".localized
+        loginButton.text = "menu_login".localized.uppercased()
+        loginButton.layer.cornerRadius = 18
+        loginButton.layer.borderWidth = 1.0
+        loginButton.layer.borderColor = UIColor.white.cgColor
+        let color = Constants.Colors.orangeColor()
+        desc1Checkmark.tintColor = color
+        desc2Checkmark.tintColor = color
+        desc3Checkmark.tintColor = color
+
+    }
+    
+    @IBAction func loginButtonAction(_ sender: Any) {
+        appDelegate.login(fromViewController: self.navigationController!)
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Menu/Cells/MenuBottomTableViewCell.swift b/iOS/WolneLektury/Screens/Menu/Cells/MenuBottomTableViewCell.swift
new file mode 100644 (file)
index 0000000..9b64523
--- /dev/null
@@ -0,0 +1,57 @@
+//
+//  MenuBottomTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+protocol MenuBottomTableViewCellDelegate: class{
+    
+    func menuBottomProfileButtonTapped()
+    func menuBottomSignOutButtonButtonTapped()
+}
+
+class MenuBottomTableViewCell: WLTableViewCell {
+    weak var delegate: MenuBottomTableViewCellDelegate?
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var showProfileButton: UIButton!
+    @IBOutlet weak var signOutButton: UIButton!
+    
+    class func instance(delegate: MenuBottomTableViewCellDelegate?) -> MenuBottomTableViewCell{
+        let cell = MenuBottomTableViewCell.instance(type: MenuBottomTableViewCell.self)
+        cell.delegate = delegate
+        return cell
+    }
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        
+        showProfileButton.layer.cornerRadius = 13
+        showProfileButton.layer.borderColor = UIColor.white.cgColor
+        showProfileButton.layer.borderWidth = 1
+        
+        let titleText = NSMutableAttributedString(attributedString: NSAttributedString(string: "logged_as".localized + "\n", font: UIFont.systemFont(ofSize: 10)))
+        titleText.append(NSAttributedString(string: "Zuzanna Stańska", font: UIFont.systemFont(ofSize: 12)))
+        titleLabel.attributedText = titleText
+        showProfileButton.text = "show_profile".localized
+        signOutButton.text = "sign_out".localized
+        
+        let bgColor = Constants.Colors.menuTintColor()
+        backgroundColor = bgColor
+        contentView.backgroundColor = bgColor
+    }
+    
+    override func getHeight() -> CGFloat {
+        return 180
+    }
+    
+    @IBAction func signoutButtonAction(_ sender: Any) {
+        delegate?.menuBottomSignOutButtonButtonTapped()
+    }
+    
+    @IBAction func profileButtonAction(_ sender: Any) {
+        delegate?.menuBottomProfileButtonTapped()
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Menu/Cells/MenuBottomTableViewCell.xib b/iOS/WolneLektury/Screens/Menu/Cells/MenuBottomTableViewCell.xib
new file mode 100644 (file)
index 0000000..ddc5600
--- /dev/null
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="182" id="KGk-i7-Jjw" customClass="MenuBottomTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="180"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="179.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ZALOGOWANY JAKO" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OVD-fI-PiE">
+                        <rect key="frame" x="109.5" y="16" width="102" height="12"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                        <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Cdu-3X-GgU">
+                        <rect key="frame" x="98.5" y="66" width="123" height="26"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="123" id="9mh-MX-sWe"/>
+                            <constraint firstAttribute="height" constant="26" id="gEC-l6-bGj"/>
+                        </constraints>
+                        <state key="normal" title="Button">
+                            <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        </state>
+                        <connections>
+                            <action selector="profileButtonAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="ahg-5f-Js8"/>
+                        </connections>
+                    </button>
+                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VAR-p5-YtT">
+                        <rect key="frame" x="98.5" y="107" width="123" height="26"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="26" id="2KW-Tz-XUh"/>
+                            <constraint firstAttribute="width" constant="123" id="LWL-Gh-Kb2"/>
+                        </constraints>
+                        <state key="normal" title="Button">
+                            <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        </state>
+                        <connections>
+                            <action selector="signoutButtonAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="wG6-Sz-kC4"/>
+                        </connections>
+                    </button>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="OVD-fI-PiE" firstAttribute="centerX" secondItem="H2p-sc-9uM" secondAttribute="centerX" id="9YP-Zg-HEc"/>
+                    <constraint firstItem="Cdu-3X-GgU" firstAttribute="centerX" secondItem="H2p-sc-9uM" secondAttribute="centerX" id="MKx-bl-FHc"/>
+                    <constraint firstItem="OVD-fI-PiE" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="16" id="PWf-ts-iMt"/>
+                    <constraint firstItem="VAR-p5-YtT" firstAttribute="centerX" secondItem="H2p-sc-9uM" secondAttribute="centerX" id="Tww-yy-2ao"/>
+                    <constraint firstItem="VAR-p5-YtT" firstAttribute="top" secondItem="Cdu-3X-GgU" secondAttribute="bottom" constant="15" id="c4v-ah-SbM"/>
+                    <constraint firstItem="Cdu-3X-GgU" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="66" id="og9-te-YTr"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="showProfileButton" destination="Cdu-3X-GgU" id="U5y-QI-dJy"/>
+                <outlet property="signOutButton" destination="VAR-p5-YtT" id="EhO-2K-gBD"/>
+                <outlet property="titleLabel" destination="OVD-fI-PiE" id="R2n-2l-SrZ"/>
+            </connections>
+            <point key="canvasLocation" x="25" y="121"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/Menu/Cells/MenuLineTableViewCell.swift b/iOS/WolneLektury/Screens/Menu/Cells/MenuLineTableViewCell.swift
new file mode 100644 (file)
index 0000000..69f6090
--- /dev/null
@@ -0,0 +1,35 @@
+//
+//  MenuLineTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class MenuLineTableViewCell: WLTableViewCell {
+
+    class func instance() -> MenuLineTableViewCell{
+        let cell = MenuLineTableViewCell.instance(type: MenuLineTableViewCell.self)
+        return cell
+    }
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        // Initialization code
+        backgroundColor = .clear
+        contentView.backgroundColor = .clear
+    }
+
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        super.setSelected(selected, animated: animated)
+
+        // Configure the view for the selected state
+    }
+    
+    override func getHeight() -> CGFloat {
+        return 23
+    }
+    
+}
diff --git a/iOS/WolneLektury/Screens/Menu/Cells/MenuLineTableViewCell.xib b/iOS/WolneLektury/Screens/Menu/Cells/MenuLineTableViewCell.xib
new file mode 100644 (file)
index 0000000..b35c300
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="MenuLineTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZUC-N6-Jpb">
+                        <rect key="frame" x="21" y="21.5" width="278" height="1"/>
+                        <color key="backgroundColor" red="0.0" green="0.50588235294117645" blue="0.53333333333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="1" id="lvM-lo-K39"/>
+                        </constraints>
+                    </view>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="ZUC-N6-Jpb" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="21" id="SCL-Tx-RWY"/>
+                    <constraint firstItem="ZUC-N6-Jpb" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="UW8-vs-keX"/>
+                    <constraint firstAttribute="trailing" secondItem="ZUC-N6-Jpb" secondAttribute="trailing" constant="21" id="yJq-Nu-r3B"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/Menu/Cells/MenuSupportUsTableViewCell.swift b/iOS/WolneLektury/Screens/Menu/Cells/MenuSupportUsTableViewCell.swift
new file mode 100644 (file)
index 0000000..80f7567
--- /dev/null
@@ -0,0 +1,47 @@
+//
+//  MenuSupportUsTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+protocol MenuSupportUsTableViewCellDelegate: class{
+    
+    func menuSupportUsButtonTapped()
+}
+
+class MenuSupportUsTableViewCell: WLTableViewCell {
+    weak var delegate: MenuSupportUsTableViewCellDelegate?
+    @IBOutlet weak var button: UIButton!
+    
+    class func instance(delegate: MenuSupportUsTableViewCellDelegate?) -> MenuSupportUsTableViewCell{
+        let cell = MenuSupportUsTableViewCell.instance(type: MenuSupportUsTableViewCell.self)
+        cell.delegate = delegate
+        return cell
+    }
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        button.layer.cornerRadius = 15
+        button.text = "support_us".localized.uppercased()
+        backgroundColor = UIColor.clear
+        contentView.backgroundColor = UIColor.clear
+    }
+
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        super.setSelected(selected, animated: animated)
+
+        // Configure the view for the selected state
+    }
+    
+    @IBAction func buttonAction(_ sender: Any) {
+        delegate?.menuSupportUsButtonTapped()
+    }
+    
+    override func getHeight() -> CGFloat {
+        return 50
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Menu/Cells/MenuSupportUsTableViewCell.xib b/iOS/WolneLektury/Screens/Menu/Cells/MenuSupportUsTableViewCell.xib
new file mode 100644 (file)
index 0000000..44ef88b
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="MenuSupportUsTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FTf-D1-5mj">
+                        <rect key="frame" x="94.5" y="6" width="131" height="30"/>
+                        <color key="backgroundColor" red="1" green="0.6470588235294118" blue="0.18431372549019609" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="30" id="HMW-9Z-wtm"/>
+                        </constraints>
+                        <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
+                        <inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
+                        <state key="normal" title="WESPRZYJ NAS"/>
+                        <connections>
+                            <action selector="buttonAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="JLY-Ch-Zst"/>
+                        </connections>
+                    </button>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="FTf-D1-5mj" firstAttribute="centerX" secondItem="H2p-sc-9uM" secondAttribute="centerX" id="ejS-f8-9FJ"/>
+                    <constraint firstItem="FTf-D1-5mj" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="yOa-wp-2Xo"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="button" destination="FTf-D1-5mj" id="4I1-Ee-YE4"/>
+            </connections>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/Menu/Cells/MenuTableViewCell.swift b/iOS/WolneLektury/Screens/Menu/Cells/MenuTableViewCell.swift
new file mode 100644 (file)
index 0000000..66ee7a4
--- /dev/null
@@ -0,0 +1,48 @@
+//
+//  MenuTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 29/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class MenuTableViewCell: WLTableViewCell {
+
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var iconImageView: UIImageView!
+    @IBOutlet weak var selectedIndicator: UIView!
+    private(set) var menuItem: MenuItem!
+    
+    class func instance(menuItem: MenuItem) -> MenuTableViewCell{
+        let cell = MenuTableViewCell.instance(type: MenuTableViewCell.self)
+        cell.setup(menuItem: menuItem, selected: false)
+        return cell
+    }
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        backgroundColor = UIColor.clear
+        contentView.backgroundColor = UIColor.clear
+    }
+    
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        super.setSelected(selected, animated: animated)
+        selectedIndicator.isHidden = !selected
+        iconImageView.tintColor = menuItem == .premium ? MenuItem.premium.tintColor : (selected ? UIColor.white : Constants.Colors.menuTintColor())
+    }
+    
+    func setup(menuItem: MenuItem, selected:Bool) {
+        setSelected(selected, animated: false)
+        self.menuItem = menuItem
+        iconImageView.image = menuItem.image
+        titleLabel.text = menuItem.title
+        let tintColor = menuItem.tintColor
+        titleLabel.textColor = tintColor
+    }
+    
+    override func getHeight() -> CGFloat {
+        return 33
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Menu/Cells/MenuTableViewCell.xib b/iOS/WolneLektury/Screens/Menu/Cells/MenuTableViewCell.xib
new file mode 100644 (file)
index 0000000..7f19d40
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="MenuTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="247" height="30"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="247" height="29.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qze-rZ-gsD">
+                        <rect key="frame" x="26" y="6" width="37.5" height="18"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                        <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="rRg-fv-tpw">
+                        <rect key="frame" x="200" y="7.5" width="15" height="15"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="15" id="ZK2-bE-LJu"/>
+                            <constraint firstAttribute="width" constant="15" id="kpb-lu-1dh"/>
+                        </constraints>
+                    </imageView>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1i3-QA-p7q">
+                        <rect key="frame" x="13" y="5" width="2" height="20"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="20" id="ov3-77-jB8"/>
+                            <constraint firstAttribute="width" constant="2" id="rJk-NC-m4i"/>
+                        </constraints>
+                    </view>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="rRg-fv-tpw" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="2tX-Ph-fe8"/>
+                    <constraint firstItem="qze-rZ-gsD" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="26" id="3QL-KZ-Id2"/>
+                    <constraint firstAttribute="trailing" secondItem="rRg-fv-tpw" secondAttribute="trailing" constant="32" id="7fW-Lx-D7N"/>
+                    <constraint firstItem="1i3-QA-p7q" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="bgK-G6-6Hb"/>
+                    <constraint firstItem="qze-rZ-gsD" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="cxr-So-NJs"/>
+                    <constraint firstItem="1i3-QA-p7q" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="13" id="evO-KL-Cys"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="iconImageView" destination="rRg-fv-tpw" id="9y5-Rr-JbP"/>
+                <outlet property="selectedIndicator" destination="1i3-QA-p7q" id="1kn-uW-X5h"/>
+                <outlet property="titleLabel" destination="qze-rZ-gsD" id="Ytq-fz-vcA"/>
+            </connections>
+            <point key="canvasLocation" x="-3.5" y="111.5"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/Menu/MenuItem.swift b/iOS/WolneLektury/Screens/Menu/MenuItem.swift
new file mode 100644 (file)
index 0000000..20f0248
--- /dev/null
@@ -0,0 +1,42 @@
+//
+//  MenuItem.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 30/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+enum MenuItem {
+    
+    case wolne_lektury
+    case premium
+    case audiobooks
+    case downloaded
+    case catalog
+    case news
+    case settings
+    case about
+
+    case reading
+    case favourites
+    case completed
+
+    var title: String{
+        return "nav_\(self)".localized
+    }
+    
+    var image: UIImage?{
+        return UIImage(named: "menu_\(self)")
+    }
+    
+    var tintColor: UIColor{
+        switch self {
+        case .premium:
+            return Constants.Colors.orangeColor()
+        default:
+            return .white
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Menu/MenuViewController.swift b/iOS/WolneLektury/Screens/Menu/MenuViewController.swift
new file mode 100644 (file)
index 0000000..3cb5917
--- /dev/null
@@ -0,0 +1,348 @@
+//
+//  MenuViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 29/05/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import OAuthSwift
+import SafariServices
+
+class MenuViewController: UIViewController {
+
+    @IBOutlet weak var tableView: UITableView!
+    
+    var cellsArray: [WLTableViewCell]!
+    var loggedIn = false
+    
+    @IBOutlet weak var bottomView: UIView!
+    @IBOutlet weak var bottomViewBgView: UIView!
+
+    @IBOutlet weak var guestBottomView: UIView!
+    @IBOutlet weak var loginButton: UIButton!
+    
+    @IBOutlet weak var userBottomView: UIView!
+    @IBOutlet weak var loggedInLabel: UILabel!
+    @IBOutlet weak var logoutButton: UIButton!
+    
+    @IBOutlet weak var bottomViewHeightConstraint: NSLayoutConstraint!
+    
+    var selectedRow: IndexPath?
+    var wlRow: Int = 0
+    var firstAppear = true
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        tableView.separatorStyle = .none
+
+        var contentInset = tableView.contentInset
+        contentInset.top = 10
+        tableView.contentInset = contentInset
+        
+        loginButton.text = "menu_login".localized
+        logoutButton.text = "sign_out".localized
+        loginButton.layer.cornerRadius = 15
+        loginButton.layer.borderColor = UIColor.white.cgColor
+        loginButton.layer.borderWidth = 1
+        logoutButton.layer.cornerRadius = 15
+        logoutButton.layer.borderColor = UIColor.white.cgColor
+        logoutButton.layer.borderWidth = 1
+
+        bottomView.backgroundColor = Constants.Colors.menuTintColor()
+        bottomViewBgView.backgroundColor = Constants.Colors.menuTintColor()
+
+        
+        setup()
+        
+        appDelegate.sideMenuNavigationController = self.navigationController
+        
+    }
+    
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        if firstAppear{
+            tableView.selectRow(at: IndexPath(row: wlRow, section: 0), animated: false, scrollPosition: .none)
+            firstAppear = false
+        }
+    }
+    func setup() {
+        if syncManager.isLoggedIn(){
+            cellsArray = [WLTableViewCell]()
+            
+            if DatabaseManager.shared.isUserPremium() == false && Constants.donateEnabled{
+                cellsArray.append(MenuSupportUsTableViewCell.instance(delegate: self))
+            }
+            
+            if selectedRow == nil {
+                selectedRow = IndexPath(row: cellsArray.count, section: 0)
+                tableView.selectRow(at: selectedRow, animated: false, scrollPosition: .none)
+            }
+
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .wolne_lektury))
+            wlRow = cellsArray.count - 1
+            cellsArray.append(MenuLineTableViewCell.instance())
+            if Constants.donateEnabled {
+                cellsArray.append(MenuTableViewCell.instance(menuItem: .premium))
+            }
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .catalog))
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .audiobooks))
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .reading))
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .favourites))
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .completed))
+            cellsArray.append(MenuLineTableViewCell.instance())
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .downloaded))
+            cellsArray.append(MenuLineTableViewCell.instance())
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .news))
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .settings))
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .about))
+            
+        }
+        else{
+            cellsArray = [WLTableViewCell]()
+            
+            if Constants.donateEnabled {
+                cellsArray.append(MenuSupportUsTableViewCell.instance(delegate: self))
+            }
+
+            if selectedRow == nil {
+                selectedRow = IndexPath(row: cellsArray.count, section: 0)
+                tableView.selectRow(at: selectedRow, animated: false, scrollPosition: .none)
+            }
+
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .wolne_lektury))
+            wlRow = cellsArray.count - 1
+            cellsArray.append(MenuLineTableViewCell.instance())
+            if Constants.donateEnabled {
+                cellsArray.append(MenuTableViewCell.instance(menuItem: .premium))
+            }
+            
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .catalog))
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .audiobooks))
+            cellsArray.append(MenuLineTableViewCell.instance())
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .downloaded))
+            cellsArray.append(MenuLineTableViewCell.instance())
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .news))
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .settings))
+            cellsArray.append(MenuTableViewCell.instance(menuItem: .about))
+        }
+        tableView.reloadData()
+        
+        refreshBottomView()
+    }
+    
+    func selectItem(menuItem: MenuItem){
+        var index = 0
+        for cell in cellsArray {
+            if let cell = cell as? MenuTableViewCell, cell.menuItem == menuItem {
+                tableView.selectRow(at: IndexPath(row: index, section: 0), animated: false, scrollPosition: .none)
+                return
+            }
+            index += 1
+        }
+    }
+    
+    func refreshBottomView() {
+        
+        userBottomView.isHidden = true
+        guestBottomView.isHidden = true
+        
+        if syncManager.isLoggedIn(){
+            
+            var titleText = NSMutableAttributedString(string: "")
+            
+            if let user = DatabaseManager.shared.rlmApplication?.user {
+                
+                titleText = NSMutableAttributedString(attributedString: NSAttributedString(string: "logged_as".localized + "\n", font: UIFont.systemFont(ofSize: 10)))
+                titleText.append(NSAttributedString(string: user.username, font: UIFont.systemFont(ofSize: 12)))
+                loggedInLabel.attributedText = titleText
+            }
+
+            userBottomView.isHidden = false
+            bottomViewHeightConstraint.constant = userBottomView.frame.size.height
+        }
+        else{
+            guestBottomView.isHidden = false
+            bottomViewHeightConstraint.constant = guestBottomView.frame.size.height
+        }
+    }
+    
+    func getPreview() {
+        appDelegate.showWindowHud()
+        syncManager.getPreview { [weak self] (result) in
+            
+            guard let strongSelf = self else { return }
+            strongSelf.appDelegate.hideWindowHud()
+            switch result {
+            case .success(let model):
+                let array = model as! [BookModel]
+                if array.count > 0 {
+                    let book = array[0]
+                    
+                    strongSelf.appDelegate.mainNavigator.presentPremiumBook(bookSlug: book.slug)
+                }
+                else {
+                    strongSelf.presentNoPremiereAlert()
+                }
+                
+            case .failure/*(let error)*/:
+                strongSelf.presentToast(message: "fetching_premium_failed".localized)
+            }
+        }
+    }
+    
+    func presentNoPremiereAlert() {
+    
+        let message = syncManager.isLoggedIn() ? "no_prapremiere_message_logged".localized : "no_prapremiere_message".localized
+        
+        let alertController = UIAlertController(
+            title: "no_prapremiere_title".localized,
+            message: message,
+            preferredStyle: UIAlertControllerStyle.alert
+        )
+        
+        if syncManager.isLoggedIn() {
+            let action = UIAlertAction(title: "OK".localized, style: UIAlertActionStyle.cancel, handler: nil)
+            alertController.addAction(action)
+        }
+        else{
+            let cancelAction = UIAlertAction(title: "no_thanks".localized, style: UIAlertActionStyle.cancel, handler: nil)
+            alertController.addAction(cancelAction)
+            
+            let okAction = UIAlertAction(title: "become_a_friend".localized, style: UIAlertActionStyle.default) { [weak self]
+                (result : UIAlertAction) -> Void in
+                self?.onBecomeAFriendClick()
+            }
+            alertController.addAction(okAction)
+        }
+        present(alertController, animated: true, completion: nil)
+    }
+    
+    func onBecomeAFriendClick() {
+        if syncManager.isLoggedIn() {
+            showPremiumForm()
+        }
+        else {
+            showLoginFirst()
+        }
+    }
+    
+    func showPremiumForm() {
+        showPayPalForm()
+    }
+    
+    func showPayPalForm() {
+        
+        if syncManager.isLoggedIn() {
+            appDelegate.mainNavigator.presentPayPalForm()
+        }
+        else {
+            presentToast(message: "login_first".localized)
+        }
+    }
+    
+    func showLoginFirst() {
+        
+        let alertController = UIAlertController(
+            title: "login".localized,
+            message: "login_first".localized,
+            preferredStyle: UIAlertControllerStyle.alert
+        )
+        
+        let cancelAction = UIAlertAction(title: "no_thanks".localized, style: UIAlertActionStyle.cancel, handler: nil)
+        alertController.addAction(cancelAction)
+        
+        let loginAction = UIAlertAction(title: "login".localized, style: UIAlertActionStyle.default) { [weak self]
+            (result : UIAlertAction) -> Void in
+            self?.onLoginClicked()
+        }
+        alertController.addAction(loginAction)
+
+        self.present(alertController, animated: true, completion: nil)
+    }
+    
+    func onLoginClicked() {
+        appDelegate.login(fromViewController: self)
+    }
+
+    deinit {
+        print("deinit")
+        appDelegate.sideMenuNavigationController = nil
+    }
+    
+    @IBAction func logoutButtonAction(_ sender: Any) {
+        syncManager.logout()
+        appDelegate.mainNavigator.reset()
+    }
+    
+    @IBAction func loginButtonAction(_ sender: Any) {
+        onLoginClicked()
+    }
+}
+
+extension MenuViewController: UITableViewDataSource{
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        return cellsArray[indexPath.row]
+    }
+    
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return cellsArray.count
+    }
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return cellsArray[indexPath.row].getHeight()
+    }
+}
+
+extension MenuViewController: UITableViewDelegate{
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        
+        if let cell = cellsArray[indexPath.row] as? MenuTableViewCell{
+            switch cell.menuItem{
+            case .wolne_lektury:
+                appDelegate.mainNavigator.presentLibrary(dismissSideMenu: true)
+            case .premium:
+                getPreview()
+                if let selectedRow = selectedRow{
+                    tableView.selectRow(at: selectedRow, animated: false, scrollPosition: .none)
+                }
+                return
+            case .audiobooks:
+                appDelegate.mainNavigator.presentBookList(listViewControllerType: .audiobooks)
+            case .downloaded:
+                appDelegate.mainNavigator.presentDownloaded()
+            case .catalog:
+                appDelegate.mainNavigator.presentSearch()
+            case .news:
+                appDelegate.mainNavigator.presentNews()
+            case .settings:
+                appDelegate.mainNavigator.presentSettings()
+            case .about:
+                appDelegate.mainNavigator.presentAbout()
+            case .reading:
+                appDelegate.mainNavigator.presentBookList(listViewControllerType: .reading_now)
+            case .favourites:
+                appDelegate.mainNavigator.presentBookList(listViewControllerType: .favourites)
+            case .completed:
+                appDelegate.mainNavigator.presentBookList(listViewControllerType: .completed)
+            default:
+                break
+            }
+            selectedRow = indexPath
+        }
+        else if cellsArray[indexPath.row] is MenuSupportUsTableViewCell{
+            selectedRow = indexPath
+        }
+        else {
+            if let selectedRow = selectedRow{
+                tableView.selectRow(at: selectedRow, animated: false, scrollPosition: .none)
+            }
+        }
+    }
+}
+
+extension MenuViewController: MenuSupportUsTableViewCellDelegate{
+    func menuSupportUsButtonTapped(){
+        appDelegate.mainNavigator.presentSupportUs()
+    }
+}
+
diff --git a/iOS/WolneLektury/Screens/News/Cells/NewsTableViewCell.swift b/iOS/WolneLektury/Screens/News/Cells/NewsTableViewCell.swift
new file mode 100644 (file)
index 0000000..7a1e8c4
--- /dev/null
@@ -0,0 +1,49 @@
+//
+//  NewsTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 15/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Kingfisher
+
+class NewsTableViewCell: UITableViewCell {
+
+    @IBOutlet weak var miniatureImageBgView: UIView!
+    @IBOutlet weak var miniatureImageView: UIImageView!
+    @IBOutlet weak var dateLabel: UILabel!
+    @IBOutlet weak var titleLabel: UILabel!
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        
+        miniatureImageBgView.layer.masksToBounds = false
+        miniatureImageBgView.layer.shadowColor = UIColor.black.cgColor
+        miniatureImageBgView.layer.shadowOffset = CGSize(width: 0, height: 2)
+        miniatureImageBgView.layer.shadowOpacity = 0.2
+        miniatureImageBgView.layer.shadowRadius = 2
+        miniatureImageBgView.layer.cornerRadius = 4
+        miniatureImageView.layer.cornerRadius = 4
+        miniatureImageView.clipsToBounds = true
+        selectionStyle = .none
+        miniatureImageView.isUserInteractionEnabled = true
+        selectionStyle = .none
+    }
+    
+    func setup(newsModel: NewsModel){
+        dateLabel.text = newsModel.time
+        titleLabel.text = newsModel.title
+        miniatureImageView.image = nil
+        if let url = newsModel.getCoverThumbUrl(){
+            
+            miniatureImageView.kf.setImage(with: ImageResource(downloadURL: url),
+                                       placeholder: #imageLiteral(resourceName: "list_nocover"),
+                                       options: [.transition(.fade(1))],
+                                       progressBlock: nil,
+                                       completionHandler: { (image, error, cacheType, url) in
+            })
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Screens/News/Cells/NewsTableViewCell.xib b/iOS/WolneLektury/Screens/News/Cells/NewsTableViewCell.xib
new file mode 100644 (file)
index 0000000..09e47bc
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="97" id="KGk-i7-Jjw" customClass="NewsTableViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="320" height="97"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="320" height="96.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="EPOKA" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Not-YN-F7o">
+                        <rect key="frame" x="117" y="33" width="187" height="14"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="11"/>
+                        <color key="textColor" red="0.0039215686269999999" green="0.50588235290000005" blue="0.53725490200000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="zkA-Lu-9hs">
+                        <rect key="frame" x="117" y="27" width="187" height="1"/>
+                        <color key="backgroundColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="1" id="GQe-HB-xhG"/>
+                        </constraints>
+                    </view>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Adam Mickiewicz" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ePu-I8-QqW">
+                        <rect key="frame" x="117" y="8" width="187" height="14"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="11"/>
+                        <color key="textColor" red="0.32156862749999998" green="0.32156862749999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SsI-aA-jd7">
+                        <rect key="frame" x="11" y="3" width="90" height="90"/>
+                        <subviews>
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="SAD-Fu-C39">
+                                <rect key="frame" x="0.0" y="0.0" width="90" height="90"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="90" id="2mT-fA-cGd"/>
+                                    <constraint firstAttribute="width" constant="90" id="JSt-H8-xYY"/>
+                                </constraints>
+                            </imageView>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstAttribute="bottom" secondItem="SAD-Fu-C39" secondAttribute="bottom" id="5g9-dH-oFl"/>
+                            <constraint firstItem="SAD-Fu-C39" firstAttribute="leading" secondItem="SsI-aA-jd7" secondAttribute="leading" id="Au8-rf-Pqp"/>
+                            <constraint firstItem="SAD-Fu-C39" firstAttribute="top" secondItem="SsI-aA-jd7" secondAttribute="top" id="GYD-xC-owN"/>
+                            <constraint firstAttribute="width" constant="90" id="Jwg-8B-W9D"/>
+                            <constraint firstAttribute="height" constant="90" id="rtZ-dc-rU4"/>
+                            <constraint firstAttribute="trailing" secondItem="SAD-Fu-C39" secondAttribute="trailing" id="tit-Rm-fvb"/>
+                        </constraints>
+                    </view>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="ePu-I8-QqW" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="8" id="2Fh-cp-dNN"/>
+                    <constraint firstItem="Not-YN-F7o" firstAttribute="top" secondItem="zkA-Lu-9hs" secondAttribute="bottom" constant="5" id="6ic-qV-E1m"/>
+                    <constraint firstItem="zkA-Lu-9hs" firstAttribute="top" secondItem="ePu-I8-QqW" secondAttribute="bottom" constant="5" id="TDl-C0-3Be"/>
+                    <constraint firstItem="ePu-I8-QqW" firstAttribute="leading" secondItem="zkA-Lu-9hs" secondAttribute="leading" id="Ups-Kk-deQ"/>
+                    <constraint firstItem="zkA-Lu-9hs" firstAttribute="leading" secondItem="SsI-aA-jd7" secondAttribute="trailing" constant="16" id="XOt-aK-tme"/>
+                    <constraint firstItem="Not-YN-F7o" firstAttribute="trailing" secondItem="zkA-Lu-9hs" secondAttribute="trailing" id="YT5-z8-0xI"/>
+                    <constraint firstItem="Not-YN-F7o" firstAttribute="leading" secondItem="zkA-Lu-9hs" secondAttribute="leading" id="atj-Z1-8Iu"/>
+                    <constraint firstItem="SsI-aA-jd7" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="3" id="b2W-mv-dwX"/>
+                    <constraint firstAttribute="trailing" secondItem="zkA-Lu-9hs" secondAttribute="trailing" constant="16" id="cro-HZ-Dt1"/>
+                    <constraint firstItem="SsI-aA-jd7" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="11" id="wyP-TA-d2w"/>
+                    <constraint firstItem="ePu-I8-QqW" firstAttribute="trailing" secondItem="zkA-Lu-9hs" secondAttribute="trailing" id="xUO-ls-7bf"/>
+                </constraints>
+            </tableViewCellContentView>
+            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
+            <connections>
+                <outlet property="dateLabel" destination="ePu-I8-QqW" id="UBg-Vk-uXa"/>
+                <outlet property="miniatureImageBgView" destination="SsI-aA-jd7" id="5t9-kh-dWY"/>
+                <outlet property="miniatureImageView" destination="SAD-Fu-C39" id="bfH-CO-snn"/>
+                <outlet property="titleLabel" destination="Not-YN-F7o" id="iI2-E7-dJc"/>
+            </connections>
+            <point key="canvasLocation" x="34" y="80.5"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/News/NewsViewController.swift b/iOS/WolneLektury/Screens/News/NewsViewController.swift
new file mode 100644 (file)
index 0000000..755edf2
--- /dev/null
@@ -0,0 +1,45 @@
+//
+//  NewsViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 15/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class NewsViewController: ListViewController {
+    
+    static func instance(dataSource: [Any]?) -> NewsViewController {
+        let controller = NewsViewController.instance()
+        controller.dataSource = dataSource ?? [BookModel]()
+        controller.listViewControllerType = .news
+        return controller
+    }
+    
+    override func setupTableView() {
+        super.setupTableView()
+        tableView.registerNib(name: "NewsTableViewCell")
+        tableView.rowHeight = 97
+    }
+    
+    override func getLastObjectAfterParameter() -> String? {
+        
+        if let last = dataSource.last as? BookModel {
+            return last.full_sort_key
+        }
+        return nil
+    }
+    
+    override func getTableViewCell(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "NewsTableViewCell", for: indexPath) as! NewsTableViewCell
+        cell.setup(newsModel: dataSource[indexPath.row] as! NewsModel)
+        return cell
+    }
+    
+    override func didSelectRow(row: Int) {
+        if dataSource.count > row {
+            navigationController?.pushViewController(NewsDetailsViewController.instance(newsModel: dataSource[row] as! NewsModel) , animated: true)
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Screens/NewsDetails/Cells/SimpleGalleryCollectionViewCell.swift b/iOS/WolneLektury/Screens/NewsDetails/Cells/SimpleGalleryCollectionViewCell.swift
new file mode 100644 (file)
index 0000000..cf613db
--- /dev/null
@@ -0,0 +1,33 @@
+//
+//  SimpleGalleryCollectionViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 17/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Kingfisher
+class SimpleGalleryCollectionViewCell: UICollectionViewCell {
+
+    @IBOutlet weak var imageView: UIImageView!
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        imageView.clipsToBounds = true
+    }
+    
+    func setup(imageUrlString: String?) {
+        imageView.image = nil
+        
+        if let url = imageUrlString?.getPhotoUrl(){
+            
+            imageView.kf.setImage(with: ImageResource(downloadURL: url),
+                                       placeholder: #imageLiteral(resourceName: "list_nocover"),
+                                       options: [.transition(.fade(1))],
+                                       progressBlock: nil,
+                                       completionHandler: { (image, error, cacheType, url) in
+            })
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Screens/NewsDetails/Cells/SimpleGalleryCollectionViewCell.xib b/iOS/WolneLektury/Screens/NewsDetails/Cells/SimpleGalleryCollectionViewCell.xib
new file mode 100644 (file)
index 0000000..10d7d0d
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="SimpleGalleryCollectionViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
+                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                <subviews>
+                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="IDE-fz-O4z">
+                        <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
+                    </imageView>
+                </subviews>
+            </view>
+            <color key="backgroundColor" red="0.0" green="0.50196078430000002" blue="0.53333333329999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="IDE-fz-O4z" secondAttribute="trailing" id="TwC-hX-Qhb"/>
+                <constraint firstItem="IDE-fz-O4z" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="agT-iN-Bsk"/>
+                <constraint firstItem="IDE-fz-O4z" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="h1a-qt-6tB"/>
+                <constraint firstAttribute="bottom" secondItem="IDE-fz-O4z" secondAttribute="bottom" id="qnC-3p-OiC"/>
+            </constraints>
+            <viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
+            <connections>
+                <outlet property="imageView" destination="IDE-fz-O4z" id="RNJ-tf-tlx"/>
+            </connections>
+        </collectionViewCell>
+    </objects>
+</document>
diff --git a/iOS/WolneLektury/Screens/NewsDetails/Gallery/Cells/GalleryCell.swift b/iOS/WolneLektury/Screens/NewsDetails/Gallery/Cells/GalleryCell.swift
new file mode 100644 (file)
index 0000000..f1d1c4d
--- /dev/null
@@ -0,0 +1,168 @@
+//
+//  GalleryCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 17/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Kingfisher
+import MBProgressHUD
+
+public class GalleryCell: UICollectionViewCell {
+    
+    var url: String?{
+        didSet {
+            configureForNewImageUrl()
+        }
+    }
+    
+    private var scrollView: UIScrollView
+    fileprivate let imageView: UIImageView
+    var progressHud: MBProgressHUD?
+    
+    func setScrollEnabled(scrollEnabled: Bool){
+        scrollView.isUserInteractionEnabled = scrollEnabled
+        if !scrollEnabled{
+            setZoomScale()
+        }
+    }
+    
+    override init(frame: CGRect) {
+        
+        imageView = UIImageView()
+        imageView.translatesAutoresizingMaskIntoConstraints = false
+        scrollView = UIScrollView(frame: frame)
+        scrollView.translatesAutoresizingMaskIntoConstraints = false
+        scrollView.backgroundColor = Constants.Colors.darkGreenBgColor()
+        super.init(frame: frame)
+        
+        var scrollViewConstraints: [NSLayoutConstraint] = []
+        var imageViewConstraints: [NSLayoutConstraint] = []
+        
+        scrollViewConstraints.append(NSLayoutConstraint(item: scrollView, attribute: .leading, relatedBy: .equal, toItem: contentView, attribute: .leading, multiplier: 1, constant: 0))
+        scrollViewConstraints.append(NSLayoutConstraint(item: scrollView, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: 0))
+        scrollViewConstraints.append(NSLayoutConstraint(item: scrollView, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: 0))
+        scrollViewConstraints.append(NSLayoutConstraint(item: scrollView, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: 0))
+        contentView.addSubview(scrollView)
+        contentView.addConstraints(scrollViewConstraints)
+        
+        imageViewConstraints.append(NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: scrollView, attribute: .leading, multiplier: 1, constant: 0))
+        imageViewConstraints.append(NSLayoutConstraint(item: imageView, attribute: .top, relatedBy: .equal, toItem: scrollView, attribute: .top, multiplier: 1, constant: 0))
+        imageViewConstraints.append(NSLayoutConstraint(item: imageView, attribute: .trailing, relatedBy: .equal, toItem: scrollView, attribute: .trailing, multiplier: 1, constant: 0))
+        imageViewConstraints.append(NSLayoutConstraint(item: imageView, attribute: .bottom, relatedBy: .equal, toItem: scrollView, attribute: .bottom, multiplier: 1, constant: 0))
+        scrollView.addSubview(imageView)
+        scrollView.addConstraints(imageViewConstraints)
+
+        scrollView.delegate = self
+        
+        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognizerAction(recognizer:)))
+        tapGestureRecognizer.numberOfTapsRequired = 2
+        self.addGestureRecognizer(tapGestureRecognizer)
+    }
+    
+    @objc public func tapGestureRecognizerAction(recognizer: UITapGestureRecognizer) {
+        if (scrollView.zoomScale > scrollView.minimumZoomScale) {
+            scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
+        } else {
+            scrollView.setZoomScale(scrollView.maximumZoomScale, animated: true)
+        }
+    }
+
+    required public init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    public func configureForNewImageUrl() {
+        
+        guard let imageUrl = url?.getPhotoUrl() else{
+            self.setupImage(image: nil)
+            return
+        }
+        
+        if progressHud == nil{
+            progressHud = MBProgressHUD.showAdded(to: contentView, animated: true)
+            progressHud?.label.text = "loading".localized
+        }
+        else{
+            progressHud?.show(animated: true)
+        }
+        
+        //        let freeSpace = contentView.bounds.size.height - textContainerHeight
+        let contentOffsetY = ((contentView.bounds.size.height/2 /*- freeSpace/2*/) * -1)
+        progressHud!.offset.y = contentOffsetY
+        
+        KingfisherManager.shared.retrieveImage(with: ImageResource(downloadURL: imageUrl), options: nil, progressBlock: nil) { (image, error, cacheType, url) in
+            self.progressHud?.hide(animated: true)
+            self.setupImage(image: image)
+        }
+    }
+
+    
+    // ############################################
+    // MARK: - Private
+    // ############################################
+    
+    private func setupImage(image: UIImage?){
+        imageView.image = image
+        imageView.sizeToFit()
+        imageView.alpha = 0.0
+        
+        setZoomScale()
+        scrollViewDidZoom(scrollView)
+        
+        UIView.animate(withDuration: 0.5) {
+            self.imageView.alpha = 1.0
+        }
+    }
+    
+    private func setZoomScale() {
+        
+        let imageViewSize = imageView.bounds.size
+        
+        guard imageViewSize.width > 0 && imageViewSize.height > 0 else {
+            return
+        }
+        
+        let scrollViewSize =  UIScreen.main.bounds.size// scrollView.bounds.size
+        let widthScale = scrollViewSize.width / imageViewSize.width
+        let heightScale = scrollViewSize.height / imageViewSize.height
+        let miminumZoomScale = min(widthScale, heightScale)
+        scrollView.minimumZoomScale = miminumZoomScale
+        
+        let initialZoomScale = max(widthScale, heightScale)
+        
+        scrollView.maximumZoomScale = initialZoomScale * 1.5
+        
+        scrollView.setZoomScale(miminumZoomScale, animated: false)
+        
+        let newContentOffsetX = (scrollView.contentSize.width/2) - (scrollView.bounds.size.width/2);
+        
+        scrollView.contentOffset = CGPoint(x: newContentOffsetX, y: scrollView.contentOffset.y)
+    }
+}
+
+extension GalleryCell: UIScrollViewDelegate {
+    
+    public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
+        return imageView
+    }
+    
+    public func scrollViewDidZoom(_ scrollView: UIScrollView) {
+        
+        let imageViewSize = imageView.frame.size
+        let scrollViewSize = scrollView.bounds.size
+        
+        let verticalPadding = imageViewSize.height < scrollViewSize.height ? (scrollViewSize.height - imageViewSize.height) / 2 : 0
+        let horizontalPadding = imageViewSize.width < scrollViewSize.width ? (scrollViewSize.width - imageViewSize.width) / 2 : 0
+        
+        if verticalPadding >= 0 {
+            // Center the image on screen
+            scrollView.contentInset = UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding)
+        } else {
+            // Limit the image panning to the screen bounds
+            scrollView.contentSize = imageViewSize
+        }
+    }
+}
diff --git a/iOS/WolneLektury/Screens/NewsDetails/Gallery/GalleryViewController.swift b/iOS/WolneLektury/Screens/NewsDetails/Gallery/GalleryViewController.swift
new file mode 100644 (file)
index 0000000..7e8176a
--- /dev/null
@@ -0,0 +1,128 @@
+//
+//  GalleryViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 17/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class GalleryViewController: WLViewController {
+    
+    @IBOutlet weak var closeButton: UIButton!
+    
+    @IBOutlet weak var galleryCollectionView: UICollectionView!
+    
+    private var flowLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
+    
+    var dataSource: [String]!
+    
+    var countDidLayoutSubviews = 0
+    var collectionViewInitialized = false
+    var startIndex: Int!
+    
+    class func instance(photos: [String], startIndex: Int) -> GalleryViewController{
+        
+        let controller = GalleryViewController.instance()
+        controller.dataSource = photos
+        controller.startIndex = startIndex
+        return controller
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        closeButton.tintColor = UIColor.white
+        closeButton.accessibilityLabel = "close".localized
+        closeButton.accessibilityHint = "close".localized
+    }
+    
+    @IBAction func closeButtonAction(_ sender: Any) {
+        dismiss(animated: true, completion: nil)
+    }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        
+        countDidLayoutSubviews += 1
+        
+        if countDidLayoutSubviews == 2 && collectionViewInitialized == false{
+            setupCollectionViewIfNeeded()
+        }
+    }
+    
+    private func setupCollectionViewIfNeeded(){
+        if collectionViewInitialized == false{
+            setupCollectionView()
+        }
+    }
+    
+    open override var preferredStatusBarStyle : UIStatusBarStyle {
+        return .lightContent
+    }
+
+    private func setupCollectionView(){// -> UICollectionView {
+        // Set up flow layout
+        collectionViewInitialized = true
+        
+        flowLayout.scrollDirection = UICollectionViewScrollDirection.horizontal
+        flowLayout.minimumInteritemSpacing = 0
+        flowLayout.minimumLineSpacing = 0
+        
+        flowLayout.itemSize = galleryCollectionView.bounds.size
+        
+        galleryCollectionView.collectionViewLayout = flowLayout
+        
+        galleryCollectionView.register(GalleryCell.self, forCellWithReuseIdentifier: "GalleryCell")
+        galleryCollectionView.dataSource = self
+        galleryCollectionView.delegate = self
+        galleryCollectionView.backgroundColor = UIColor.black
+        galleryCollectionView.isPagingEnabled = true
+        
+        galleryCollectionView.contentSize = CGSize(width: 1000.0, height: 1.0)
+        
+        galleryCollectionView.scrollToItem(
+            at: IndexPath(row: startIndex, section: 0),
+            at: [],
+            animated: false)
+    }
+}
+
+extension GalleryViewController: UICollectionViewDataSource {
+    
+    public func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 1
+    }
+    
+    public func collectionView(_ imageCollectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return dataSource.count
+    }
+    
+    public func collectionView(_ imageCollectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        
+        let cell = imageCollectionView.dequeueReusableCell(withReuseIdentifier: "GalleryCell", for: indexPath) as! GalleryCell
+        cell.url = dataSource[indexPath.row]
+        cell.setScrollEnabled(scrollEnabled: true)
+        return cell
+    }
+}
+
+// MARK: UICollectionViewDelegate Methods
+extension GalleryViewController: UICollectionViewDelegate {
+    
+    public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
+        if let cell = cell as? GalleryCell {
+            cell.configureForNewImageUrl()
+        }
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        
+    }
+    
+    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
+        
+        // If the scroll animation ended, update the page control to reflect the current page we are on
+        //        updatePageControl()
+    }
+}
diff --git a/iOS/WolneLektury/Screens/NewsDetails/NewsDetailsViewController.swift b/iOS/WolneLektury/Screens/NewsDetails/NewsDetailsViewController.swift
new file mode 100644 (file)
index 0000000..473a66b
--- /dev/null
@@ -0,0 +1,155 @@
+//
+//  NewsDetailsViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 15/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class NewsDetailsViewController: WLViewController {
+
+    @IBOutlet weak var scrollView: UIScrollView!
+    @IBOutlet weak var backButton: UIButton!
+    @IBOutlet weak var headerView: UIView!
+    @IBOutlet weak var titleLabel: UILabel!
+    
+    @IBOutlet weak var whenTitleLabel: UILabel!
+    @IBOutlet weak var whenDescLabel: UILabel!
+    @IBOutlet weak var whereTitleLabel: UILabel!
+    @IBOutlet weak var whereDescLabel: UILabel!
+    @IBOutlet weak var descLabel: UILabel!
+    
+    @IBOutlet weak var shareButton: UIButton!
+    @IBOutlet weak var galleryCollectionView: UICollectionView!
+    @IBOutlet weak var pageControl: UIPageControl!
+    @IBOutlet weak var headerViewHeightLayoutConstraint: NSLayoutConstraint!
+
+    private var flowLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
+    
+    var imageFullScreen = false
+    var countDidLayoutSubviews = 0
+    var collectionViewInitialized = false
+    var newsModel: NewsModel!
+    var smallHeaderHeight: CGFloat = 200
+    
+    open override var preferredStatusBarStyle : UIStatusBarStyle {
+        return .lightContent
+    }
+
+    static func instance(newsModel: NewsModel) -> NewsDetailsViewController {
+        let controller = NewsDetailsViewController.instance()
+        controller.newsModel = newsModel
+        return controller
+    }
+
+    public var currentPage: Int {
+        get {
+            return Int(galleryCollectionView.contentOffset.x / galleryCollectionView.frame.size.width)
+        }
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        title = newsModel.title
+        smallHeaderHeight = 44 + UIApplication.shared.statusBarFrame.size.height
+        updatePageControl()
+
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+            self.setupCollectionViewIfNeeded()
+        }
+        shareButton.layer.cornerRadius = 21
+        shareButton.addDropShadow()
+        
+        titleLabel.text = newsModel.title
+        whenTitleLabel.text = "news_when".localized
+        whenDescLabel.text = newsModel.time
+        whereTitleLabel.text = "news_where".localized
+        whereDescLabel.text = newsModel.place
+        
+        if let htmlString = try? NSAttributedString(html: newsModel.body){
+            descLabel.attributedText =  htmlString
+        }
+        else {
+            descLabel.text = newsModel.body
+        }
+    }
+    
+    func updatePageControl()  {
+        pageControl.currentPage = currentPage
+    }
+
+    private func setupCollectionViewIfNeeded(){
+        if collectionViewInitialized == false{
+            setupCollectionView()
+        }
+    }
+
+    private func setupCollectionView(){// -> UICollectionView {
+        // Set up flow layout
+        collectionViewInitialized = true
+        
+        flowLayout.scrollDirection = UICollectionViewScrollDirection.horizontal
+        flowLayout.minimumInteritemSpacing = 0
+        flowLayout.minimumLineSpacing = 0
+        
+        let size = galleryCollectionView.bounds.size
+        flowLayout.itemSize = size
+        
+        galleryCollectionView.collectionViewLayout = flowLayout
+        galleryCollectionView.register(UINib (nibName: "SimpleGalleryCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "SimpleGalleryCollectionViewCell")
+        galleryCollectionView.dataSource = self
+        galleryCollectionView.delegate = self
+        galleryCollectionView.backgroundColor = UIColor.black
+        galleryCollectionView.isPagingEnabled = true
+        
+        galleryCollectionView.contentSize = CGSize(width: 1000.0, height: 1.0)
+        
+        galleryCollectionView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(NewsDetailsViewController.collectionViewTapped)))
+    }
+
+    @objc func collectionViewTapped() {
+        guard newsModel.gallery_urls.count > 0 else{
+            return
+        }
+        
+        navigationController?.present(GalleryViewController.instance(photos: newsModel.gallery_urls,startIndex: currentPage), animated: true, completion: nil)
+    }
+    
+    @IBAction func shareButtonAction(_ sender: UIButton) {
+        if let url = newsModel?.url {
+
+            self.share(string: url, button: sender)
+
+        }
+    }
+}
+
+extension NewsDetailsViewController: UICollectionViewDataSource {
+    
+    public func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 1
+    }
+    
+    public func collectionView(_ imageCollectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return newsModel.gallery_urls.count
+    }
+    
+    public func collectionView(_ imageCollectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        
+        let cell = imageCollectionView.dequeueReusableCell(withReuseIdentifier: "SimpleGalleryCollectionViewCell", for: indexPath) as! SimpleGalleryCollectionViewCell
+        cell.setup(imageUrlString: newsModel.gallery_urls[indexPath.row])
+        return cell
+    }
+}
+
+extension NewsDetailsViewController: UICollectionViewDelegate {
+    
+    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
+        
+        // If the scroll animation ended, update the page control to reflect the current page we are on
+        updatePageControl()
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Player/PlayerController.swift b/iOS/WolneLektury/Screens/Player/PlayerController.swift
new file mode 100644 (file)
index 0000000..2a5ac72
--- /dev/null
@@ -0,0 +1,320 @@
+//
+//  PlayerController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 21/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import AVFoundation
+import MediaPlayer
+
+protocol PlayerControllerDelegate: class {
+    func playerControllerDelegateTrackChanged()
+    func playerControllerDelegateUpdatePlayerProgress(timeInterval: TimeInterval)
+    func playerControllerDelegatePlayStateChanged()
+    
+}
+
+class PlayerController: UIResponder, AVAudioPlayerDelegate {
+    static let shared = PlayerController()
+
+    override init() {
+        super.init()
+
+        let commandCenter = MPRemoteCommandCenter.shared()
+        commandCenter.playCommand.isEnabled = true
+        commandCenter.pauseCommand.isEnabled = true
+        commandCenter.nextTrackCommand.isEnabled = true
+        commandCenter.previousTrackCommand.isEnabled = true
+        commandCenter.nextTrackCommand.addTarget(self, action: #selector(PlayerController.nextTrackCommand))
+        commandCenter.previousTrackCommand.addTarget(self, action: #selector(PlayerController.previousTrackCommand))
+        commandCenter.playCommand.addTarget(self, action: #selector(PlayerController.playCommand))
+        commandCenter.pauseCommand.addTarget(self, action: #selector(PlayerController.pauseCommand))
+        if #available(iOS 9.1, *) {
+            commandCenter.changePlaybackPositionCommand.addTarget(self, action: #selector(PlayerController.changePlaybackPositionCommand(event:)))
+        } else {
+            // Fallback on earlier versions
+        }
+    }
+    
+    weak var delegate: PlayerControllerDelegate?
+    
+    private(set) var currentBookDetails: BookDetailsModel? {
+        didSet {
+            if let currentBookDetails = currentBookDetails {
+                audiobookMediaModels = currentBookDetails.getAudiobookMediaModels()
+                if currentBookDetails.currentAudioChapter < audiobookMediaModels!.count {
+                    currentAudiobookIndex = currentBookDetails.currentAudioChapter
+                }
+                else {
+                    currentAudiobookIndex = 0
+                }
+            }
+            else{
+                audiobookMediaModels = nil
+                currentAudiobookIndex = 0
+            }
+        }
+    }
+    
+    private var audiobookMediaModels: [MediaModel]?
+    private(set) var currentAudiobookIndex: Int = 0
+    var audioPlayer:AVAudioPlayer? = nil
+    var timer:Timer?
+    var audioLength = 0.0
+    var currentAudioPath: URL?
+    private(set) var artwork: MPMediaItemArtwork?{
+        didSet{
+            updateMediaInfo()
+        }
+    }
+        
+    func setCoverImage(image: UIImage){
+        artwork = MPMediaItemArtwork(image: image)
+    }
+    
+    func getCurrentTime() -> TimeInterval {
+        guard let audioPlayer = audioPlayer else { return 0 }
+        return audioPlayer.currentTime
+    }
+    
+    func isPlaying() -> Bool {
+        guard let audioPlayer = audioPlayer else { return false }
+        return audioPlayer.isPlaying
+    }
+
+    func stopAndClear() {
+        stopTimer()
+        audioPlayer?.stop()
+        currentBookDetails = nil
+    }
+    
+    func preparePlayer(bookDetailsModel: BookDetailsModel, delegate: PlayerControllerDelegate, trackIndex: Int?) {
+        stopAndClear()
+        
+        self.delegate = delegate
+        currentBookDetails = bookDetailsModel
+        if let trackIndex = trackIndex {
+            currentAudiobookIndex = trackIndex
+        }
+        else if bookDetailsModel.currentAudioChapter < audiobookMediaModels!.count {
+            currentAudiobookIndex = bookDetailsModel.currentAudioChapter
+        }
+        prepareAudio()
+        delegate.playerControllerDelegateTrackChanged()
+    }
+    
+    func startOrContinuePlaying(bookDetailsModel: BookDetailsModel, delegate: PlayerControllerDelegate) {
+        guard let audioPlayer = audioPlayer, let currentBookDetails = currentBookDetails, currentBookDetails.slug == bookDetailsModel.slug else {
+            startPlaying(bookDetailsModel: bookDetailsModel, delegate: delegate, trackIndex: nil)
+            return
+        }
+        
+        
+        self.delegate = delegate
+        if audioPlayer.isPlaying {
+            delegate.playerControllerDelegateTrackChanged()
+        }
+        else {
+            prepareAudio()
+            playAudio()
+
+        }
+    }
+
+    func startPlaying(bookDetailsModel: BookDetailsModel, delegate: PlayerControllerDelegate, trackIndex: Int?) {
+        preparePlayer(bookDetailsModel: bookDetailsModel, delegate: delegate, trackIndex: trackIndex)
+        playAudio()
+    }
+    
+    func getCurentAudiobookMediaModel() -> MediaModel? {
+        guard let audiobookMediaModels = audiobookMediaModels, audiobookMediaModels.count > currentAudiobookIndex else { return nil }
+        
+        return audiobookMediaModels[currentAudiobookIndex]
+    }
+    
+    func updateMediaInfo(){
+        guard let audioPlayer = audioPlayer, let model = getCurentAudiobookMediaModel() else { return }
+        
+        let artistName = model.artist
+        let songName = model.name
+        
+        if let artwork = artwork {
+            MPNowPlayingInfoCenter.default().nowPlayingInfo = [MPMediaItemPropertyArtist : artistName,  MPMediaItemPropertyTitle : songName, MPMediaItemPropertyPlaybackDuration:  audioLength, MPNowPlayingInfoPropertyElapsedPlaybackTime: audioPlayer.currentTime, MPMediaItemPropertyArtwork: artwork]
+        }
+        else {
+            MPNowPlayingInfoCenter.default().nowPlayingInfo = [MPMediaItemPropertyArtist : artistName,  MPMediaItemPropertyTitle : songName, MPMediaItemPropertyPlaybackDuration:  audioLength, MPNowPlayingInfoPropertyElapsedPlaybackTime: audioPlayer.currentTime]
+        }
+    }
+    
+    // Prepare audio for playing
+    func prepareAudio(){
+        guard let currentBook = currentBookDetails, let currentMediaModel = getCurentAudiobookMediaModel() else { return }
+        
+        guard let path = NSObject.audiobookPathIfExists(audioBookUrlString: currentMediaModel.url, bookSlug: currentBook.slug) else { return }
+        
+        currentAudioPath = path
+        audioPlayer = try? AVAudioPlayer(contentsOf: path)
+
+        guard let audioPlayer = audioPlayer else { return }
+        
+        do {
+            //keep alive audio at background
+            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
+        } catch _ {
+        }
+        do {
+            try AVAudioSession.sharedInstance().setActive(true)
+        } catch _ {
+        }
+        UIApplication.shared.beginReceivingRemoteControlEvents()
+        audioPlayer.delegate = self
+        audioLength = audioPlayer.duration
+        audioPlayer.prepareToPlay()
+        
+    }
+    
+    //MARK:- Player Controls Methods
+    func  playAudio(){
+        audioPlayer?.play()
+        startTimer()
+        saveCurrentTrackNumber()
+        updateMediaInfo()
+        delegate?.playerControllerDelegateTrackChanged()
+    }
+    
+    func playNextAudio(){
+        guard let audiobookMediaModels = audiobookMediaModels, currentAudiobookIndex < (audiobookMediaModels.count  - 1) else { return }
+        
+        currentAudiobookIndex += 1
+        
+        prepareAudio()
+        playAudio()
+    }
+    
+    func playPreviousAudio(){
+        
+        guard currentAudiobookIndex > 0 else { return }
+
+        currentAudiobookIndex -= 1
+        
+        prepareAudio()
+        playAudio()
+    }
+    
+    func forward() {
+        guard let audioPlayer = audioPlayer, audioPlayer.isPlaying else { return }
+        if audioPlayer.currentTime + 12 < audioPlayer.duration {
+            audioPlayer.currentTime += 10
+            updateMediaInfo()
+        }
+        else {
+            playNextAudio()
+        }
+    }
+
+    func rewind() {
+        guard let audioPlayer = audioPlayer, audioPlayer.isPlaying else { return }
+        if audioPlayer.currentTime > 10 {
+            audioPlayer.currentTime -= 10
+            updateMediaInfo()
+        }
+        else {
+            playPreviousAudio()
+        }
+    }
+        
+    func pauseAudioPlayer(){
+        audioPlayer?.pause()
+        updateMediaInfo()
+        delegate?.playerControllerDelegatePlayStateChanged()
+    }
+    
+    func playAudioPlayer(){
+        audioPlayer?.play()
+        updateMediaInfo()
+        delegate?.playerControllerDelegatePlayStateChanged()
+    }
+    
+    func changeTime(timeInterval: TimeInterval) {
+        guard let audioPlayer = audioPlayer else { return }
+        if audioLength > timeInterval {
+            audioPlayer.currentTime = timeInterval
+        }
+        updateMediaInfo()
+    }
+    
+    func saveCurrentTrackNumber(){
+        guard let bookDetailsModel = currentBookDetails else { return }
+        
+        DatabaseManager.shared.updateCurrentChapters(bookDetailsModel: bookDetailsModel, currentChapter: nil, totalChapters: nil, currentAudioChapter: currentAudiobookIndex, totalAudioChapters: nil)
+    }
+
+
+    func startTimer(){
+        if timer == nil {
+            timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(PlayerController.update(_:)), userInfo: nil,repeats: true)
+            timer?.fire()
+        }
+        else if timer!.isValid == false {
+            timer?.invalidate()
+            timer = nil
+            
+            timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(PlayerController.update(_:)), userInfo: nil,repeats: true)
+            timer?.fire()
+        }
+    }
+    
+    func stopTimer(){
+        timer?.invalidate()
+        
+    }
+    
+    @objc func update(_ timer: Timer){
+        guard let audioPlayer = audioPlayer else { return }
+        if !audioPlayer.isPlaying{
+            return
+        }
+        
+        delegate?.playerControllerDelegateUpdatePlayerProgress(timeInterval: audioPlayer.currentTime)
+    }
+    
+    
+    /* audioPlayerDidFinishPlaying:successfully: is called when a sound has finished playing. This method is NOT called if the player is stopped due to an interruption. */
+    public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
+        if flag == true {
+            playNextAudio()
+        }
+    }
+
+    @objc func changePlaybackPositionCommand(event: MPChangePlaybackPositionCommandEvent) -> MPRemoteCommandHandlerStatus{
+        guard let audioPlayer = audioPlayer else { return .commandFailed }
+        
+        if audioPlayer.duration - 5 < event.positionTime && audioPlayer.duration > 5 {
+            audioPlayer.currentTime = audioPlayer.duration - 5
+        }
+        else {
+            audioPlayer.currentTime = event.positionTime
+        }
+        updateMediaInfo()
+        return .success
+    }
+
+    @objc func nextTrackCommand() -> Void{
+        forward()
+    }
+    
+    @objc func previousTrackCommand() -> Void{
+        rewind()
+    }
+    
+    @objc func playCommand() -> Void{
+        playAudioPlayer()
+    }
+    
+    @objc func pauseCommand() -> Void{
+        pauseAudioPlayer()
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Player/PlayerItemTableViewCell.swift b/iOS/WolneLektury/Screens/Player/PlayerItemTableViewCell.swift
new file mode 100644 (file)
index 0000000..415a87c
--- /dev/null
@@ -0,0 +1,32 @@
+//
+//  PlayerItemTableViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 22/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class PlayerItemTableViewCell: UITableViewCell {
+
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var playingIcon: UIImageView!
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        // Initialization code
+        selectionStyle = .none
+        backgroundColor = .clear
+        contentView.backgroundColor = .clear
+    }
+    
+    func setup(mediaModel: MediaModel, isPlaying: Bool) {
+        titleLabel.text = mediaModel.name
+    }
+    
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        super.setSelected(selected, animated: animated)
+        playingIcon.isHidden = !selected
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Player/PlayerViewController.swift b/iOS/WolneLektury/Screens/Player/PlayerViewController.swift
new file mode 100644 (file)
index 0000000..777fee9
--- /dev/null
@@ -0,0 +1,270 @@
+//
+//  PlayerViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 07/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import AVFoundation
+import MediaPlayer
+import Kingfisher
+
+class PlayerViewController: WLViewController {
+
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var bgImageView: UIImageView!
+    @IBOutlet weak var bgOverlayView: UIView!
+    @IBOutlet weak var miniatureImageView: UIImageView!
+    @IBOutlet weak var tableView: UITableView!
+    @IBOutlet weak var backButton: UIButton!
+    @IBOutlet weak var toggleListButton: UIButton!
+    @IBOutlet weak var nextButton: UIButton!
+    @IBOutlet weak var prevButton: UIButton!
+    @IBOutlet weak var forwardButton: UIButton!
+    @IBOutlet weak var rewindButton: UIButton!
+    @IBOutlet weak var playPauseButton: UIButton!
+    @IBOutlet weak var trackNumberLabel: UILabel!
+    @IBOutlet weak var trackTitleLabel: UILabel!
+    @IBOutlet weak var trackCurrentTimeLabel: UILabel!
+    @IBOutlet weak var trackTimeLabel: UILabel!
+    @IBOutlet weak var progressSlider: UISlider!
+    @IBOutlet weak var miniatureImageWidthConstraint: NSLayoutConstraint!
+    @IBOutlet weak var trackTitleLabelTopConstraint: NSLayoutConstraint!
+
+    var bookDetailsModel: BookDetailsModel!
+    var mediaModels: [MediaModel]!
+    var selectedRow: Int?
+    
+    static func instance(bookDetailsModel: BookDetailsModel) -> PlayerViewController{
+        let controller = PlayerViewController.instance()
+        controller.bookDetailsModel = bookDetailsModel
+        controller.mediaModels = bookDetailsModel.getAudiobookMediaModels()
+        return controller
+    }
+
+    open override var preferredStatusBarStyle : UIStatusBarStyle {
+        return .lightContent
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        tableView.separatorStyle = .none
+        tableView.isHidden = true
+        
+        if UIScreen.main.bounds.size.width == 320 || UIScreen.main.bounds.size.height == 320 {
+            miniatureImageWidthConstraint.constant = 100
+        }
+        
+        bgOverlayView.alpha = 0.7
+        bgOverlayView.backgroundColor = bookDetailsModel.bgColor
+        let titleAttributedText = bookDetailsModel.getAttributedAuthorAndTitle(titleFont: UIFont.systemFont(ofSize: 21, weight: .light), descFont: UIFont.systemFont(ofSize: 28, weight: .light))
+        titleLabel.attributedText = titleAttributedText
+        
+        if let bookDetails = PlayerController.shared.currentBookDetails, bookDetails.slug == bookDetailsModel.slug {
+            PlayerController.shared.startOrContinuePlaying(bookDetailsModel: bookDetailsModel, delegate: self)
+        }
+        else {
+            PlayerController.shared.stopAndClear()
+            PlayerController.shared.preparePlayer(bookDetailsModel: bookDetailsModel, delegate: self, trackIndex: 0)
+        }
+        
+        if mediaModels.count < 2 {
+            trackNumberLabel.isHidden = true
+            trackNumberLabel.text = ""
+            toggleListButton.isHidden = true
+            trackTitleLabelTopConstraint.constant = 10
+        }
+
+        refreshButtonsVisibility()
+        
+        if let url = bookDetailsModel.getCoverThumbUrl(){
+            ImageDownloader.default.downloadImage(with: url, options: [], progressBlock: nil) {
+                [weak self] (image, error, url, data) in
+                if let image = image{
+                    self?.bgImageView.image = image
+                    self?.miniatureImageView.image = image
+                    PlayerController.shared.setCoverImage(image: image)
+                }
+            }
+        }
+    }
+    
+    // ############################################
+    // MARK: - Private
+    // ############################################
+
+    private func toggleList() {
+        tableView.isHidden = !tableView.isHidden
+    }
+    
+    private func startNextTrack() {
+        PlayerController.shared.playNextAudio()
+    }
+
+    private func startPrevTrack() {
+        PlayerController.shared.playPreviousAudio()
+    }
+    
+    //This returns song length
+    private func calculateTimeFromNSTimeInterval(_ duration:TimeInterval) ->(hour: String, minute:String, second:String){
+        let hour_   = abs(Int(duration)/3600)
+        let minute_ = abs(Int((duration/60).truncatingRemainder(dividingBy: 60)))
+        let second_ = abs(Int(duration.truncatingRemainder(dividingBy: 60)))
+        
+        var hour = hour_ > 9 ? "\(hour_)" : "0\(hour_)"
+        if hour_ == 0 {
+            hour = ""
+        }
+        let minute = minute_ > 9 ? "\(minute_)" : "0\(minute_)"
+        let second = second_ > 9 ? "\(second_)" : "0\(second_)"
+        return (hour, minute,second)
+    }
+
+    fileprivate func updateCurrentTime(timeInterval: TimeInterval?) {
+        
+        var ti = timeInterval
+        if ti == nil {
+            ti = PlayerController.shared.getCurrentTime()
+        }
+        
+        if let ti = ti {
+            let timeIntervalFloat = Float(ti)
+            progressSlider.value = timeIntervalFloat
+            let time = calculateTimeFromNSTimeInterval(ti)
+            if time.hour.count == 0 {
+                trackCurrentTimeLabel.text = "\(time.minute):\(time.second)"
+            }
+            else {
+                trackCurrentTimeLabel.text = "\(time.hour):\(time.minute):\(time.second)"
+            }
+        }
+    }
+
+    func refreshButtonsVisibility(){
+        
+        prevButton.isHidden = true
+        nextButton.isHidden = true
+        
+        let currentAudiobookIndex = PlayerController.shared.currentAudiobookIndex
+        let allMediaCount = mediaModels.count
+        
+        if currentAudiobookIndex > 0{
+            prevButton.isHidden = false
+        }
+        if allMediaCount > currentAudiobookIndex + 1 {
+            nextButton.isHidden = false
+        }
+    }
+
+    // ############################################
+    // MARK: - Action
+    // ############################################
+    
+    @IBAction func backButtonAction(sender: UIButton) {
+        PlayerController.shared.delegate = nil
+        navigationController?.popViewController(animated: true)
+    }
+    
+    @IBAction func toggleListButtonAction(sender: UIButton) {
+        toggleList()
+    }
+    
+    @IBAction func nextButtonAction(sender: UIButton) {
+        PlayerController.shared.playNextAudio()
+    }
+    
+    @IBAction func prevButtonAction(sender: UIButton) {
+        PlayerController.shared.playPreviousAudio()
+    }
+    
+    @IBAction func forwardButtonAction(sender: UIButton) {
+        PlayerController.shared.forward()
+    }
+    
+    @IBAction func rewindButtonAction(sender: UIButton) {
+        PlayerController.shared.rewind()
+    }
+    
+    @IBAction func playPauseButtonAction(sender: UIButton) {
+        let player = PlayerController.shared
+        if player.isPlaying() {
+            player.pauseAudioPlayer()
+        }
+        else {
+            if player.audioPlayer == nil {
+                player.startOrContinuePlaying(bookDetailsModel: bookDetailsModel, delegate: self)
+            }
+            else {
+                player.playAudio()
+            }
+        }
+        playPauseButton.setImage(player.isPlaying() ? #imageLiteral(resourceName: "player_controls_pause") : #imageLiteral(resourceName: "player_controls_play"), for: .normal)
+    }
+    
+    @IBAction func progressSliderAction(sender: UISlider) {
+        PlayerController.shared.changeTime(timeInterval: TimeInterval(sender.value))
+    }
+}
+
+extension PlayerViewController: UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return mediaModels.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerItemTableViewCell", for: indexPath) as! PlayerItemTableViewCell
+        let isSelected = PlayerController.shared.currentAudiobookIndex == indexPath.row
+        cell.setup(mediaModel:  mediaModels[indexPath.row], isPlaying: isSelected)
+        return cell
+    }
+}
+
+extension PlayerViewController: UITableViewDelegate {
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        if mediaModels.count > indexPath.row {
+            
+            PlayerController.shared.startPlaying(bookDetailsModel: bookDetailsModel, delegate: self, trackIndex: indexPath.row)
+        }
+    }
+}
+
+extension PlayerViewController: PlayerControllerDelegate {
+    
+    func playerControllerDelegateTrackChanged() {
+        
+        let player = PlayerController.shared
+        progressSlider.maximumValue = Float(player.audioLength)
+        
+        updateCurrentTime(timeInterval: player.getCurrentTime())
+        trackNumberLabel.text = String(format: "player_chapter_number".localized, player.currentAudiobookIndex + 1)
+        
+        if let currentModel = player.getCurentAudiobookMediaModel() {
+            trackTitleLabel.text = currentModel.name
+            let time = calculateTimeFromNSTimeInterval(player.audioLength)
+            if time.hour.count == 0 {
+                trackTimeLabel.text = "\(time.minute):\(time.second)"
+            }
+            else {
+                trackTimeLabel.text = "\(time.hour):\(time.minute):\(time.second)"
+            }
+        }
+        
+        playPauseButton.setImage(player.isPlaying() ? #imageLiteral(resourceName: "player_controls_pause") : #imageLiteral(resourceName: "player_controls_play"), for: .normal)
+        
+        let newSelectedIndex =  IndexPath(row: PlayerController.shared.currentAudiobookIndex, section: 0)
+        tableView.selectRow(at: newSelectedIndex, animated: true, scrollPosition: .middle)
+        
+        refreshButtonsVisibility()
+    }
+    
+    func playerControllerDelegateUpdatePlayerProgress(timeInterval: TimeInterval) {
+        updateCurrentTime(timeInterval: timeInterval)
+    }
+
+    func playerControllerDelegatePlayStateChanged() {
+        playPauseButton.setImage(PlayerController.shared.isPlaying() ? #imageLiteral(resourceName: "player_controls_pause") : #imageLiteral(resourceName: "player_controls_play"), for: .normal)
+    }
+}
+
diff --git a/iOS/WolneLektury/Screens/Search/Cells/SearchFilterCollectionViewCell.swift b/iOS/WolneLektury/Screens/Search/Cells/SearchFilterCollectionViewCell.swift
new file mode 100644 (file)
index 0000000..98552da
--- /dev/null
@@ -0,0 +1,27 @@
+//
+//  SearchFilterCollectionViewCell.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 13/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class SearchFilterCollectionViewCell: UICollectionViewCell {
+
+    @IBOutlet weak var closeImageView: UIImageView!
+    @IBOutlet weak var bgView: UIView!
+    @IBOutlet weak var titleLabel: UILabel!
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        bgView.layer.cornerRadius = 12
+        bgView.layer.borderColor = UIColor.white.cgColor
+        bgView.layer.borderWidth = 1
+        closeImageView.tintColor = .white
+    }
+    
+    func setup(categoryModel: CategoryModel) {
+        titleLabel.text = categoryModel.name
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Search/Cells/SearchFilterCollectionViewCell.xib b/iOS/WolneLektury/Screens/Search/Cells/SearchFilterCollectionViewCell.xib
new file mode 100644 (file)
index 0000000..4eff7be
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="SearchFilterCollectionViewCell" customModule="WolneLektury" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="181" height="44"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                <rect key="frame" x="0.0" y="0.0" width="181" height="44"/>
+                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                <subviews>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Nva-d8-i6t">
+                        <rect key="frame" x="4" y="9" width="173" height="26"/>
+                        <subviews>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fow-zA-bpI">
+                                <rect key="frame" x="10" y="7" width="135" height="12"/>
+                                <fontDescription key="fontDescription" type="system" weight="medium" pointSize="10"/>
+                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="navbar_close-small" translatesAutoresizingMaskIntoConstraints="NO" id="o8d-qe-icZ">
+                                <rect key="frame" x="155" y="9" width="8" height="8"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="8" id="8z7-wb-e3P"/>
+                                    <constraint firstAttribute="width" constant="8" id="T7P-wR-VHu"/>
+                                </constraints>
+                            </imageView>
+                        </subviews>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="o8d-qe-icZ" firstAttribute="centerY" secondItem="Nva-d8-i6t" secondAttribute="centerY" id="9Vt-bT-Nl6"/>
+                            <constraint firstItem="o8d-qe-icZ" firstAttribute="leading" secondItem="fow-zA-bpI" secondAttribute="trailing" constant="10" id="Kss-Ho-SNe"/>
+                            <constraint firstItem="fow-zA-bpI" firstAttribute="leading" secondItem="Nva-d8-i6t" secondAttribute="leading" constant="10" id="WVg-6A-mAf"/>
+                            <constraint firstAttribute="trailing" secondItem="o8d-qe-icZ" secondAttribute="trailing" constant="10" id="dk1-ih-bxG"/>
+                            <constraint firstAttribute="height" constant="26" id="kWf-zO-agm"/>
+                            <constraint firstItem="fow-zA-bpI" firstAttribute="centerY" secondItem="o8d-qe-icZ" secondAttribute="centerY" id="pJS-P6-tVw"/>
+                        </constraints>
+                    </view>
+                </subviews>
+            </view>
+            <constraints>
+                <constraint firstItem="Nva-d8-i6t" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="4" id="BTp-mh-yd2"/>
+                <constraint firstItem="Nva-d8-i6t" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" id="EH7-TQ-TKr"/>
+                <constraint firstAttribute="trailing" secondItem="Nva-d8-i6t" secondAttribute="trailing" constant="4" id="Tjl-ql-jM7"/>
+            </constraints>
+            <viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
+            <size key="customSize" width="181" height="40"/>
+            <connections>
+                <outlet property="bgView" destination="Nva-d8-i6t" id="JLc-zu-XDN"/>
+                <outlet property="closeImageView" destination="o8d-qe-icZ" id="ZEi-CL-DSi"/>
+                <outlet property="titleLabel" destination="fow-zA-bpI" id="PJa-h9-MSR"/>
+            </connections>
+            <point key="canvasLocation" x="99.5" y="54"/>
+        </collectionViewCell>
+    </objects>
+    <resources>
+        <image name="navbar_close-small" width="7" height="7"/>
+    </resources>
+</document>
diff --git a/iOS/WolneLektury/Screens/Search/SearchFiltersManager.swift b/iOS/WolneLektury/Screens/Search/SearchFiltersManager.swift
new file mode 100644 (file)
index 0000000..a8adb92
--- /dev/null
@@ -0,0 +1,224 @@
+//
+//  SearchViewFiltersManager.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 16/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+protocol SearchFiltersManagerDelegate: class {
+    func searchFiltersManagerFiltersChanged(reloadData: Bool)
+}
+
+class SearchFiltersManager: NSObject{
+    
+    init(delegate: SearchFiltersManagerDelegate) {
+        self.delegate = delegate
+    }
+    
+    weak var delegate: SearchFiltersManagerDelegate?
+    
+    enum FilterType{
+        case onlyLectures
+        case hasAudiobook
+        case epochs
+        case kinds
+        case genres
+    }
+    
+    lazy var onlyLecturesCategory : CategoryModel = {
+        var mdl = CategoryModel()
+        mdl.name = "only_lecture".localized
+        mdl.slug = "only_lectures_slug"
+        mdl.checked = false
+        return mdl
+    }()
+
+    lazy var hasAudiobookCategory : CategoryModel = {
+        var mdl = CategoryModel()
+        mdl.name = "has_audiobook".localized
+        mdl.slug = "has_audiobook_slug"
+        mdl.checked = false
+        return mdl
+    }()
+
+    var selectedKindsArray = [CategoryModel]()
+    var selectedEpochsArray = [CategoryModel]()
+    var selectedGenresArray = [CategoryModel]()
+    
+    func numberOfFilters() -> Int{
+        return (onlyLecturesCategory.checked ? 1 : 0) + (hasAudiobookCategory.checked ? 1 : 0) + selectedKindsArray.count + selectedGenresArray.count + selectedEpochsArray.count
+    }
+    
+    func removeFilter(atIndex: Int) {
+        
+        if let obj = getFilterTypeAndIndex(forIndex: atIndex){
+                
+            print("removeFilter getFilterType \(obj.0) AndIndex \(obj.1)")
+            switch obj.0{
+            case .onlyLectures:
+                onlyLecturesCategory.checked = false
+            case .hasAudiobook:
+                hasAudiobookCategory.checked = false
+            case .kinds:
+                print("remove selectedKindsArray.count: \(selectedKindsArray.count), index: \(obj.1)")
+                selectedKindsArray.remove(at: obj.1)
+            case .epochs:
+                print("remove selectedEpochsArray.count: \(selectedEpochsArray.count), index: \(obj.1)")
+                selectedEpochsArray.remove(at: obj.1)
+            case .genres:
+                print("remove selectedGenresArray.count: \(selectedGenresArray.count), index: \(obj.1)")
+                selectedGenresArray.remove(at: obj.1)
+            }
+        }
+    }
+    
+    func getFilterArrayAndIndex(forIndex: Int) -> ([CategoryModel], Int)? {
+        if let obj = getFilterTypeAndIndex(forIndex: forIndex){
+            print("FilterArrayAndIndex getFilterType \(obj.0) AndIndex \(obj.1)")
+
+            switch obj.0{
+            case .onlyLectures:
+                return ([onlyLecturesCategory], 0)
+            case .hasAudiobook:
+                return ([hasAudiobookCategory], 0)
+            case .kinds:
+                print("selectedKindsArray.count: \(selectedKindsArray.count), index: \(obj.1)")
+                return (selectedKindsArray, obj.1)
+            case .epochs:
+                print("selectedEpochsArray.count: \(selectedEpochsArray.count), index: \(obj.1)")
+                return (selectedEpochsArray, obj.1)
+            case .genres:
+                print("selectedGenresArray.count: \(selectedGenresArray.count), index: \(obj.1)")
+                return (selectedGenresArray, obj.1)
+            }
+        }
+        return nil
+    }
+    
+    func getFilterTypeAndIndex(forIndex: Int) -> (FilterType, Int)? {
+        
+        var index = forIndex
+        if onlyLecturesCategory.checked{
+            if forIndex == 0{
+                return (.onlyLectures, 0)
+            }
+            else{
+                index -= 1
+            }
+        }
+        
+        if hasAudiobookCategory.checked {
+            if forIndex == 0 || (forIndex == 1 && onlyLecturesCategory.checked){
+                return (.hasAudiobook, 0)
+            }
+            else {
+                index -= 1
+            }
+        }
+        
+        if index < selectedEpochsArray.count{
+            return (.epochs, index)
+        }
+        else if index < (selectedEpochsArray.count + selectedKindsArray.count){
+            return (.kinds, index - selectedEpochsArray.count)
+        }
+        else if index < (selectedEpochsArray.count + selectedKindsArray.count + selectedGenresArray.count){
+            return (.genres, index - selectedEpochsArray.count - selectedKindsArray.count )
+        }
+
+        return nil
+    }
+    
+    func getFilter(forIndex: Int) -> CategoryModel? {
+        
+        if let obj = getFilterArrayAndIndex(forIndex: forIndex){
+            return obj.0[obj.1]
+        }
+        return nil
+    }
+    
+    func getFilterForApi(filterType: FilterType) -> String? {
+        
+        var value: String = ""
+        switch filterType {
+        case .onlyLectures:
+            return onlyLecturesCategory.slug
+        case .hasAudiobook:
+            return hasAudiobookCategory.slug
+        case .epochs:
+            value = selectedEpochsArray.map({$0.slug}).joined(separator: ",")
+        case .genres:
+            value = selectedGenresArray.map({$0.slug}).joined(separator: ",")
+        case .kinds:
+            value = selectedKindsArray.map({$0.slug}).joined(separator: ",")
+        }
+        
+        return value.count > 0 ? value : nil
+    }
+    
+    func getParametersForApi() -> FilterBooksParameters {
+        let params = FilterBooksParameters()
+        params.epochs = getFilterForApi(filterType: .epochs)
+        params.genres = getFilterForApi(filterType: .genres)
+        params.kinds = getFilterForApi(filterType: .kinds)
+        params.onlyLectures = onlyLecturesCategory.checked
+        params.hasAudiobook = hasAudiobookCategory.checked
+
+        return params
+    }
+    
+    func clearFilters() {
+        selectedKindsArray = [CategoryModel]()
+        selectedEpochsArray = [CategoryModel]()
+        selectedGenresArray = [CategoryModel]()
+        onlyLecturesCategory.checked = false
+        hasAudiobookCategory.checked = false
+        delegate?.searchFiltersManagerFiltersChanged(reloadData: true)
+    }
+}
+
+extension SearchFiltersManager: UICollectionViewDataSource{
+    
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
+        let numerOfFilters = numberOfFilters()
+        print("numerOfFilters += \(numerOfFilters)")
+        return numerOfFilters
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
+        
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SearchFilterCollectionViewCell", for: indexPath) as! SearchFilterCollectionViewCell
+        print("beforeGetFilter")
+        if let filter = getFilter(forIndex: indexPath.row){
+            cell.setup(categoryModel: filter)
+        }
+        print("afterGetFilter")
+        return cell
+    }
+}
+
+extension SearchFiltersManager: UICollectionViewDelegate{
+    
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        print("didSelectItems.row += \(indexPath.row)")
+        
+        removeFilter(atIndex: indexPath.row)
+        collectionView.deleteItems(at: [indexPath])
+        
+        delegate?.searchFiltersManagerFiltersChanged(reloadData: numberOfFilters() == 0)
+    }
+}
+
+extension SearchFiltersManager: UICollectionViewDelegateFlowLayout {
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+        var size = CGSize(width: 200, height: 44)
+        if let filter = getFilter(forIndex: indexPath.row){
+            
+            size.width = filter.name.sizeOf(UIFont.systemFont(ofSize: 10, weight: .medium)).width + 14 + 35
+        }
+        return size
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Search/SearchViewController.swift b/iOS/WolneLektury/Screens/Search/SearchViewController.swift
new file mode 100644 (file)
index 0000000..d2ff84e
--- /dev/null
@@ -0,0 +1,349 @@
+//
+//  SearchViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 13/06/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import Toast_Swift
+
+enum SearchViewControllerType{
+    case search
+}
+
+protocol DataLoaderDelegate: class{
+    func dataLoaderFetchingServerFinished()
+    func dataLoaderFetchingServerSuccess()
+    func dataLoaderFetchingServerFailed(showRefreshButton: Bool)
+}
+
+class DataLoader: NSObject{
+    var dataSource = [BookModel]()
+    var loadingMore = false
+    var canLoadMore = false
+    var currentParams =  FilterBooksParameters()
+    weak var delegate: DataLoaderDelegate?
+    
+    init(delegate: DataLoaderDelegate) {
+        self.delegate = delegate
+    }
+    
+    func setNewFilters(filterParams: FilterBooksParameters) {
+        let newParams = filterParams
+        newParams.search = currentParams.search
+        currentParams = newParams
+        dataSource = [BookModel]()
+    }
+    
+    func loadData(more: Bool){
+        
+        let params = currentParams
+        if more{
+            loadingMore = true
+            currentParams.after = dataSource.last?.key
+        }
+        
+        appDelegate.syncManager.filterBooks(params: params) { [weak self] (result) in
+            
+            guard let strongSelf = self else{
+                return
+            }
+            
+            strongSelf.delegate?.dataLoaderFetchingServerFinished()
+            strongSelf.loadingMore = false
+            
+            guard strongSelf.currentParams == params else{
+                print("strongSelf.currentParams != cp")
+                return
+            }
+            
+            switch result {
+            case .success(let model):
+                
+                let array = model as! [BookModel]
+                strongSelf.canLoadMore = array.count == FilterBooksParameters.SEARCH_ITEMS_COUNT
+                strongSelf.dataSource.append(contentsOf: array)
+                strongSelf.delegate?.dataLoaderFetchingServerSuccess()
+                
+                
+            case .failure(let error):
+                if strongSelf.dataSource.count > 0{
+                    strongSelf.canLoadMore = false
+                    strongSelf.delegate?.dataLoaderFetchingServerFailed(showRefreshButton: false)
+                }
+                else{
+                    strongSelf.delegate?.dataLoaderFetchingServerFailed(showRefreshButton: true)
+                }
+                break
+            }
+        }
+    }
+}
+
+class SearchViewController: MainViewController {
+
+    @IBOutlet weak var filterCloseButton: UIButton!
+    @IBOutlet weak var filterContainer: UIView!
+    @IBOutlet weak var filterCollectionView: UICollectionView!
+    @IBOutlet weak var filterTopConstraint: NSLayoutConstraint!
+    @IBOutlet weak var tableView: UITableView!
+    @IBOutlet weak var footerViewActivityIndicator: UIActivityIndicatorView!
+    @IBOutlet weak var noDataLabel: UILabel!
+    @IBOutlet weak var refreshDataButton: ActivityIndicatorButton!
+    
+    var dataLoader: DataLoader!
+    var filtersManager : SearchFiltersManager!
+    
+    var controllerType = SearchViewControllerType.search
+    
+    lazy var searchBar = UISearchBar(frame: CGRect.zero)
+    var currentSearchQuery = ""
+    
+    class func instance(type: SearchViewControllerType) -> SearchViewController{
+        let controller = SearchViewController.instance()
+        controller.controllerType = type
+        controller.dataLoader = DataLoader(delegate: controller)
+        return controller
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        title = "nav_search".localized
+        
+        filterCloseButton.layer.cornerRadius = 14
+        filterCloseButton.tintColor = Constants.Colors.darkGreenBgColor()
+        
+        noDataLabel.text = "no_results".localized
+        noDataLabel.isHidden = true
+
+        refreshDataButton.tintColor = .black
+        
+        filtersManager = SearchFiltersManager(delegate: self)
+        setupTableView()
+        setupCollectionView()
+        filtersChanged(initially: true, reloadData: true)
+        setupSearchBar()
+//        searchBar.becomeFirstResponder()
+    }
+    
+    func setupSearchBar() {
+        searchBar.placeholder = "search_placeholder".localized
+        searchBar.enablesReturnKeyAutomatically = false
+        UITextField.appearance(whenContainedInInstancesOf: [type(of: searchBar)]).tintColor = UIColor.gray
+        searchBar.delegate = self
+        searchBar.translatesAutoresizingMaskIntoConstraints = false
+
+        searchBar.layoutIfNeeded()
+        searchBar.sizeToFit()
+        searchBar.translatesAutoresizingMaskIntoConstraints = true // make nav bar happy
+
+        navigationItem.titleView = searchBar
+//        if #available(iOS 11.0, *) {
+////            searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
+//        }
+        let singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.singleTap(sender:)))
+        singleTapGestureRecognizer.numberOfTapsRequired = 1
+        singleTapGestureRecognizer.isEnabled = true
+        singleTapGestureRecognizer.cancelsTouchesInView = false
+        self.view.addGestureRecognizer(singleTapGestureRecognizer)
+    }
+    
+    func setupTableView(){
+        tableView.registerNib(name: "BookTableViewCell")
+        tableView.separatorStyle = .none
+        tableView.delegate = self
+        tableView.dataSource = self
+        tableView.rowHeight = 137
+        var insets = tableView.contentInset
+        insets.top = 11
+        tableView.contentInset = insets
+        footerViewActivityIndicator.color = Constants.Colors.darkGreenBgColor()
+        footerViewActivityIndicator.hidesWhenStopped = true
+    }
+    
+    @objc func singleTap(sender: UITapGestureRecognizer) {
+        self.searchBar.resignFirstResponder()
+    }
+
+    @IBAction func filterCloseButtonAction(_ sender: Any) {
+        filtersManager.clearFilters()
+    }
+    
+    @IBAction func refreshDataButtonAction(_ sender: Any) {
+        loadData(more: false, fromRefreshControl: false)
+    }
+
+    func setupCollectionView(){
+        
+        filterCollectionView.backgroundColor = UIColor.clear
+        filterCollectionView.delegate = filtersManager
+        filterCollectionView.dataSource = filtersManager
+        filterCollectionView.register(UINib.init(nibName: "SearchFilterCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "SearchFilterCollectionViewCell")
+    }
+
+    func filtersChanged(initially: Bool = false, reloadData: Bool){
+        if reloadData{
+            filterCollectionView.reloadData()
+            
+            if filtersManager.numberOfFilters() == 0{
+                if filterTopConstraint.constant != 0 && !initially{
+                    filterTopConstraint.constant = 0
+
+                    UIView.animate(withDuration:0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: [], animations: {
+                        self.view.layoutIfNeeded()
+                    }, completion: {
+                        [weak self] (value: Bool) in
+                        self?.filterContainer.isHidden = true
+                    })
+                }
+                else{
+                    filterTopConstraint.constant = 0
+                    filterContainer.isHidden = true
+                }
+            }
+            else{
+                filterContainer.isHidden = false
+                filterTopConstraint.constant = 44
+            }
+        }
+        
+        dataLoader.setNewFilters(filterParams: filtersManager.getParametersForApi())
+        
+        tableView.reloadData()
+        
+        loadData(more: false, fromRefreshControl: false)
+    }
+    
+    func loadData(more: Bool, fromRefreshControl: Bool){
+        
+        refreshDataButton.setIndicatorButtonState(state: .hidden)
+        if more && dataLoader.loadingMore{
+            return
+        }
+        
+        if !fromRefreshControl{
+            footerViewActivityIndicator.startAnimating()
+        }
+        
+        dataLoader.loadData(more: more)
+    }
+    
+    @IBAction func filterButtonAction(_ sender: Any) {
+        self.navigationController?.present( WLNavigationController(rootViewController: FilterViewController.instance(delegate: self, selectedKindsArray: filtersManager.selectedKindsArray, selectedEpochsArray: filtersManager.selectedEpochsArray, selectedGenresArray: filtersManager.selectedGenresArray, onlyLectures: filtersManager.onlyLecturesCategory.checked, hasAudiobook: filtersManager.hasAudiobookCategory.checked)) , animated: true, completion: nil)
+    }
+    
+    @objc func getDataForCurrentSearchQuery(){
+        if currentSearchQuery.count > 0 {
+            dataLoader.currentParams.search = currentSearchQuery
+        }
+        else{
+            dataLoader.currentParams.search = nil
+        }
+        dataLoader.dataSource = [BookModel]()
+        tableView.reloadData()
+        loadData(more: false, fromRefreshControl: false)
+    }
+}
+
+extension SearchViewController: UITableViewDataSource{
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
+        return dataLoader.dataSource.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
+        
+        let cell = tableView.dequeueReusableCell(withIdentifier: "BookTableViewCell", for: indexPath) as! BookTableViewCell
+        cell.setup(bookModel: dataLoader.dataSource[indexPath.row])
+        return cell
+    }
+    
+    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
+        if indexPath.row > 0 && indexPath.row == (dataLoader.dataSource.count-1) && dataLoader.canLoadMore && !dataLoader.loadingMore{
+            let _ = loadData(more: true, fromRefreshControl: false)
+        }
+    }
+}
+
+extension SearchViewController: UITableViewDelegate{
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        tableView.deselectRow(at: indexPath, animated: true)
+        searchBar.resignFirstResponder()
+        if dataLoader.dataSource.count > indexPath.row{
+            navigationController?.pushViewController(BookDetailsViewController.instance(bookSlug: dataLoader.dataSource[indexPath.row].slug) , animated: true)
+        }
+    }
+}
+
+extension SearchViewController: FilterViewControllerDelegate{
+    func filterViewControllerDidSelectItems(kindsArray: [CategoryModel], epochsArray: [CategoryModel], genresArray: [CategoryModel], onlyLectures: Bool, hasAudiobook: Bool, filterChanged: Bool) {
+        
+        if filterChanged{
+            filtersManager.selectedKindsArray = kindsArray
+            filtersManager.selectedEpochsArray = epochsArray
+            filtersManager.selectedGenresArray = genresArray
+            filtersManager.onlyLecturesCategory.checked = onlyLectures
+            filtersManager.hasAudiobookCategory.checked = hasAudiobook
+
+            filtersChanged(reloadData: true)
+        }
+        self.navigationController?.presentedViewController?.dismiss(animated: true, completion: nil)
+    }
+}
+
+extension SearchViewController: UISearchBarDelegate{
+    
+    func textFieldShouldReturn(_ textField: UITextField) -> Bool {// called when 'return' key pressed. return NO to ignore.
+        textField.resignFirstResponder()
+        return true
+    }
+
+    func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
+        var searchQueryCandidate = ""
+        if let txt = searchBar.text{
+            searchQueryCandidate = txt.trimmed
+        }
+
+        if(currentSearchQuery != searchQueryCandidate){
+            currentSearchQuery = searchQueryCandidate
+            getDataForCurrentSearchQuery()
+        }
+    }
+    
+    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
+        searchBar.resignFirstResponder()
+    }
+    
+    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
+        currentSearchQuery = searchText.trimmed
+        
+        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(SearchViewController.getDataForCurrentSearchQuery), object: nil)
+        self.perform(#selector(SearchViewController.getDataForCurrentSearchQuery), with: nil, afterDelay: 0.8)
+    }
+}
+
+extension SearchViewController: SearchFiltersManagerDelegate{
+    func searchFiltersManagerFiltersChanged(reloadData: Bool) {
+        filtersChanged(reloadData: reloadData)
+    }
+}
+
+extension SearchViewController: DataLoaderDelegate{
+    func dataLoaderFetchingServerFinished() {
+        footerViewActivityIndicator.stopAnimating()
+    }
+    
+    func dataLoaderFetchingServerSuccess() {
+        noDataLabel.isHidden = dataLoader.dataSource.count > 0
+        tableView.reloadData()
+    }
+    
+    func dataLoaderFetchingServerFailed(showRefreshButton: Bool) {
+    
+        if showRefreshButton{
+            refreshDataButton.setIndicatorButtonState(state: .button)
+        }
+        view.makeToast("book_loading_error".localized, duration: 3.0, position: .bottom)
+    }
+}
diff --git a/iOS/WolneLektury/Screens/Settings/SettingsViewController.swift b/iOS/WolneLektury/Screens/Settings/SettingsViewController.swift
new file mode 100644 (file)
index 0000000..20d669c
--- /dev/null
@@ -0,0 +1,115 @@
+//
+//  SettingsViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 17/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import UserNotifications
+class SettingsViewController: MainViewController {
+
+    @IBOutlet weak var notificationsLabel: UILabel!
+    @IBOutlet weak var notificationsSwitch: UISwitch!
+    @IBOutlet weak var subscriptionLabel: UILabel!
+    @IBOutlet weak var subscriptionStatusLabel: UILabel!
+    @IBOutlet weak var deleteFilesLabel: UILabel!
+    @IBOutlet weak var deleteFilesButton: UIButton!
+    @IBOutlet weak var becomeFriendButton: UIButton!
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        title = "nav_settings".localized
+        notificationsLabel.text = "settings_notifications".localized.uppercased()
+        subscriptionLabel.text = "subscribtion_state".localized.uppercased()
+        deleteFilesLabel.text = "delete_files".localized.uppercased()
+        deleteFilesButton.text = "delete".localized.uppercased()
+        deleteFilesButton.layer.cornerRadius = 15
+        deleteFilesButton.layer.borderColor = UIColor.white.cgColor
+        deleteFilesButton.layer.borderWidth = 1.0
+        deleteFilesButton.backgroundColor = Constants.Colors.navbarBgColor()
+        
+        let userIsPremium = DatabaseManager.shared.isUserPremium()
+        subscriptionStatusLabel.text = userIsPremium ? "active".localized.uppercased() : "inactive".localized.uppercased()
+        
+        var becomeFriendButtonHidden = DatabaseManager.shared.isUserPremium()
+        if Constants.donateEnabled == false {
+            becomeFriendButtonHidden = true
+        }
+        
+        becomeFriendButton.isHidden = becomeFriendButtonHidden
+        becomeFriendButton.layer.cornerRadius = 18
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        updateNotificationsSwitchValue()
+    }
+
+    func updateNotificationsSwitchValue() {
+        if #available(iOS 10.0, *) {
+            let current = UNUserNotificationCenter.current()
+            current.getNotificationSettings(completionHandler: { [weak self] settings in
+                DispatchQueue.main.async {
+                    switch settings.authorizationStatus {
+                    case .notDetermined:
+                        self?.notificationsSwitch.setOn(false, animated: true)
+                    // Authorization request has not been made yet
+                    case .denied:
+                        self?.notificationsSwitch.setOn(false, animated: true)
+                        // User has denied authorization.
+                    // You could tell them to change this in Settings
+                    case .authorized:
+                        self?.notificationsSwitch.setOn(true, animated: true)
+                    }
+                }
+
+            })
+        } else {
+            // Fallback on earlier versions
+            if UIApplication.shared.isRegisteredForRemoteNotifications {
+                notificationsSwitch.setOn(true, animated: true)
+            } else {
+                notificationsSwitch.setOn(false, animated: true)
+            }
+        }
+    }
+    
+    @IBAction func deleteFilesButtonAction() {
+        
+        let alertController = UIAlertController(
+            title: nil,
+            message: "delete_files_question".localized,
+            preferredStyle: UIAlertControllerStyle.alert
+        )
+        
+        let yesAction = UIAlertAction(title: "yes".localized, style: UIAlertActionStyle.default) { [weak self]
+            (result : UIAlertAction) -> Void in
+            self?.deleteAllFiles()
+        }
+        
+        let noAction = UIAlertAction(title: "no".localized, style: UIAlertActionStyle.cancel) { [weak self]
+            (result : UIAlertAction) -> Void in
+        }
+        alertController.addAction(yesAction)
+        alertController.addAction(noAction)
+
+        self.present(alertController, animated: true, completion: nil)
+    }
+    
+    private func deleteAllFiles() {
+        
+        PlayerController.shared.stopAndClear()
+        DatabaseManager.shared.removeAllBooksFromDownloaded()
+        presentToast(message: "all_files_removed".localized)
+    }
+    
+    @IBAction func switchAction() {
+        UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
+    }
+    
+    @IBAction func becomeFriendButtonAction() {
+        appDelegate.mainNavigator.presentPayPalForm()
+    }
+}
diff --git a/iOS/WolneLektury/Screens/SupportUs/SupportUsViewController.swift b/iOS/WolneLektury/Screens/SupportUs/SupportUsViewController.swift
new file mode 100644 (file)
index 0000000..883d62b
--- /dev/null
@@ -0,0 +1,29 @@
+//
+//  SupportUsViewController.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 18/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class SupportUsViewController: MainViewController {
+
+    @IBOutlet weak var textView: UITextView!
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        title = "support_us".localized
+        
+        if let htmlString = try? NSAttributedString(html: "support_us_text".localized){
+            textView.attributedText =  htmlString
+        }
+        else {
+            textView.text = "support_us_text".localized
+        }
+        
+        textView.tintColor = Constants.Colors.darkGreenBgColor()
+    }
+
+}
diff --git a/iOS/WolneLektury/Utils/DatabaseManager.swift b/iOS/WolneLektury/Utils/DatabaseManager.swift
new file mode 100644 (file)
index 0000000..d157dd0
--- /dev/null
@@ -0,0 +1,146 @@
+//
+//  DatabaseManager.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 11/09/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+import RealmSwift
+
+class DatabaseManager: NSObject {
+    static let shared = DatabaseManager()
+
+    override init() {
+        super.init()
+        realmConfiguration()
+    }
+    
+    var rlmApplication:RLMApplication?
+
+    func realmConfiguration()
+    {
+        Realm.Configuration.defaultConfiguration = Realm.Configuration(
+            schemaVersion: 1,
+            migrationBlock: { migration, oldSchemaVersion in
+                /*
+                 if (oldSchemaVersion < 1) {
+                 
+                 } */
+        })
+        
+        let realm = try! Realm()
+        self.rlmApplication = realm.objects(RLMApplication.self).last
+        
+        print(realm.configuration.fileURL)
+        
+        if  self.rlmApplication  == nil
+        {
+            self.rlmApplication  = RLMApplication()
+            try! realm.write {
+                realm.add(self.rlmApplication!)
+            }
+        }
+    }
+
+    /// removing database
+    func purgeData(){
+        let realm = try! Realm()
+        self.rlmApplication = nil
+        try! realm.write {
+            realm.deleteAll()
+        }
+        
+        self.rlmApplication  = RLMApplication()
+        try! realm.write {
+            realm.add(self.rlmApplication!)
+        }
+    }
+
+    func isUserPremium()-> Bool{
+        if let premium = rlmApplication?.user?.premium, premium == true{
+            return true
+        }
+        return false
+    }
+
+    func updateUser(usernameModel: UsernameModel?){
+        self.rlmApplication?.updateUser(usernameModel: usernameModel)
+    }
+
+    func addBookToDownloaded(bookDetailsModel: BookDetailsModel) {
+        rlmApplication?.addBookToDownloaded(bookDetailsModel: bookDetailsModel)
+    }
+    
+    func removeAllBooksFromDownloaded() {
+        guard let application = rlmApplication else { return }
+        for book in application.downloadedBooks {
+            DownloadManager.sharedInstance.deleteEbook(bookSlug: book.slug)
+            DownloadManager.sharedInstance.deleteAudiobook(bookSlug: book.slug)
+        }
+        
+        application.removeAllBooksFromDownloaded()
+    }
+    
+    func removeBookFromDownloaded(bookSlug: String) -> Bool{
+        guard let application = rlmApplication else {
+            return false
+        }
+
+        DownloadManager.sharedInstance.deleteEbook(bookSlug: bookSlug)
+        DownloadManager.sharedInstance.deleteAudiobook(bookSlug: bookSlug)
+        return application.removeBookFromDownloaded(bookSlug: bookSlug)
+    }
+    
+    func getBookFromDownloaded(bookSlug: String) -> BookDetailsModel? {
+        return rlmApplication?.getBookFromDownloaded(bookSlug: bookSlug)
+    }
+
+    func updateCurrentChapters(bookDetailsModel: BookDetailsModel, currentChapter: Int?, totalChapters: Int?, currentAudioChapter: Int?, totalAudioChapters: Int?)
+    {
+        let realm = try! Realm()
+        realm.beginWrite()
+     
+        if let currentChapter = currentChapter{
+            bookDetailsModel.currentChapter = currentChapter
+        }
+        
+        if let totalChapters = totalChapters{
+            bookDetailsModel.totalChapters = totalChapters
+        }
+
+        if let currentAudioChapter = currentAudioChapter{
+            bookDetailsModel.currentAudioChapter = currentAudioChapter
+        }
+
+        if let totalAudioChapters = totalAudioChapters{
+            bookDetailsModel.totalAudioChapters = totalAudioChapters
+        }
+
+        try! realm.commitWrite()
+    }
+    
+    func removeBookFromDownloadedIfNoFiles(bookSlug: String) {
+        
+        if FileManager.default.fileExists(atPath: FileType.ebook.destinationPathWithSlug(bookSlug: bookSlug)) {
+            return
+        }
+        
+        let fileNames = try! FileManager.default.contentsOfDirectory(atPath: FileType.audiobook.destinationPathWithSlug(bookSlug: bookSlug))
+
+        if fileNames.count > 0 {
+            return
+        }
+        
+        let _ = rlmApplication?.removeBookFromDownloaded(bookSlug: bookSlug)
+    }
+    
+    func refresh(){
+        rlmApplication?.realm?.refresh()
+    }
+    
+    func setUserPremium() {
+        rlmApplication?.setUserPermium()
+    }
+}
diff --git a/iOS/WolneLektury/Utils/SharedGlobals.swift b/iOS/WolneLektury/Utils/SharedGlobals.swift
new file mode 100644 (file)
index 0000000..94e0653
--- /dev/null
@@ -0,0 +1,44 @@
+//
+//  SharedGlobals.swift
+//  WolneLektury
+//
+//  Created by Pawel Dabrowski on 23/08/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import UIKit
+
+class SharedGlobals: NSObject {
+    
+    static let shared = SharedGlobals()
+        
+    let kFirstUse = "user_first_use"
+    let kCanShowNewCardAlert = "can_show_new_card_alert"
+    
+    func isFirstUse()-> Bool{
+        let defaults = UserDefaults.standard
+        if (defaults.value(forKey: kFirstUse) as? Bool) != nil{
+            return false
+        }
+        else{
+            defaults.set(false, forKey: kFirstUse)
+            defaults.synchronize()
+            return true
+        }
+    }
+    
+    var canShowNewCardAlert: Bool{
+        get{
+            let defaults = UserDefaults.standard
+            if let value = defaults.value(forKey: kCanShowNewCardAlert) as? Bool{
+                return value
+            }
+            return true
+        }
+        set(newVal){
+            let defaults = UserDefaults.standard
+            defaults.set(newVal, forKey: kCanShowNewCardAlert)
+            defaults.synchronize()
+        }
+    }
+}
diff --git a/iOS/WolneLektury/WolneLektury.entitlements b/iOS/WolneLektury/WolneLektury.entitlements
new file mode 100644 (file)
index 0000000..903def2
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>aps-environment</key>
+       <string>development</string>
+</dict>
+</plist>
diff --git a/iOS/WolneLektury/en.lproj/Localizable.strings b/iOS/WolneLektury/en.lproj/Localizable.strings
new file mode 100644 (file)
index 0000000..4d10fd4
--- /dev/null
@@ -0,0 +1,191 @@
+/* 
+  Localizable.strings
+  WolneLektury
+
+  Created by Pawel Dabrowski on 30/05/2018.
+  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+*/
+
+"app_name"="Wolne Lektury";
+"nav_wolne_lektury"="Wolne Lektury";
+"nav_catalog"="Biblioteka";
+"nav_news"="Aktualności";
+"nav_my_collection"="Moja kolekcja";
+"nav_about"="O aplikacji";
+"nav_reading"="Teraz czytam";
+"nav_settings"="Ustawienia";
+"nav_completed"="Przeczytane";
+"nav_library"="Wolne Lektury";
+"nav_search"="Wyszukaj";
+"nav_early_access"="Wczesny dostęp";
+"nav_audiobooks"="Audiobooki";
+"nav_ebooks"="Ebooki";
+"nav_newest"="Nowości";
+"nav_recommended"="Polecane";
+"see_all"="Zobacz wszystkie";
+"open"="Otwórz";
+"close"="Zamknij";
+"no_results"="Brak wyników";
+"loading_results_failed"="Wystąpił błąd podczas ładowania danych";
+"no_search_results"="Brak rezultatów wyszukiwania";
+"book_epoch"="Epoka";
+"book_kind"="Rodzaj";
+"book_genre"="Gatunek";
+"newest"="Nowości";
+"recommended"="Polecane";
+"my_collection"="Moja kolekcja";
+"audiobooks"="Audiobooki";
+"news"="Aktualności";
+"completed"="Przeczytane";
+
+"support_us"="Wesprzyj nas";
+"pages"="Stron";
+"back"="Powrót";
+"refresh"="Odśwież";
+"forward"="Do przodu";
+"page_error"="Nie udało się załadować zawartości.\nOdśwież stronę.";
+"download_ebook"="Czytaj";
+"download_ebook_read"="Czytaj dalej";
+"download_ebook_loading"="Pobieranie";
+"download_audiobook"="Pobierz audiobook";
+"download_audiobook"="Słuchaj";
+"download_audiobook_listen"="Słuchaj dalej";
+"download_audiobook_loading"="Pobieranie";
+"downloading"="Pobieranie";
+
+"menu_filter"="Filtruj";
+"menu_search"="Wyszukaj";
+"only_lecture"="Lektury szkolne";
+"filter_epochs"="Epoki";
+"filter_genres"="Gatunki";
+"filter_kinds"="Rodzaje";
+"filters"="Filtrowanie";
+"menu_accept"="Potwierdź";
+"lectures"="Lektury";
+"book_loading_error"="Nie udało się załadować danych. Proszę spróbować ponownie.";
+"book_download_error"="Nie udało się pobrać pliku ebook\'a. Proszę spróbować ponownie.";
+"audiobook_download_error"="Nie udało się pobrać pliku audiobook\'a. Proszę spróbować ponownie.";
+"share"="Udostępnij";
+"load_epochs_failed"="Nie udało się załadować epok";
+"load_genres_failed"="Nie udało się załadować gatunków";
+"load_kinds_failed"="Nie udało się załadować rodzajów";
+"about_text"="Nie potrzebujesz już dostępu do komputera, żeby skorzystać z zasobów biblioteki WolneLektury.pl! Aplikacja mobilna daje Ci dostęp do kilku tysięcy bezpłatnych utworów oraz kilkuset audiobooków, zawsze tam, gdzie masz ochotę na przeczytanie książki!<br /><br />Wolne Lektury są najpopularniejszą w Polsce biblioteką internetową. Od 2007 roku publikujemy utwory należące do klasyki literatury oraz literaturę współczesną i udostępniamy je za darmo wszystkim zainteresowanym – znajdą tutaj dla siebie coś zarówno dzieci, młodzież, jak i dorośli. Dzięki aplikacji czytelnicy uzyskują bezpośredni i wygodny dostęp na urządzeniach mobilnych do zasobów biblioteki.<br /><br />Wszystkie utwory zamieszczone w bibliotece Wolne Lektury można zgodnie z prawem bezpłatnie przeglądać, słuchać, pobierać na swój komputer, a także udostępniać innym i cytować. Większość utworów nie jest objęta majątkowym prawem autorskim i znajduje się w <a href=\"https://domenapubliczna.org/co-to-jest-domena-publiczna/\">domenie publicznej</a>, co oznacza że można je swobodnie publikować i rozpowszechniać. Pozostałe zostały udostępnione na jednej z wolnych licencji - <a href=\"https://creativecommons.org/licenses/by-sa/3.0/\">Creative Commons</a> Uznanie Autorstwa - Na Tych Samych Warunkach 3.0.PL lub <a href=\"http://artlibre.org/licence/lal/pl/\">Licencja Wolnej Sztuki 1.3</a>.<br /><br /> Zapoznaj się z <a href=\"https://wolnelektury.pl/info/regulamin/\">Regulaminem biblioteki</a> oraz <a href=\"https://nowoczesnapolska.org.pl/prywatnosc/\">Polityką prywatności</a>.<br /><br /> Znajdziesz nas również pod adresem <a href=\"https://www.wolnelektury.pl\">www.wolnelektury.pl</a>.<br /><br /> Kontakt:";
+"about_text_contact"="wolnelektury@nowoczesnapolska.org.pl";
+"about_text_fundation"="Bibliotekę prowadzi fundacja Nowoczesna Polska.";
+"about_text_mkdn"="Aplikacja mobilna dofinansowana ze środków Ministra Kultury i Dziedzictwa Narodowego.";
+"support_us_header"="Podaruj nam swój 1%! Fundacja Nowoczesna Polska posiada status Organizacji Pożytku Publicznego dzięki czemu na rozwój biblioteki Wolne Lektury możesz przekazać 1% swojego podatku. W tym celu wypełnij odpowiednią rubrykę w rocznym zeznaniu podatkowym (PIT-36, PIT-37 lub PIT-28). W formularzu należy podać nazwę: Fundacja Nowoczesna Polska oraz numer z Krajowego Rejestru Sądowego (KRS) 0000070056.";
+"support_us_text"="Pomóż uwolnić konkretną książkę, wspierając zbiórkę na stronie <a href=\"https://nowoczesnapolska.org.pl/pomoz-nam/wesprzyj-nas/\">nowoczesnapolska.org.pl</a>.<br/><br/>Przekaż darowiznę na konto: szczegóły na stronie <a href=\"https://nowoczesnapolska.org.pl/\">Fundacji</a>.<br/><br/>Podaruj nam swój 1%! Fundacja Nowoczesna Polska posiada status Organizacji Pożytku Publicznego dzięki czemu na rozwój projektu Wolne Lektury można przekazać 1% swojego podatku. W tym celu należy wypełnić odpowiednią rubrykę w rocznym zeznaniu podatkowym (PIT-36, PIT-37 lub PIT-28). W zeznaniu należy podać nazwę: Fundacja Nowoczesna Polska oraz numer z Krajowego Rejestru Sądowego (KRS) 0000070056.";
+"support_us_one_percent"="Przekaż 1% podatku na Wolne Lektury";
+
+"button_cancel" = "Cancel";
+"button_ok" = "Ok";
+"problem_occurred" = "Wystąpił problem";
+"logged_as"="ZALOGOWANY JAKO";
+"show_profile"="ZOBACZ PROFIL";
+"sign_out"="WYLOGUJ";
+"search_placeholder" = "Szukaj...";
+"reading_now" = "Teraz czytam";
+"favourites" = "Ulubione";
+
+"login_request_token_failed"="TMP";
+"login_access_token_failed"="TMP";
+"menu_login"="Zaloguj się";
+"main_view_progress"="Trwa ładowanie danych…";
+"login_auth_callback_malformed"="TMP";
+"menu_logout"="Wyloguj";
+"logout_successful"="Wylogowano";
+"unauthorized"="Wygasły dane logowania. Proszę zalogować się ponownie.";
+"nav_premium"="Prapremiera";
+"nav_favourites"="Ulubione";
+"audiobook"="Audiobook";
+"has_audiobook"="Audiobooki";
+"nav_downloaded"="Moja kolekcja";
+"library_empty_header" = "Obecnie przygotowujemy dla Ciebie nową Prapremierę.";// Jeśli chcesz mieć do niej dostęp kliknij tutaj i zostań naszym Przyjacielem";
+"library_empty_header_logged" = "Obecnie przygotowujemy dla Ciebie nową Prapremierę.";
+"become_friend_desc" = "Powyższe dzieło dostępne jest w ramach\nprogramu Wczesnego Dostępu dla\nużytkowników, którzy zdecydowali się\nwesprzeć Wolne Lektury. Chcesz czytać\njuż teraz? Wesprzyj nas! Zostań naszym\nprzyjacielem!";
+"become_friend_button" = "Zostań przyjacielem";
+
+"become_a_friend" = "Zostań przyjacielem";
+"reading_progress" = "Przeczytano %.0f%%";
+"listening_progress" = "Odsłuchano %.0f%%";
+"player_timer_null" = "0:00";
+"news_when" = "Kiedy:";
+"news_where" = "Gdzie:";
+"retry" = "Spróbuj ponownie";
+"load_again" = "Załaduj ponownie";
+"prapremiere_info" = "Powyższe dzieło dostępne jest w ramach programu Wczesnego Dostępu dla użytkowników, którzy zdecydowali się wesprzeć Wolne Lektury. Chcesz czytać już teraz? Wesprzyj nas! Zostań naszym przyjacielem!";
+"player_chapter_number" = "Rozdział %02d";
+"label_stop" = "Stop";
+"label_pause" = "Pauza";
+"label_play" = "Odtwórz";
+"label_previous" = "Poprzedni";
+"label_next" = "Następny";
+"settings_notifications" = "Zezwalaj na powiadomienia";
+"subscribtion_state" = "Stan subskrybcji";
+"delete_files" = "Usuń wszystkie pobrane pliki";
+"active" = "Aktywny";
+"inactive" = "Nieaktywny";
+"delete" = "Usuń";
+"premium_purchase_failed" = "Płatność nie powiodła się. Prosimy o kontakt z administracją.";
+"premium_purchase_succeeded" = "Od tej pory jesteś naszym subskrybentem! Dziękujemy!";
+"login_first" = "Aby zostać naszym przyjacielem musisz się najpierw zalogować";
+"default_notification_channel_id" = "wolne_lektury_default_channel_id";
+"default_notification_topic" = "wolnelektury";
+"fetching_premium_failed" = "Nie udało się pobrać informacji o prapremierze. Spróbuj jeszcze raz!";
+"book_deleted_message" = "Pozycja została usunięta z pamięci twojego urządzenia";
+"all_files_removed" = "Wszystkie pliki zostały usunięte z Twojego urządzenia";
+"removing_all_files" = "Trwa usuwanie plików...";
+"no_thanks" = "Nie dziękuje";
+"no_prapremiere_title" = "Prapremiera";
+"login" = "Zaloguj";
+"downloaded_empty_list" = "Brak pobranych plików";
+"audiobooks_empty_list" = "Brak audiobooków";
+"newest_empty_list" = "Brak najnowszych pozycji";
+"recommended_empty_list" = "Brak rekomendowanych pozycji";
+"reading_now_empty_list" = "Nie czytasz jeszcze żadnej pozycji";
+"favourites_empty_list" = "Nie masz jeszcze ulubionych pozycji";
+"news_empty_list" = "Brak aktualności";
+"completed_empty_list" = "Nie przeczytałeś jeszcze żadnej pozycji";
+"subscription_lost" = "Subskrypcja wygasła";
+"install_chrome" = "Twój system nie posiada przeglądarki. Zalecamy instalację Google Chrom i ponowienie tej próby.";
+"library_now_reading" = "Teraz czytam";
+"no_prapremiere_message" = "Obecnie przygotowujemy dla Ciebie nową Prapremierę. Jeśli chcesz mieć do niej dostęp kliknij tutaj i zostań naszym Przyjacielem";
+"no_prapremiere_message_logged" = "Obecnie przygotowujemy dla Ciebie nową Prapremierę.";
+"loading" = "Wczytywanie danych";
+"login_title" = "Załóż konto / Zaloguj się w\nWolnych Lekturach, aby:";
+"login_benefits" = "dodawać książki i audiobooki do ulubionych,";
+"login_benefits_2" = "wrócić do czytania rozpoczętej już książki,";
+"login_benefits_3" = "zostać naszym przyjacielem i pomóc nam rozwijać Wolne Lektury.";
+"read_now_library_empty" = "Zacznij czytać już teraz!";
+"delete_files_question" = "Czy na pewno chcesz usunąć wszystkie zapisane ebooki i audiobooki?";
+"yes" = "Tak";
+"no" = "Nie";
+
+//FolioReader
+"Highlights" = "Zaznaczenia";
+"Contents" = "Rozdziały";
+"MMM dd, YYYY | HH:mm" = "MMM dd, YYYY | HH:mm";
+"Highlight" = "Zaznacz";
+"Define" = "Zdefiniuj";
+"Play" = "Odtwarzaj";
+"Pause" = "Zatrzymaj";
+"Night" = "";
+"Style" = "Styl";
+"Day" = "";
+"Horizontal" = "Poziomo";
+"Vertical" = "Pionowo";
+"1 page left" = "1 strona do końca";
+"pages left" = "strony do końca ";
+"minutes" = "minut";
+"1 minute" = "1 minuta";
+"Less than a minute" = "Mniej niż minuta";
+"Check out this chapter from" = "Zobacz ten rozdział od";
+"Notes from" = "Notatki z";
+"All excerpts from" = "Wszystkie fragmenty z";
+"by" = "przez";
+"Cancel" = "Anuluj";
+"Share" = "Udostępnij";
+"Choose existing" = "Wybierz z";
+"Take Photo" = "Zrób zdjęcie";
+"Share image quote" = "Udostępnij zdjęcie fragmentu";
+"Share text quote" = "Udostępnij tekst fragmentu";
diff --git a/iOS/WolneLekturyTests/Info.plist b/iOS/WolneLekturyTests/Info.plist
new file mode 100644 (file)
index 0000000..6c40a6c
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>$(DEVELOPMENT_LANGUAGE)</string>
+       <key>CFBundleExecutable</key>
+       <string>$(EXECUTABLE_NAME)</string>
+       <key>CFBundleIdentifier</key>
+       <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>$(PRODUCT_NAME)</string>
+       <key>CFBundlePackageType</key>
+       <string>BNDL</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleVersion</key>
+       <string>1</string>
+</dict>
+</plist>
diff --git a/iOS/WolneLekturyTests/WolneLekturyTests.swift b/iOS/WolneLekturyTests/WolneLekturyTests.swift
new file mode 100644 (file)
index 0000000..68df28b
--- /dev/null
@@ -0,0 +1,36 @@
+//
+//  WolneLekturyTests.swift
+//  WolneLekturyTests
+//
+//  Created by Pawel Dabrowski on 29/03/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import XCTest
+@testable import WolneLektury
+
+class WolneLekturyTests: 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.
+        // Use XCTAssert and related functions to verify your tests produce the correct results.
+    }
+    
+    func testPerformanceExample() {
+        // This is an example of a performance test case.
+        self.measure {
+            // Put the code you want to measure the time of here.
+        }
+    }
+    
+}
diff --git a/iOS/WolneLekturyUITests/Info.plist b/iOS/WolneLekturyUITests/Info.plist
new file mode 100644 (file)
index 0000000..6c40a6c
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>$(DEVELOPMENT_LANGUAGE)</string>
+       <key>CFBundleExecutable</key>
+       <string>$(EXECUTABLE_NAME)</string>
+       <key>CFBundleIdentifier</key>
+       <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>$(PRODUCT_NAME)</string>
+       <key>CFBundlePackageType</key>
+       <string>BNDL</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleVersion</key>
+       <string>1</string>
+</dict>
+</plist>
diff --git a/iOS/WolneLekturyUITests/WolneLekturyUITests.swift b/iOS/WolneLekturyUITests/WolneLekturyUITests.swift
new file mode 100644 (file)
index 0000000..d01e993
--- /dev/null
@@ -0,0 +1,36 @@
+//
+//  WolneLekturyUITests.swift
+//  WolneLekturyUITests
+//
+//  Created by Pawel Dabrowski on 29/03/2018.
+//  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
+//
+
+import XCTest
+
+class WolneLekturyUITests: XCTestCase {
+        
+    override func setUp() {
+        super.setUp()
+        
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+        
+        // In UI tests it is usually best to stop immediately when a failure occurs.
+        continueAfterFailure = false
+        // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
+        XCUIApplication().launch()
+
+        // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
+    }
+    
+    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() {
+        // Use recording to get started writing UI tests.
+        // Use XCTAssert and related functions to verify your tests produce the correct results.
+    }
+    
+}